oh-pi 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.
- package/README.md +180 -0
- package/dist/bin/oh-pi.d.ts +2 -0
- package/dist/bin/oh-pi.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +62 -0
- package/dist/tui/agents-select.d.ts +1 -0
- package/dist/tui/agents-select.js +18 -0
- package/dist/tui/confirm-apply.d.ts +3 -0
- package/dist/tui/confirm-apply.js +90 -0
- package/dist/tui/extension-select.d.ts +1 -0
- package/dist/tui/extension-select.js +17 -0
- package/dist/tui/keybinding-select.d.ts +1 -0
- package/dist/tui/keybinding-select.js +16 -0
- package/dist/tui/mode-select.d.ts +3 -0
- package/dist/tui/mode-select.js +16 -0
- package/dist/tui/preset-select.d.ts +5 -0
- package/dist/tui/preset-select.js +81 -0
- package/dist/tui/provider-setup.d.ts +2 -0
- package/dist/tui/provider-setup.js +68 -0
- package/dist/tui/theme-select.d.ts +1 -0
- package/dist/tui/theme-select.js +16 -0
- package/dist/tui/welcome.d.ts +2 -0
- package/dist/tui/welcome.js +25 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.js +41 -0
- package/dist/utils/detect.d.ts +11 -0
- package/dist/utils/detect.js +56 -0
- package/dist/utils/install.d.ts +5 -0
- package/dist/utils/install.js +130 -0
- package/package.json +54 -0
- package/pi-package/agents/colony-operator.md +32 -0
- package/pi-package/agents/data-ai-engineer.md +24 -0
- package/pi-package/agents/fullstack-developer.md +24 -0
- package/pi-package/agents/general-developer.md +22 -0
- package/pi-package/agents/security-researcher.md +29 -0
- package/pi-package/extensions/ant-colony/README.md +117 -0
- package/pi-package/extensions/ant-colony/concurrency.ts +115 -0
- package/pi-package/extensions/ant-colony/index.ts +338 -0
- package/pi-package/extensions/ant-colony/nest.ts +196 -0
- package/pi-package/extensions/ant-colony/queen.ts +356 -0
- package/pi-package/extensions/ant-colony/spawner.ts +328 -0
- package/pi-package/extensions/ant-colony/types.ts +117 -0
- package/pi-package/extensions/auto-session-name.ts +29 -0
- package/pi-package/extensions/git-guard.ts +45 -0
- package/pi-package/extensions/safe-guard.ts +46 -0
- package/pi-package/prompts/commit.md +18 -0
- package/pi-package/prompts/document.md +14 -0
- package/pi-package/prompts/explain.md +13 -0
- package/pi-package/prompts/fix.md +11 -0
- package/pi-package/prompts/optimize.md +14 -0
- package/pi-package/prompts/pr.md +24 -0
- package/pi-package/prompts/refactor.md +14 -0
- package/pi-package/prompts/review.md +18 -0
- package/pi-package/prompts/security.md +16 -0
- package/pi-package/prompts/test.md +12 -0
- package/pi-package/skills/ant-colony/SKILL.md +59 -0
- package/pi-package/skills/debug-helper/SKILL.md +43 -0
- package/pi-package/skills/git-workflow/SKILL.md +48 -0
- package/pi-package/skills/quick-setup/SKILL.md +44 -0
- package/pi-package/themes/catppuccin-mocha.json +31 -0
- package/pi-package/themes/cyberpunk.json +66 -0
- package/pi-package/themes/gruvbox-dark.json +29 -0
- package/pi-package/themes/nord.json +29 -0
- package/pi-package/themes/oh-p-dark.json +69 -0
- package/pi-package/themes/tokyo-night.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# oh-pi!
|
|
2
|
+
|
|
3
|
+
> One-click setup for [pi-coding-agent](https://github.com/badlogic/pi-mono). Like oh-my-zsh for pi.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx oh-pi
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
oh-pi! is a modern interactive TUI that configures pi-coding-agent in minutes:
|
|
12
|
+
|
|
13
|
+
- **API Key Setup** — Multi-provider configuration with validation (Anthropic, OpenAI, Google, Groq, OpenRouter, xAI, Mistral)
|
|
14
|
+
- **Preset Profiles** — Pre-made configs for different roles (Developer, Security, Data/AI, Colony Operator, Minimal)
|
|
15
|
+
- **Custom Themes** — 6 beautiful themes (oh-pi Dark, Cyberpunk, Nord, Catppuccin, Tokyo Night, Gruvbox)
|
|
16
|
+
- **Prompt Templates** — 10 ready-to-use templates (/review, /fix, /commit, /test, /security, etc.)
|
|
17
|
+
- **Extensions** — Safety guards, git checkpoints, auto session naming, ant colony swarm
|
|
18
|
+
- **Skills** — Debug helper, git workflow, quick project setup, ant colony orchestration
|
|
19
|
+
- **Keybindings** — Default, Vim, or Emacs schemes
|
|
20
|
+
- **AGENTS.md** — Role-specific project guidelines
|
|
21
|
+
- **🐜 Ant Colony** — Autonomous multi-agent swarm with adaptive concurrency
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Run the configurator
|
|
27
|
+
npx oh-pi
|
|
28
|
+
|
|
29
|
+
# Then start coding
|
|
30
|
+
pi
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Setup Modes
|
|
34
|
+
|
|
35
|
+
### 🚀 Quick Setup (3 steps)
|
|
36
|
+
1. Pick your API provider(s)
|
|
37
|
+
2. Enter API key(s)
|
|
38
|
+
3. Done — sensible defaults applied
|
|
39
|
+
|
|
40
|
+
### 📦 Preset
|
|
41
|
+
Choose a pre-made profile:
|
|
42
|
+
|
|
43
|
+
| Preset | Theme | Thinking | Focus |
|
|
44
|
+
|--------|-------|----------|-------|
|
|
45
|
+
| 🟢 Starter | oh-pi Dark | medium | Basic safety + git |
|
|
46
|
+
| 🔵 Pro Developer | Catppuccin | high | Full toolchain |
|
|
47
|
+
| 🟣 Security Researcher | Cyberpunk | high | Audit + pentesting |
|
|
48
|
+
| 🟠 Data & AI Engineer | Tokyo Night | medium | MLOps + pipelines |
|
|
49
|
+
| 🔴 Minimal | Pi Default | off | Core only |
|
|
50
|
+
| ⚫ Full Power | oh-pi Dark | high | Everything + ant colony |
|
|
51
|
+
|
|
52
|
+
### 🎛️ Custom
|
|
53
|
+
Pick every option yourself: providers, theme, keybindings, extensions, skills, AGENTS.md template.
|
|
54
|
+
|
|
55
|
+
## 🐜 Ant Colony
|
|
56
|
+
|
|
57
|
+
Autonomous multi-agent swarm built as a pi extension. Modeled after real ant colony behavior.
|
|
58
|
+
|
|
59
|
+
### How it works
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Goal → 🔍 Scouts explore → 📋 Task pool generated → ⚒️ Workers execute in parallel → 🛡️ Soldiers review → ✅ Done
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- **Scouts** (haiku) — Fast codebase recon, identify targets
|
|
66
|
+
- **Workers** (sonnet) — Execute tasks, can spawn sub-tasks
|
|
67
|
+
- **Soldiers** (sonnet) — Review quality, request fixes if needed
|
|
68
|
+
|
|
69
|
+
### Key features
|
|
70
|
+
|
|
71
|
+
- **Auto-trigger** — LLM automatically deploys colony for complex multi-file tasks
|
|
72
|
+
- **Adaptive concurrency** — Starts at 1, explores throughput ceiling, stabilizes at optimal
|
|
73
|
+
- **429 backoff** — Rate limits trigger exponential backoff (15s→30s→60s) + concurrency halving
|
|
74
|
+
- **Pheromone communication** — Ants share discoveries via file-based pheromone trails (10min half-life)
|
|
75
|
+
- **File locking** — One ant per file, blocked tasks auto-resume when locks release
|
|
76
|
+
|
|
77
|
+
### Usage
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# LLM auto-triggers for complex tasks
|
|
81
|
+
"Refactor the auth system from sessions to JWT"
|
|
82
|
+
|
|
83
|
+
# Manual command
|
|
84
|
+
/colony migrate the entire project from CJS to ESM
|
|
85
|
+
|
|
86
|
+
# Shortcut
|
|
87
|
+
Ctrl+Alt+A
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## What Gets Installed
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
~/.pi/agent/
|
|
94
|
+
├── auth.json # API keys (0600 permissions)
|
|
95
|
+
├── settings.json # Model, theme, thinking level
|
|
96
|
+
├── keybindings.json # Vim/Emacs shortcuts (if selected)
|
|
97
|
+
├── AGENTS.md # Project guidelines for the AI
|
|
98
|
+
├── extensions/ # Safety guards, git tools, ant colony
|
|
99
|
+
├── prompts/ # /review, /fix, /commit, /test, etc.
|
|
100
|
+
├── skills/ # debug-helper, git-workflow, ant-colony
|
|
101
|
+
└── themes/ # Custom color themes
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Existing config? oh-pi! detects it and offers backup before overwriting.
|
|
105
|
+
|
|
106
|
+
## Included Resources
|
|
107
|
+
|
|
108
|
+
### Themes
|
|
109
|
+
|
|
110
|
+
| Theme | Style |
|
|
111
|
+
|-------|-------|
|
|
112
|
+
| oh-pi Dark | Cyan + Purple, high contrast |
|
|
113
|
+
| Cyberpunk | Neon magenta + electric cyan |
|
|
114
|
+
| Nord | Arctic blue palette |
|
|
115
|
+
| Catppuccin Mocha | Pastel colors on dark |
|
|
116
|
+
| Tokyo Night | Blue + purple twilight |
|
|
117
|
+
| Gruvbox Dark | Warm retro tones |
|
|
118
|
+
|
|
119
|
+
### Prompt Templates
|
|
120
|
+
|
|
121
|
+
| Command | Description |
|
|
122
|
+
|---------|-------------|
|
|
123
|
+
| `/review` | Code review: bugs, security, performance |
|
|
124
|
+
| `/fix` | Fix errors with minimal changes |
|
|
125
|
+
| `/explain` | Explain code from simple to detailed |
|
|
126
|
+
| `/refactor` | Refactor while preserving behavior |
|
|
127
|
+
| `/test` | Generate tests for code |
|
|
128
|
+
| `/commit` | Conventional Commit message |
|
|
129
|
+
| `/pr` | Pull request description |
|
|
130
|
+
| `/security` | OWASP security audit |
|
|
131
|
+
| `/optimize` | Performance optimization |
|
|
132
|
+
| `/document` | Generate documentation |
|
|
133
|
+
|
|
134
|
+
### Extensions
|
|
135
|
+
|
|
136
|
+
| Extension | Description |
|
|
137
|
+
|-----------|-------------|
|
|
138
|
+
| Safe Guard | Confirms dangerous commands (rm -rf, DROP, etc.) + protects .env, .git/ |
|
|
139
|
+
| Git Guard | Auto stash checkpoints + dirty repo warning + completion notification |
|
|
140
|
+
| Auto Session Name | Names sessions from first message |
|
|
141
|
+
| 🐜 Ant Colony | Autonomous multi-agent swarm with adaptive concurrency |
|
|
142
|
+
|
|
143
|
+
### Skills
|
|
144
|
+
|
|
145
|
+
| Skill | Description |
|
|
146
|
+
|-------|-------------|
|
|
147
|
+
| `/skill:quick-setup` | Detect project type, generate .pi/ config |
|
|
148
|
+
| `/skill:debug-helper` | Error analysis, log interpretation, profiling |
|
|
149
|
+
| `/skill:git-workflow` | Branch strategy, PR workflow, conflict resolution |
|
|
150
|
+
| `/skill:ant-colony` | Colony orchestration strategies and tuning |
|
|
151
|
+
|
|
152
|
+
### AGENTS.md Templates
|
|
153
|
+
|
|
154
|
+
| Template | Description |
|
|
155
|
+
|----------|-------------|
|
|
156
|
+
| General Developer | Universal coding guidelines |
|
|
157
|
+
| Full-Stack Developer | Frontend + Backend + DB |
|
|
158
|
+
| Security Researcher | Pentesting & audit |
|
|
159
|
+
| Data & AI Engineer | MLOps & pipelines |
|
|
160
|
+
| 🐜 Colony Operator | Ant swarm multi-agent orchestration |
|
|
161
|
+
|
|
162
|
+
## Also a Pi Package
|
|
163
|
+
|
|
164
|
+
oh-pi! is also a pi package. Install just the resources without the configurator:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
pi install npm:oh-pi
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
This adds all themes, prompts, skills, and extensions to your pi setup.
|
|
171
|
+
|
|
172
|
+
## Requirements
|
|
173
|
+
|
|
174
|
+
- Node.js >= 20
|
|
175
|
+
- pi-coding-agent (installed automatically if missing)
|
|
176
|
+
- At least one LLM API key
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(): Promise<void>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { welcome } from "./tui/welcome.js";
|
|
2
|
+
import { selectMode } from "./tui/mode-select.js";
|
|
3
|
+
import { setupProviders } from "./tui/provider-setup.js";
|
|
4
|
+
import { selectPreset } from "./tui/preset-select.js";
|
|
5
|
+
import { selectTheme } from "./tui/theme-select.js";
|
|
6
|
+
import { selectKeybindings } from "./tui/keybinding-select.js";
|
|
7
|
+
import { selectExtensions } from "./tui/extension-select.js";
|
|
8
|
+
import { selectAgents } from "./tui/agents-select.js";
|
|
9
|
+
import { confirmApply } from "./tui/confirm-apply.js";
|
|
10
|
+
import { detectEnv } from "./utils/detect.js";
|
|
11
|
+
export async function run() {
|
|
12
|
+
const env = await detectEnv();
|
|
13
|
+
welcome(env);
|
|
14
|
+
const mode = await selectMode(env);
|
|
15
|
+
let config;
|
|
16
|
+
if (mode === "quick") {
|
|
17
|
+
config = await quickFlow(env);
|
|
18
|
+
}
|
|
19
|
+
else if (mode === "preset") {
|
|
20
|
+
config = await presetFlow(env);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
config = await customFlow(env);
|
|
24
|
+
}
|
|
25
|
+
await confirmApply(config, env);
|
|
26
|
+
}
|
|
27
|
+
async function quickFlow(env) {
|
|
28
|
+
const providers = await setupProviders();
|
|
29
|
+
const theme = "oh-p-dark";
|
|
30
|
+
return {
|
|
31
|
+
providers,
|
|
32
|
+
theme,
|
|
33
|
+
keybindings: "default",
|
|
34
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
35
|
+
skills: ["quick-setup", "debug-helper", "git-workflow"],
|
|
36
|
+
prompts: ["review", "fix", "explain", "commit", "test"],
|
|
37
|
+
agents: "general-developer",
|
|
38
|
+
thinking: "medium",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function presetFlow(env) {
|
|
42
|
+
const preset = await selectPreset();
|
|
43
|
+
const providers = await setupProviders();
|
|
44
|
+
return { ...preset, providers };
|
|
45
|
+
}
|
|
46
|
+
async function customFlow(env) {
|
|
47
|
+
const providers = await setupProviders();
|
|
48
|
+
const theme = await selectTheme();
|
|
49
|
+
const keybindings = await selectKeybindings();
|
|
50
|
+
const extensions = await selectExtensions();
|
|
51
|
+
const agents = await selectAgents();
|
|
52
|
+
return {
|
|
53
|
+
providers,
|
|
54
|
+
theme,
|
|
55
|
+
keybindings,
|
|
56
|
+
extensions,
|
|
57
|
+
skills: ["quick-setup", "debug-helper", "git-workflow"],
|
|
58
|
+
prompts: ["review", "fix", "explain", "commit", "test", "refactor", "optimize", "security", "document", "pr"],
|
|
59
|
+
agents,
|
|
60
|
+
thinking: "medium",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function selectAgents(): Promise<string>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
export async function selectAgents() {
|
|
3
|
+
const agent = await p.select({
|
|
4
|
+
message: "AGENTS.md template:",
|
|
5
|
+
options: [
|
|
6
|
+
{ value: "general-developer", label: "📋 General Developer", hint: "Universal coding guidelines" },
|
|
7
|
+
{ value: "fullstack-developer", label: "🏗️ Full-Stack Developer", hint: "Frontend + Backend + DB" },
|
|
8
|
+
{ value: "security-researcher", label: "🔒 Security Researcher", hint: "Pentesting & audit" },
|
|
9
|
+
{ value: "data-ai-engineer", label: "🤖 Data & AI Engineer", hint: "MLOps & pipelines" },
|
|
10
|
+
{ value: "colony-operator", label: "🐜 Colony Operator", hint: "Ant swarm multi-agent" },
|
|
11
|
+
],
|
|
12
|
+
});
|
|
13
|
+
if (p.isCancel(agent)) {
|
|
14
|
+
p.cancel("Cancelled.");
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
return agent;
|
|
18
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { applyConfig, installPi, backupConfig } from "../utils/install.js";
|
|
4
|
+
function countExisting(env, dir) {
|
|
5
|
+
return env.existingFiles.filter(f => f.startsWith(dir + "/")).length;
|
|
6
|
+
}
|
|
7
|
+
export async function confirmApply(config, env) {
|
|
8
|
+
// ═══ Summary ═══
|
|
9
|
+
const summary = [
|
|
10
|
+
`Providers: ${chalk.cyan(config.providers.map(p => p.name).join(", "))}`,
|
|
11
|
+
`Model: ${chalk.cyan(config.providers[0]?.defaultModel || "default")}`,
|
|
12
|
+
`Theme: ${chalk.cyan(config.theme)}`,
|
|
13
|
+
`Keybindings: ${chalk.cyan(config.keybindings)}`,
|
|
14
|
+
`Thinking: ${chalk.cyan(config.thinking)}`,
|
|
15
|
+
`Extensions: ${chalk.cyan(config.extensions.join(", ") || "none")}`,
|
|
16
|
+
`Skills: ${chalk.cyan(config.skills.join(", ") || "none")}`,
|
|
17
|
+
`Prompts: ${chalk.cyan(`${config.prompts.length} templates`)}`,
|
|
18
|
+
`AGENTS.md: ${chalk.cyan(config.agents)}`,
|
|
19
|
+
].join("\n");
|
|
20
|
+
p.note(summary, "Configuration");
|
|
21
|
+
// ═══ Diff (if existing) ═══
|
|
22
|
+
if (env.hasExistingConfig) {
|
|
23
|
+
const diff = [
|
|
24
|
+
`Extensions: ${chalk.dim(countExisting(env, "extensions"))} ${chalk.yellow("→")} ${chalk.green(config.extensions.length)}`,
|
|
25
|
+
`Skills: ${chalk.dim(countExisting(env, "skills"))} ${chalk.yellow("→")} ${chalk.green(config.skills.length)}`,
|
|
26
|
+
`Prompts: ${chalk.dim(countExisting(env, "prompts"))} ${chalk.yellow("→")} ${chalk.green(config.prompts.length)}`,
|
|
27
|
+
].join("\n");
|
|
28
|
+
p.note(diff, "⚠ Changes");
|
|
29
|
+
}
|
|
30
|
+
// ═══ Backup prompt ═══
|
|
31
|
+
if (env.hasExistingConfig) {
|
|
32
|
+
const action = await p.select({
|
|
33
|
+
message: "Existing config detected. How to proceed?",
|
|
34
|
+
options: [
|
|
35
|
+
{ value: "backup", label: "📦 Backup & apply", hint: "Safe — backup first, then overwrite" },
|
|
36
|
+
{ value: "overwrite", label: "⚡ Overwrite", hint: "Replace without backup" },
|
|
37
|
+
{ value: "cancel", label: "✖ Cancel", hint: "Keep current config" },
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
if (p.isCancel(action) || action === "cancel") {
|
|
41
|
+
p.cancel("No changes made.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (action === "backup") {
|
|
45
|
+
const s = p.spinner();
|
|
46
|
+
s.start("Backing up ~/.pi/agent/");
|
|
47
|
+
const backupDir = backupConfig();
|
|
48
|
+
s.stop(`Backed up to ${chalk.dim(backupDir)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const ok = await p.confirm({ message: "Apply configuration?" });
|
|
53
|
+
if (p.isCancel(ok) || !ok) {
|
|
54
|
+
p.cancel("No changes made.");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ═══ Install pi if needed ═══
|
|
59
|
+
if (!env.piInstalled) {
|
|
60
|
+
const s = p.spinner();
|
|
61
|
+
s.start("Installing pi-coding-agent");
|
|
62
|
+
try {
|
|
63
|
+
installPi();
|
|
64
|
+
s.stop("pi installed");
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
s.stop(`Failed: ${e}`);
|
|
68
|
+
p.log.warn("Run manually: npm install -g @mariozechner/pi-coding-agent");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// ═══ Apply ═══
|
|
72
|
+
const s = p.spinner();
|
|
73
|
+
s.start("Writing configuration");
|
|
74
|
+
applyConfig(config);
|
|
75
|
+
s.stop("Configuration applied");
|
|
76
|
+
// ═══ Result ═══
|
|
77
|
+
const tree = [
|
|
78
|
+
`${chalk.gray("~/.pi/agent/")}`,
|
|
79
|
+
`${chalk.gray("├── ")}auth.json ${chalk.dim("🔒")}`,
|
|
80
|
+
`${chalk.gray("├── ")}settings.json`,
|
|
81
|
+
...(config.keybindings !== "default" ? [`${chalk.gray("├── ")}keybindings.json`] : []),
|
|
82
|
+
`${chalk.gray("├── ")}AGENTS.md ${chalk.dim(config.agents)}`,
|
|
83
|
+
...(config.extensions.length > 0 ? [`${chalk.gray("├── ")}extensions/ ${chalk.dim(`${config.extensions.length} items`)}`] : []),
|
|
84
|
+
...(config.prompts.length > 0 ? [`${chalk.gray("├── ")}prompts/ ${chalk.dim(`${config.prompts.length} templates`)}`] : []),
|
|
85
|
+
...(config.skills.length > 0 ? [`${chalk.gray("├── ")}skills/ ${chalk.dim(`${config.skills.length} skills`)}`] : []),
|
|
86
|
+
...(!["dark", "light"].includes(config.theme) ? [`${chalk.gray("└── ")}themes/ ${chalk.dim(config.theme)}`] : []),
|
|
87
|
+
].join("\n");
|
|
88
|
+
p.note(tree, "✓ Installed");
|
|
89
|
+
p.outro(`Run ${chalk.cyan.bold("pi")} to start coding!`);
|
|
90
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function selectExtensions(): Promise<string[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { EXTENSIONS } from "../types.js";
|
|
3
|
+
export async function selectExtensions() {
|
|
4
|
+
const exts = await p.multiselect({
|
|
5
|
+
message: "Select extensions:",
|
|
6
|
+
options: EXTENSIONS.map(e => ({
|
|
7
|
+
value: e.name,
|
|
8
|
+
label: e.label,
|
|
9
|
+
})),
|
|
10
|
+
initialValues: EXTENSIONS.filter(e => e.default).map(e => e.name),
|
|
11
|
+
});
|
|
12
|
+
if (p.isCancel(exts)) {
|
|
13
|
+
p.cancel("Cancelled.");
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
return exts;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function selectKeybindings(): Promise<string>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
export async function selectKeybindings() {
|
|
3
|
+
const kb = await p.select({
|
|
4
|
+
message: "Keybinding scheme:",
|
|
5
|
+
options: [
|
|
6
|
+
{ value: "default", label: "⌨️ Default", hint: "Pi standard keybindings" },
|
|
7
|
+
{ value: "vim", label: "🟢 Vim", hint: "Alt+hjkl navigation" },
|
|
8
|
+
{ value: "emacs", label: "🔵 Emacs", hint: "Ctrl+pnbf navigation" },
|
|
9
|
+
],
|
|
10
|
+
});
|
|
11
|
+
if (p.isCancel(kb)) {
|
|
12
|
+
p.cancel("Cancelled.");
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
return kb;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
export async function selectMode(env) {
|
|
3
|
+
const mode = await p.select({
|
|
4
|
+
message: "How would you like to set up pi?",
|
|
5
|
+
options: [
|
|
6
|
+
{ value: "quick", label: "🚀 Quick Setup", hint: "Recommended defaults, 3 steps" },
|
|
7
|
+
{ value: "preset", label: "📦 Preset", hint: "Choose a pre-made configuration" },
|
|
8
|
+
{ value: "custom", label: "🎛️ Custom", hint: "Pick everything yourself" },
|
|
9
|
+
],
|
|
10
|
+
});
|
|
11
|
+
if (p.isCancel(mode)) {
|
|
12
|
+
p.cancel("Cancelled.");
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
return mode;
|
|
16
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
const PRESETS = {
|
|
3
|
+
starter: {
|
|
4
|
+
label: "🟢 Starter",
|
|
5
|
+
hint: "New to AI coding? Start here",
|
|
6
|
+
config: {
|
|
7
|
+
theme: "oh-p-dark", keybindings: "default", thinking: "medium",
|
|
8
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
9
|
+
skills: ["quick-setup", "debug-helper"],
|
|
10
|
+
prompts: ["review", "fix", "explain", "commit"],
|
|
11
|
+
agents: "general-developer",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
pro: {
|
|
15
|
+
label: "🔵 Pro Developer",
|
|
16
|
+
hint: "Full-stack dev with all the bells and whistles",
|
|
17
|
+
config: {
|
|
18
|
+
theme: "catppuccin-mocha", keybindings: "default", thinking: "high",
|
|
19
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
20
|
+
skills: ["quick-setup", "debug-helper", "git-workflow"],
|
|
21
|
+
prompts: ["review", "fix", "explain", "commit", "test", "refactor", "optimize", "document", "pr"],
|
|
22
|
+
agents: "fullstack-developer",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
security: {
|
|
26
|
+
label: "🟣 Security Researcher",
|
|
27
|
+
hint: "Pentesting, auditing, vulnerability research",
|
|
28
|
+
config: {
|
|
29
|
+
theme: "cyberpunk", keybindings: "default", thinking: "high",
|
|
30
|
+
extensions: ["safe-guard"],
|
|
31
|
+
skills: ["debug-helper"],
|
|
32
|
+
prompts: ["review", "security", "fix", "explain"],
|
|
33
|
+
agents: "security-researcher",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
dataai: {
|
|
37
|
+
label: "🟠 Data & AI Engineer",
|
|
38
|
+
hint: "MLOps, data pipelines, AI applications",
|
|
39
|
+
config: {
|
|
40
|
+
theme: "tokyo-night", keybindings: "default", thinking: "medium",
|
|
41
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name"],
|
|
42
|
+
skills: ["quick-setup", "debug-helper"],
|
|
43
|
+
prompts: ["review", "fix", "explain", "optimize", "document", "test"],
|
|
44
|
+
agents: "data-ai-engineer",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
minimal: {
|
|
48
|
+
label: "🔴 Minimal",
|
|
49
|
+
hint: "Just the core, nothing extra",
|
|
50
|
+
config: {
|
|
51
|
+
theme: "dark", keybindings: "default", thinking: "off",
|
|
52
|
+
extensions: [], skills: [], prompts: [], agents: "general-developer",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
full: {
|
|
56
|
+
label: "⚫ Full Power",
|
|
57
|
+
hint: "Everything installed, ant colony included",
|
|
58
|
+
config: {
|
|
59
|
+
theme: "oh-p-dark", keybindings: "default", thinking: "high",
|
|
60
|
+
extensions: ["safe-guard", "git-guard", "auto-session-name", "ant-colony"],
|
|
61
|
+
skills: ["quick-setup", "debug-helper", "git-workflow", "ant-colony"],
|
|
62
|
+
prompts: ["review", "fix", "explain", "commit", "test", "refactor", "optimize", "security", "document", "pr"],
|
|
63
|
+
agents: "colony-operator",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
export async function selectPreset() {
|
|
68
|
+
const key = await p.select({
|
|
69
|
+
message: "Choose a preset:",
|
|
70
|
+
options: Object.entries(PRESETS).map(([k, v]) => ({
|
|
71
|
+
value: k,
|
|
72
|
+
label: v.label,
|
|
73
|
+
hint: v.hint,
|
|
74
|
+
})),
|
|
75
|
+
});
|
|
76
|
+
if (p.isCancel(key)) {
|
|
77
|
+
p.cancel("Cancelled.");
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
return PRESETS[key].config;
|
|
81
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { PROVIDERS } from "../types.js";
|
|
4
|
+
export async function setupProviders() {
|
|
5
|
+
const entries = Object.entries(PROVIDERS);
|
|
6
|
+
const selected = await p.multiselect({
|
|
7
|
+
message: "Select API providers",
|
|
8
|
+
options: entries.map(([key, info]) => ({
|
|
9
|
+
value: key,
|
|
10
|
+
label: info.label,
|
|
11
|
+
hint: info.env,
|
|
12
|
+
})),
|
|
13
|
+
initialValues: ["anthropic"],
|
|
14
|
+
required: true,
|
|
15
|
+
});
|
|
16
|
+
if (p.isCancel(selected)) {
|
|
17
|
+
p.cancel("Cancelled.");
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
const configs = [];
|
|
21
|
+
for (const name of selected) {
|
|
22
|
+
const info = PROVIDERS[name];
|
|
23
|
+
const envVal = process.env[info.env];
|
|
24
|
+
let apiKey;
|
|
25
|
+
if (envVal) {
|
|
26
|
+
const useEnv = await p.confirm({
|
|
27
|
+
message: `Found ${chalk.cyan(info.env)} in environment. Use it?`,
|
|
28
|
+
});
|
|
29
|
+
if (p.isCancel(useEnv)) {
|
|
30
|
+
p.cancel("Cancelled.");
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
apiKey = useEnv ? info.env : await promptKey(info);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
apiKey = await promptKey(info);
|
|
37
|
+
}
|
|
38
|
+
let defaultModel;
|
|
39
|
+
if (info.models.length > 1) {
|
|
40
|
+
const model = await p.select({
|
|
41
|
+
message: `Default model for ${info.label}:`,
|
|
42
|
+
options: info.models.map(m => ({ value: m, label: m })),
|
|
43
|
+
});
|
|
44
|
+
if (p.isCancel(model)) {
|
|
45
|
+
p.cancel("Cancelled.");
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
defaultModel = model;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
defaultModel = info.models[0];
|
|
52
|
+
}
|
|
53
|
+
configs.push({ name, apiKey, defaultModel });
|
|
54
|
+
p.log.success(`${info.label} configured`);
|
|
55
|
+
}
|
|
56
|
+
return configs;
|
|
57
|
+
}
|
|
58
|
+
async function promptKey(info) {
|
|
59
|
+
const key = await p.password({
|
|
60
|
+
message: `API key for ${info.label}:`,
|
|
61
|
+
validate: (v) => (!v || v.trim().length === 0) ? "API key cannot be empty" : undefined,
|
|
62
|
+
});
|
|
63
|
+
if (p.isCancel(key)) {
|
|
64
|
+
p.cancel("Cancelled.");
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
return key;
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function selectTheme(): Promise<string>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { THEMES } from "../types.js";
|
|
3
|
+
export async function selectTheme() {
|
|
4
|
+
const theme = await p.select({
|
|
5
|
+
message: "Choose a theme:",
|
|
6
|
+
options: THEMES.map(t => ({
|
|
7
|
+
value: t.name,
|
|
8
|
+
label: `${t.style === "dark" ? "🌙" : "☀️"} ${t.label}`,
|
|
9
|
+
})),
|
|
10
|
+
});
|
|
11
|
+
if (p.isCancel(theme)) {
|
|
12
|
+
p.cancel("Cancelled.");
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
return theme;
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
export function welcome(env) {
|
|
4
|
+
console.clear();
|
|
5
|
+
p.intro(chalk.cyan.bold(" oh-pi ") + chalk.dim("— one-click setup for pi agent"));
|
|
6
|
+
if (env.piInstalled) {
|
|
7
|
+
p.log.success(`pi ${env.piVersion} detected`);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
p.log.warn("pi not found — will install");
|
|
11
|
+
}
|
|
12
|
+
p.log.info(`${env.terminal} │ ${env.os} │ Node ${process.version}`);
|
|
13
|
+
if (env.hasExistingConfig) {
|
|
14
|
+
p.note(`${env.existingFiles.length} files (${env.configSizeKB}KB) at ~/.pi/agent/\n` +
|
|
15
|
+
categorize(env.existingFiles), "⚠ Existing config found");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function categorize(files) {
|
|
19
|
+
const cats = {};
|
|
20
|
+
for (const f of files) {
|
|
21
|
+
const cat = f.includes("/") ? f.split("/")[0] : f;
|
|
22
|
+
cats[cat] = (cats[cat] || 0) + 1;
|
|
23
|
+
}
|
|
24
|
+
return Object.entries(cats).map(([k, v]) => `${k} (${v})`).join(" ");
|
|
25
|
+
}
|