@trim21/personal-pi-extensions 0.1.472 → 0.1.474
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/package.json +2 -2
- package/src/lib/lsp/watcher.ts +39 -3
- package/src/system-prompt/index.ts +10 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trim21/personal-pi-extensions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.474",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
|
|
6
6
|
"keywords": [
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
},
|
|
95
95
|
"packageManager": "pnpm@11.24.0",
|
|
96
96
|
"engines": {
|
|
97
|
-
"node": ">=24"
|
|
97
|
+
"node": ">=24.14"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
100
|
"@cortexkit/aft-bridge": "0.54.0",
|
package/src/lib/lsp/watcher.ts
CHANGED
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
* - 尾部去抖(缺省 300ms)合并短时洪峰,最长 flushMs(缺省 1s)强制清批;
|
|
9
9
|
* - 内置忽略 `node_modules` / `.git` / `dist` / `build` / `.venv` / `venv` /
|
|
10
10
|
* `target` / `coverage`,配置可追加;
|
|
11
|
+
* - 忽略列表同时下传给 `fs.watch` 的 `ignore` 选项做内核层排除(Node >= 24.14 /
|
|
12
|
+
* 26 的 recursive watch 对命中路径不创建 inotify watch,避免大型 `.git` 等
|
|
13
|
+
* 子树耗尽 watch 配额导致 ENOSPC 崩溃);运行环境不支持时静默回退为事件层过滤;
|
|
11
14
|
* - 单批超过 maxBatch(缺省 500)截断并回调 onTruncated 提示一次;
|
|
12
15
|
* - 目录事件默认丢弃;目录被删除时上报(isDirectory: true),由上层对其中
|
|
13
16
|
* 的驻留文档补 deleted 事件。监听器启动或运行期失败时调用 onError 降级,
|
|
14
17
|
* 不抛错。
|
|
15
18
|
*/
|
|
16
19
|
|
|
17
|
-
import { lstat, watch } from "node:fs/promises";
|
|
20
|
+
import { lstat, watch, type WatchOptions as FsWatchOptions } from "node:fs/promises";
|
|
18
21
|
import { join, normalize, relative, sep } from "node:path";
|
|
19
22
|
|
|
20
23
|
import { minimatch } from "minimatch";
|
|
@@ -65,6 +68,34 @@ function isIgnored(path: string, dir: string, patterns: string[]): boolean {
|
|
|
65
68
|
return patterns.some((pattern) => minimatch(candidate, pattern));
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
/**
|
|
72
|
+
* 为内核层 `fs.watch` 的 `ignore` 选项派生 pattern:为每条「目录内容」形态的
|
|
73
|
+
* glob(尾部为通配目录段)追加「目录本身」形态并去重——Node 内部按相对路径
|
|
74
|
+
* 对每个子项逐一匹配,只给内容形态时忽略目录本身仍会建 watch。
|
|
75
|
+
*
|
|
76
|
+
* 与事件层 minimatch 的语义差异(Node 内部 matcher 固定 `matchBase: true`、
|
|
77
|
+
* `nonegate: true`):无斜杠 pattern 按 basename 匹配任意层级,`!` 否定在
|
|
78
|
+
* 内核层不生效。内核层只会少产生事件,差异部分仍由事件层兜底。
|
|
79
|
+
*/
|
|
80
|
+
function kernelIgnorePatterns(patterns: readonly string[]): string[] {
|
|
81
|
+
const derived = new Set<string>();
|
|
82
|
+
for (const pattern of patterns) {
|
|
83
|
+
derived.add(pattern);
|
|
84
|
+
if (pattern.endsWith("/**")) derived.add(pattern.slice(0, -3));
|
|
85
|
+
}
|
|
86
|
+
return [...derived];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* `ignore` 选项的运行时支持始于 Node 24.14 / 26,@types/node 24.x 尚未声明;
|
|
91
|
+
* 同时需剔除 fs 模块 WatchOptions.encoding 中的 "buffer" 字面量,否则不可赋给
|
|
92
|
+
* fs/promises watch 返回 string filename 的重载。
|
|
93
|
+
*/
|
|
94
|
+
type FsWatchOptionsWithIgnore = Omit<FsWatchOptions, "encoding"> & {
|
|
95
|
+
encoding?: BufferEncoding;
|
|
96
|
+
ignore?: readonly string[];
|
|
97
|
+
};
|
|
98
|
+
|
|
68
99
|
/**
|
|
69
100
|
* 启动对 dir 的递归监听。onBatch 收到去抖合并后的批次;stop 后不再回调。
|
|
70
101
|
* 返回的 promise 只在监听器无法建立(目录不存在等)时 reject——运行期
|
|
@@ -78,7 +109,7 @@ export function watchWorkspace(
|
|
|
78
109
|
const debounceMs = options?.debounceMs ?? 300;
|
|
79
110
|
const flushMs = options?.flushMs ?? 1_000;
|
|
80
111
|
const maxBatch = options?.maxBatch ?? 500;
|
|
81
|
-
const ignorePatterns = [...DEFAULT_IGNORE, ...(options?.ignore ?? [])];
|
|
112
|
+
const ignorePatterns = kernelIgnorePatterns([...DEFAULT_IGNORE, ...(options?.ignore ?? [])]);
|
|
82
113
|
const abort = new AbortController();
|
|
83
114
|
|
|
84
115
|
// 从 stat 成功事件学习已知目录,目录被删除时据此标记 isDirectory
|
|
@@ -143,7 +174,12 @@ export function watchWorkspace(
|
|
|
143
174
|
|
|
144
175
|
const consumer = (async () => {
|
|
145
176
|
try {
|
|
146
|
-
const
|
|
177
|
+
const watchOptions: FsWatchOptionsWithIgnore = {
|
|
178
|
+
recursive: true,
|
|
179
|
+
signal: abort.signal,
|
|
180
|
+
ignore: ignorePatterns,
|
|
181
|
+
};
|
|
182
|
+
const iterator = watch(dir, watchOptions);
|
|
147
183
|
for await (const event of iterator) {
|
|
148
184
|
if (!event.filename) continue;
|
|
149
185
|
const change = await classify(
|
|
@@ -61,8 +61,9 @@ export function formatTools(
|
|
|
61
61
|
selectedTools: string[] | undefined,
|
|
62
62
|
toolSnippets: Record<string, string> | undefined,
|
|
63
63
|
): string {
|
|
64
|
+
const names = (selectedTools ?? []).toSorted((a, b) => a.localeCompare(b));
|
|
64
65
|
const lines: string[] = [];
|
|
65
|
-
for (const name of
|
|
66
|
+
for (const name of names) {
|
|
66
67
|
const snippet = toolSnippets?.[name];
|
|
67
68
|
if (snippet) {
|
|
68
69
|
lines.push(`- ${name}: ${snippet}`);
|
|
@@ -73,7 +74,10 @@ export function formatTools(
|
|
|
73
74
|
|
|
74
75
|
/** 渲染工具特定 guideline 块;为空时整个块(含标题)省略。guide 本身是 md 文档,直接拼接。 */
|
|
75
76
|
export function formatGuidelines(promptGuidelines: string[] | undefined): string {
|
|
76
|
-
const items = (promptGuidelines ?? [])
|
|
77
|
+
const items = (promptGuidelines ?? [])
|
|
78
|
+
.map((g) => g.trim())
|
|
79
|
+
.filter((g) => g.length > 0)
|
|
80
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
77
81
|
if (items.length === 0) return "";
|
|
78
82
|
return `## Guidelines\n\n${items.join("\n\n")}`;
|
|
79
83
|
}
|
|
@@ -84,6 +88,7 @@ export function formatContextFiles(
|
|
|
84
88
|
): string {
|
|
85
89
|
if (!contextFiles || contextFiles.length === 0) return "";
|
|
86
90
|
const inner = contextFiles
|
|
91
|
+
.toSorted((a, b) => a.path.localeCompare(b.path))
|
|
87
92
|
.map(
|
|
88
93
|
({ path, content }) =>
|
|
89
94
|
`<project_instructions path="${path}">\n${content}\n</project_instructions>`,
|
|
@@ -97,7 +102,9 @@ export function formatContextFiles(
|
|
|
97
102
|
* 一致;disableModelInvocation 的 skill 不展示;为空时省略。
|
|
98
103
|
*/
|
|
99
104
|
export function formatSkills(skills: SkillLike[] | undefined): string {
|
|
100
|
-
const visible = (skills ?? [])
|
|
105
|
+
const visible = (skills ?? [])
|
|
106
|
+
.filter((skill) => !skill.disableModelInvocation)
|
|
107
|
+
.toSorted((a, b) => a.name.localeCompare(b.name) || a.filePath.localeCompare(b.filePath));
|
|
101
108
|
if (visible.length === 0) return "";
|
|
102
109
|
const lines = [
|
|
103
110
|
"\n\nThe following skills provide specialized instructions for specific tasks.",
|