@pify/cli 0.2.2 → 0.3.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
@@ -24,6 +24,7 @@ pify setup --pi-version 0.84.4 # pin the version your team has tested
24
24
  pify setup --installer # run pi's official interactive installer (ps1/sh)
25
25
  pify list # show the @pify catalog with install state
26
26
  pify install goal task # short names resolve to @pify/goal, @pify/task
27
+ pify install suite # the whole suite in one go (also: core, agents)
27
28
  pify install goal -l -a # project scope, pre-approved (CI-friendly)
28
29
  pify remove goal
29
30
  pify update # update pi + every installed @pify package
package/catalog.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": 15,
2
+ "version": 16,
3
3
  "org": "pifydev",
4
4
  "scope": "@pify",
5
5
  "remote": "https://raw.githubusercontent.com/pifydev/cli/main/catalog.json",
@@ -102,5 +102,47 @@
102
102
  "description": "One toggle to auto-approve everything, with an undo trail: three-tier bash guard, file pre-images",
103
103
  "status": "published"
104
104
  }
105
+ ],
106
+ "bundles": [
107
+ {
108
+ "name": "suite",
109
+ "description": "Every published @pify extension",
110
+ "packages": [
111
+ "ask-question",
112
+ "btw",
113
+ "goal",
114
+ "memory",
115
+ "plan-mode",
116
+ "pretty",
117
+ "subagent",
118
+ "swarm",
119
+ "task",
120
+ "todo",
121
+ "usage",
122
+ "workflow",
123
+ "worktree",
124
+ "yolo"
125
+ ]
126
+ },
127
+ {
128
+ "name": "core",
129
+ "description": "The everyday four: task tracking, planning, memory, and a safety gate",
130
+ "packages": [
131
+ "todo",
132
+ "plan-mode",
133
+ "memory",
134
+ "yolo"
135
+ ]
136
+ },
137
+ {
138
+ "name": "agents",
139
+ "description": "The agent stack: subagents, parallel fan-out, scripted workflows, worktrees",
140
+ "packages": [
141
+ "subagent",
142
+ "swarm",
143
+ "workflow",
144
+ "worktree"
145
+ ]
146
+ }
105
147
  ]
106
148
  }
package/dist/cli.js CHANGED
@@ -492,8 +492,38 @@ function validateCatalog(data) {
492
492
  if (typeof p.npm !== "string" || !p.npm.startsWith("@pify/")) return false;
493
493
  if (p.status !== "published" && p.status !== "planned") return false;
494
494
  }
495
+ if (c.bundles !== void 0) {
496
+ if (!Array.isArray(c.bundles)) return false;
497
+ const known = new Set(c.packages.map((p) => p.name));
498
+ for (const entry of c.bundles) {
499
+ if (typeof entry !== "object" || entry === null) return false;
500
+ const b = entry;
501
+ if (typeof b.name !== "string" || !/^[a-z0-9-]+$/.test(b.name)) return false;
502
+ if (typeof b.description !== "string") return false;
503
+ if (!Array.isArray(b.packages) || b.packages.length === 0) return false;
504
+ for (const member of b.packages) {
505
+ if (typeof member !== "string" || !known.has(member)) return false;
506
+ }
507
+ }
508
+ }
495
509
  return true;
496
510
  }
