my-pi 0.1.21 → 0.1.22

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
@@ -276,9 +276,10 @@ still force-disable those extensions for the current process only.
276
276
 
277
277
  ### Themes
278
278
 
279
- `my-pi` ships a bundled theme pack from `./themes` and loads it into
280
- the runtime automatically. Pick a theme in `/settings`, or persist one
281
- via Pi settings JSON:
279
+ `my-pi` bundles `@spences10/pi-themes` and loads that theme pack into
280
+ the runtime automatically. Vanilla Pi users can install it separately
281
+ with `pi install npm:@spences10/pi-themes`. Pick a theme in
282
+ `/settings`, or persist one via Pi settings JSON:
282
283
 
283
284
  ```json
284
285
  {
@@ -381,6 +382,12 @@ and headless sessions skip it unless `MY_PI_MCP_PROJECT_CONFIG=allow`
381
382
  or `MY_PI_MCP_PROJECT_CONFIG=trust` is set. If both configs define the
382
383
  same server name, the trusted project config wins.
383
384
 
385
+ Use `/mcp` in interactive mode to open the searchable MCP server
386
+ modal. Enter/Space toggles servers on or off, updates the active tool
387
+ set, and persists the choice as `disabled`/`enabled` in `mcp.json`.
388
+ Use `/mcp backup`, `/mcp restore`, and `/mcp profile ...` to back up,
389
+ restore, save, and load reusable MCP server sets.
390
+
384
391
  ### Hooks
385
392
 
386
393
  Claude-style hooks are discovered from `.claude/settings.json`,
@@ -582,7 +589,11 @@ session start and shutdown when the local recall database exists.
582
589
 
583
590
  This repo is a pnpm workspace. The `my-pi` harness depends on reusable
584
591
  Pi packages via `workspace:*`, and those packages can also be
585
- published and installed into vanilla `pi` independently:
592
+ published and installed into vanilla `pi` independently. Shared helper
593
+ packages such as `@spences10/pi-child-env`,
594
+ `@spences10/pi-project-trust`, and `@spences10/pi-tui-modal` are
595
+ published only as dependencies and are not Pi packages to install via
596
+ `pi install`.
586
597
 
587
598
  ```bash
588
599
  pi install npm:@spences10/pi-redact
@@ -596,6 +607,7 @@ pi install npm:@spences10/pi-nopeek
596
607
  pi install npm:@spences10/pi-omnisearch
597
608
  pi install npm:@spences10/pi-sqlite-tools
598
609
  pi install npm:@spences10/pi-team-mode
