1st-pi 0.3.3 → 0.3.4
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
CHANGED
|
@@ -8,7 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
## 安装
|
|
10
10
|
|
|
11
|
-
需要 Pi `>=1.1.0`;`@tintinweb/pi-subagents` 0.20 起要求该版本,`pi-btw` 0.7 起要求 `>=0.85.1`。
|
|
11
|
+
需要 Pi `>=1.1.0`;`@tintinweb/pi-subagents` 0.20 起要求该版本,`pi-btw` 0.7 起要求 `>=0.85.1`。`pi-web-access` 0.44 要求 Node `>=22.19.0`。
|
|
12
|
+
|
|
13
|
+
macOS 上 `pi-readseek` 的原生二进制动态链接 Homebrew 库,缺少任意一个时 `read`/`edit`/`write` 会因 dyld 报错失败(0.11 起新增 `ftxui`,0.12.1 起新增 `re2`):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
brew install abseil fmt ftxui gettext glib openssl@4 re2 vips xxhash
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
preset 的 readseek adapter 会在启动时读取该二进制的 Mach-O load commands,若有 dylib 缺失,就在 session 开始时给出 warning 和对应的 `brew install` 命令(会遵循 `READSEEK_BINARY` 覆盖)。
|
|
12
20
|
|
|
13
21
|
### GitHub
|
|
14
22
|
|
|
@@ -131,7 +139,7 @@ Preset 默认开启以下 TUI 增强,session 中持久化的个人选择优先
|
|
|
131
139
|
| [@juicesharp/rpiv-ask-user-question](https://www.npmjs.com/package/@juicesharp/rpiv-ask-user-question) | 多问题、选项预览和自由输入的结构化提问 UI |
|
|
132
140
|
| [@narumitw/pi-goal](https://www.npmjs.com/package/@narumitw/pi-goal) | Session-scoped `/goal` 持续执行与完成/阻塞门禁;可选 experimental ordered-goal queue |
|
|
133
141
|
| [@ff-labs/pi-fff](https://www.npmjs.com/package/@ff-labs/pi-fff) | 本地 Rust 模糊文件/内容搜索与 `@` 自动补全;`find`/`grep` 支持 workspace-relative 约束和 workspace 外的绝对路径 |
|
|
134
|
-
| [pi-readseek](https://www.npmjs.com/package/pi-readseek) | Parser-backed code map/search、PDF index/view、definition/reference、hashline edit 与 workspace rename
|
|
142
|
+
| [pi-readseek](https://www.npmjs.com/package/pi-readseek) | Parser-backed code map/search、PDF index/view、definition/reference、syntax check、hashline edit 与 workspace rename;caption/OCR/object detection 只在显式调用对应工具时使用本地已安装模型,不会自动下载;其 edit 可进行带警告的 fuzzy anchor relocation,不等同于严格 stale rejection |
|
|
135
143
|
| [pi-btw](https://www.npmjs.com/package/pi-btw) | 不污染主上下文的并行 `/btw` 旁路对话 |
|
|
136
144
|
| [@mrclrchtr/supi-context](https://www.npmjs.com/package/@mrclrchtr/supi-context) | `/supi-context` 上下文组成与 token 占用报告 |
|
|
137
145
|
| [@tintinweb/pi-subagents](https://www.npmjs.com/package/@tintinweb/pi-subagents) | Claude Code 风格 sub-agents |
|
|
@@ -140,7 +148,7 @@ Preset 默认开启以下 TUI 增强,session 中持久化的个人选择优先
|
|
|
140
148
|
|
|
141
149
|
这些包位于普通 npm `dependencies`,安装 preset 时由 npm 获取,不会塞入 `1st-pi` tarball。`extensions/packages/*.ts` 是只负责调用 dependency extension entrypoint 的 thin adapters;因此依赖可以正常 hoist,同时 Pi manifest 只引用本包内稳定路径。FFF 也会由 npm 在 macOS、Linux 和 Windows 上选择正确的原生 binary。
|
|
142
150
|
|
|
143
|
-
当前 dependency extension 版本以 `package.json` 与 lockfile 为准;维护时使用 npm `latest`
|
|
151
|
+
当前 dependency extension 版本以 `package.json` 与 lockfile 为准;维护时使用 npm `latest` 稳定标签,并运行 `npm run check:outdated` 确认没有落后版本。该检查会指出 `package.json` 范围挡住最新版的依赖(例如 `^0.20.0` 不包含 0.21.0,`npm install` 不会提示),以及 lockfile 落后于范围的依赖。
|
|
144
152
|
|
|
145
153
|
Themes 以及 `pi-init` skill 是发布所需的静态资源,会从锁定依赖同步到本包。`pi-web-access` 从 0.14.0 起不再提供 `librarian` skill,preset 同步移除该过期副本,保留 extension 自身的搜索、抓取与 source-check surface。更新 dependencies 后运行:
|
|
146
154
|
|
|
@@ -190,6 +198,14 @@ Tool Call Markers 默认分组相邻调用并折叠工具详情;可用 `PI_TOO
|
|
|
190
198
|
|
|
191
199
|
该 SOP 覆盖 license/compatibility/ownership gate、thin adapter、README 同步、hoisted npm 安装、native binary、tarball size 与 clean-room smoke tests。
|
|
192
200
|
|
|
201
|
+
`npm run smoke` 在隔离的 `PI_CODING_AGENT_DIR` 中通过 Pi SDK 加载 preset,不访问网络、不调用模型,检查:
|
|
202
|
+
|
|
203
|
+
- 扩展加载无错误且与 `package.json#pi.extensions` 一致;工具无重名 owner;`read/edit/write`、`grep/find`、`bash/ls` 的 owner 符合上文
|
|
204
|
+
- 全新安装时写入 preset 默认值,已有的显式 `subagents.json` / workflow 设置保持不变
|
|
205
|
+
- `pi-tasks` 与 `pi-subagents` 的 RPC 协议版本匹配
|
|
206
|
+
- readseek 在临时文件上执行 read → anchored edit → write,以及 macOS dylib 检测与 `otool -L` 一致
|
|
207
|
+
- 工具与命令集合与 `scripts/smoke-surface.json` 快照一致;有意变更时用 `npm run smoke -- --update` 更新快照,并在 diff 中审阅
|
|
208
|
+
|
|
193
209
|
## 发布
|
|
194
210
|
|
|
195
211
|
```bash
|
|
@@ -197,9 +213,11 @@ npm login
|
|
|
197
213
|
npm publish
|
|
198
214
|
```
|
|
199
215
|
|
|
200
|
-
|
|
216
|
+
发布前建议运行检查并确认包内容:
|
|
201
217
|
|
|
202
218
|
```bash
|
|
219
|
+
npm run check:outdated
|
|
220
|
+
npm run smoke
|
|
203
221
|
npm pack --dry-run
|
|
204
222
|
```
|
|
205
223
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { closeSync, existsSync, openSync, readSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const MH_MAGIC_64 = 0xfeedfacf;
|
|
6
|
+
const FAT_MAGIC = 0xcafebabe;
|
|
7
|
+
const CPU_TYPE_BY_ARCH: Record<string, number> = { arm64: 0x0100000c, x64: 0x01000007 };
|
|
8
|
+
const LC_LOAD_DYLIB = 0x0c;
|
|
9
|
+
const LC_LOAD_WEAK_DYLIB = 0x80000018;
|
|
10
|
+
const LC_REEXPORT_DYLIB = 0x8000001f;
|
|
11
|
+
const LC_LOAD_UPWARD_DYLIB = 0x80000023;
|
|
12
|
+
const DYLIB_COMMANDS = new Set([LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB]);
|
|
13
|
+
const BREW_FORMULA = /^(?:\/opt\/homebrew|\/usr\/local)\/opt\/([^/]+)\//;
|
|
14
|
+
|
|
15
|
+
function readAt(fd: number, position: number, length: number): Buffer {
|
|
16
|
+
const buffer = Buffer.alloc(length);
|
|
17
|
+
return buffer.subarray(0, readSync(fd, buffer, 0, length, position));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reads only the 64-bit Mach-O header and load commands for `arch`, selecting
|
|
22
|
+
* the matching slice of a universal binary. Returns undefined for other formats.
|
|
23
|
+
*/
|
|
24
|
+
export function readMachOLoadCommands(path: string, arch: string = process.arch): Buffer | undefined {
|
|
25
|
+
const fd = openSync(path, "r");
|
|
26
|
+
try {
|
|
27
|
+
let offset = 0;
|
|
28
|
+
const head = readAt(fd, 0, 4096);
|
|
29
|
+
if (head.length >= 8 && head.readUInt32BE(0) === FAT_MAGIC) {
|
|
30
|
+
const count = head.readUInt32BE(4);
|
|
31
|
+
const slice = Array.from({ length: count }, (_, i) => 8 + i * 20)
|
|
32
|
+
.find((at) => at + 20 <= head.length && head.readUInt32BE(at) === CPU_TYPE_BY_ARCH[arch]);
|
|
33
|
+
if (slice === undefined) return undefined;
|
|
34
|
+
offset = head.readUInt32BE(slice + 8);
|
|
35
|
+
}
|
|
36
|
+
const header = readAt(fd, offset, 32);
|
|
37
|
+
if (header.length < 32 || header.readUInt32LE(0) !== MH_MAGIC_64) return undefined;
|
|
38
|
+
return readAt(fd, offset, 32 + header.readUInt32LE(20));
|
|
39
|
+
} finally {
|
|
40
|
+
closeSync(fd);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Returns the dylib install names linked by a thin 64-bit Mach-O header plus its load commands. */
|
|
45
|
+
export function machODylibs(binary: Buffer): string[] {
|
|
46
|
+
if (binary.length < 32 || binary.readUInt32LE(0) !== MH_MAGIC_64) return [];
|
|
47
|
+
|
|
48
|
+
const commandCount = binary.readUInt32LE(16);
|
|
49
|
+
const dylibs: string[] = [];
|
|
50
|
+
let at = 32;
|
|
51
|
+
for (let i = 0; i < commandCount && at + 8 <= binary.length; i++) {
|
|
52
|
+
const cmd = binary.readUInt32LE(at);
|
|
53
|
+
const size = binary.readUInt32LE(at + 4);
|
|
54
|
+
if (size < 8) break;
|
|
55
|
+
if (DYLIB_COMMANDS.has(cmd)) {
|
|
56
|
+
const nameStart = at + binary.readUInt32LE(at + 8);
|
|
57
|
+
const nameEnd = binary.indexOf(0, nameStart);
|
|
58
|
+
dylibs.push(binary.toString("utf8", nameStart, nameEnd < 0 || nameEnd > at + size ? at + size : nameEnd));
|
|
59
|
+
}
|
|
60
|
+
at += size;
|
|
61
|
+
}
|
|
62
|
+
return dylibs;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Dylibs that dyld would fail to load. System libraries live in the dyld shared
|
|
67
|
+
* cache rather than on disk, and `@rpath`-style names are relative, so only
|
|
68
|
+
* other absolute paths can be checked.
|
|
69
|
+
*/
|
|
70
|
+
export function missingDylibs(dylibs: string[], exists: (path: string) => boolean = existsSync): string[] {
|
|
71
|
+
return dylibs.filter(
|
|
72
|
+
(dylib) => dylib.startsWith("/") && !dylib.startsWith("/usr/lib/") && !dylib.startsWith("/System/") && !exists(dylib),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function missingDylibsMessage(missing: string[]): string {
|
|
77
|
+
const formulas = [...new Set(missing.map((dylib) => BREW_FORMULA.exec(dylib)?.[1]).filter((f): f is string => !!f))];
|
|
78
|
+
const unknown = missing.filter((dylib) => !BREW_FORMULA.test(dylib));
|
|
79
|
+
const lines = ["pi-readseek's native binary is missing shared libraries, so read/edit/write will fail until they are installed."];
|
|
80
|
+
if (formulas.length > 0) lines.push(`Run: brew install ${formulas.join(" ")}`);
|
|
81
|
+
if (unknown.length > 0) lines.push(`Missing: ${unknown.join(", ")}`);
|
|
82
|
+
return lines.join("\n");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Mirrors pi-readseek's own lookup: READSEEK_BINARY, then the platform package beside @jarkkojs/readseek. */
|
|
86
|
+
export function resolveReadseekBinary(): string | undefined {
|
|
87
|
+
const override = process.env.READSEEK_BINARY;
|
|
88
|
+
if (override) return override;
|
|
89
|
+
if (process.platform !== "darwin") return undefined;
|
|
90
|
+
try {
|
|
91
|
+
const require = createRequire(createRequire(import.meta.url).resolve("pi-readseek"));
|
|
92
|
+
const metaDir = dirname(require.resolve("@jarkkojs/readseek/package.json"));
|
|
93
|
+
const platformJson = require.resolve(`@jarkkojs/readseek-darwin-${process.arch}/package.json`, { paths: [metaDir] });
|
|
94
|
+
return join(dirname(platformJson), "bin", "readseek");
|
|
95
|
+
} catch {
|
|
96
|
+
// pi-readseek reports a missing platform package itself.
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Returns a warning when the readseek binary cannot load on this machine, otherwise undefined. */
|
|
102
|
+
export function readseekDylibWarning(): string | undefined {
|
|
103
|
+
if (process.platform !== "darwin") return undefined;
|
|
104
|
+
const binary = resolveReadseekBinary();
|
|
105
|
+
if (!binary || !existsSync(binary)) return undefined;
|
|
106
|
+
const commands = readMachOLoadCommands(binary);
|
|
107
|
+
const missing = commands ? missingDylibs(machODylibs(commands)) : [];
|
|
108
|
+
return missing.length > 0 ? missingDylibsMessage(missing) : undefined;
|
|
109
|
+
}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import registerReadseek from "pi-readseek";
|
|
3
|
+
import { readseekDylibWarning } from "./readseek-dylibs.ts";
|
|
3
4
|
import { registerWithToolTransform } from "./tool-registration-transform.ts";
|
|
4
5
|
|
|
5
6
|
const READSEEK_GREP_PREFERENCE = "Prefer readSeek_grep over grep when both are available;";
|
|
6
7
|
|
|
7
8
|
export default function readseekAdapter(pi: ExtensionAPI): void {
|
|
9
|
+
// The macOS binary links Homebrew dylibs it does not ship; surface the exact
|
|
10
|
+
// brew command up front instead of a dyld crash on the first read.
|
|
11
|
+
const dylibWarning = readseekDylibWarning();
|
|
12
|
+
if (dylibWarning) {
|
|
13
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
14
|
+
if (ctx.hasUI) ctx.ui.notify(dylibWarning, "warning");
|
|
15
|
+
else console.warn(dylibWarning);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
registerWithToolTransform(pi, registerReadseek, (tool) => {
|
|
9
20
|
if (tool.name !== "readSeek_grep") return tool;
|
|
10
21
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "1st-pi",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "est7's Pi coding agent preset — skills, extensions, prompts, and themes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "est7",
|
|
@@ -33,23 +33,25 @@
|
|
|
33
33
|
"homepage": "https://github.com/est7/1st-pi#readme",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@ff-labs/pi-fff": "^0.11.0",
|
|
36
|
-
"@juicesharp/rpiv-ask-user-question": "^2.
|
|
37
|
-
"@mrclrchtr/supi-context": "^
|
|
38
|
-
"@narumitw/pi-goal": "^0.54.
|
|
36
|
+
"@juicesharp/rpiv-ask-user-question": "^2.15.0",
|
|
37
|
+
"@mrclrchtr/supi-context": "^11.0.1",
|
|
38
|
+
"@narumitw/pi-goal": "^0.54.13",
|
|
39
39
|
"@pi-kaush/pi-tool-call-markers": "^0.3.19",
|
|
40
40
|
"@quintinshaw/pi-dynamic-workflows": "^3.14.0",
|
|
41
|
-
"@tintinweb/pi-subagents": "^0.
|
|
42
|
-
"@tintinweb/pi-tasks": "^0.9.
|
|
43
|
-
"pi-btw": "^0.
|
|
41
|
+
"@tintinweb/pi-subagents": "^0.22.0",
|
|
42
|
+
"@tintinweb/pi-tasks": "^0.9.1",
|
|
43
|
+
"pi-btw": "^0.8.0",
|
|
44
44
|
"pi-everforest-tui": "^0.2.1",
|
|
45
45
|
"pi-init": "^1.0.0",
|
|
46
|
-
"pi-readseek": "^0.
|
|
46
|
+
"pi-readseek": "^0.12.3",
|
|
47
47
|
"pi-session-name": "^0.1.7",
|
|
48
|
-
"pi-web-access": "^0.
|
|
48
|
+
"pi-web-access": "^0.44.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"sync:resources": "node scripts/sync-package-resources.mjs",
|
|
52
52
|
"check:resources": "node scripts/sync-package-resources.mjs --check",
|
|
53
|
+
"check:outdated": "node scripts/check-outdated.mjs",
|
|
54
|
+
"smoke": "node scripts/smoke.mjs",
|
|
53
55
|
"prepack": "npm run check:resources",
|
|
54
56
|
"pack:dry-run": "npm pack --dry-run --json"
|
|
55
57
|
},
|
|
@@ -67,11 +67,15 @@ Run:
|
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
npm run check:resources
|
|
70
|
+
npm run check:outdated
|
|
71
|
+
npm run smoke
|
|
70
72
|
npm audit --omit=dev
|
|
71
73
|
npm pack --dry-run --json
|
|
72
74
|
git diff --check
|
|
73
75
|
```
|
|
74
76
|
|
|
77
|
+
`npm run smoke` fails when the tool or command surface differs from `scripts/smoke-surface.json`. Review the reported additions and removals against the intended change, then run `npm run smoke -- --update` and commit the snapshot with the dependency change. When adding a preset default or ownership rule, extend `scripts/smoke.mjs` with the fresh-install and explicit-preference cases.
|
|
78
|
+
|
|
75
79
|
Then verify:
|
|
76
80
|
|
|
77
81
|
1. local source: `pi --no-extensions -e .`
|