oh-pi 0.1.7 β 0.1.8
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 +3 -2
- package/dist/tui/preset-select.js +5 -5
- package/dist/types.d.ts +1 -0
- package/dist/types.js +1 -1
- package/dist/utils/install.js +41 -20
- package/package.json +1 -1
- package/pi-package/extensions/startup-banner.ts +35 -12
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
|
-
import { selectLanguage } from "./i18n.js";
|
|
2
|
+
import { selectLanguage, getLocale } from "./i18n.js";
|
|
3
3
|
import { t } from "./i18n.js";
|
|
4
4
|
import { welcome } from "./tui/welcome.js";
|
|
5
5
|
import { selectMode } from "./tui/mode-select.js";
|
|
@@ -26,6 +26,7 @@ export async function run() {
|
|
|
26
26
|
else {
|
|
27
27
|
config = await customFlow(env);
|
|
28
28
|
}
|
|
29
|
+
config.locale = getLocale();
|
|
29
30
|
await confirmApply(config, env);
|
|
30
31
|
}
|
|
31
32
|
async function quickFlow(env) {
|
|
@@ -35,7 +36,7 @@ async function quickFlow(env) {
|
|
|
35
36
|
providers,
|
|
36
37
|
theme,
|
|
37
38
|
keybindings: "default",
|
|
38
|
-
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
39
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name", "custom-footer", "startup-banner"],
|
|
39
40
|
skills: ["quick-setup", "debug-helper", "git-workflow"],
|
|
40
41
|
prompts: ["review", "fix", "explain", "commit", "test"],
|
|
41
42
|
agents: "general-developer",
|
|
@@ -5,7 +5,7 @@ const PRESETS = {
|
|
|
5
5
|
labelKey: "preset.starter", hintKey: "preset.starterHint",
|
|
6
6
|
config: {
|
|
7
7
|
theme: "oh-p-dark", keybindings: "default", thinking: "medium",
|
|
8
|
-
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
8
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name", "custom-footer", "startup-banner"],
|
|
9
9
|
skills: ["quick-setup", "debug-helper"],
|
|
10
10
|
prompts: ["review", "fix", "explain", "commit"],
|
|
11
11
|
agents: "general-developer",
|
|
@@ -15,7 +15,7 @@ const PRESETS = {
|
|
|
15
15
|
labelKey: "preset.pro", hintKey: "preset.proHint",
|
|
16
16
|
config: {
|
|
17
17
|
theme: "catppuccin-mocha", keybindings: "default", thinking: "high",
|
|
18
|
-
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
18
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name", "custom-footer", "startup-banner"],
|
|
19
19
|
skills: ["quick-setup", "debug-helper", "git-workflow"],
|
|
20
20
|
prompts: ["review", "fix", "explain", "commit", "test", "refactor", "optimize", "document", "pr"],
|
|
21
21
|
agents: "fullstack-developer",
|
|
@@ -25,7 +25,7 @@ const PRESETS = {
|
|
|
25
25
|
labelKey: "preset.security", hintKey: "preset.securityHint",
|
|
26
26
|
config: {
|
|
27
27
|
theme: "cyberpunk", keybindings: "default", thinking: "high",
|
|
28
|
-
extensions: ["safe-guard"],
|
|
28
|
+
extensions: ["safe-guard", "custom-footer", "startup-banner"],
|
|
29
29
|
skills: ["debug-helper"],
|
|
30
30
|
prompts: ["review", "security", "fix", "explain"],
|
|
31
31
|
agents: "security-researcher",
|
|
@@ -35,7 +35,7 @@ const PRESETS = {
|
|
|
35
35
|
labelKey: "preset.dataai", hintKey: "preset.dataaiHint",
|
|
36
36
|
config: {
|
|
37
37
|
theme: "tokyo-night", keybindings: "default", thinking: "medium",
|
|
38
|
-
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
38
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name", "custom-footer", "startup-banner"],
|
|
39
39
|
skills: ["quick-setup", "debug-helper"],
|
|
40
40
|
prompts: ["review", "fix", "explain", "optimize", "document", "test"],
|
|
41
41
|
agents: "data-ai-engineer",
|
|
@@ -52,7 +52,7 @@ const PRESETS = {
|
|
|
52
52
|
labelKey: "preset.full", hintKey: "preset.fullHint",
|
|
53
53
|
config: {
|
|
54
54
|
theme: "oh-p-dark", keybindings: "default", thinking: "high",
|
|
55
|
-
extensions: ["safe-guard", "git-guard", "auto-session-name", "ant-colony"],
|
|
55
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name", "custom-footer", "startup-banner", "ant-colony"],
|
|
56
56
|
skills: ["quick-setup", "debug-helper", "git-workflow", "ant-colony"],
|
|
57
57
|
prompts: ["review", "fix", "explain", "commit", "test", "refactor", "optimize", "security", "document", "pr"],
|
|
58
58
|
agents: "colony-operator",
|
package/dist/types.d.ts
CHANGED
package/dist/types.js
CHANGED
|
@@ -41,7 +41,7 @@ export const EXTENSIONS = [
|
|
|
41
41
|
{ name: "safe-guard", label: "π‘οΈ Safe Guard β Dangerous command confirm + path protection", default: true },
|
|
42
42
|
{ name: "git-guard", label: "π¦ Git Guard β Auto stash checkpoint + dirty repo warning + notify", default: true },
|
|
43
43
|
{ name: "auto-session-name", label: "π Auto Session Name β Name sessions from first message", default: true },
|
|
44
|
-
{ name: "custom-footer", label: "π Custom Footer β Enhanced status bar with tokens, cost, time, git, cwd", default:
|
|
44
|
+
{ name: "custom-footer", label: "π Custom Footer β Enhanced status bar with tokens, cost, time, git, cwd", default: true },
|
|
45
45
|
{ name: "startup-banner", label: "β‘ Startup Banner β Clean compact startup info (replaces verbose output)", default: true },
|
|
46
46
|
{ name: "ant-colony", label: "π Ant Colony β Autonomous multi-agent swarm with adaptive concurrency", default: false },
|
|
47
47
|
];
|
package/dist/utils/install.js
CHANGED
|
@@ -25,10 +25,11 @@ function copyDir(src, dest) {
|
|
|
25
25
|
export function applyConfig(config) {
|
|
26
26
|
const agentDir = join(homedir(), ".pi", "agent");
|
|
27
27
|
ensureDir(agentDir);
|
|
28
|
-
// 1. auth.json (skip
|
|
29
|
-
|
|
28
|
+
// 1. auth.json (skip providers that have apiKey in models.json via baseUrl)
|
|
29
|
+
const authProviders = config.providers.filter(p => !p.baseUrl && p.apiKey !== "none");
|
|
30
|
+
if (authProviders.length > 0) {
|
|
30
31
|
const auth = {};
|
|
31
|
-
for (const p of
|
|
32
|
+
for (const p of authProviders) {
|
|
32
33
|
auth[p.name] = { type: "api_key", key: p.apiKey };
|
|
33
34
|
}
|
|
34
35
|
const authPath = join(agentDir, "auth.json");
|
|
@@ -61,24 +62,39 @@ export function applyConfig(config) {
|
|
|
61
62
|
// 3. models.json (custom endpoints / providers)
|
|
62
63
|
const customProviders = config.providers.filter(p => p.baseUrl);
|
|
63
64
|
if (customProviders.length > 0) {
|
|
64
|
-
const
|
|
65
|
+
const providers = {};
|
|
65
66
|
for (const cp of customProviders) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
baseUrl
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
const isBuiltin = !!PROVIDERS[cp.name];
|
|
68
|
+
if (isBuiltin) {
|
|
69
|
+
// Known provider with custom baseUrl β just override endpoint, keep built-in models
|
|
70
|
+
const entry = { baseUrl: cp.baseUrl };
|
|
71
|
+
if (cp.apiKey !== "none")
|
|
72
|
+
entry.apiKey = cp.apiKey;
|
|
73
|
+
providers[cp.name] = entry;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Fully custom provider β need api, models, etc.
|
|
77
|
+
const caps = cp.defaultModel ? MODEL_CAPABILITIES[cp.defaultModel] : undefined;
|
|
78
|
+
const entry = {
|
|
79
|
+
baseUrl: cp.baseUrl,
|
|
80
|
+
api: "openai-completions",
|
|
81
|
+
};
|
|
82
|
+
if (cp.apiKey !== "none")
|
|
83
|
+
entry.apiKey = cp.apiKey;
|
|
84
|
+
if (cp.defaultModel) {
|
|
85
|
+
entry.models = [{
|
|
86
|
+
id: cp.defaultModel,
|
|
87
|
+
name: cp.defaultModel,
|
|
88
|
+
reasoning: cp.reasoning ?? caps?.reasoning ?? false,
|
|
89
|
+
input: cp.multimodal ? ["text", "image"] : (caps?.input ?? ["text"]),
|
|
90
|
+
contextWindow: cp.contextWindow ?? caps?.contextWindow ?? 128000,
|
|
91
|
+
maxTokens: cp.maxTokens ?? caps?.maxTokens ?? 8192,
|
|
92
|
+
}];
|
|
93
|
+
}
|
|
94
|
+
providers[cp.name] = entry;
|
|
95
|
+
}
|
|
80
96
|
}
|
|
81
|
-
writeFileSync(join(agentDir, "models.json"), JSON.stringify(
|
|
97
|
+
writeFileSync(join(agentDir, "models.json"), JSON.stringify({ providers }, null, 2));
|
|
82
98
|
}
|
|
83
99
|
// 4. keybindings.json
|
|
84
100
|
const kb = KEYBINDING_SCHEMES[config.keybindings];
|
|
@@ -88,7 +104,12 @@ export function applyConfig(config) {
|
|
|
88
104
|
// 5. AGENTS.md
|
|
89
105
|
const agentsSrc = join(PKG_ROOT, "pi-package", "agents", `${config.agents}.md`);
|
|
90
106
|
try {
|
|
91
|
-
|
|
107
|
+
let content = readFileSync(agentsSrc, "utf8");
|
|
108
|
+
if (config.locale && config.locale !== "en") {
|
|
109
|
+
const langNames = { zh: "Chinese (δΈζ)", fr: "French (FranΓ§ais)" };
|
|
110
|
+
const lang = langNames[config.locale] ?? config.locale;
|
|
111
|
+
content = `## Language\nAlways respond in ${lang}. Use the user's language for all conversations and explanations. Code, commands, and technical terms can remain in English.\n\n${content}`;
|
|
112
|
+
}
|
|
92
113
|
writeFileSync(join(agentDir, "AGENTS.md"), content);
|
|
93
114
|
}
|
|
94
115
|
catch { /* template not found, skip */ }
|
package/package.json
CHANGED
|
@@ -1,32 +1,55 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* oh-pi Startup Banner Extension
|
|
3
|
+
*
|
|
4
|
+
* Replaces the built-in verbose header with a compact one-line info display.
|
|
5
|
+
* Auto-restores the default header after 8 seconds.
|
|
6
|
+
*/
|
|
7
|
+
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
|
8
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
export default function (pi: ExtensionAPI) {
|
|
11
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
12
|
+
if (!ctx.hasUI) return;
|
|
2
13
|
|
|
3
|
-
export default function activate(pi: PiSdk) {
|
|
4
|
-
pi.on("session_start", async (_event, ctx: ExtensionContext) => {
|
|
5
14
|
const model = ctx.model;
|
|
6
15
|
const thinking = pi.getThinkingLevel();
|
|
7
16
|
const usage = ctx.getContextUsage();
|
|
8
|
-
const branch = ctx.sessionManager.getBranch();
|
|
9
|
-
const session = ctx.sessionManager.getSessionFile() ?? "ephemeral";
|
|
10
17
|
const cwd = process.cwd().replace(process.env.HOME ?? "", "~");
|
|
11
18
|
|
|
19
|
+
// Sum cost from branch
|
|
20
|
+
let totalCost = 0;
|
|
21
|
+
for (const e of ctx.sessionManager.getBranch()) {
|
|
22
|
+
if (e.type === "message" && e.message.role === "assistant") {
|
|
23
|
+
totalCost += (e.message as AssistantMessage).usage.cost.total;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
// Git branch
|
|
13
28
|
let git = "";
|
|
14
29
|
try {
|
|
15
|
-
const {
|
|
16
|
-
git =
|
|
30
|
+
const { stdout } = await pi.exec("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: 2000 });
|
|
31
|
+
git = stdout.trim();
|
|
17
32
|
} catch { /* not a git repo */ }
|
|
18
33
|
|
|
19
34
|
const modelStr = model ? `${model.provider}/${model.id}` : "no model";
|
|
20
35
|
const thinkStr = thinking !== "off" ? ` β’ π§ ${thinking}` : "";
|
|
21
36
|
const ctxStr = usage ? ` β’ π ${Math.round(usage.percent)}%` : "";
|
|
22
37
|
const gitStr = git ? ` β’ πΏ ${git}` : "";
|
|
23
|
-
const costStr =
|
|
38
|
+
const costStr = totalCost > 0 ? ` β’ $${totalCost.toFixed(3)}` : "";
|
|
39
|
+
|
|
40
|
+
const session = ctx.sessionManager.getSessionFile?.() ?? "ephemeral";
|
|
41
|
+
const sessionName = session.split("/").pop();
|
|
24
42
|
|
|
25
|
-
const line = `β‘ ${modelStr}${thinkStr}${ctxStr}${costStr} β π ${cwd}${gitStr} β π ${
|
|
43
|
+
const line = `β‘ ${modelStr}${thinkStr}${ctxStr}${costStr} β π ${cwd}${gitStr} β π ${sessionName}`;
|
|
26
44
|
|
|
27
|
-
ctx.ui.
|
|
45
|
+
ctx.ui.setHeader((_tui, theme) => ({
|
|
46
|
+
render(_width: number): string[] {
|
|
47
|
+
return ["", theme.fg("accent", line), ""];
|
|
48
|
+
},
|
|
49
|
+
invalidate() {},
|
|
50
|
+
}));
|
|
28
51
|
|
|
29
|
-
//
|
|
30
|
-
setTimeout(() => ctx.ui.
|
|
52
|
+
// Restore default header after 8 seconds
|
|
53
|
+
setTimeout(() => ctx.ui.setHeader(undefined), 8000);
|
|
31
54
|
});
|
|
32
55
|
}
|