@gotgenes/pi-colgrep 1.1.0 → 1.2.0
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 +20 -0
- package/package.json +1 -1
- package/src/extension.ts +71 -1
- package/src/lib/reindex.ts +111 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0](https://github.com/gotgenes/pi-packages/compare/pi-colgrep-v1.1.0...pi-colgrep-v1.2.0) (2026-05-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add debounced reindex scheduling ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([f12bd7d](https://github.com/gotgenes/pi-packages/commit/f12bd7d2fb47b7dbe7edfbfa65705ebe727280cc))
|
|
9
|
+
* add reindexer shutdown ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([55db463](https://github.com/gotgenes/pi-packages/commit/55db4631386856e1bc791f6c01b3f03251f05c23))
|
|
10
|
+
* add reindexer with immediate execution ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([84356be](https://github.com/gotgenes/pi-packages/commit/84356befd9c146ca518919587a27361965671c5f))
|
|
11
|
+
* clean up reindexer on session shutdown ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([9a87869](https://github.com/gotgenes/pi-packages/commit/9a878697f352bf111ca6c1dc86ce99d6c3916eef))
|
|
12
|
+
* handle reindex errors gracefully ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([2ca14d5](https://github.com/gotgenes/pi-packages/commit/2ca14d544275cf16412435079206a0c9184bb266))
|
|
13
|
+
* queue reindex behind in-flight run ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([bb9ccbe](https://github.com/gotgenes/pi-packages/commit/bb9ccbe1eb56df768e547649a069ac3231835bfe))
|
|
14
|
+
* register /colgrep-reindex manual command ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([c591e80](https://github.com/gotgenes/pi-packages/commit/c591e8016ea9dac9db8d47e9fe51e01febc0c5b6))
|
|
15
|
+
* reindex on session start ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([d18cb8a](https://github.com/gotgenes/pi-packages/commit/d18cb8a1de49c7d92535a1b14d8d02cd6a7f93eb))
|
|
16
|
+
* schedule reindex on write/edit tool results ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([bb17ea0](https://github.com/gotgenes/pi-packages/commit/bb17ea0b6bb532f0d1a64e0e07a914f408482633))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Documentation
|
|
20
|
+
|
|
21
|
+
* plan auto-reindex on session start and file mutations ([#91](https://github.com/gotgenes/pi-packages/issues/91)) ([291d55f](https://github.com/gotgenes/pi-packages/commit/291d55f2a03674c3d84ab2e59d053b72c1b2d9b6))
|
|
22
|
+
|
|
3
23
|
## [1.1.0](https://github.com/gotgenes/pi-packages/compare/pi-colgrep-v1.0.0...pi-colgrep-v1.1.0) (2026-05-23)
|
|
4
24
|
|
|
5
25
|
|
package/package.json
CHANGED
package/src/extension.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { createAvailabilityState } from "./lib/availability.js";
|
|
3
|
+
import { createReindexer, type Reindexer } from "./lib/reindex.js";
|
|
3
4
|
import { registerColGrep } from "./tools/colgrep.js";
|
|
4
5
|
|
|
6
|
+
const COLGREP_STATUS_KEY = "colgrep";
|
|
7
|
+
|
|
8
|
+
function setColGrepStatus(
|
|
9
|
+
ctx: { ui: { setStatus?: (key: string, text: string | undefined) => void } },
|
|
10
|
+
text: string | undefined,
|
|
11
|
+
): void {
|
|
12
|
+
if (typeof ctx.ui.setStatus === "function") {
|
|
13
|
+
ctx.ui.setStatus(COLGREP_STATUS_KEY, text);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
export default function piColGrepExtension(pi: ExtensionAPI): void {
|
|
6
18
|
const availability = createAvailabilityState();
|
|
19
|
+
let reindexer: Reindexer | undefined;
|
|
7
20
|
|
|
8
21
|
registerColGrep(pi, {
|
|
9
22
|
exec: (cmd, args, opts) => pi.exec(cmd, args, opts),
|
|
@@ -11,7 +24,13 @@ export default function piColGrepExtension(pi: ExtensionAPI): void {
|
|
|
11
24
|
});
|
|
12
25
|
|
|
13
26
|
pi.on("session_start", async (_event, ctx) => {
|
|
14
|
-
|
|
27
|
+
const exec = (
|
|
28
|
+
cmd: string,
|
|
29
|
+
args: string[],
|
|
30
|
+
opts?: { cwd?: string; timeout?: number; signal?: AbortSignal },
|
|
31
|
+
) => pi.exec(cmd, args, opts);
|
|
32
|
+
|
|
33
|
+
await availability.refresh(exec);
|
|
15
34
|
|
|
16
35
|
if (!availability.available) {
|
|
17
36
|
ctx.ui.notify(
|
|
@@ -19,6 +38,57 @@ export default function piColGrepExtension(pi: ExtensionAPI): void {
|
|
|
19
38
|
"Install from: https://github.com/lightonai/next-plaid#installation",
|
|
20
39
|
"warning",
|
|
21
40
|
);
|
|
41
|
+
return;
|
|
22
42
|
}
|
|
43
|
+
|
|
44
|
+
reindexer = createReindexer({
|
|
45
|
+
exec,
|
|
46
|
+
cwd: ctx.cwd,
|
|
47
|
+
onStatus: (text) => setColGrepStatus(ctx, text),
|
|
48
|
+
});
|
|
49
|
+
await reindexer.runNow();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
pi.on("tool_result", async (event, _ctx) => {
|
|
53
|
+
if (event.isError) return;
|
|
54
|
+
if (event.toolName !== "write" && event.toolName !== "edit") return;
|
|
55
|
+
reindexer?.schedule();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
pi.on("session_shutdown", async (_event, _ctx) => {
|
|
59
|
+
await reindexer?.shutdown();
|
|
60
|
+
reindexer = undefined;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
pi.registerCommand("colgrep-reindex", {
|
|
64
|
+
description: "Manually refresh the ColGrep semantic search index",
|
|
65
|
+
handler: async (_args, ctx) => {
|
|
66
|
+
if (!availability.available) {
|
|
67
|
+
ctx.ui.notify(
|
|
68
|
+
"colgrep is not installed. Install from: https://github.com/lightonai/next-plaid#installation",
|
|
69
|
+
"warning",
|
|
70
|
+
);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const exec = (
|
|
75
|
+
cmd: string,
|
|
76
|
+
args: string[],
|
|
77
|
+
opts?: { cwd?: string; timeout?: number; signal?: AbortSignal },
|
|
78
|
+
) => pi.exec(cmd, args, opts);
|
|
79
|
+
|
|
80
|
+
// Use the session reindexer if available; otherwise create a one-shot
|
|
81
|
+
// one (e.g., if the command is invoked before session_start has run).
|
|
82
|
+
const indexer =
|
|
83
|
+
reindexer ??
|
|
84
|
+
createReindexer({
|
|
85
|
+
exec,
|
|
86
|
+
cwd: ctx.cwd,
|
|
87
|
+
onStatus: (text) => setColGrepStatus(ctx, text),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await indexer.runNow();
|
|
91
|
+
ctx.ui.notify("ColGrep index updated.", "info");
|
|
92
|
+
},
|
|
23
93
|
});
|
|
24
94
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { Exec } from "./exec.js";
|
|
2
|
+
|
|
3
|
+
export type ReindexStatusCallback = (status: string | undefined) => void;
|
|
4
|
+
|
|
5
|
+
export interface ReindexerDeps {
|
|
6
|
+
exec: Exec;
|
|
7
|
+
cwd: string;
|
|
8
|
+
onStatus: ReindexStatusCallback;
|
|
9
|
+
/** Debounce quiet period before a scheduled reindex fires. Defaults to 4000 ms. */
|
|
10
|
+
debounceMs?: number;
|
|
11
|
+
/** Exec timeout for the reindex command. Defaults to 300 000 ms (5 min). */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Reindexer {
|
|
16
|
+
/** Schedule a debounced reindex. Safe to call repeatedly. */
|
|
17
|
+
schedule(): void;
|
|
18
|
+
/** Run a reindex immediately, bypassing debounce. Resolves when complete. */
|
|
19
|
+
runNow(): Promise<void>;
|
|
20
|
+
/** Cancel pending timers and wait for any in-flight reindex to finish. */
|
|
21
|
+
shutdown(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULT_DEBOUNCE_MS = 4_000;
|
|
25
|
+
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
26
|
+
const INDEXING_STATUS = "colgrep: indexing\u2026";
|
|
27
|
+
const QUEUED_STATUS = "colgrep: indexing\u2026 (queued updates)";
|
|
28
|
+
const INDEXING_FAILED_STATUS = "colgrep: indexing failed";
|
|
29
|
+
|
|
30
|
+
export function createReindexer(deps: ReindexerDeps): Reindexer {
|
|
31
|
+
const { exec, cwd, onStatus } = deps;
|
|
32
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
33
|
+
const debounceMs = deps.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
34
|
+
|
|
35
|
+
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
36
|
+
let inFlight = false;
|
|
37
|
+
let queued = false;
|
|
38
|
+
let isShutdown = false;
|
|
39
|
+
let inflightPromise: Promise<void> | undefined;
|
|
40
|
+
|
|
41
|
+
async function runReindex(): Promise<void> {
|
|
42
|
+
inFlight = true;
|
|
43
|
+
onStatus(INDEXING_STATUS);
|
|
44
|
+
let failed = false;
|
|
45
|
+
try {
|
|
46
|
+
const result = await exec("colgrep", ["init", "-y", "."], {
|
|
47
|
+
cwd,
|
|
48
|
+
timeout: timeoutMs,
|
|
49
|
+
});
|
|
50
|
+
if (result.code !== 0) {
|
|
51
|
+
failed = true;
|
|
52
|
+
const detail = result.stderr.trim();
|
|
53
|
+
console.error(
|
|
54
|
+
`colgrep reindex failed: ${detail || `exit code ${result.code}`}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
failed = true;
|
|
59
|
+
console.error("colgrep reindex failed:", err);
|
|
60
|
+
}
|
|
61
|
+
if (failed) {
|
|
62
|
+
onStatus(INDEXING_FAILED_STATUS);
|
|
63
|
+
}
|
|
64
|
+
onStatus(undefined);
|
|
65
|
+
inFlight = false;
|
|
66
|
+
inflightPromise = undefined;
|
|
67
|
+
|
|
68
|
+
// Drain: if a reindex was queued while we were running, start it now
|
|
69
|
+
// (unless shut down in the meantime).
|
|
70
|
+
if (queued && !isShutdown) {
|
|
71
|
+
queued = false;
|
|
72
|
+
inflightPromise = runReindex();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
async runNow(): Promise<void> {
|
|
78
|
+
await runReindex();
|
|
79
|
+
},
|
|
80
|
+
schedule(): void {
|
|
81
|
+
if (isShutdown) return;
|
|
82
|
+
|
|
83
|
+
// While a reindex is in flight, mark a queued follow-up instead of
|
|
84
|
+
// starting another debounce timer.
|
|
85
|
+
if (inFlight) {
|
|
86
|
+
if (!queued) {
|
|
87
|
+
queued = true;
|
|
88
|
+
onStatus(QUEUED_STATUS);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (debounceTimer !== undefined) {
|
|
93
|
+
clearTimeout(debounceTimer);
|
|
94
|
+
}
|
|
95
|
+
debounceTimer = setTimeout(() => {
|
|
96
|
+
debounceTimer = undefined;
|
|
97
|
+
inflightPromise = runReindex();
|
|
98
|
+
}, debounceMs);
|
|
99
|
+
},
|
|
100
|
+
async shutdown(): Promise<void> {
|
|
101
|
+
isShutdown = true;
|
|
102
|
+
if (debounceTimer !== undefined) {
|
|
103
|
+
clearTimeout(debounceTimer);
|
|
104
|
+
debounceTimer = undefined;
|
|
105
|
+
}
|
|
106
|
+
if (inflightPromise !== undefined) {
|
|
107
|
+
await inflightPromise;
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|