pi-directory-context 0.1.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.
@@ -0,0 +1,133 @@
1
+ # 首个 npm 发布说明
2
+
3
+ 本文档描述 `pi-directory-context` 第一次发布到 npm 时的最小步骤。
4
+
5
+ ## 发布前检查
6
+
7
+ 在仓库根目录执行:
8
+
9
+ ```bash
10
+ npm run prepublishOnly
11
+ ```
12
+
13
+ 当前会执行:
14
+ - `npm run check:import`
15
+ - `npm run pack:check`
16
+
17
+ 通过标准:
18
+ - 扩展入口 `extensions/directory-context.ts` 可以被直接导入
19
+ - `npm pack --dry-run` 输出中包含预期发布文件
20
+
21
+ ## 发布前需要你确认的事项
22
+
23
+ ### 1. npm 登录状态
24
+
25
+ 确认本机已经登录 npm:
26
+
27
+ ```bash
28
+ npm whoami
29
+ ```
30
+
31
+ 如果未登录:
32
+
33
+ ```bash
34
+ npm login
35
+ ```
36
+
37
+ ### 2. 包名可用性
38
+
39
+ 确认 `pi-directory-context` 尚未被占用:
40
+
41
+ ```bash
42
+ npm view pi-directory-context name
43
+ ```
44
+
45
+ 如果返回 `E404`,通常表示这个包名还没有被发布。
46
+
47
+ ### 3. 仓库内容
48
+
49
+ 建议在发布前至少确认这些文件已经存在且内容正确:
50
+ - `package.json`
51
+ - `README.md`
52
+ - `CHANGELOG.md`
53
+ - `extensions/directory-context.ts`
54
+ - `src/directory-context/index.ts`
55
+ - `src/directory-context/context.ts`
56
+
57
+ ## 首次发布步骤
58
+
59
+ ### 1. 确认版本号
60
+
61
+ 当前版本是:
62
+
63
+ ```json
64
+ "version": "0.1.0"
65
+ ```
66
+
67
+ 如果准备首次发布 `0.1.0`,保持不变即可。
68
+
69
+ 如果要调整版本,可修改 `package.json` 后再执行发布前检查。
70
+
71
+ ### 2. 本地打包预览
72
+
73
+ ```bash
74
+ npm pack --dry-run
75
+ ```
76
+
77
+ 确认 tarball 中没有意外文件,例如:
78
+ - `.git/`
79
+ - `node_modules/`
80
+ - 本地缓存文件
81
+ - 临时测试文件
82
+
83
+ ### 3. 正式发布
84
+
85
+ ```bash
86
+ npm publish
87
+ ```
88
+
89
+ 如果后续改为 scoped package,通常需要:
90
+
91
+ ```bash
92
+ npm publish --access public
93
+ ```
94
+
95
+ ## 发布后验证
96
+
97
+ ### 1. npm 安装验证
98
+
99
+ 等待 npm 同步完成后,可以执行:
100
+
101
+ ```bash
102
+ pi install npm:pi-directory-context
103
+ ```
104
+
105
+ ### 2. Pi 加载验证
106
+
107
+ 安装后重启 Pi,或执行:
108
+
109
+ ```text
110
+ /reload
111
+ ```
112
+
113
+ 验证点:
114
+ - Pi 能成功加载扩展
115
+ - system prompt 中出现 `<environment_context>` 注入片段
116
+
117
+ ## 常见问题
118
+
119
+ ### 包名已被占用
120
+
121
+ 如果 `pi-directory-context` 已被占用,你需要:
122
+ - 改 `package.json` 里的 `name`
123
+ - 同步更新 `README.md` 中的安装示例
124
+
125
+ ### peer dependency 提示
126
+
127
+ 当前包把 `@earendil-works/pi-coding-agent` 放在 `peerDependencies`,这是符合 Pi package 文档约定的。只要安装环境中有 Pi,一般不需要额外处理。
128
+
129
+ ### 是否需要编译
130
+
131
+ 当前不需要。
132
+
133
+ 这个包采用 TypeScript 直载方案,Pi 可直接加载 `.ts` 扩展入口。只有在你后续需要额外导出普通 Node 库接口、类型产物、或更复杂的构建流程时,才建议引入 `tsc` 或其他构建链。
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.0] - 2026-06-20
6
+
7
+ ### Added
8
+ - 初始化 Git 仓库并推送到 GitHub。
9
+ - 将单文件扩展整理为可安装的 Pi package 骨架。
10
+ - 新增 `extensions/` 入口层与 `src/directory-context/` 实现层。
11
+ - 新增 `package.json`、`.gitignore`、`README.md`。
12
+ - 新增发布前校验脚本:`check:import`、`pack:check`、`prepublishOnly`。
13
+ - 新增首个 npm 发布说明文档。
14
+
15
+ ### Changed
16
+ - 根 `index.ts` 调整为兼容转发入口,实际扩展实现迁移到 `src/directory-context/index.ts`。
package/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # pi-directory-context
2
+
3
+ `pi-directory-context` 是一个 Pi extension package。
4
+
5
+ 它会在 `before_agent_start` 阶段,把当前工作目录相关的环境上下文注入到 system prompt,帮助模型更快理解自己所处的执行环境。
6
+
7
+ ## 当前能力
8
+
9
+ 注入的信息包括:
10
+ - `cwd`
11
+ - `workspaceRoot`(向上查找 `.git` 时可用)
12
+ - `currentDate`
13
+ - `timezone`
14
+ - `shell`
15
+ - `operating_system`
16
+ - `runtime`
17
+
18
+ 注入格式示例:
19
+
20
+ ```xml
21
+ <environment_context>
22
+ <cwd>/path/to/project</cwd>
23
+ <workspace>
24
+ <root>/path/to/project</root>
25
+ </workspace>
26
+ <shell type="zsh">/bin/zsh</shell>
27
+ <current_date>2026-06-20</current_date>
28
+ <timezone>Asia/Shanghai</timezone>
29
+ <operating_system>
30
+ <platform>darwin</platform>
31
+ <arch>arm64</arch>
32
+ <type>Darwin</type>
33
+ <release>24.3.0</release>
34
+ </operating_system>
35
+ <runtime>
36
+ <name>Node.js</name>
37
+ <version>v25.0.0</version>
38
+ </runtime>
39
+ </environment_context>
40
+ ```
41
+
42
+ ## 安装
43
+
44
+ ### 本地路径安装
45
+
46
+ ```bash
47
+ pi install /absolute/path/to/pi-directory-context
48
+ ```
49
+
50
+ 或在当前目录附近:
51
+
52
+ ```bash
53
+ pi install ./pi-directory-context
54
+ ```
55
+
56
+ ### npm 安装
57
+
58
+ 等你正式发布到 npm 后可用:
59
+
60
+ ```bash
61
+ pi install npm:pi-directory-context
62
+ ```
63
+
64
+ ### 临时试用
65
+
66
+ 只在当前运行中加载:
67
+
68
+ ```bash
69
+ pi -e ./pi-directory-context
70
+ ```
71
+
72
+ ## 使用
73
+
74
+ 安装后重新启动 Pi,或执行:
75
+
76
+ ```text
77
+ /reload
78
+ ```
79
+
80
+ 这个包通过 `package.json` 里的 `pi.extensions` 声明扩展入口:
81
+
82
+ - `extensions/directory-context.ts`
83
+
84
+ Pi 会加载该入口,并注册环境上下文注入逻辑。
85
+
86
+ ## 开发
87
+
88
+ 当前采用 TypeScript 直载方案,不引入编译产物,便于快速迭代:
89
+
90
+ ```bash
91
+ npm run check:import
92
+ npm run pack:check
93
+ ```
94
+
95
+ 这两个命令分别用于:
96
+ - 验证扩展入口可以被 Node 的 TypeScript 直载能力导入
97
+ - 预览 npm 打包时会包含哪些文件
98
+
99
+ ## 首次发布
100
+
101
+ 如果这是你第一次把它发布到 npm,建议按下面顺序执行:
102
+
103
+ ```bash
104
+ npm whoami
105
+ npm run prepublishOnly
106
+ npm publish
107
+ ```
108
+
109
+ 如果你后续改用 scoped package,例如 `@your-scope/pi-directory-context`,通常需要:
110
+
111
+ ```bash
112
+ npm publish --access public
113
+ ```
114
+
115
+ 更完整的首次发布步骤见:
116
+ - `.docs/first-npm-release.md`
117
+
118
+ ## 目录结构
119
+
120
+ ```text
121
+ pi-directory-context/
122
+ ├── extensions/
123
+ │ └── directory-context.ts
124
+ ├── src/
125
+ │ └── directory-context/
126
+ │ ├── context.ts
127
+ │ └── index.ts
128
+ ├── index.ts
129
+ ├── package.json
130
+ └── README.md
131
+ ```
132
+
133
+ 说明:
134
+ - `extensions/` 是 Pi 发现和加载的扩展入口层
135
+ - `src/` 是内部实现层
136
+ - 根 `index.ts` 当前保留为兼容入口,便于平滑过渡
137
+
138
+ ## 面向后续扩展库演进
139
+
140
+ 你当前目标是先做成“可安装的单扩展包”,后续再演进为“多扩展库”。
141
+
142
+ 为了减少后续搬迁成本,这个仓库已经按“入口层 + 实现层”拆开。后面如果要增加新的扩展,建议继续按下面模式扩展:
143
+
144
+ ```text
145
+ extensions/
146
+ another-extension.ts
147
+ src/
148
+ another-extension/
149
+ index.ts
150
+ ```
151
+
152
+ 这样未来只需要在 `package.json` 的 `pi.extensions` 中继续追加入口即可。
@@ -0,0 +1 @@
1
+ export { default } from "../src/directory-context/index.ts";
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { default } from "./src/directory-context/index.ts";
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "pi-directory-context",
3
+ "version": "0.1.0",
4
+ "description": "Pi extension package that injects working-directory environment context into the system prompt.",
5
+ "author": "rao",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/MuJianxuan/pi-directory-context.git"
11
+ },
12
+ "homepage": "https://github.com/MuJianxuan/pi-directory-context#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/MuJianxuan/pi-directory-context/issues"
15
+ },
16
+ "keywords": [
17
+ "pi-package",
18
+ "pi",
19
+ "pi-coding-agent",
20
+ "extension",
21
+ "context",
22
+ "directory"
23
+ ],
24
+ "files": [
25
+ "extensions/",
26
+ "src/",
27
+ "index.ts",
28
+ "README.md",
29
+ "CHANGELOG.md",
30
+ ".docs/"
31
+ ],
32
+ "scripts": {
33
+ "check:import": "node --experimental-strip-types --input-type=module -e \"import('./extensions/directory-context.ts')\"",
34
+ "pack:check": "npm pack --dry-run",
35
+ "prepublishOnly": "npm run check:import && npm run pack:check"
36
+ },
37
+ "pi": {
38
+ "extensions": [
39
+ "./extensions/directory-context.ts"
40
+ ]
41
+ },
42
+ "peerDependencies": {
43
+ "@earendil-works/pi-coding-agent": "*"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "@earendil-works/pi-coding-agent": {
47
+ "optional": true
48
+ }
49
+ },
50
+ "devDependencies": {
51
+ "@earendil-works/pi-coding-agent": "0.79.8"
52
+ }
53
+ }
@@ -0,0 +1,143 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+
5
+ export interface EnvironmentContext {
6
+ cwd: string;
7
+ workspaceRoot?: string;
8
+ currentDate: string;
9
+ timezone: string;
10
+ shell: {
11
+ type: string;
12
+ path: string;
13
+ };
14
+ os: {
15
+ platform: string;
16
+ arch: string;
17
+ release: string;
18
+ type: string;
19
+ };
20
+ runtime: {
21
+ name: string;
22
+ version: string;
23
+ };
24
+ }
25
+
26
+ export async function collectEnvironmentContext(
27
+ cwd: string,
28
+ ): Promise<EnvironmentContext> {
29
+ const context: EnvironmentContext = {
30
+ cwd,
31
+ currentDate: new Date().toISOString().split("T")[0],
32
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
33
+ shell: detectShell(),
34
+ os: collectOsInfo(),
35
+ runtime: detectRuntime(),
36
+ };
37
+
38
+ context.workspaceRoot = findWorkspaceRoot(cwd);
39
+
40
+ return context;
41
+ }
42
+
43
+ function detectShell(): { type: string; path: string } {
44
+ const shellPath = process.env.SHELL || "";
45
+ const shellType = path.basename(shellPath) || "unknown";
46
+
47
+ return {
48
+ type: shellType,
49
+ path: shellPath,
50
+ };
51
+ }
52
+
53
+ function collectOsInfo() {
54
+ return {
55
+ platform: os.platform(),
56
+ arch: os.arch(),
57
+ release: os.release(),
58
+ type: os.type(),
59
+ };
60
+ }
61
+
62
+ function detectRuntime(): { name: string; version: string } {
63
+ if (typeof Bun !== "undefined") {
64
+ return {
65
+ name: "Bun",
66
+ version: Bun.version || "unknown",
67
+ };
68
+ }
69
+
70
+ return {
71
+ name: "Node.js",
72
+ version: process.version,
73
+ };
74
+ }
75
+
76
+ function findWorkspaceRoot(startDir: string): string | undefined {
77
+ let currentDir = startDir;
78
+
79
+ while (true) {
80
+ const gitPath = path.join(currentDir, ".git");
81
+
82
+ try {
83
+ if (fs.existsSync(gitPath)) {
84
+ return currentDir;
85
+ }
86
+ } catch {
87
+ // Ignore permission errors and continue scanning upward.
88
+ }
89
+
90
+ const parentDir = path.dirname(currentDir);
91
+ if (parentDir === currentDir) {
92
+ break;
93
+ }
94
+
95
+ currentDir = parentDir;
96
+ }
97
+
98
+ return undefined;
99
+ }
100
+
101
+ export function buildContextXml(context: EnvironmentContext): string {
102
+ const lines: string[] = [];
103
+
104
+ lines.push("<environment_context>");
105
+ lines.push(` <cwd>${escapeXml(context.cwd)}</cwd>`);
106
+
107
+ if (context.workspaceRoot) {
108
+ lines.push(" <workspace>");
109
+ lines.push(` <root>${escapeXml(context.workspaceRoot)}</root>`);
110
+ lines.push(" </workspace>");
111
+ }
112
+
113
+ lines.push(
114
+ ` <shell type="${escapeXml(context.shell.type)}">${escapeXml(context.shell.path)}</shell>`,
115
+ );
116
+ lines.push(` <current_date>${context.currentDate}</current_date>`);
117
+ lines.push(` <timezone>${context.timezone}</timezone>`);
118
+
119
+ lines.push(" <operating_system>");
120
+ lines.push(` <platform>${context.os.platform}</platform>`);
121
+ lines.push(` <arch>${context.os.arch}</arch>`);
122
+ lines.push(` <type>${escapeXml(context.os.type)}</type>`);
123
+ lines.push(` <release>${escapeXml(context.os.release)}</release>`);
124
+ lines.push(" </operating_system>");
125
+
126
+ lines.push(" <runtime>");
127
+ lines.push(` <name>${context.runtime.name}</name>`);
128
+ lines.push(` <version>${context.runtime.version}</version>`);
129
+ lines.push(" </runtime>");
130
+
131
+ lines.push("</environment_context>");
132
+
133
+ return lines.join("\n");
134
+ }
135
+
136
+ function escapeXml(value: string): string {
137
+ return value
138
+ .replace(/&/g, "&amp;")
139
+ .replace(/</g, "&lt;")
140
+ .replace(/>/g, "&gt;")
141
+ .replace(/"/g, "&quot;")
142
+ .replace(/'/g, "&apos;");
143
+ }
@@ -0,0 +1,17 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import {
3
+ buildContextXml,
4
+ collectEnvironmentContext,
5
+ } from "./context.ts";
6
+
7
+ export default function directoryContextExtension(pi: ExtensionAPI) {
8
+ pi.on("before_agent_start", async (event, ctx) => {
9
+ const context = await collectEnvironmentContext(ctx.cwd);
10
+ const contextXml = buildContextXml(context);
11
+ const enhancedPrompt = `${event.systemPrompt}\n\n${contextXml}`;
12
+
13
+ return {
14
+ systemPrompt: enhancedPrompt,
15
+ };
16
+ });
17
+ }