oh-pi 0.1.65 → 0.1.67

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/dist/index.js CHANGED
@@ -70,7 +70,7 @@ async function customFlow(env) {
70
70
  const keybindings = await selectKeybindings();
71
71
  const extensions = await selectExtensions();
72
72
  const agents = await selectAgents();
73
- // Advanced: auto-compaction threshold
73
+ // Advanced: auto-compaction is now automatic based on model context window
74
74
  const wantAdvanced = await p.confirm({
75
75
  message: t("advanced.configure"),
76
76
  initialValue: false,
@@ -79,25 +79,6 @@ async function customFlow(env) {
79
79
  p.cancel(t("cancelled"));
80
80
  process.exit(0);
81
81
  }
82
- let compactThreshold = 0.80;
83
- if (wantAdvanced) {
84
- const threshold = await p.text({
85
- message: t("advanced.compactThreshold"),
86
- placeholder: "75",
87
- initialValue: "75",
88
- validate: (v) => {
89
- const n = Number(v);
90
- if (isNaN(n) || n < 10 || n > 100)
91
- return t("advanced.compactValidation");
92
- return undefined;
93
- },
94
- });
95
- if (p.isCancel(threshold)) {
96
- p.cancel(t("cancelled"));
97
- process.exit(0);
98
- }
99
- compactThreshold = Number(threshold) / 100;
100
- }
101
82
  return {
102
83
  providers,
103
84
  theme,
@@ -106,6 +87,5 @@ async function customFlow(env) {
106
87
  prompts: ["review", "fix", "explain", "commit", "test", "refactor", "optimize", "security", "document", "pr"],
107
88
  agents,
108
89
  thinking: "medium",
109
- compactThreshold,
110
90
  };
111
91
  }
@@ -24,7 +24,7 @@ export async function confirmApply(config, env) {
24
24
  `${t("confirm.theme")} ${chalk.cyan(config.theme)}`,
25
25
  `${t("confirm.keybindings")}${chalk.cyan(config.keybindings)}`,
26
26
  `${t("confirm.thinking")} ${chalk.cyan(config.thinking)}`,
27
- `${t("confirm.compaction")} ${chalk.cyan(t("confirm.compactionValue", { pct: Math.round((config.compactThreshold ?? 0.75) * 100) }))}`,
27
+ `${t("confirm.compaction")} ${chalk.cyan("auto")}`,
28
28
  `${t("confirm.extensions")} ${chalk.cyan(config.extensions.join(", ") || t("confirm.none"))}`,
29
29
  `${t("confirm.prompts")} ${chalk.cyan(t("confirm.promptsValue", { count: config.prompts.length }))}`,
30
30
  `${t("confirm.agents")} ${chalk.cyan(config.agents)}`,
@@ -81,17 +81,22 @@ function detectProviders(agentDir) {
81
81
  */
82
82
  export async function detectEnv() {
83
83
  const agentDir = join(homedir(), ".pi", "agent");
84
- let piVersion = null;
85
- let piInstalled = false;
86
- try {
87
- piVersion = execSync("pi --version", { encoding: "utf8", timeout: 5000 }).trim();
88
- piInstalled = true;
89
- }
90
- catch { /* not installed */ }
91
- const existingFiles = scanDir(agentDir);
84
+ // 并行检测 pi 版本和扫描配置
85
+ const [versionResult, existingFiles] = await Promise.all([
86
+ new Promise((resolve) => {
87
+ try {
88
+ const v = execSync("pi --version", { encoding: "utf8", timeout: 3000 }).trim();
89
+ resolve({ installed: true, version: v });
90
+ }
91
+ catch {
92
+ resolve({ installed: false, version: null });
93
+ }
94
+ }),
95
+ Promise.resolve(scanDir(agentDir)),
96
+ ]);
92
97
  return {
93
- piInstalled,
94
- piVersion,
98
+ piInstalled: versionResult.installed,
99
+ piVersion: versionResult.version,
95
100
  hasExistingConfig: existsSync(join(agentDir, "settings.json")),
96
101
  agentDir,
97
102
  terminal: process.env.TERM_PROGRAM ?? process.env.TERM ?? "unknown",
@@ -12,13 +12,40 @@ function ensureDir(dir) {
12
12
  mkdirSync(dir, { recursive: true });
13
13
  }
14
14
  /**
15
- * 清空并重建目录,若目录已存在则先删除再重新创建
16
- * @param dir - 目标目录路径
15
+ * 增量同步目录:只复制有变化的文件,删除源中不存在的文件
16
+ * @param src - 源目录路径
17
+ * @param dest - 目标目录路径
17
18
  */
18
- function cleanDir(dir) {
19
- if (existsSync(dir))
20
- rmSync(dir, { recursive: true });
21
- ensureDir(dir);
19
+ function syncDir(src, dest) {
20
+ ensureDir(dest);
21
+ const srcEntries = new Set();
22
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
23
+ srcEntries.add(entry.name);
24
+ const srcPath = join(src, entry.name);
25
+ const destPath = join(dest, entry.name);
26
+ if (entry.isDirectory()) {
27
+ syncDir(srcPath, destPath);
28
+ }
29
+ else {
30
+ // 只在文件大小不同时复制
31
+ try {
32
+ if (existsSync(destPath) && statSync(destPath).size === statSync(srcPath).size)
33
+ continue;
34
+ }
35
+ catch { /* copy anyway */ }
36
+ copyFileSync(srcPath, destPath);
37
+ }
38
+ }
39
+ // 删除目标中源不存在的文件
40
+ try {
41
+ for (const entry of readdirSync(dest, { withFileTypes: true })) {
42
+ if (!srcEntries.has(entry.name)) {
43
+ const p = join(dest, entry.name);
44
+ rmSync(p, { recursive: true });
45
+ }
46
+ }
47
+ }
48
+ catch { /* skip */ }
22
49
  }
23
50
  /**
24
51
  * 递归复制目录及其所有内容到目标路径
@@ -60,14 +87,19 @@ export function applyConfig(config) {
60
87
  const primary = config.providers.find(p => p.baseUrl && p.defaultModel) ?? config.providers[0];
61
88
  const providerInfo = primary ? PROVIDERS[primary.name] : undefined;
62
89
  const compactThreshold = config.compactThreshold ?? 0.75;
63
- const reserveTokens = 32000;
90
+ // 根据主模型的 contextWindow 自动计算压缩参数
91
+ const primaryModelId = primary?.defaultModel ?? providerInfo?.models[0];
92
+ const caps = primaryModelId ? MODEL_CAPABILITIES[primaryModelId] : undefined;
93
+ const ctxWindow = caps?.contextWindow ?? primary?.contextWindow ?? 128000;
94
+ const reserveTokens = Math.max(16384, Math.round(ctxWindow * 0.15));
95
+ const keepRecentTokens = Math.max(16384, Math.round(ctxWindow * 0.15));
64
96
  const primaryModel = primary?.defaultModel ?? providerInfo?.models[0];
65
97
  const settings = {
66
98
  ...(primary ? { defaultProvider: primary.name, defaultModel: primaryModel } : {}),
67
99
  defaultThinkingLevel: config.thinking,
68
100
  theme: config.theme,
69
101
  enableSkillCommands: true,
70
- compaction: { enabled: true, reserveTokens, keepRecentTokens: 20000 },
102
+ compaction: { enabled: true, reserveTokens, keepRecentTokens },
71
103
  retry: { enabled: true, maxRetries: 3 },
72
104
  quietStartup: true,
73
105
  };
@@ -149,12 +181,12 @@ export function applyConfig(config) {
149
181
  catch { /* template not found, skip */ }
150
182
  // 6. Copy extensions (single file .ts or directory with index.ts)
151
183
  const extDir = join(agentDir, "extensions");
152
- cleanDir(extDir);
184
+ ensureDir(extDir);
153
185
  for (const ext of config.extensions) {
154
186
  const dirSrc = resources.extension(ext);
155
187
  const fileSrc = resources.extensionFile(ext);
156
188
  if (existsSync(dirSrc) && statSync(dirSrc).isDirectory()) {
157
- copyDir(dirSrc, join(extDir, ext));
189
+ syncDir(dirSrc, join(extDir, ext));
158
190
  }
159
191
  else {
160
192
  try {
@@ -165,7 +197,7 @@ export function applyConfig(config) {
165
197
  }
166
198
  // 7. Copy prompts
167
199
  const promptDir = join(agentDir, "prompts");
168
- cleanDir(promptDir);
200
+ ensureDir(promptDir);
169
201
  for (const p of config.prompts) {
170
202
  const src = resources.prompt(p);
171
203
  try {
@@ -175,19 +207,15 @@ export function applyConfig(config) {
175
207
  }
176
208
  // 8. Copy skills (auto-discover all from pi-package/skills/)
177
209
  const skillDir = join(agentDir, "skills");
178
- cleanDir(skillDir);
179
210
  const skillsSrcDir = resources.skillsDir();
180
211
  try {
181
- for (const entry of readdirSync(skillsSrcDir, { withFileTypes: true })) {
182
- if (entry.isDirectory() && existsSync(join(skillsSrcDir, entry.name, "SKILL.md"))) {
183
- copyDir(join(skillsSrcDir, entry.name), join(skillDir, entry.name));
184
- }
185
- }
212
+ if (existsSync(skillsSrcDir))
213
+ syncDir(skillsSrcDir, skillDir);
186
214
  }
187
215
  catch { /* skills dir not found, skip */ }
188
216
  // 9. Copy themes (only custom ones)
189
217
  const themeDir = join(agentDir, "themes");
190
- cleanDir(themeDir);
218
+ ensureDir(themeDir);
191
219
  const themeSrc = resources.theme(config.theme);
192
220
  try {
193
221
  copyFileSync(themeSrc, join(themeDir, `${config.theme}.json`));
@@ -199,7 +227,7 @@ export function applyConfig(config) {
199
227
  */
200
228
  export function installPi() {
201
229
  try {
202
- execSync("npm install -g @mariozechner/pi-coding-agent", { stdio: "inherit" });
230
+ execSync("npm install -g @mariozechner/pi-coding-agent", { stdio: "pipe", timeout: 120000 });
203
231
  }
204
232
  catch {
205
233
  throw new Error("Failed to install pi-coding-agent");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-pi",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "description": "One-click setup for pi-coding-agent. Like oh-my-zsh for pi.",
5
5
  "type": "module",
6
6
  "bin": {