@pify/cli 0.4.6 → 0.4.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/catalog.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": 28,
2
+ "version": 31,
3
3
  "org": "pifydev",
4
4
  "scope": "@pify",
5
5
  "remote": "https://raw.githubusercontent.com/pifydev/cli/main/catalog.json",
@@ -30,7 +30,13 @@
30
30
  "npm": "@pify/compact",
31
31
  "repo": "https://github.com/pifydev/compact",
32
32
  "description": "Proactive auto-compaction: compact between turns when the context window crosses a threshold",
33
- "status": "published"
33
+ "status": "published",
34
+ "conflicts": [
35
+ "pi-auto-compact",
36
+ "@capyup/pi-auto-compact",
37
+ "@loka1/pi-auto-compact",
38
+ "@sting8k/pi-vcc"
39
+ ]
34
40
  },
35
41
  {
36
42
  "name": "goal",
@@ -63,12 +69,15 @@
63
69
  "name": "pretty",
64
70
  "npm": "@pify/pretty",
65
71
  "repo": "https://github.com/pifydev/pretty",
66
- "description": "Compact theme-aware rendering for built-in tools: summaries, highlighted reads, word-level diff emphasis",
72
+ "description": "Compact theme-aware rendering for built-in tools: summaries, highlighted reads, word-level diffs, pre-apply diff previews",
67
73
  "status": "published",
68
74
  "conflicts": [
69
75
  "pi-pretty",
70
76
  "pi-pretty-tui",
71
- "pi-tool-display"
77
+ "pi-tool-display",
78
+ "pi-rtk-optimizer",
79
+ "pi-claude-code-ui",
80
+ "pi-compact-ui"
72
81
  ]
73
82
  },
74
83
  {
@@ -103,14 +112,14 @@
103
112
  "name": "subagent",
104
113
  "npm": "@pify/subagent",
105
114
  "repo": "https://github.com/pifydev/subagent",
106
- "description": "Spawn scoped subagents: agent_run/agent_result, @agent mentions, custom agent types, ask_supervisor, results delivered not polled",
115
+ "description": "Spawn scoped subagents: agent_run/agent_result, @agent mentions, custom agent types, ask_supervisor, shell gates with bounded repair, task outcome separate from session status, results delivered not polled",
107
116
  "status": "published"
108
117
  },
109
118
  {
110
119
  "name": "swarm",
111
120
  "npm": "@pify/swarm",
112
121
  "repo": "https://github.com/pifydev/swarm",
113
- "description": "Run many pi agents in parallel: fan-out with per-item routing, concurrency queue, shared mailbox, results delivered not polled",
122
+ "description": "Run many pi agents in parallel: fan-out with per-item routing, needs-DAG with skip-on-upstream-failure, per-item shell gates, concurrency queue, shared mailbox, first hard failure interrupts early, results delivered not polled",
114
123
  "status": "published"
115
124
  },
