@xynogen/pix-find 0.1.14 → 0.1.16
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 +2 -2
- package/package.json +3 -3
- package/src/find.test.ts +105 -1
- package/src/find.ts +22 -4
package/README.md
CHANGED
|
@@ -8,13 +8,13 @@ Replaces Pi's default `find` tool with an enhanced version backed by `pix-pretty
|
|
|
8
8
|
|
|
9
9
|
## Auto-collapse
|
|
10
10
|
|
|
11
|
-
After a configurable delay (default 10 seconds)
|
|
11
|
+
After a configurable delay (default 10 seconds), completed output collapses to a row such as `✓ find **/*.ts · 24 files`. Structured failures use a compact `✗` row after the delay. Expanding restores the existing bounded result list or exact diagnostic without restarting the elapsed timer. This is controlled via the `collapse` section of `~/.pi/agent/pix.json`:
|
|
12
12
|
|
|
13
13
|
```jsonc
|
|
14
14
|
{
|
|
15
15
|
"collapse": {
|
|
16
16
|
"enabled": true,
|
|
17
|
-
"
|
|
17
|
+
"delaySec": 10,
|
|
18
18
|
"tools": { "find": true }
|
|
19
19
|
}
|
|
20
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-find",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Pi tool — glob file search",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@xynogen/pix-pretty": "^1.7.
|
|
38
|
-
"@xynogen/pix-data": "^0.3.
|
|
37
|
+
"@xynogen/pix-pretty": "^1.7.22",
|
|
38
|
+
"@xynogen/pix-data": "^0.3.4"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@earendil-works/pi-coding-agent": "*"
|
package/src/find.test.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import type { CursorStore, FffState } from "@xynogen/pix-pretty/fff";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
PiPrettyApi,
|
|
5
|
+
RenderContextLike,
|
|
6
|
+
TextComponentCtor,
|
|
7
|
+
ThemeLike,
|
|
8
|
+
} from "@xynogen/pix-pretty/types";
|
|
4
9
|
import { applyFindDefaults, DEFAULT_FIND_LIMIT, registerFindTool } from "./find";
|
|
5
10
|
|
|
6
11
|
class MockTextComponent {
|
|
@@ -58,4 +63,103 @@ describe("registerFindTool", () => {
|
|
|
58
63
|
);
|
|
59
64
|
expect(tools).toEqual(["find"]);
|
|
60
65
|
});
|
|
66
|
+
|
|
67
|
+
it("restores result paths when an elapsed card is expanded", () => {
|
|
68
|
+
const registered: { renderResult?: (...args: unknown[]) => MockTextComponent } = {};
|
|
69
|
+
const mockPi: PiPrettyApi = {
|
|
70
|
+
registerTool(tool: unknown) {
|
|
71
|
+
Object.assign(registered, tool);
|
|
72
|
+
},
|
|
73
|
+
registerCommand() {},
|
|
74
|
+
on() {},
|
|
75
|
+
};
|
|
76
|
+
registerFindTool(
|
|
77
|
+
mockPi,
|
|
78
|
+
() => ({ execute: async () => ({ content: [], details: undefined }) }),
|
|
79
|
+
{
|
|
80
|
+
cwd: process.cwd(),
|
|
81
|
+
sp: (p: string) => p,
|
|
82
|
+
TextComponent: MockTextComponent as unknown as TextComponentCtor,
|
|
83
|
+
fffState: { module: null, finder: null, partialIndex: false, dbDir: null },
|
|
84
|
+
cursorStore: { store: () => "", get: () => undefined } as unknown as CursorStore,
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
const theme: ThemeLike = {
|
|
88
|
+
fg: (_key: string, value: string) => value,
|
|
89
|
+
bold: (value: string) => value,
|
|
90
|
+
};
|
|
91
|
+
const result = registered.renderResult?.(
|
|
92
|
+
{
|
|
93
|
+
content: [{ type: "text", text: "src/one.ts\nsrc/two.ts" }],
|
|
94
|
+
details: {
|
|
95
|
+
_type: "findResult",
|
|
96
|
+
text: "src/one.ts\nsrc/two.ts",
|
|
97
|
+
pattern: "**/*.ts",
|
|
98
|
+
matchCount: 2,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
undefined,
|
|
102
|
+
theme,
|
|
103
|
+
{
|
|
104
|
+
expanded: true,
|
|
105
|
+
isError: false,
|
|
106
|
+
invalidate: () => {},
|
|
107
|
+
state: { collapsed: true },
|
|
108
|
+
} as unknown as RenderContextLike,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(result?.getText()).toContain("src/one.ts");
|
|
112
|
+
expect(result?.getText()).toContain("src/two.ts");
|
|
113
|
+
expect(result?.getText()).not.toContain("2 files");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("collapses structured errors and restores the exact diagnostic on expansion", () => {
|
|
117
|
+
const registered: { renderResult?: (...args: unknown[]) => MockTextComponent } = {};
|
|
118
|
+
const mockPi: PiPrettyApi = {
|
|
119
|
+
registerTool(tool: unknown) {
|
|
120
|
+
Object.assign(registered, tool);
|
|
121
|
+
},
|
|
122
|
+
registerCommand() {},
|
|
123
|
+
on() {},
|
|
124
|
+
};
|
|
125
|
+
registerFindTool(
|
|
126
|
+
mockPi,
|
|
127
|
+
() => ({ execute: async () => ({ content: [], details: undefined }) }),
|
|
128
|
+
{
|
|
129
|
+
cwd: process.cwd(),
|
|
130
|
+
sp: (p: string) => p,
|
|
131
|
+
TextComponent: MockTextComponent as unknown as TextComponentCtor,
|
|
132
|
+
fffState: { module: null, finder: null, partialIndex: false, dbDir: null },
|
|
133
|
+
cursorStore: { store: () => "", get: () => undefined } as unknown as CursorStore,
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
const theme: ThemeLike = {
|
|
137
|
+
fg: (_key: string, value: string) => value,
|
|
138
|
+
bold: (value: string) => value,
|
|
139
|
+
};
|
|
140
|
+
const diagnostic = "Invalid glob pattern: [";
|
|
141
|
+
const result = {
|
|
142
|
+
content: [{ type: "text", text: diagnostic }],
|
|
143
|
+
details: {
|
|
144
|
+
_type: "findResult",
|
|
145
|
+
text: diagnostic,
|
|
146
|
+
pattern: "[",
|
|
147
|
+
path: "src",
|
|
148
|
+
matchCount: 0,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
const render = (state: Record<string, unknown>, expanded = false) =>
|
|
152
|
+
registered
|
|
153
|
+
.renderResult?.(result, { isPartial: false }, theme, {
|
|
154
|
+
expanded,
|
|
155
|
+
isError: true,
|
|
156
|
+
invalidate: () => {},
|
|
157
|
+
state,
|
|
158
|
+
} as unknown as RenderContextLike)
|
|
159
|
+
?.getText() ?? "";
|
|
160
|
+
|
|
161
|
+
expect(render({ timer: 1 })).toContain(diagnostic);
|
|
162
|
+
expect(render({ collapsed: true })).toContain("✗ find [ in src · failed");
|
|
163
|
+
expect(render({ collapsed: true }, true)).toContain(diagnostic);
|
|
164
|
+
});
|
|
61
165
|
});
|
package/src/find.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ToolRenderResultOptions,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { type CollapseState, tickCollapse } from "@xynogen/pix-data/collapse";
|
|
7
|
+
import { resolveBaseBackground } from "@xynogen/pix-pretty/ansi";
|
|
7
8
|
import type { ToolContext } from "@xynogen/pix-pretty/context";
|
|
8
9
|
import type {
|
|
9
10
|
FindParams,
|
|
@@ -108,6 +109,7 @@ export function registerFindTool(
|
|
|
108
109
|
},
|
|
109
110
|
|
|
110
111
|
renderCall(args: FindParams, theme: ThemeLike, renderCtx: RenderContextLike) {
|
|
112
|
+
resolveBaseBackground(theme);
|
|
111
113
|
const pattern = args.pattern ?? "";
|
|
112
114
|
const path = args.path ? ` ${theme.fg("muted", `in ${sp(args.path)}`)}` : "";
|
|
113
115
|
const text = renderCtx.lastComponent ?? new TextComponent("", 0, 0);
|
|
@@ -131,22 +133,38 @@ export function registerFindTool(
|
|
|
131
133
|
theme: ThemeLike,
|
|
132
134
|
renderCtx: RenderContextLike,
|
|
133
135
|
) {
|
|
136
|
+
resolveBaseBackground(theme);
|
|
134
137
|
const text = renderCtx.lastComponent ?? new TextComponent("", 0, 0);
|
|
138
|
+
const d = result.details;
|
|
139
|
+
const isPartial = _opt?.isPartial === true;
|
|
140
|
+
const structuredError = renderCtx.isError && d?._type === "findResult";
|
|
135
141
|
|
|
136
|
-
if (renderCtx.isError) {
|
|
142
|
+
if (renderCtx.isError && (!structuredError || isPartial)) {
|
|
137
143
|
text.setText(renderToolError(getTextContent(result) || "Error", theme));
|
|
138
144
|
return text;
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
// Auto-collapse: show summary line after delay
|
|
142
148
|
const cs = renderCtx.state as CollapseState;
|
|
143
|
-
if (tickCollapse("find", cs, renderCtx.invalidate)) {
|
|
144
|
-
const d = result.details;
|
|
149
|
+
if (!isPartial && tickCollapse("find", cs, renderCtx.invalidate, renderCtx.expanded)) {
|
|
145
150
|
const summary =
|
|
146
151
|
d?._type === "findResult" && d.matchCount != null ? `${d.matchCount} files` : "found";
|
|
147
152
|
const target = d?._type === "findResult" ? d.pattern : "";
|
|
148
153
|
const scope = d?._type === "findResult" && d.path ? ` in ${sp(d.path)}` : "";
|
|
149
|
-
text.setText(
|
|
154
|
+
text.setText(
|
|
155
|
+
renderCollapsedToolRow(
|
|
156
|
+
theme,
|
|
157
|
+
"find",
|
|
158
|
+
`${target}${scope}`,
|
|
159
|
+
renderCtx.isError ? "failed" : summary,
|
|
160
|
+
renderCtx.isError ? "error" : "success",
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
return text;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (renderCtx.isError) {
|
|
167
|
+
text.setText(renderToolError(getTextContent(result) || "Error", theme));
|
|
150
168
|
return text;
|
|
151
169
|
}
|
|
152
170
|
|