610
+ pi install npm:@spences10/pi-themes
599
611
  ```
600
612
 
601
613
  - [`@spences10/pi-redact`](./packages/pi-redact/README.md) — output
@@ -621,6 +633,8 @@ pi install npm:@spences10/pi-team-mode
621
633
  - [`@spences10/pi-team-mode`](./packages/pi-team-mode/README.md) —
622
634
  local orchestrator/team mode with RPC teammates, tasks, and
623
635
  mailboxes
636
+ - [`@spences10/pi-themes`](./packages/pi-themes/README.md) — bundled
637
+ theme pack for Pi
624
638
 
625
639
  Each package README is the entry point for install instructions,
626
640
  commands, runtime behavior, and development notes.
@@ -647,6 +661,11 @@ packages/
647
661
  pi-nopeek/ Installable Pi package for nopeek reminders
648
662
  pi-omnisearch/ Installable Pi package for mcp-omnisearch reminders
649
663
  pi-sqlite-tools/ Installable Pi package for mcp-sqlite-tools reminders
664
+ pi-team-mode/ Installable Pi package for team orchestration
665
+ pi-themes/ Installable Pi theme pack
666
+ pi-child-env/ Shared support package, not a Pi package
667
+ pi-project-trust/ Shared support package, not a Pi package
668
+ pi-tui-modal/ Shared support package, not a Pi package
650
669
  .pi/
651
670
  presets.json Optional project prompt presets (JSON)
652
671
  presets/*.md Optional project prompt presets (Markdown files)
@@ -1,7 +1,7 @@
1
+ import { createRequire } from "node:module";
1
2
  import { BorderedLoader, InteractiveMode as InteractiveMode$1, SessionManager, convertToLlm, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, getAgentDir, runPrintMode as runPrintMode$1, runRpcMode as runRpcMode$1, serializeConversation } from "@mariozechner/pi-coding-agent";
2
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
4
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
5
  import confirm_destructive_extension from "@spences10/pi-confirm-destructive";
6
6
  import lsp_extension from "@spences10/pi-lsp";
7
7
  import mcp_extension from "@spences10/pi-mcp";
@@ -18,7 +18,8 @@ import { spawn } from "node:child_process";
18
18
  import { createHash } from "node:crypto";
19
19
  import { create_child_process_env } from "@spences10/pi-child-env";
20
20
  import { homedir } from "node:os";
21
- import { Container, SettingsList, Text, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
21
+ import { show_settings_modal } from "@spences10/pi-tui-modal";
22
+ import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
22
23
  import { complete } from "@mariozechner/pi-ai";
23
24
  //#region src/extensions/hooks-resolution/env.ts
24
25
  function create_child_process_env$1(explicit_env = {}, source_env = process.env) {
@@ -620,8 +621,8 @@ function find_builtin_extension(query) {
620
621
  }
621
622
  //#endregion
622
623
  //#region src/extensions/manager/index.ts
623
- const ENABLED$1 = "[x]";
624
- const DISABLED$1 = "[ ]";
624
+ const ENABLED$1 = "● enabled";
625
+ const DISABLED$1 = " disabled";
625
626
  function to_force_disabled_set(force_disabled) {
626
627
  return new Set(force_disabled ?? []);
627
628
  }
@@ -687,54 +688,24 @@ function create_extensions_extension(options = {}) {
687
688
  const states = resolve_builtin_extension_states(force_disabled);
688
689
  const initial_enabled = new Set(states.filter((state) => state.saved_enabled).map((state) => state.key));
689
690
  const current_enabled = new Set(initial_enabled);
690
- await ctx.ui.custom((tui, theme, _kb, done) => {
691
- const items = states.map(to_setting_item);
692
- const container = new Container();
693
- container.addChild({
694
- render: () => {
695
- const saved_enabled = current_enabled.size;
696
- const saved_disabled = states.length - saved_enabled;
697
- const enabled_now = [...current_enabled].filter((key) => !force_disabled.has(key)).length;
698
- const disabled_now = states.length - enabled_now;
699
- return [
700
- theme.fg("accent", theme.bold("Built-in extensions")),
701
- theme.fg("muted", `${saved_enabled} saved enabled • ${saved_disabled} saved disabled • ${enabled_now} enabled now • ${disabled_now} disabled now`),
702
- ""
703
- ];
704
- },
705
- invalidate: () => {}
706
- });
707
- const settings_list = new SettingsList(items, Math.min(Math.max(items.length + 4, 8), 16), {
708
- cursor: theme.fg("accent", "›"),
709
- label: (text, selected) => selected ? theme.fg("accent", text) : text,
710
- value: (text, selected) => {
711
- const color = text === ENABLED$1 ? "success" : "dim";
712
- const rendered = theme.fg(color, text);
713
- return selected ? theme.bold(theme.fg("accent", rendered)) : rendered;
714
- },
715
- description: (text) => theme.fg("muted", text),
716
- hint: (text) => theme.fg("dim", text)
717
- }, (id, new_value) => {
691
+ await show_settings_modal(ctx, {
692
+ title: "Built-in extensions",
693
+ subtitle: () => {
694
+ const saved_enabled = current_enabled.size;
695
+ const saved_disabled = states.length - saved_enabled;
696
+ const enabled_now = [...current_enabled].filter((key) => !force_disabled.has(key)).length;
697
+ return `${saved_enabled} saved enabled • ${saved_disabled} saved disabled • ${enabled_now} enabled now • ${states.length - enabled_now} disabled now`;
698
+ },
699
+ items: states.map(to_setting_item),
700
+ enable_search: true,
701
+ footer: "esc close • search filters • changes save immediately • CLI --no-* flags still win in this process",
702
+ on_change: (id, new_value) => {
718
703
  const key = id;
719
704
  const enabled = new_value === ENABLED$1;
720
705
  if (enabled) current_enabled.add(key);
721
706
  else current_enabled.delete(key);
722
707
  save_extension_enabled(key, enabled);
723
- }, () => done(void 0), { enableSearch: true });
724
- container.addChild(settings_list);
725
- container.addChild(new Text(theme.fg("dim", "esc close • search filters • changes save immediately • CLI --no-* flags still win in this process"), 0, 1));
726
- return {
727
- render(width) {
728
- return container.render(width);
729
- },
730
- invalidate() {
731
- container.invalidate();
732
- },
733
- handleInput(data) {
734
- settings_list.handleInput(data);
735
- tui.requestRender();
736
- }
737
- };
708
+ }
738
709
  });
739
710
  if (!sets_equal$1(initial_enabled, current_enabled)) {
740
711
  ctx.ui.notify(force_disabled.size > 0 ? "Reloading to apply updated built-in extensions. CLI --no-* flags still force-disable some extensions in this process." : "Reloading to apply updated built-in extensions...", "info");
@@ -826,10 +797,10 @@ create_extensions_extension();
826
797
  //#region src/extensions/prompt-presets/index.ts
827
798
  const PROJECT_PROMPT_PRESETS_ENV = "MY_PI_PROMPT_PRESETS_PROJECT";
828
799
  const PRESET_STATE_TYPE = "prompt-preset-state";
829
- const ENABLED = "[x]";
830
- const DISABLED = "[ ]";
831
- const SELECTED = "(x)";
832
- const UNSELECTED = "( )";
800
+ const ENABLED = "● enabled";
801
+ const DISABLED = " disabled";
802
+ const SELECTED = "● selected";
803
+ const UNSELECTED = "";
833
804
  const NONE_BASE_ID = "__base_none__";
834
805
  const DEFAULT_PROMPT_PRESETS = {
835
806
  terse: {
@@ -1549,21 +1520,13 @@ async function prompt_presets(pi) {
1549
1520
  else if (layer_ids.has(item.id)) item.currentValue = enabled_layers.has(item.id) ? ENABLED : DISABLED;
1550
1521
  }
1551
1522
  sync_values();
1552
- await ctx.ui.custom((tui, theme, _kb, done) => {
1553
- const list = new SettingsList(items, Math.min(Math.max(items.length + 4, 8), 24), {
1554
- cursor: theme.fg("accent", "›"),
1555
- label: (text, selected) => {
1556
- if (text.startsWith("──") && text.endsWith("──")) return theme.fg("dim", theme.bold(text));
1557
- return selected ? theme.fg("accent", text) : text;
1558
- },
1559
- value: (text, selected) => {
1560
- const color = text === ENABLED || text === SELECTED ? "success" : "dim";
1561
- const rendered = theme.fg(color, text);
1562
- return selected ? theme.bold(theme.fg("accent", rendered)) : rendered;
1563
- },
1564
- description: (text) => theme.fg("muted", text),
1565
- hint: (text) => theme.fg("dim", text)
1566
- }, (id, new_value) => {
1523
+ await show_settings_modal(ctx, {
1524
+ title: "Prompt presets",
1525
+ subtitle: () => `base: ${selected_base ?? "(none)"} • ${enabled_layers.size} layer(s) enabled`,
1526
+ items,
1527
+ max_visible: Math.min(Math.max(items.length + 4, 8), 24),
1528
+ enable_search: true,
1529
+ on_change: (id, new_value) => {
1567
1530
  if (id.startsWith("__header_")) return;
1568
1531
  if (base_ids.has(id)) {
1569
1532
  selected_base = new_value === SELECTED && id !== NONE_BASE_ID ? id : void 0;
@@ -1575,37 +1538,7 @@ async function prompt_presets(pi) {
1575
1538
  else enabled_layers.delete(id);
1576
1539
  sync_values();
1577
1540
  }
1578
- }, () => done(void 0), { enableSearch: true });
1579
- const container = new Container();
1580
- container.addChild({
1581
- render: () => [
1582
- theme.fg("accent", theme.bold("Prompt presets")),
1583
- theme.fg("muted", `base: ${selected_base ?? "(none)"} • ${enabled_layers.size} layer(s) enabled`),
1584
- ""
1585
- ],
1586
- invalidate: () => {}
1587
- });
1588
- container.addChild({
1589
- render(width) {
1590
- return list.render(width);
1591
- },
1592
- invalidate() {
1593
- list.invalidate();
1594
- }
1595
- });
1596
- container.addChild(new Text(theme.fg("dim", "search filters • enter toggles • esc close"), 0, 1));
1597
- return {
1598
- render(width) {
1599
- return container.render(width);
1600
- },
1601
- invalidate() {
1602
- container.invalidate();
1603
- },
1604
- handleInput(data) {
1605
- list.handleInput(data);
1606
- tui.requestRender();
1607
- }
1608
- };
1541
+ }
1609
1542
  });
1610
1543
  if (selected_base !== initial_base || !sets_equal(initial_layers, enabled_layers)) commit_state(ctx, selected_base, enabled_layers, { notify: "Updated prompt preset selection" });
1611
1544
  }
@@ -1971,7 +1904,7 @@ const BUILTIN_EXTENSION_FACTORIES = {
1971
1904
  "hooks-resolution": hooks_resolution_default,
1972
1905
  "team-mode": team_mode_extension
1973
1906
  };
1974
- const PACKAGE_THEME_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "themes");
1907
+ const PACKAGE_THEME_DIR = resolve(dirname(createRequire(import.meta.url).resolve("@spences10/pi-themes/package.json")), "themes");
1975
1908
  const PI_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
1976
1909
  const UNTRUSTED_CHILD_ENV_DEFAULTS = {
1977
1910
  MY_PI_CHILD_ENV_ALLOWLIST: "",
@@ -2069,7 +2002,7 @@ function create_extensions_override(managed_inline_paths) {
2069
2002
  };
2070
2003
  }
2071
2004
  async function create_my_pi(options = {}) {
2072
- const { cwd = process.cwd(), agent_dir, extensions = [], extensionFactories: user_factories = [], runtime_mode = "interactive", mcp = true, skills = true, filter_output = true, recall = true, nopeek = true, omnisearch = true, sqlite_tools = true, prompt_presets = true, lsp = true, session_name = true, confirm_destructive = true, hooks_resolution = true, team_mode = true, telemetry, telemetry_db_path, model, session_dir, system_prompt, append_system_prompt, untrusted_repo = false } = options;
2005
+ const { cwd = process.cwd(), agent_dir, extensions = [], extensionFactories: user_factories = [], runtime_mode = "interactive", mcp = true, skills = true, filter_output = true, recall = true, nopeek = true, omnisearch = true, sqlite_tools = true, prompt_presets = true, lsp = true, session_name = true, confirm_destructive = true, hooks_resolution = true, team_mode = true, telemetry, telemetry_db_path, model, selected_tools, selected_skills, session_dir, system_prompt, append_system_prompt, untrusted_repo = false } = options;
2073
2006
  if (untrusted_repo) apply_untrusted_repo_defaults();
2074
2007
  const effective_agent_dir = resolve_agent_dir(cwd, agent_dir);
2075
2008
  if (agent_dir) process.env[PI_AGENT_DIR_ENV] = effective_agent_dir;
@@ -2115,10 +2048,12 @@ async function create_my_pi(options = {}) {
2115
2048
  if (!is_builtin_extension_active(load_builtin_extensions_config(), "skills", force_disabled)) return base;
2116
2049
  const include_project_skills = is_resource_enabled(process.env.MY_PI_PROJECT_SKILLS);
2117
2050
  const skills_manager = create_skills_manager();
2051
+ const selected_skill_names = selected_skills?.length ? new Set(selected_skills) : void 0;
2118
2052
  return {
2119
2053
  ...base,
2120
2054
  skills: base.skills.filter((skill) => {
2121
2055
  if (!include_project_skills && is_project_local_skill_path(runtime_cwd, skill.filePath)) return false;
2056
+ if (selected_skill_names && !selected_skill_names.has(skill.name)) return false;
2122
2057
  return skills_manager.is_enabled_by_skill(skill.name, skill.filePath);
2123
2058
  })
2124
2059
  };
@@ -2131,7 +2066,8 @@ async function create_my_pi(options = {}) {
2131
2066
  services,
2132
2067
  sessionManager,
2133
2068
  sessionStartEvent,
2134
- ...requested_model ? { model: requested_model } : {}
2069
+ ...requested_model ? { model: requested_model } : {},
2070
+ ...selected_tools?.length ? { tools: selected_tools } : {}
2135
2071
  }),
2136
2072
  services,
2137
2073
  diagnostics: services.diagnostics
@@ -2146,4 +2082,4 @@ async function create_my_pi(options = {}) {
2146
2082
  //#endregion
2147
2083
  export { is_project_local_skill_path as a, runRpcMode$1 as c, get_force_disabled_builtins as i, apply_untrusted_repo_defaults as n, resolve_model_reference as o, create_my_pi as r, runPrintMode$1 as s, InteractiveMode$1 as t };
2148
2084
 
2149
- //# sourceMappingURL=api-SEiGG2V_.js.map
2085
+ //# sourceMappingURL=api-ByzD9jne.js.map