116
125
  {
package/dist/cli.js CHANGED
@@ -210,19 +210,20 @@ function piStatus() {
210
210
  }
211
211
  async function fetchLatestPiVersion() {
212
212
  if (isOffline()) return null;
213
+ const controller = new AbortController();
214
+ const timer = setTimeout(() => controller.abort(), 5e3);
213
215
  try {
214
- const controller = new AbortController();
215
- const timer = setTimeout(() => controller.abort(), 5e3);
216
216
  const res = await fetch(LATEST_VERSION_URL, {
217
217
  signal: controller.signal,
218
218
  headers: { "user-agent": `pify/${VERSION}` }
219
219
  });
220
- clearTimeout(timer);
221
220
  if (!res.ok) return null;
222
221
  const data = await res.json();
223
222
  return typeof data.version === "string" ? data.version : null;
224
223
  } catch {
225
224
  return null;
225
+ } finally {
226
+ clearTimeout(timer);
226
227
  }
227
228
  }
228
229
  function compareSemver(a, b) {
@@ -432,20 +433,21 @@ function installedPifyPackages() {
432
433
  }
433
434
  async function fetchLatestPackageVersion(name) {
434
435
  if (isOffline()) return null;
436
+ const controller = new AbortController();
437
+ const timer = setTimeout(() => controller.abort(), 5e3);
435
438
  try {
436
- const controller = new AbortController();
437
- const timer = setTimeout(() => controller.abort(), 5e3);
438
439
  const res = await fetch(`https://registry.npmjs.org/@pify/${encodeURIComponent(name)}/latest`, {
439
440
  signal: controller.signal,
440
441
  headers: { "user-agent": `pify/${VERSION}`, accept: "application/json" },
441
442
  redirect: "error"
442
443
  });
443
- clearTimeout(timer);
444
444
  if (!res.ok) return null;
445
445
  const data = await res.json();
446
446
  return typeof data.version === "string" ? data.version : null;
447
447
  } catch {
448
448
  return null;
449
+ } finally {
450
+ clearTimeout(timer);
449
451
  }
450
452
  }
451
453
  function installedVersionOnDisk(name, scope) {
@@ -592,16 +594,17 @@ async function refreshCatalog() {
592
594
  async function fetchRemoteCatalog(defaultUrl, timeoutMs) {
593
595
  if (isOffline()) return null;
594
596
  const url = process.env.PIFY_CATALOG_URL || defaultUrl;
597
+ const controller = new AbortController();
598
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
595
599
  try {
596
- const controller = new AbortController();
597
- const timer = setTimeout(() => controller.abort(), timeoutMs);
598
600
  const res = await fetch(url, { signal: controller.signal });
599
- clearTimeout(timer);
600
601
  if (!res.ok) return null;
601
602
  const data = await res.json();
602
603
  return validateCatalog(data) ? data : null;
603
604
  } catch {
604
605
  return null;
606
+ } finally {
607
+ clearTimeout(timer);
605
608
  }
606
609
  }
607
610
  function readCache() {
@@ -866,31 +869,34 @@ function planApply(profile2, installedNow) {
866
869
  for (const entry of profile2.packages) {
867
870
  const current = installedNow.get(entry.name);
868
871
  if (!current) {
869
- rows.push({ name: entry.name, action: "install", from: null, to: entry.version });
872
+ rows.push({ name: entry.name, action: "install", from: null, to: entry.version, scope: entry.scope });
870
873
  continue;
871
874
  }
872
875
  const onDisk = installedVersionOnDisk(entry.name, current.scope).version;
873
- if (entry.version && onDisk && entry.version !== onDisk) {
874
- rows.push({ name: entry.name, action: "change", from: onDisk, to: entry.version });
876
+ const scopeMismatch = current.scope !== entry.scope;
877
+ const versionMismatch = Boolean(entry.version && onDisk && entry.version !== onDisk);
878
+ if (scopeMismatch || versionMismatch) {
879
+ rows.push({ name: entry.name, action: "change", from: onDisk, to: entry.version, scope: entry.scope });
875
880
  } else {
876
- rows.push({ name: entry.name, action: "keep", from: onDisk, to: entry.version });
881
+ rows.push({ name: entry.name, action: "keep", from: onDisk, to: entry.version, scope: entry.scope });
877
882
  }
878
883
  }
879
- for (const [name] of installedNow) {
884
+ for (const [name, cur] of installedNow) {
880
885
  if (!profile2.packages.some((p) => p.name === name)) {
881
- rows.push({ name, action: "extra", from: installedVersionOnDisk(name, "user").version, to: null });
886
+ rows.push({ name, action: "extra", from: installedVersionOnDisk(name, cur.scope).version, to: null, scope: cur.scope });
882
887
  }
883
888
  }
884
889
  return rows.sort((a, b) => a.name.localeCompare(b.name));
885
890
  }
886
891
  function describe(row) {
892
+ const scope = row.scope === "project" ? " [project]" : "";
887
893
  switch (row.action) {
888
894
  case "install":
889
- return `install ${row.to ? `@ ${row.to}` : "(latest)"}`;
895
+ return `install ${row.to ? `@ ${row.to}` : "(latest)"}${scope}`;
890
896
  case "change":
891
- return `${row.from} \u2192 ${row.to}`;
897
+ return `${row.from ?? "unknown"} \u2192 ${row.to ?? "latest"}${scope}`;
892
898
  case "keep":
893
- return `already ${row.from ?? "installed"}`;
899
+ return `already ${row.from ?? "installed"}${scope}`;
894
900
  case "extra":
895
901
  return `installed here, not in the profile (left alone)`;
896
902
  }
@@ -942,6 +948,7 @@ async function profile(args, opts) {
942
948
  for (const row of work) {
943
949
  const spec = row.to ? `npm:@pify/${row.name}@${row.to}` : `npm:@pify/${row.name}`;
944
950
  const argv = ["install", spec];
951
+ if (row.scope === "project") argv.push("-l");
945
952
  step(`pi ${argv.join(" ")}`);
946
953
  if (await delegate(argv) !== 0) failed.push(row.name);
947
954
  }
package/dist/index.d.ts CHANGED
@@ -21,6 +21,13 @@ interface PlanRow {
21
21
  action: PlanAction;
22
22
  from: string | null;
23
23
  to: string | null;
24
+ /**
25
+ * Where the package should end up. For install/change/keep this is the
26
+ * profile entry's recorded scope, so applying installs into the same scope it
27
+ * was saved from (project scope needs pi's `-l`). For `extra` rows there is
28
+ * no profile entry, so it reflects where the package is currently installed.
29
+ */
30
+ scope: "user" | "project";
24
31
  }
25
32
  /**
26
33
  * What applying this profile would do. Packages installed here but absent from
package/dist/index.js CHANGED
@@ -221,16 +221,17 @@ async function refreshCatalog() {
221
221
  async function fetchRemoteCatalog(defaultUrl, timeoutMs) {
222
222
  if (isOffline()) return null;
223
223
  const url = process.env.PIFY_CATALOG_URL || defaultUrl;
224
+ const controller = new AbortController();
225
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
224
226
  try {
225
- const controller = new AbortController();
226
- const timer = setTimeout(() => controller.abort(), timeoutMs);
227
227
  const res = await fetch(url, { signal: controller.signal });
228
- clearTimeout(timer);
229
228
  if (!res.ok) return null;
230
229
  const data = await res.json();
231
230
  return validateCatalog(data) ? data : null;
232
231
  } catch {
233
232
  return null;
233
+ } finally {
234
+ clearTimeout(timer);
234
235
  }
235
236
  }
236
237
  function readCache() {
@@ -399,19 +400,20 @@ function piStatus() {
399
400
  }
400
401
  async function fetchLatestPiVersion() {
401
402
  if (isOffline()) return null;
403
+ const controller = new AbortController();
404
+ const timer = setTimeout(() => controller.abort(), 5e3);
402
405
  try {
403
- const controller = new AbortController();
404
- const timer = setTimeout(() => controller.abort(), 5e3);
405
406
  const res = await fetch(LATEST_VERSION_URL, {
406
407
  signal: controller.signal,
407
408
  headers: { "user-agent": `pify/${VERSION}` }
408
409
  });
409
- clearTimeout(timer);
410
410
  if (!res.ok) return null;
411
411
  const data = await res.json();
412
412
  return typeof data.version === "string" ? data.version : null;
413
413
  } catch {
414
414
  return null;
415
+ } finally {
416
+ clearTimeout(timer);
415
417
  }
416
418
  }
417
419
  function compareSemver(a, b) {
@@ -640,19 +642,21 @@ function planApply(profile, installedNow) {
640
642
  for (const entry of profile.packages) {
641
643
  const current = installedNow.get(entry.name);
642
644
  if (!current) {
643
- rows.push({ name: entry.name, action: "install", from: null, to: entry.version });
645
+ rows.push({ name: entry.name, action: "install", from: null, to: entry.version, scope: entry.scope });
644
646
  continue;
645
647
  }
646
648
  const onDisk = installedVersionOnDisk(entry.name, current.scope).version;
647
- if (entry.version && onDisk && entry.version !== onDisk) {
648
- rows.push({ name: entry.name, action: "change", from: onDisk, to: entry.version });
649
+ const scopeMismatch = current.scope !== entry.scope;
650
+ const versionMismatch = Boolean(entry.version && onDisk && entry.version !== onDisk);
651
+ if (scopeMismatch || versionMismatch) {
652
+ rows.push({ name: entry.name, action: "change", from: onDisk, to: entry.version, scope: entry.scope });
649
653
  } else {
650
- rows.push({ name: entry.name, action: "keep", from: onDisk, to: entry.version });
654
+ rows.push({ name: entry.name, action: "keep", from: onDisk, to: entry.version, scope: entry.scope });
651
655
  }
652
656
  }
653
- for (const [name] of installedNow) {
657
+ for (const [name, cur] of installedNow) {
654
658
  if (!profile.packages.some((p) => p.name === name)) {
655
- rows.push({ name, action: "extra", from: installedVersionOnDisk(name, "user").version, to: null });
659
+ rows.push({ name, action: "extra", from: installedVersionOnDisk(name, cur.scope).version, to: null, scope: cur.scope });
656
660
  }
657
661
  }
658
662
  return rows.sort((a, b) => a.name.localeCompare(b.name));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pify/cli",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
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",