511
+ function findBundle(catalog, name) {
512
+ const wanted = name.trim().toLowerCase();
513
+ if (catalog.packages.some((p) => p.name === wanted)) return null;
514
+ return catalog.bundles?.find((b) => b.name === wanted) ?? null;
515
+ }
516
+ function expandBundles(catalog, names) {
517
+ const expanded = [];
518
+ for (const name of names) {
519
+ const bundle = findBundle(catalog, name);
520
+ const members = bundle ? bundle.packages : [name];
521
+ for (const member of members) {
522
+ if (!expanded.includes(member)) expanded.push(member);
523
+ }
524
+ }
525
+ return expanded;
526
+ }
497
527
  async function loadCatalog(opts = {}) {
498
528
  const bundled = loadBundledCatalog();
499
529
  const cached = readCache();
@@ -659,7 +689,12 @@ async function install(names, opts) {
659
689
  throw usageError("Missing package name.", "Usage: pify install <name...> [-l] [-a]");
660
690
  }
661
691
  const catalog = await loadCatalog();
662
- const targets = names.map((name) => resolveInstallTarget(catalog, name));
692
+ for (const name of names) {
693
+ const bundle = findBundle(catalog, name);
694
+ if (bundle) step(`${bundle.name}: ${bundle.packages.length} packages \u2014 ${bundle.description}`);
695
+ }
696
+ const expanded = expandBundles(catalog, names);
697
+ const targets = expanded.map((name) => resolveInstallTarget(catalog, name));
663
698
  for (const target of targets) {
664
699
  if (target.explicit && (!target.inCatalog || target.status !== "published")) {
665
700
  warn(`${target.npm} is not in the catalog (or not marked published); installing anyway.`);
@@ -1288,7 +1323,11 @@ function parseArgsOptionsFor(spec) {
1288
1323
 
1289
1324
  // src/completions.ts
1290
1325
  function packageNames() {
1291
- return loadBundledCatalog().packages.map((p) => p.name);
1326
+ const catalog = loadBundledCatalog();
1327
+ return [
1328
+ ...catalog.packages.map((p) => p.name),
1329
+ ...(catalog.bundles ?? []).map((b) => b.name)
1330
+ ];
1292
1331
  }
1293
1332
  function allNames(spec) {
1294
1333
  return [spec.name, ...spec.aliases];
@@ -1451,7 +1490,7 @@ Usage:
1451
1490
 
1452
1491
  Commands:
1453
1492
  setup Install the pi coding agent (safe to re-run)
1454
- install <name...> Install @pify packages by short name
1493
+ install <name...> Install @pify packages, or a bundle (suite|core|agents)
1455
1494
  remove <name...> Remove installed @pify packages
1456
1495
  update [pi|<name...>] Update pi and installed @pify packages
1457
1496
  list Show the @pify catalog with install state
@@ -1526,6 +1565,11 @@ Install @pify packages by short name. Names resolve against the Pify catalog
1526
1565
  (goal -> npm:@pify/goal); the install itself is delegated to pi install.
1527
1566
  Exact version pins are accepted (goal@0.3.0); range specifiers are refused.
1528
1567
 
1568
+ A bundle name installs several at once \u2014 pify install suite (everything),
1569
+ core (todo, plan-mode, memory, yolo), or agents (subagent, swarm, workflow,
1570
+ worktree). Bundle members are resolved exactly like typed names, and a
1571
+ package name always wins over a bundle with the same name.
1572
+
1529
1573
  Options:
1530
1574
  -l, --local Project scope (.pi/settings.json, .pi/npm)
1531
1575
  -a, --approve Forward --approve to pi (scripted -l installs)
package/dist/index.d.ts CHANGED
@@ -11,6 +11,12 @@ interface CatalogPackage {
11
11
  /** "published" once it exists on the npm registry; "planned" before that. */
12
12
  status: "published" | "planned";
13
13
  }
14
+ /** A named set of packages installable in one go, e.g. `pify install suite`. */
15
+ interface CatalogBundle {
16
+ name: string;
17
+ description: string;
18
+ packages: string[];
19
+ }
14
20
  interface Catalog {
15
21
  version: number;
16
22
  org: string;
@@ -18,6 +24,8 @@ interface Catalog {
18
24
  /** URL of the always-current catalog on the org's main branch. */
19
25
  remote: string;
20
26
  packages: CatalogPackage[];
27
+ /** Optional (v0.3): older CLIs ignore this key and keep working. */
28
+ bundles?: CatalogBundle[];
21
29
  }
22
30
  declare function loadBundledCatalog(): Catalog;
23
31
  /**
@@ -29,6 +37,13 @@ declare function loadBundledCatalog(): Catalog;
29
37
  * radius of a compromised remote to "packages the org itself published".
30
38
  */
31
39
  declare function validateCatalog(data: unknown): data is Catalog;
40
+ /** The bundle by that name, or null. Package names always win over bundles. */
41
+ declare function findBundle(catalog: Catalog, name: string): CatalogBundle | null;
42
+ /**
43
+ * Expand bundle names into their members, keeping order and dropping
44
+ * duplicates — installing `suite goal` must not install goal twice.
45
+ */
46
+ declare function expandBundles(catalog: Catalog, names: string[]): string[];
32
47
  /**
33
48
  * Load the catalog. Resolution chain, newest `version` wins:
34
49
  * bundled snapshot (offline floor) -> ~/.pify cache -> remote fetch.
@@ -244,4 +259,4 @@ declare class PifyError extends Error {
244
259
  constructor(message: string, code?: ExitCodeValue, hint?: string);
245
260
  }
246
261
 
247
- export { COMMAND_SPECS, COMPLETION_SHELLS, type Catalog, type CatalogPackage, type CommandSpec, type CompletionShell, ExitCode, type FlagSpec, GLOBAL_FLAGS, INDEX_TS, type InstallSpec, type InstallTarget, type InstalledPifyPackage, NODE_FLOOR, PI_PACKAGE, type PiSettings, type PiStatus, PifyError, README_MD, TESTED_PI_VERSION, type TemplateVars, VERSION, agentDir, assertSafeArg, compareSemver, fetchLatestPiVersion, generateCompletion, installPi, installedPifyPackages, installedVersionOnDisk, interpolate, isOffline, loadBundledCatalog, loadCatalog, npmInstallPiArgs, officialInstallerCommand, packageJsonTemplate, parseArgsOptionsFor, parseInstallSpec, piStatus, readSettings, refreshCatalog, resolveInstallTarget, resolvePifyName, updatePi, validateCatalog, which };
262
+ export { COMMAND_SPECS, COMPLETION_SHELLS, type Catalog, type CatalogBundle, type CatalogPackage, type CommandSpec, type CompletionShell, ExitCode, type FlagSpec, GLOBAL_FLAGS, INDEX_TS, type InstallSpec, type InstallTarget, type InstalledPifyPackage, NODE_FLOOR, PI_PACKAGE, type PiSettings, type PiStatus, PifyError, README_MD, TESTED_PI_VERSION, type TemplateVars, VERSION, agentDir, assertSafeArg, compareSemver, expandBundles, fetchLatestPiVersion, findBundle, generateCompletion, installPi, installedPifyPackages, installedVersionOnDisk, interpolate, isOffline, loadBundledCatalog, loadCatalog, npmInstallPiArgs, officialInstallerCommand, packageJsonTemplate, parseArgsOptionsFor, parseInstallSpec, piStatus, readSettings, refreshCatalog, resolveInstallTarget, resolvePifyName, updatePi, validateCatalog, which };
package/dist/index.js CHANGED
@@ -150,8 +150,38 @@ function validateCatalog(data) {
150
150
  if (typeof p.npm !== "string" || !p.npm.startsWith("@pify/")) return false;
151
151
  if (p.status !== "published" && p.status !== "planned") return false;
152
152
  }
153
+ if (c.bundles !== void 0) {
154
+ if (!Array.isArray(c.bundles)) return false;
155
+ const known = new Set(c.packages.map((p) => p.name));
156
+ for (const entry of c.bundles) {
157
+ if (typeof entry !== "object" || entry === null) return false;
158
+ const b = entry;
159
+ if (typeof b.name !== "string" || !/^[a-z0-9-]+$/.test(b.name)) return false;
160
+ if (typeof b.description !== "string") return false;
161
+ if (!Array.isArray(b.packages) || b.packages.length === 0) return false;
162
+ for (const member of b.packages) {
163
+ if (typeof member !== "string" || !known.has(member)) return false;
164
+ }
165
+ }
166
+ }
153
167
  return true;
154
168
  }
169
+ function findBundle(catalog, name) {
170
+ const wanted = name.trim().toLowerCase();
171
+ if (catalog.packages.some((p) => p.name === wanted)) return null;
172
+ return catalog.bundles?.find((b) => b.name === wanted) ?? null;
173
+ }
174
+ function expandBundles(catalog, names) {
175
+ const expanded = [];
176
+ for (const name of names) {
177
+ const bundle = findBundle(catalog, name);
178
+ const members = bundle ? bundle.packages : [name];
179
+ for (const member of members) {
180
+ if (!expanded.includes(member)) expanded.push(member);
181
+ }
182
+ }
183
+ return expanded;
184
+ }
155
185
  async function loadCatalog(opts = {}) {
156
186
  const bundled = loadBundledCatalog();
157
187
  const cached = readCache();
@@ -644,7 +674,11 @@ function parseArgsOptionsFor(spec) {
644
674
 
645
675
  // src/completions.ts
646
676
  function packageNames() {
647
- return loadBundledCatalog().packages.map((p) => p.name);
677
+ const catalog = loadBundledCatalog();
678
+ return [
679
+ ...catalog.packages.map((p) => p.name),
680
+ ...(catalog.bundles ?? []).map((b) => b.name)
681
+ ];
648
682
  }
649
683
  function allNames(spec) {
650
684
  return [spec.name, ...spec.aliases];
@@ -921,7 +955,9 @@ export {
921
955
  agentDir,
922
956
  assertSafeArg,
923
957
  compareSemver,
958
+ expandBundles,
924
959
  fetchLatestPiVersion,
960
+ findBundle,
925
961
  generateCompletion,
926
962
  installPi,
927
963
  installedPifyPackages,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pify/cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Front door for the Pify suite: install and update the pi coding agent, manage @pify packages, and scaffold Pi Packages",
5
5
  "keywords": [
6
6
  "pify",