pi-lazy-setup 2.0.0 → 3.0.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 CHANGED
@@ -8,10 +8,13 @@ npx pi-lazy-setup
8
8
 
9
9
  ## What it does
10
10
 
11
- 1. **Installs the binary** — `pi` via npm. Skips if already on PATH.
12
- 2. **Deploys configs** — overwrites `~/.pi/agent` from bundled `configs/pi/`.
11
+ 1. **Deploys configs** — overwrites `~/.pi/agent` from bundled `configs/pi/` (binary install is out of scope: `pi` must already be on PATH).
12
+ 2. **Deploys the `/pi-setup` extension** — copies `configs/pi/extensions/pi-setup.js` into `~/.pi/agent/extensions/` so Pi auto-discovers it on the next start.
13
13
  3. **Deploys reference docs** — copies into `~/.pi/agent/pi-setup/`.
14
- 4. **Installs default skills** — via `npx skills add` (https://skills.sh/).
14
+
15
+ Then, inside Pi, run `/pi-setup`: it interactively installs **packages** (`pi install <source>`), **skills** (`npx skills add`, https://skills.sh/), and **CLI tools** (officecli: skill file from <https://officecli.ai/SKILL.md> + binary via `curl -fsSL https://d.officecli.ai/install.sh | bash`). Every item is confirmed individually, and already-installed items are skipped. Pattern follows the `/rpiv-setup` command of `@juicesharp/rpiv-pi`.
16
+
17
+ The candidate lists (packages, skills, CLI tools) live as snapshot data inside the extension file itself.
15
18
 
16
19
  ## Flags
17
20
 
@@ -27,7 +30,7 @@ This is a **snapshot**, not a generic installer. Configs ship exactly as the aut
27
30
 
28
31
  ## Scope
29
32
 
30
- **Pi** (<https://github.com/mariozechner/pi-coding-agent>). MCP server binaries are not installed — only the declaration file (`mcp.json`) and config files (`settings.json`, `models.json`, `AGENTS.md`, `prompts/`) are deployed.
33
+ **Pi** (<https://github.com/mariozechner/pi-coding-agent>). The CLI installs nothing: it only deploys the declaration file (`mcp.json`) and config files (`settings.json`, `models.json`, `AGENTS.md`, `prompts/`, `extensions/pi-setup.js`). MCP server binaries are not installed either. Everything interactive (packages, skills, CLI tools) happens via `/pi-setup` inside Pi.
31
34
 
32
35
  For skill management, see [@kkmila/isc](https://github.com/machuqiji/isc).
33
36
 
package/bin/pi-setup.mjs CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  // pi-setup — initialize the Pi agent environment from a snapshot.
4
4
  //
5
+ // Deploy-only: no binaries, packages, or skills are installed here. Instead
6
+ // the /pi-setup extension lands in ~/.pi/agent/extensions/ — run `/pi-setup`
7
+ // inside Pi to install packages, skills, and CLI tools interactively
8
+ // (each item confirmed individually).
9
+ //
5
10
  // Usage:
6
11
  // pi-setup run directly (all = pi)
7
12
  // pi-setup pi explicit
@@ -27,7 +32,6 @@ program
27
32
  const code = await initialize({
28
33
  dryRun: !!opts.dryRun,
29
34
  backup: !!opts.backup,
30
- tty: !!process.stdin.isTTY,
31
35
  agents: chosen,
32
36
  });
33
37
  process.exit(code);
@@ -0,0 +1,228 @@
1
+ // /pi-setup — interactive installer for Pi packages, skills, and CLI tools.
2
+ //
3
+ // Deployed by the pi-setup CLI (npx pi-setup) into ~/.pi/agent/extensions/pi-setup.js.
4
+ // Run `/pi-setup` inside Pi: every candidate is confirmed individually, and
5
+ // already-installed items are skipped automatically. Pattern follows the
6
+ // /rpiv-setup command of @juicesharp/rpiv-pi (serial installs, per-item
7
+ // confirm, succeeded/failed report, restart hint).
8
+ //
9
+ // Candidates (snapshot data, kept in sync with the owner's setup):
10
+ // packages → `pi install <source>` (writes into ~/.pi/agent/settings.json)
11
+ // skills → `npx --yes skills add <repo> --skill <name> -g --agent pi -y`
12
+ // clis → officecli: skill file from https://officecli.ai/SKILL.md plus
13
+ // binary via `curl -fsSL https://d.officecli.ai/install.sh | bash`
14
+
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
+ import { homedir } from "node:os";
17
+ import { join } from "node:path";
18
+
19
+ // Resolved lazily (not at module load) so tests can redirect HOME.
20
+ function piSettingsPath() {
21
+ return join(homedir(), ".pi", "agent", "settings.json");
22
+ }
23
+
24
+ function globalSkillsDir() {
25
+ return join(homedir(), ".agents", "skills");
26
+ }
27
+
28
+ const INSTALL_TIMEOUT_MS = 300_000;
29
+ const BIN_CHECK_TIMEOUT_MS = 15_000;
30
+ const STDERR_SNIPPET_CHARS = 300;
31
+
32
+ /** Pi extension packages offered via `pi install <source>`. */
33
+ const PACKAGES = [
34
+ { source: "npm:pi-footer", description: "Footer status line (token usage, model, cost)." },
35
+ { source: "npm:pi-tps-meter", description: "Tokens-per-second meter for streaming responses." },
36
+ { source: "npm:@juicesharp/rpiv-pi", description: "RPIV pipeline skills (code review, commit, handoff, diagrams)." },
37
+ { source: "https://github.com/cathrynlavery/diagram-design", description: "Branded diagram/chart generation skills." },
38
+ ];
39
+
40
+ /** Skills offered via `npx skills add` (https://skills.sh/). */
41
+ const SKILLS = [
42
+ {
43
+ repo: "mattpocock/skills",
44
+ name: "grill-me",
45
+ description: "Matt Pocock — a relentless interview to sharpen a plan or design.",
46
+ },
47
+ {
48
+ repo: "microsoft/playwright-cli",
49
+ name: "playwright-cli",
50
+ description: "Microsoft — browser automation and Playwright test driving.",
51
+ },
52
+ ];
53
+
54
+ /** CLI tools. Each entry installs two parts: a skill file and the binary itself. */
55
+ const CLIS = [
56
+ {
57
+ id: "officecli",
58
+ bin: "officecli",
59
+ description: "AI-friendly CLI for Office documents (.docx/.xlsx/.pptx).",
60
+ skillFile: { name: "officecli", url: "https://officecli.ai/SKILL.md" },
61
+ installCommand: "curl -fsSL https://d.officecli.ai/install.sh | bash",
62
+ },
63
+ ];
64
+
65
+ export default function piSetupExtension(pi) {
66
+ pi.registerCommand("pi-setup", {
67
+ description: "Install packages, skills, and CLI tools (each confirmed individually)",
68
+ handler: async (_args, ctx) => {
69
+ if (!ctx.hasUI) {
70
+ ctx.ui.notify("/pi-setup requires interactive mode", "error");
71
+ return;
72
+ }
73
+ const report = { installed: [], skipped: [], failed: [] };
74
+ await installPackages(pi, ctx, report);
75
+ await installSkills(pi, ctx, report);
76
+ await installClis(pi, ctx, report);
77
+ ctx.ui.notify(buildReport(report), report.failed.length > 0 ? "warning" : "info");
78
+ },
79
+ });
80
+ }
81
+
82
+ /** Packages section: offer each undeclared package via confirm, then `pi install`. */
83
+ async function installPackages(pi, ctx, report) {
84
+ const declared = declaredPackageSources();
85
+ for (const p of PACKAGES) {
86
+ if (declared.has(p.source)) {
87
+ report.skipped.push(p.source);
88
+ continue;
89
+ }
90
+ const confirmed = await ctx.ui.confirm("Install Pi package?", `${p.source}\n${p.description}`);
91
+ if (!confirmed) continue;
92
+ ctx.ui.notify(`Installing ${p.source}…`, "info");
93
+ const result = await runExec(pi, "pi", ["install", p.source]);
94
+ settle(result, p.source, report);
95
+ }
96
+ }
97
+
98
+ /** Skills section: offer each missing skill via confirm, then `npx skills add`. */
99
+ async function installSkills(pi, ctx, report) {
100
+ for (const s of SKILLS) {
101
+ if (skillInstalled(s.name)) {
102
+ report.skipped.push(`skill:${s.name}`);
103
+ continue;
104
+ }
105
+ const confirmed = await ctx.ui.confirm("Install skill?", `${s.repo}@${s.name}\n${s.description}`);
106
+ if (!confirmed) continue;
107
+ ctx.ui.notify(`Installing skill ${s.name}…`, "info");
108
+ const result = await runExec(pi, "npx", [
109
+ "--yes",
110
+ "skills",
111
+ "add",
112
+ s.repo,
113
+ "--skill",
114
+ s.name,
115
+ "-g",
116
+ "--agent",
117
+ "pi",
118
+ "-y",
119
+ ]);
120
+ settle(result, `skill:${s.name}`, report);
121
+ }
122
+ }
123
+
124
+ /**
125
+ * CLI tools section. Each entry installs two parts — the skill file and the
126
+ * binary — and each part is confirmed individually.
127
+ */
128
+ async function installClis(pi, ctx, report) {
129
+ for (const c of CLIS) {
130
+ if (c.skillFile) {
131
+ const { name, url } = c.skillFile;
132
+ if (skillInstalled(name)) {
133
+ report.skipped.push(`skill:${name}`);
134
+ } else {
135
+ const confirmed = await ctx.ui.confirm(
136
+ "Install skill file?",
137
+ `${url} → ~/.agents/skills/${name}/SKILL.md\n${c.description}`,
138
+ );
139
+ if (confirmed) {
140
+ ctx.ui.notify(`Downloading ${url}…`, "info");
141
+ try {
142
+ await downloadSkillFile(name, url);
143
+ report.installed.push(`skill:${name}`);
144
+ } catch (err) {
145
+ report.failed.push({ item: `skill:${name}`, error: toMessage(err) });
146
+ }
147
+ }
148
+ }
149
+ }
150
+ if (await binaryAvailable(pi, c.bin)) {
151
+ report.skipped.push(`cli:${c.bin}`);
152
+ } else {
153
+ const confirmed = await ctx.ui.confirm("Install CLI tool?", `${c.id} — ${c.description}\n${c.installCommand}`);
154
+ if (confirmed) {
155
+ ctx.ui.notify(`Installing ${c.id}…`, "info");
156
+ const result = await runExec(pi, "bash", ["-c", c.installCommand]);
157
+ settle(result, `cli:${c.bin}`, report);
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ /** Run a command via pi.exec and normalize into { ok, error }. */
164
+ async function runExec(pi, command, args, timeoutMs = INSTALL_TIMEOUT_MS) {
165
+ try {
166
+ const result = await pi.exec(command, args, { timeout: timeoutMs });
167
+ if (result.code === 0) return { ok: true };
168
+ const error = (result.stderr || result.stdout || `exit ${result.code}`).trim().slice(0, STDERR_SNIPPET_CHARS);
169
+ return { ok: false, error };
170
+ } catch (err) {
171
+ return { ok: false, error: toMessage(err) };
172
+ }
173
+ }
174
+
175
+ function settle(result, item, report) {
176
+ if (result.ok) report.installed.push(item);
177
+ else report.failed.push({ item, error: result.error });
178
+ }
179
+
180
+ function declaredPackageSources() {
181
+ try {
182
+ const settings = JSON.parse(readFileSync(piSettingsPath(), "utf8"));
183
+ return new Set((settings.packages ?? []).map((p) => (typeof p === "string" ? p : p.source)));
184
+ } catch {
185
+ return new Set();
186
+ }
187
+ }
188
+
189
+ /** Whether a skill directory with SKILL.md exists in ~/.agents/skills/. */
190
+ function skillInstalled(name) {
191
+ return existsSync(join(globalSkillsDir(), name, "SKILL.md"));
192
+ }
193
+
194
+ /** Whether a binary responds to --version. */
195
+ async function binaryAvailable(pi, name) {
196
+ const result = await runExec(pi, name, ["--version"], BIN_CHECK_TIMEOUT_MS);
197
+ return result.ok;
198
+ }
199
+
200
+ /** Download a SKILL.md into ~/.agents/skills/<name>/SKILL.md. */
201
+ async function downloadSkillFile(name, url) {
202
+ const res = await fetch(url);
203
+ if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
204
+ const text = await res.text();
205
+ const dir = join(globalSkillsDir(), name);
206
+ mkdirSync(dir, { recursive: true });
207
+ writeFileSync(join(dir, "SKILL.md"), text);
208
+ }
209
+
210
+ function toMessage(err) {
211
+ return String(err?.message ?? err);
212
+ }
213
+
214
+ function buildReport(report) {
215
+ const lines = [];
216
+ if (report.installed.length > 0) lines.push(`✓ Installed: ${report.installed.join(", ")}`);
217
+ if (report.skipped.length > 0) lines.push(`• Already installed (skipped): ${report.skipped.join(", ")}`);
218
+ if (report.failed.length > 0) {
219
+ lines.push("✗ Failed:");
220
+ for (const f of report.failed) lines.push(` ${f.item}: ${f.error}`);
221
+ }
222
+ if (report.installed.length > 0) {
223
+ lines.push("");
224
+ lines.push("Restart Pi (or run /reload) to load the newly installed packages and skills.");
225
+ }
226
+ if (lines.length === 0) lines.push("Nothing to install.");
227
+ return lines.join("\n");
228
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lazy-setup",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Initialize the Pi agent environment from a curated snapshot",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agents/pi.mjs CHANGED
@@ -1,8 +1,11 @@
1
1
  // pi.mjs — Pi agent descriptor.
2
2
  // Pure data; the initializer in src/commands/init.mjs loops over a list of
3
3
  // these. Paths with ~ are expanded at runtime by the initializer.
4
- import { DEFAULT_PACKAGES } from "./packages.mjs";
5
- import { DEFAULT_SKILLS } from "./skills.mjs";
4
+ //
5
+ // The CLI only deploys the config snapshot and the /pi-setup extension
6
+ // (deploy.extensionFiles). Installing the pi binary, packages, skills, and
7
+ // CLI tools is out of scope — `/pi-setup` handles those interactively inside
8
+ // Pi, each item confirmed individually.
6
9
 
7
10
  /** @type {import('./types.mjs').AgentDescriptor} */
8
11
  export const pi = {
@@ -11,17 +14,11 @@ export const pi = {
11
14
  home: "~/.pi/agent",
12
15
  configSource: "configs/pi",
13
16
  referenceDest: "~/.pi/agent/pi-setup",
14
- install: {
15
- kind: "npm",
16
- pkg: "@mariozechner/pi-coding-agent",
17
- bin: "pi",
18
- },
19
17
  deploy: {
20
18
  overwriteFiles: ["settings.json", "mcp.json", "models.json", "AGENTS.md"],
21
19
  replaceDirs: ["prompts"],
20
+ extensionFiles: ["pi-setup.js"],
22
21
  overwriteExtra: [{ file: "web-config.json", dest: "~/.config/rpiv-web-tools/config.json" }],
23
22
  referenceSource: "configs/reference",
24
23
  },
25
- skills: { specs: DEFAULT_SKILLS, cliAgent: "pi" },
26
- packages: { specs: DEFAULT_PACKAGES },
27
24
  };
@@ -1,43 +1,13 @@
1
1
  // types.mjs — JSDoc type shapes for agent descriptors.
2
2
  // No runtime exports; used only for type hints in editors.
3
3
 
4
- /**
5
- * @typedef {Object} InstallSpec
6
- * @property {"npm"} kind
7
- * @property {string} [pkg] — npm package name
8
- * @property {string} bin — binary name to detect on PATH
9
- */
10
-
11
4
  /**
12
5
  * @typedef {Object} DeploySpec
13
6
  * @property {string[]} overwriteFiles — files copied flat into home
14
7
  * @property {string[]} replaceDirs — subdirs rm-rf'd then fully replaced
8
+ * @property {string[]} [extensionFiles] — extension files copied flat into `<home>/extensions/`
15
9
  * @property {{ file: string, dest: string }[]} [overwriteExtra] — files copied to a ~-prefixed dest outside home
16
10
  * @property {string} referenceSource — package-relative path to reference docs
17
- * @property {SkillsSpec} [skills] — default skills to install from remote
18
- * @property {PackagesSpec} [packages] — extension packages offered via `pi install`
19
- */
20
-
21
- /**
22
- * @typedef {Object} SkillSpec
23
- * @property {string} spec — "owner/repo@skill" identifier
24
- * @property {string} [description] — shown in the interactive skill picker
25
- */
26
-
27
- /**
28
- * @typedef {Object} SkillsSpec
29
- * @property {SkillSpec[]} specs — skills offered for install (user selects)
30
- * @property {string} [cliAgent] — lowercase skills-CLI agent name; install via `npx skills add`
31
- */
32
- /**
33
- * @typedef {Object} PackageSpec
34
- * @property {string} source — package source passed to `pi install`
35
- * @property {string} [description] — shown in the interactive package picker
36
- */
37
-
38
- /**
39
- * @typedef {Object} PackagesSpec
40
- * @property {PackageSpec[]} specs — packages offered for install (user selects)
41
11
  */
42
12
 
43
13
  /**
@@ -47,7 +17,6 @@
47
17
  * @property {string} home — ~-prefixed agent home
48
18
  * @property {string} configSource — package-relative path to agent configs
49
19
  * @property {string} referenceDest — ~-prefixed destination for reference docs
50
- * @property {InstallSpec} install
51
20
  * @property {DeploySpec} deploy
52
21
  */
53
22
 
@@ -1,14 +1,15 @@
1
1
  // init.mjs — the initializer.
2
- // Loops over the agent descriptors and runs detect → install → (backup)
3
- // → deploy-configs → deploy-reference → select-packages → install-packages
4
- // → select-skills → install-skills.
2
+ // Loops over the agent descriptors and runs detect → (backup)
3
+ // → deploy-configs → deploy-reference.
5
4
  // Each step honors { dryRun }.
5
+ //
6
+ // Binaries, packages, skills, and CLI tools are NOT installed here. Instead
7
+ // the /pi-setup extension is deployed (see deploy.extensions) — run `/pi-setup`
8
+ // inside Pi to install those interactively, each item confirmed individually.
6
9
 
7
10
  import { homedir } from "node:os";
8
11
  import { resolve } from "node:path";
9
- import { checkbox } from "@inquirer/prompts";
10
12
  import { pi } from "../agents/pi.mjs";
11
- import { detectBinary, runCommand } from "../lib/exec.mjs";
12
13
  import { backupToTar, copyDir, copyDirFiles, copyFile, ensureDir } from "../lib/fs.mjs";
13
14
  import { log } from "../lib/log.mjs";
14
15
  import { expandHome, packageRoot, pkgPath } from "../lib/paths.mjs";
@@ -24,32 +25,9 @@ function backupDirFor() {
24
25
  }
25
26
 
26
27
  /**
27
- * Install step: detect the agent's binary on PATH; if missing, run the
28
- * install command. No-op (print only) in dry-run.
29
- */
30
- async function installStep(agent, { dryRun }) {
31
- const present = dryRun ? false : detectBinary(agent.install.bin);
32
- if (present) {
33
- if (!dryRun) log.skip(agent.name);
34
- else log.dry(`detect ${agent.install.bin} (assume present → skip)`);
35
- return 0;
36
- }
37
- // install
38
- const cmdline = installCmdline(agent.install);
39
- if (dryRun) {
40
- log.dry(`detect ${agent.install.bin} → missing → install`);
41
- }
42
- log.install(agent.name);
43
- return runCommand(cmdline, { dryRun });
44
- }
45
-
46
- function installCmdline(spec) {
47
- if (spec.kind === "npm") return `npm install -g ${spec.pkg}`;
48
- throw new Error(`unknown install kind: ${spec.kind}`);
49
- }
50
-
51
- /**
52
- * Deploy agent configs: overwrite flat files, replace subdirs wholesale.
28
+ * Deploy step: copy the agent snapshot into its home. Flat files are
29
+ * overwritten, subdirs replaced wholesale, extension files deployed so they
30
+ * auto-load on the next Pi start. No binary installs happen here.
53
31
  */
54
32
  function deployConfigs(agent, pkgRoot, { dryRun }) {
55
33
  const home = expandHome(agent.home);
@@ -64,6 +42,12 @@ function deployConfigs(agent, pkgRoot, { dryRun }) {
64
42
  for (const extra of agent.deploy.overwriteExtra ?? []) {
65
43
  copyFile(resolve(srcDir, extra.file), expandHome(extra.dest), { dryRun });
66
44
  }
45
+ // extensions: deploy each file flat into ~/.pi/agent/extensions/ so Pi
46
+ // auto-discovers it on the next start. Existing files with the same name
47
+ // are overwritten (snapshot semantics).
48
+ for (const file of agent.deploy.extensionFiles ?? []) {
49
+ copyFile(resolve(srcDir, "extensions", file), resolve(home, "extensions", file), { dryRun });
50
+ }
67
51
  }
68
52
 
69
53
  function deployReference(agent, pkgRoot, { dryRun }) {
@@ -73,89 +57,6 @@ function deployReference(agent, pkgRoot, { dryRun }) {
73
57
  copyDirFiles(src, dest, { dryRun });
74
58
  }
75
59
 
76
- /**
77
- * Skills step: let the user pick which skills to install (interactive TTY),
78
- * then install each selected one from remote via `npx skills add`
79
- * (https://skills.sh/). Non-interactive runs skip skill install entirely —
80
- * nothing is auto-installed.
81
- */
82
- async function installSkillsStep(agent, { dryRun, tty }) {
83
- const spec = agent.skills;
84
- if (!spec) return 0;
85
- let selected = [];
86
- if (tty) {
87
- selected = await checkbox({
88
- message: "Select skills to install:",
89
- choices: spec.specs.map((s) => ({
90
- name: s.spec,
91
- value: s.spec,
92
- description: s.description,
93
- checked: true,
94
- })),
95
- });
96
- if (selected.length === 0) {
97
- log.info("No skills selected — skipping skill install");
98
- return 0;
99
- }
100
- } else {
101
- log.info(`Non-interactive mode — skipping skill install (available: ${spec.specs.map((s) => s.spec).join(", ")})`);
102
- return 0;
103
- }
104
- for (const skill of selected) {
105
- const { repo, name } = parseSkillSpec(skill);
106
- const cmdline = `npx --yes skills add ${repo} --skill ${name} -g --agent ${spec.cliAgent} -y`;
107
- log.install(`${skill} → ${spec.cliAgent}`);
108
- const code = await runCommand(cmdline, { dryRun });
109
- if (code !== 0) return code;
110
- }
111
- return 0;
112
- }
113
-
114
- /**
115
- * Packages step: let the user pick which Pi extension packages to install
116
- * (interactive TTY), then install each via `pi install <source>`, which
117
- * writes the declaration into ~/.pi/agent/settings.json. Non-interactive
118
- * runs skip package install entirely — nothing is auto-installed.
119
- */
120
- async function installPackagesStep(agent, { dryRun, tty }) {
121
- const spec = agent.packages;
122
- if (!spec) return 0;
123
- let selected = [];
124
- if (tty) {
125
- selected = await checkbox({
126
- message: "Select Pi extension packages to install:",
127
- choices: spec.specs.map((p) => ({
128
- name: p.source,
129
- value: p.source,
130
- description: p.description,
131
- checked: false,
132
- })),
133
- });
134
- if (selected.length === 0) {
135
- log.info("No packages selected — skipping package install");
136
- return 0;
137
- }
138
- } else {
139
- const available = spec.specs.map((p) => p.source).join(", ");
140
- log.info(`Non-interactive mode — skipping package install (available: ${available})`);
141
- return 0;
142
- }
143
- for (const source of selected) {
144
- const cmdline = `pi install ${source}`;
145
- log.install(`${source} → ${agent.name}`);
146
- const code = await runCommand(cmdline, { dryRun });
147
- if (code !== 0) return code;
148
- }
149
- return 0;
150
- }
151
-
152
- /** Parse "owner/repo@skill" into { repo, name }. */
153
- function parseSkillSpec(spec) {
154
- const at = spec.lastIndexOf("@");
155
- if (at < 0) throw new Error(`skill spec missing @skill: ${spec}`);
156
- return { repo: spec.slice(0, at), name: spec.slice(at + 1) };
157
- }
158
-
159
60
  /**
160
61
  * Back up the agent's existing home to a tar (no-op if absent).
161
62
  */
@@ -164,26 +65,27 @@ function backupAgentHome(agent, backupDir, { dryRun }) {
164
65
  }
165
66
 
166
67
  /**
167
- * Initialize the configured agents. Returns 0 on success, non-zero on failure.
68
+ * Initialize the configured agents: (backup) → deploy configs (including the
69
+ * /pi-setup extension) → deploy reference docs. Returns 0 on success.
70
+ *
71
+ * Installing binaries, packages, skills, and CLI tools is deferred to the
72
+ * `/pi-setup` command inside Pi (see configs/pi/extensions/pi-setup.js).
168
73
  *
169
- * @param {{ dryRun?: boolean, backup?: boolean, tty?: boolean, agents?: Array }} opts
74
+ * @param {{ dryRun?: boolean, backup?: boolean, agents?: Array }} opts
170
75
  * @returns {Promise<number>}
171
76
  */
172
- export async function initialize({ dryRun = false, backup = false, tty = false, agents = AGENTS } = {}) {
77
+ export async function initialize({ dryRun = false, backup = false, agents = AGENTS } = {}) {
173
78
  const pkgRoot = packageRoot(import.meta.url);
174
79
  const backupDir = backup ? backupDirFor() : null;
175
80
  log.info(dryRun ? "Dry-run mode — no changes will be made" : "Initializing agent environments");
176
81
  if (backup) log.info(`Backing up existing agent homes to ${backupDir}`);
177
- let rc = 0;
178
82
  for (const agent of agents) {
179
83
  log.step(`Configure ${agent.name}`);
180
- rc ||= await installStep(agent, { dryRun });
181
84
  if (backup) backupAgentHome(agent, backupDir, { dryRun });
182
85
  deployConfigs(agent, pkgRoot, { dryRun });
183
86
  deployReference(agent, pkgRoot, { dryRun });
184
- rc ||= await installPackagesStep(agent, { dryRun, tty });
185
- rc ||= await installSkillsStep(agent, { dryRun, tty });
186
87
  }
187
- if (rc === 0) log.success("Setup complete");
188
- return rc;
88
+ log.success("Setup complete");
89
+ log.info("Run /pi-setup inside Pi to install packages, skills, and CLI tools (each confirmed individually)");
90
+ return 0;
189
91
  }
@@ -1,28 +0,0 @@
1
- // packages.mjs — Pi extension packages offered for install via `pi install`.
2
- // The settings.json snapshot ships WITHOUT a "packages" key; the initializer
3
- // prompts the user to pick which ones to install (interactive TTY only;
4
- // non-interactive runs skip package install entirely — nothing is
5
- // auto-installed).
6
-
7
- /**
8
- * Packages offered for installation. `source` is passed verbatim to
9
- * `pi install`, which writes the declaration into ~/.pi/agent/settings.json.
10
- */
11
- export const DEFAULT_PACKAGES = [
12
- {
13
- source: "npm:pi-footer",
14
- description: "Footer status line (token usage, model, cost).",
15
- },
16
- {
17
- source: "npm:pi-tps-meter",
18
- description: "Tokens-per-second meter for streaming responses.",
19
- },
20
- {
21
- source: "npm:@juicesharp/rpiv-pi",
22
- description: "RPIV pipeline skills (code review, commit, handoff, diagrams).",
23
- },
24
- {
25
- source: "https://github.com/cathrynlavery/diagram-design",
26
- description: "Branded diagram/chart generation skills.",
27
- },
28
- ];
@@ -1,19 +0,0 @@
1
- // skills.mjs — skill specs available to install from remote via `npx skills add`.
2
- // Each spec is "owner/repo@skill" (see https://skills.sh/).
3
- // The initializer prompts the user to pick which ones to install
4
- // (interactive TTY only; non-interactive runs skip skill install).
5
-
6
- /**
7
- * Skills offered for installation. Sourced from remote via
8
- * `npx skills add` (https://skills.sh/).
9
- */
10
- export const DEFAULT_SKILLS = [
11
- {
12
- spec: "mattpocock/skills@grill-me",
13
- description: "Matt Pocock — a relentless interview to sharpen a plan or design.",
14
- },
15
- {
16
- spec: "microsoft/playwright-cli@playwright-cli",
17
- description: "Microsoft — browser automation and Playwright test driving.",
18
- },
19
- ];
package/src/lib/exec.mjs DELETED
@@ -1,40 +0,0 @@
1
- // exec.mjs — exec + detect helpers that honor --dry-run.
2
- //
3
- // runCommand takes a single shell command string. In dry-run mode it prints
4
- // "[dry] <cmd>" instead of spawning. In real mode it spawns with shell:true
5
- // and inherited stdio, returning the exit code.
6
-
7
- import { spawn, spawnSync } from "node:child_process";
8
-
9
- /**
10
- * Run a shell command. If dryRun is true, only print the command.
11
- * Returns 0 on success, non-zero on failure. Resolves a Promise.
12
- */
13
- export function runCommand(cmdline, { dryRun = false, cwd } = {}) {
14
- if (dryRun) {
15
- console.log(`[dry] ${cmdline}`);
16
- return Promise.resolve(0);
17
- }
18
- return new Promise((resolve) => {
19
- const child = spawn(cmdline, { cwd, stdio: "inherit", shell: true });
20
- child.on("close", (code) => resolve(code ?? 1));
21
- child.on("error", () => resolve(1));
22
- });
23
- }
24
-
25
- /**
26
- * Detect whether a binary is on PATH. Synchronous — used by the detect step
27
- * to decide install-vs-skip. Returns boolean.
28
- *
29
- * The command is passed as a single string, NOT as an args array: args +
30
- * shell:true triggers Node's DEP0190 DeprecationWarning, which is printed
31
- * asynchronously — it lands inside the first interactive prompt that follows
32
- * and shifts the cursor, breaking @inquirer's screen clearing (stale frames).
33
- */
34
- export function detectBinary(name) {
35
- const result = spawnSync(`command -v ${name}`, {
36
- shell: true,
37
- stdio: "ignore",
38
- });
39
- return result.status === 0;
40
- }