@pify/cli 0.4.5 → 0.4.7
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 +1 -0
- package/catalog.json +21 -4
- package/dist/cli.js +25 -18
- package/dist/index.d.ts +7 -0
- package/dist/index.js +16 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,7 @@ pify completions powershell | Out-String | Invoke-Expression # add to $PROFILE
|
|
|
64
64
|
| Package | Description |
|
|
65
65
|
|---|---|
|
|
66
66
|
| `@pify/ask-question` | Structured questions on built-in dialogs: 1-4 questions, multi-select, Other free-text |
|
|
67
|
+
| `@pify/autopilot` | Let the main agent keep going on its own between turns — opt-in, hard-capped |
|
|
67
68
|
| `@pify/btw` | By-the-way side conversations: a read-only, codebase-aware side agent |
|
|
68
69
|
| `@pify/compact` | Proactive auto-compaction when the context window crosses a threshold |
|
|
69
70
|
| `@pify/goal` | Pin a session goal and keep the agent anchored to it |
|
package/catalog.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 30,
|
|
3
3
|
"org": "pifydev",
|
|
4
4
|
"scope": "@pify",
|
|
5
5
|
"remote": "https://raw.githubusercontent.com/pifydev/cli/main/catalog.json",
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
"description": "Structured questions on built-in dialogs: 1-4 questions, options with trade-offs, multi-select, Other free-text",
|
|
12
12
|
"status": "published"
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
"name": "autopilot",
|
|
16
|
+
"npm": "@pify/autopilot",
|
|
17
|
+
"repo": "https://github.com/pifydev/autopilot",
|
|
18
|
+
"description": "Let the main agent keep going on its own between turns — opt-in, hard-capped, never over your head",
|
|
19
|
+
"status": "published"
|
|
20
|
+
},
|
|
14
21
|
{
|
|
15
22
|
"name": "btw",
|
|
16
23
|
"npm": "@pify/btw",
|
|
@@ -23,7 +30,13 @@
|
|
|
23
30
|
"npm": "@pify/compact",
|
|
24
31
|
"repo": "https://github.com/pifydev/compact",
|
|
25
32
|
"description": "Proactive auto-compaction: compact between turns when the context window crosses a threshold",
|
|
26
|
-
"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
|
+
]
|
|
27
40
|
},
|
|
28
41
|
{
|
|
29
42
|
"name": "goal",
|
|
@@ -56,12 +69,15 @@
|
|
|
56
69
|
"name": "pretty",
|
|
57
70
|
"npm": "@pify/pretty",
|
|
58
71
|
"repo": "https://github.com/pifydev/pretty",
|
|
59
|
-
"description": "Compact theme-aware rendering for built-in tools: summaries, highlighted reads, word-level diff
|
|
72
|
+
"description": "Compact theme-aware rendering for built-in tools: summaries, highlighted reads, word-level diffs, pre-apply diff previews",
|
|
60
73
|
"status": "published",
|
|
61
74
|
"conflicts": [
|
|
62
75
|
"pi-pretty",
|
|
63
76
|
"pi-pretty-tui",
|
|
64
|
-
"pi-tool-display"
|
|
77
|
+
"pi-tool-display",
|
|
78
|
+
"pi-rtk-optimizer",
|
|
79
|
+
"pi-claude-code-ui",
|
|
80
|
+
"pi-compact-ui"
|
|
65
81
|
]
|
|
66
82
|
},
|
|
67
83
|
{
|
|
@@ -155,6 +171,7 @@
|
|
|
155
171
|
"description": "Every published @pify extension",
|
|
156
172
|
"packages": [
|
|
157
173
|
"ask-question",
|
|
174
|
+
"autopilot",
|
|
158
175
|
"btw",
|
|
159
176
|
"compact",
|
|
160
177
|
"goal",
|
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
|
-
|
|
874
|
-
|
|
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,
|
|
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
|
-
|
|
648
|
-
|
|
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,
|
|
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));
|