@pify/cli 0.1.3 → 0.2.1
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 +16 -4
- package/catalog.json +44 -23
- package/dist/cli.js +305 -99
- package/dist/index.d.ts +34 -1
- package/dist/index.js +243 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,15 @@ pify init my-extension # scaffold a new Pi Package
|
|
|
34
34
|
|
|
35
35
|
Every command supports `--help`; `install`/`remove`/`update` support `--dry-run`; `list`/`doctor` support `--json`.
|
|
36
36
|
|
|
37
|
+
**Tab completion** — generated from the live command registry (commands, aliases, flags, and package names all complete):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
eval "$(pify completions bash)" # bash — add to ~/.bashrc
|
|
41
|
+
eval "$(pify completions zsh)" # zsh — add to ~/.zshrc
|
|
42
|
+
pify completions fish > ~/.config/fish/completions/pify.fish
|
|
43
|
+
pify completions powershell | Out-String | Invoke-Expression # add to $PROFILE
|
|
44
|
+
```
|
|
45
|
+
|
|
37
46
|
## How it relates to `pi`
|
|
38
47
|
|
|
39
48
|
`pify` deliberately does **not** replace pi's package manager. Every package operation delegates to `pi install` / `pi remove` / `pi update`, which own `~/.pi/agent/settings.json` and the extension npm workspace — pify never edits pi's settings. What `pify` adds:
|
|
@@ -48,16 +57,19 @@ Every command supports `--help`; `install`/`remove`/`update` support `--dry-run`
|
|
|
48
57
|
|
|
49
58
|
| Package | Description |
|
|
50
59
|
|---|---|
|
|
51
|
-
| `@pify/
|
|
60
|
+
| `@pify/ask-question` | Structured questions on built-in dialogs: 1-4 questions, multi-select, Other free-text |
|
|
61
|
+
| `@pify/btw` | By-the-way side conversations: a read-only, codebase-aware side agent |
|
|
52
62
|
| `@pify/goal` | Pin a session goal and keep the agent anchored to it |
|
|
53
63
|
| `@pify/memory` | Persistent memory across pi sessions |
|
|
54
64
|
| `@pify/plan-mode` | Read-only planning mode with approve-then-execute gate |
|
|
55
65
|
| `@pify/pretty` | Prettier TUI rendering for tool calls, diffs, markdown |
|
|
56
66
|
| `@pify/subagent` | Spawn scoped subagents from within a pi session |
|
|
57
67
|
| `@pify/swarm` | Coordinate multiple pi agents working in parallel |
|
|
58
|
-
| `@pify/task` |
|
|
59
|
-
| `@pify/
|
|
60
|
-
| `@pify/
|
|
68
|
+
| `@pify/task` | Task tracking: dependency graph, evidence-gated completion, reminders |
|
|
69
|
+
| `@pify/todo` | Agent working-memory checklist: TodoWrite-style, next-item surfacing |
|
|
70
|
+
| `@pify/usage` | Token and cost reporting: live footer, local-history dashboard |
|
|
71
|
+
| `@pify/workflow` | Deterministic agent orchestration: CC-style workflow scripts |
|
|
72
|
+
| `@pify/worktree` | Safe git-worktree management with safety rails and merge-back |
|
|
61
73
|
| `@pify/yolo` | Auto-approve everything, with an undo trail |
|
|
62
74
|
|
|
63
75
|
The catalog ships inside the CLI and refreshes (at most daily) from [`catalog.json` on `main`](https://github.com/pifydev/cli/blob/main/catalog.json), so newly published packages appear without a CLI update. A fetched catalog is validated before use — every entry must stay inside the `@pify` scope — and any invalid document is discarded entirely. Overrides: `PIFY_CATALOG_URL` (remote URL), `PIFY_OFFLINE=1` or `PI_OFFLINE=1` (skip all network lookups).
|
package/catalog.json
CHANGED
|
@@ -1,85 +1,106 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 15,
|
|
3
3
|
"org": "pifydev",
|
|
4
4
|
"scope": "@pify",
|
|
5
5
|
"remote": "https://raw.githubusercontent.com/pifydev/cli/main/catalog.json",
|
|
6
6
|
"packages": [
|
|
7
|
+
{
|
|
8
|
+
"name": "ask-question",
|
|
9
|
+
"npm": "@pify/ask-question",
|
|
10
|
+
"repo": "https://github.com/pifydev/ask-question",
|
|
11
|
+
"description": "CC AskUserQuestion-shaped tool on built-in dialogs: 1-4 questions, multi-select, Other free-text",
|
|
12
|
+
"status": "published"
|
|
13
|
+
},
|
|
7
14
|
{
|
|
8
15
|
"name": "btw",
|
|
9
16
|
"npm": "@pify/btw",
|
|
10
17
|
"repo": "https://github.com/pifydev/btw",
|
|
11
|
-
"description": "By-the-way
|
|
12
|
-
"status": "
|
|
18
|
+
"description": "By-the-way side conversations: a read-only, codebase-aware side agent in a widget",
|
|
19
|
+
"status": "published"
|
|
13
20
|
},
|
|
14
21
|
{
|
|
15
22
|
"name": "goal",
|
|
16
23
|
"npm": "@pify/goal",
|
|
17
24
|
"repo": "https://github.com/pifydev/goal",
|
|
18
|
-
"description": "Pin a session goal and keep the agent anchored to it",
|
|
19
|
-
"status": "
|
|
25
|
+
"description": "Pin a session goal and keep the agent anchored to it: settled-idle continuation with safety limits",
|
|
26
|
+
"status": "published"
|
|
20
27
|
},
|
|
21
28
|
{
|
|
22
29
|
"name": "memory",
|
|
23
30
|
"npm": "@pify/memory",
|
|
24
31
|
"repo": "https://github.com/pifydev/memory",
|
|
25
|
-
"description": "Persistent memory
|
|
26
|
-
"status": "
|
|
32
|
+
"description": "Persistent memory: two-tier markdown storage, FTS5 search, secret-scanned writes",
|
|
33
|
+
"status": "published"
|
|
27
34
|
},
|
|
28
35
|
{
|
|
29
36
|
"name": "plan-mode",
|
|
30
37
|
"npm": "@pify/plan-mode",
|
|
31
38
|
"repo": "https://github.com/pifydev/plan-mode",
|
|
32
|
-
"description": "Read-only planning mode with an
|
|
33
|
-
"status": "
|
|
39
|
+
"description": "Read-only planning mode with an enforced approve-then-execute gate: tool policy, plan files, fresh-session handoff",
|
|
40
|
+
"status": "published"
|
|
34
41
|
},
|
|
35
42
|
{
|
|
36
43
|
"name": "pretty",
|
|
37
44
|
"npm": "@pify/pretty",
|
|
38
45
|
"repo": "https://github.com/pifydev/pretty",
|
|
39
|
-
"description": "
|
|
40
|
-
"status": "
|
|
46
|
+
"description": "Compact theme-aware rendering for built-in tools: summaries, highlighted reads, colorized diffs",
|
|
47
|
+
"status": "published"
|
|
41
48
|
},
|
|
42
49
|
{
|
|
43
50
|
"name": "subagent",
|
|
44
51
|
"npm": "@pify/subagent",
|
|
45
52
|
"repo": "https://github.com/pifydev/subagent",
|
|
46
|
-
"description": "Spawn scoped subagents
|
|
47
|
-
"status": "
|
|
53
|
+
"description": "Spawn scoped subagents: agent_run/agent_result, custom agent types, turn caps and tool allowlists",
|
|
54
|
+
"status": "published"
|
|
48
55
|
},
|
|
49
56
|
{
|
|
50
57
|
"name": "swarm",
|
|
51
58
|
"npm": "@pify/swarm",
|
|
52
59
|
"repo": "https://github.com/pifydev/swarm",
|
|
53
|
-
"description": "Coordinate multiple pi agents
|
|
54
|
-
"status": "
|
|
60
|
+
"description": "Coordinate multiple pi agents in parallel: fan-out with auto-routing, concurrency queue, aggregated reports",
|
|
61
|
+
"status": "published"
|
|
55
62
|
},
|
|
56
63
|
{
|
|
57
64
|
"name": "task",
|
|
58
65
|
"npm": "@pify/task",
|
|
59
66
|
"repo": "https://github.com/pifydev/task",
|
|
60
|
-
"description": "
|
|
61
|
-
"status": "
|
|
67
|
+
"description": "Task tracking with dependency graph, evidence-gated completion, live widget, CC-style reminders",
|
|
68
|
+
"status": "published"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "todo",
|
|
72
|
+
"npm": "@pify/todo",
|
|
73
|
+
"repo": "https://github.com/pifydev/todo",
|
|
74
|
+
"description": "Agent working-memory checklist: CC TodoWrite-style complete-replacement, next-item surfacing, live widget",
|
|
75
|
+
"status": "published"
|
|
62
76
|
},
|
|
63
77
|
{
|
|
64
78
|
"name": "usage",
|
|
65
79
|
"npm": "@pify/usage",
|
|
66
80
|
"repo": "https://github.com/pifydev/usage",
|
|
67
|
-
"description": "Token and cost usage
|
|
68
|
-
"status": "
|
|
81
|
+
"description": "Token and cost reporting: live footer, /usage dashboard over local session history, zero network",
|
|
82
|
+
"status": "published"
|
|
69
83
|
},
|
|
70
84
|
{
|
|
71
85
|
"name": "workflow",
|
|
72
86
|
"npm": "@pify/workflow",
|
|
73
87
|
"repo": "https://github.com/pifydev/workflow",
|
|
74
|
-
"description": "Deterministic
|
|
75
|
-
"status": "
|
|
88
|
+
"description": "Deterministic agent orchestration: CC-style workflow scripts with agent()/parallel()/pipeline()",
|
|
89
|
+
"status": "published"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "worktree",
|
|
93
|
+
"npm": "@pify/worktree",
|
|
94
|
+
"repo": "https://github.com/pifydev/worktree",
|
|
95
|
+
"description": "Safe git-worktree management: create/list/merge/remove with safety rails, no shell interpolation",
|
|
96
|
+
"status": "published"
|
|
76
97
|
},
|
|
77
98
|
{
|
|
78
99
|
"name": "yolo",
|
|
79
100
|
"npm": "@pify/yolo",
|
|
80
101
|
"repo": "https://github.com/pifydev/yolo",
|
|
81
|
-
"description": "One toggle to auto-approve everything, with an undo trail",
|
|
82
|
-
"status": "
|
|
102
|
+
"description": "One toggle to auto-approve everything, with an undo trail: three-tier bash guard, file pre-images",
|
|
103
|
+
"status": "published"
|
|
83
104
|
}
|
|
84
105
|
]
|
|
85
106
|
}
|
package/dist/cli.js
CHANGED
|
@@ -762,12 +762,12 @@ async function update(targets, opts) {
|
|
|
762
762
|
const updateEverything = targets.length === 0;
|
|
763
763
|
const piOnly = targets.length === 1 && (targets[0] === "pi" || targets[0] === "self");
|
|
764
764
|
const installed = installedPifyPackages();
|
|
765
|
-
let
|
|
765
|
+
let packageNames2 = [];
|
|
766
766
|
if (updateEverything) {
|
|
767
|
-
|
|
767
|
+
packageNames2 = [...installed.keys()].sort();
|
|
768
768
|
} else if (!piOnly) {
|
|
769
|
-
|
|
770
|
-
for (const name of
|
|
769
|
+
packageNames2 = targets.map(resolvePifyName);
|
|
770
|
+
for (const name of packageNames2) {
|
|
771
771
|
if (!installed.has(name)) {
|
|
772
772
|
throw notFoundError(`${name} is not installed.`, `pify install ${name}`);
|
|
773
773
|
}
|
|
@@ -775,7 +775,7 @@ async function update(targets, opts) {
|
|
|
775
775
|
}
|
|
776
776
|
if (opts.dryRun) {
|
|
777
777
|
if (updateEverything || piOnly) out("pi update");
|
|
778
|
-
for (const name of
|
|
778
|
+
for (const name of packageNames2) out(`pi update npm:@pify/${name}`);
|
|
779
779
|
if (updateEverything) out("refresh catalog");
|
|
780
780
|
return 0;
|
|
781
781
|
}
|
|
@@ -789,20 +789,20 @@ async function update(targets, opts) {
|
|
|
789
789
|
if (code !== 0) failed.push("pi");
|
|
790
790
|
}
|
|
791
791
|
}
|
|
792
|
-
if (
|
|
792
|
+
if (packageNames2.length > 0 && !piStatus().installed) {
|
|
793
793
|
throw new PifyError(
|
|
794
794
|
"pi is required to update packages.",
|
|
795
795
|
ExitCode.ENVIRONMENT,
|
|
796
796
|
"Run pify setup first."
|
|
797
797
|
);
|
|
798
798
|
}
|
|
799
|
-
for (const name of
|
|
799
|
+
for (const name of packageNames2) {
|
|
800
800
|
const args = ["update", `npm:@pify/${name}`];
|
|
801
801
|
step(`pi ${args.join(" ")}`);
|
|
802
802
|
const code = await delegate(args);
|
|
803
803
|
if (code !== 0) failed.push(name);
|
|
804
804
|
}
|
|
805
|
-
if (updateEverything &&
|
|
805
|
+
if (updateEverything && packageNames2.length === 0) {
|
|
806
806
|
hint("No @pify packages installed; nothing more to update.");
|
|
807
807
|
}
|
|
808
808
|
if (updateEverything) {
|
|
@@ -1195,6 +1195,244 @@ async function init(dirArg, opts) {
|
|
|
1195
1195
|
return 0;
|
|
1196
1196
|
}
|
|
1197
1197
|
|
|
1198
|
+
// src/registry.ts
|
|
1199
|
+
var GLOBAL_FLAGS = [
|
|
1200
|
+
{ name: "help", short: "h", description: "Show help" },
|
|
1201
|
+
{ name: "version", short: "v", description: "Print the pify version" },
|
|
1202
|
+
{ name: "no-color", description: "Disable colored output" }
|
|
1203
|
+
];
|
|
1204
|
+
var COMMAND_SPECS = [
|
|
1205
|
+
{
|
|
1206
|
+
name: "setup",
|
|
1207
|
+
aliases: [],
|
|
1208
|
+
description: "Install the pi coding agent (safe to re-run)",
|
|
1209
|
+
positional: "none",
|
|
1210
|
+
flags: [
|
|
1211
|
+
{ name: "force", description: "Reinstall pi even if present" },
|
|
1212
|
+
{ name: "pi-version", takesValue: true, description: "Install an exact pi version" },
|
|
1213
|
+
{ name: "installer", description: "Run pi's official interactive installer" }
|
|
1214
|
+
]
|
|
1215
|
+
},
|
|
1216
|
+
{
|
|
1217
|
+
name: "install",
|
|
1218
|
+
aliases: ["i", "add"],
|
|
1219
|
+
description: "Install @pify packages by short name",
|
|
1220
|
+
positional: "packages",
|
|
1221
|
+
flags: [
|
|
1222
|
+
{ name: "local", short: "l", description: "Project scope" },
|
|
1223
|
+
{ name: "approve", short: "a", description: "Forward --approve to pi" },
|
|
1224
|
+
{ name: "dry-run", description: "Print the pi commands without running them" }
|
|
1225
|
+
]
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
name: "remove",
|
|
1229
|
+
aliases: ["rm", "uninstall"],
|
|
1230
|
+
description: "Remove installed @pify packages",
|
|
1231
|
+
positional: "packages",
|
|
1232
|
+
flags: [
|
|
1233
|
+
{ name: "local", short: "l", description: "Project scope" },
|
|
1234
|
+
{ name: "approve", short: "a", description: "Forward --approve to pi" },
|
|
1235
|
+
{ name: "dry-run", description: "Print the pi commands without running them" }
|
|
1236
|
+
]
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
name: "update",
|
|
1240
|
+
aliases: ["up"],
|
|
1241
|
+
description: "Update pi and installed @pify packages",
|
|
1242
|
+
positional: "updateTargets",
|
|
1243
|
+
flags: [
|
|
1244
|
+
{ name: "catalog", description: "Refresh the catalog cache only" },
|
|
1245
|
+
{ name: "dry-run", description: "Print the update plan without executing" }
|
|
1246
|
+
]
|
|
1247
|
+
},
|
|
1248
|
+
{
|
|
1249
|
+
name: "list",
|
|
1250
|
+
aliases: ["ls"],
|
|
1251
|
+
description: "Show the @pify catalog with install state",
|
|
1252
|
+
positional: "none",
|
|
1253
|
+
flags: [{ name: "json", description: "Machine-readable output" }]
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
name: "doctor",
|
|
1257
|
+
aliases: [],
|
|
1258
|
+
description: "Diagnose the local pi/pify environment",
|
|
1259
|
+
positional: "none",
|
|
1260
|
+
flags: [{ name: "json", description: "Machine-readable output" }]
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
name: "init",
|
|
1264
|
+
aliases: [],
|
|
1265
|
+
description: "Scaffold a new Pi Package",
|
|
1266
|
+
positional: "directory",
|
|
1267
|
+
flags: [
|
|
1268
|
+
{ name: "name", takesValue: true, description: "npm package name" },
|
|
1269
|
+
{ name: "description", takesValue: true, description: "Package description" }
|
|
1270
|
+
]
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
name: "completions",
|
|
1274
|
+
aliases: [],
|
|
1275
|
+
description: "Print a shell completion script",
|
|
1276
|
+
positional: "shell",
|
|
1277
|
+
flags: []
|
|
1278
|
+
}
|
|
1279
|
+
];
|
|
1280
|
+
var COMPLETION_SHELLS = ["bash", "zsh", "fish", "powershell"];
|
|
1281
|
+
function parseArgsOptionsFor(spec) {
|
|
1282
|
+
const options = {};
|
|
1283
|
+
for (const flag of spec.flags) {
|
|
1284
|
+
options[flag.name] = flag.takesValue ? { type: "string", ...flag.short ? { short: flag.short } : {} } : { type: "boolean", default: false, ...flag.short ? { short: flag.short } : {} };
|
|
1285
|
+
}
|
|
1286
|
+
return options;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// src/completions.ts
|
|
1290
|
+
function packageNames() {
|
|
1291
|
+
return loadBundledCatalog().packages.map((p) => p.name);
|
|
1292
|
+
}
|
|
1293
|
+
function allNames(spec) {
|
|
1294
|
+
return [spec.name, ...spec.aliases];
|
|
1295
|
+
}
|
|
1296
|
+
function flagTokens(spec) {
|
|
1297
|
+
return spec.flags.flatMap((f) => [`--${f.name}`, ...f.short ? [`-${f.short}`] : []]);
|
|
1298
|
+
}
|
|
1299
|
+
function positionalWords(spec) {
|
|
1300
|
+
switch (spec.positional) {
|
|
1301
|
+
case "packages":
|
|
1302
|
+
return packageNames();
|
|
1303
|
+
case "updateTargets":
|
|
1304
|
+
return ["pi", ...packageNames()];
|
|
1305
|
+
case "shell":
|
|
1306
|
+
return [...COMPLETION_SHELLS];
|
|
1307
|
+
default:
|
|
1308
|
+
return [];
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function bashScript() {
|
|
1312
|
+
const commands = COMMAND_SPECS.flatMap(allNames).join(" ");
|
|
1313
|
+
const cases = COMMAND_SPECS.map((spec) => {
|
|
1314
|
+
const words = [...positionalWords(spec), ...flagTokens(spec)].join(" ");
|
|
1315
|
+
return ` ${allNames(spec).join("|")}) words="${words}" ;;`;
|
|
1316
|
+
}).join("\n");
|
|
1317
|
+
return `# bash completion for pify \u2014 generated by \`pify completions bash\`
|
|
1318
|
+
# Install: eval "$(pify completions bash)" (or save to a bash-completion dir)
|
|
1319
|
+
_pify_completions() {
|
|
1320
|
+
local cur words
|
|
1321
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
1322
|
+
if [ "$COMP_CWORD" -eq 1 ]; then
|
|
1323
|
+
COMPREPLY=($(compgen -W "${commands} ${GLOBAL_FLAGS.map((f) => `--${f.name}`).join(" ")}" -- "$cur"))
|
|
1324
|
+
return
|
|
1325
|
+
fi
|
|
1326
|
+
case "\${COMP_WORDS[1]}" in
|
|
1327
|
+
${cases}
|
|
1328
|
+
*) words="" ;;
|
|
1329
|
+
esac
|
|
1330
|
+
COMPREPLY=($(compgen -W "$words" -- "$cur"))
|
|
1331
|
+
}
|
|
1332
|
+
complete -F _pify_completions pify
|
|
1333
|
+
`;
|
|
1334
|
+
}
|
|
1335
|
+
function zshScript() {
|
|
1336
|
+
const commandDescs = COMMAND_SPECS.flatMap(
|
|
1337
|
+
(spec) => allNames(spec).map((n) => ` '${n}:${spec.description.replace(/'/g, "")}'`)
|
|
1338
|
+
).join("\n");
|
|
1339
|
+
const cases = COMMAND_SPECS.map((spec) => {
|
|
1340
|
+
const words = [...positionalWords(spec), ...flagTokens(spec)].map((w) => `'${w}'`).join(" ");
|
|
1341
|
+
return ` ${allNames(spec).join("|")}) _values 'value' ${words || "''"} ;;`;
|
|
1342
|
+
}).join("\n");
|
|
1343
|
+
return `#compdef pify
|
|
1344
|
+
# zsh completion for pify \u2014 generated by \`pify completions zsh\`
|
|
1345
|
+
# Install: eval "$(pify completions zsh)" (or save into a directory on $fpath)
|
|
1346
|
+
_pify() {
|
|
1347
|
+
local -a commands
|
|
1348
|
+
commands=(
|
|
1349
|
+
${commandDescs}
|
|
1350
|
+
)
|
|
1351
|
+
if (( CURRENT == 2 )); then
|
|
1352
|
+
_describe 'command' commands
|
|
1353
|
+
return
|
|
1354
|
+
fi
|
|
1355
|
+
case "$words[2]" in
|
|
1356
|
+
${cases}
|
|
1357
|
+
esac
|
|
1358
|
+
}
|
|
1359
|
+
_pify "$@"
|
|
1360
|
+
`;
|
|
1361
|
+
}
|
|
1362
|
+
function fishScript() {
|
|
1363
|
+
const lines = [
|
|
1364
|
+
`# fish completion for pify \u2014 generated by \`pify completions fish\``,
|
|
1365
|
+
`# Install: pify completions fish > ~/.config/fish/completions/pify.fish`,
|
|
1366
|
+
`complete -c pify -f`
|
|
1367
|
+
];
|
|
1368
|
+
for (const spec of COMMAND_SPECS) {
|
|
1369
|
+
for (const name of allNames(spec)) {
|
|
1370
|
+
lines.push(
|
|
1371
|
+
`complete -c pify -n __fish_use_subcommand -a ${name} -d '${spec.description.replace(/'/g, "")}'`
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
const condition = `__fish_seen_subcommand_from ${allNames(spec).join(" ")}`;
|
|
1375
|
+
const words = positionalWords(spec);
|
|
1376
|
+
if (words.length > 0) {
|
|
1377
|
+
lines.push(`complete -c pify -n '${condition}' -a '${words.join(" ")}'`);
|
|
1378
|
+
}
|
|
1379
|
+
for (const flag of spec.flags) {
|
|
1380
|
+
const short = flag.short ? ` -s ${flag.short}` : "";
|
|
1381
|
+
lines.push(
|
|
1382
|
+
`complete -c pify -n '${condition}' -l ${flag.name}${short} -d '${flag.description.replace(/'/g, "")}'`
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
for (const flag of GLOBAL_FLAGS) {
|
|
1387
|
+
const short = flag.short ? ` -s ${flag.short}` : "";
|
|
1388
|
+
lines.push(`complete -c pify -l ${flag.name}${short} -d '${flag.description.replace(/'/g, "")}'`);
|
|
1389
|
+
}
|
|
1390
|
+
return `${lines.join("\n")}
|
|
1391
|
+
`;
|
|
1392
|
+
}
|
|
1393
|
+
function powershellScript() {
|
|
1394
|
+
const commands = COMMAND_SPECS.flatMap(allNames);
|
|
1395
|
+
const caseBlocks = COMMAND_SPECS.map((spec) => {
|
|
1396
|
+
const words = [...positionalWords(spec), ...flagTokens(spec)].map((w) => `'${w}'`).join(", ");
|
|
1397
|
+
return ` ${allNames(spec).map((n) => `'${n}'`).join(" ")} { @(${words}) }`;
|
|
1398
|
+
}).join("\n");
|
|
1399
|
+
return `# PowerShell completion for pify \u2014 generated by \`pify completions powershell\`
|
|
1400
|
+
# Install: pify completions powershell | Out-String | Invoke-Expression (add to $PROFILE)
|
|
1401
|
+
Register-ArgumentCompleter -Native -CommandName pify -ScriptBlock {
|
|
1402
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
1403
|
+
$tokens = $commandAst.CommandElements | ForEach-Object { $_.ToString() }
|
|
1404
|
+
$candidates = if ($tokens.Count -le 1 -or ($tokens.Count -eq 2 -and $wordToComplete)) {
|
|
1405
|
+
@(${commands.map((c) => `'${c}'`).join(", ")})
|
|
1406
|
+
} else {
|
|
1407
|
+
switch ($tokens[1]) {
|
|
1408
|
+
${caseBlocks}
|
|
1409
|
+
default { @() }
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
$candidates | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
1413
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
`;
|
|
1417
|
+
}
|
|
1418
|
+
function generateCompletion(shell) {
|
|
1419
|
+
switch (shell) {
|
|
1420
|
+
case "bash":
|
|
1421
|
+
return bashScript();
|
|
1422
|
+
case "zsh":
|
|
1423
|
+
return zshScript();
|
|
1424
|
+
case "fish":
|
|
1425
|
+
return fishScript();
|
|
1426
|
+
case "powershell":
|
|
1427
|
+
return powershellScript();
|
|
1428
|
+
default:
|
|
1429
|
+
throw usageError(
|
|
1430
|
+
`Unknown shell ${JSON.stringify(shell)}.`,
|
|
1431
|
+
`Usage: pify completions <${COMPLETION_SHELLS.join("|")}>`
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1198
1436
|
// src/help.ts
|
|
1199
1437
|
function render(text) {
|
|
1200
1438
|
return text.split("\n").map((line) => {
|
|
@@ -1219,6 +1457,7 @@ Commands:
|
|
|
1219
1457
|
list Show the @pify catalog with install state
|
|
1220
1458
|
doctor Diagnose the local pi/pify environment
|
|
1221
1459
|
init [dir] Scaffold a new Pi Package
|
|
1460
|
+
completions <shell> Print a completion script (bash|zsh|fish|powershell)
|
|
1222
1461
|
|
|
1223
1462
|
Short forms:
|
|
1224
1463
|
i = install, rm = remove, up = update, ls = list
|
|
@@ -1355,6 +1594,23 @@ Options:
|
|
|
1355
1594
|
|
|
1356
1595
|
Examples:
|
|
1357
1596
|
pify doctor
|
|
1597
|
+
`,
|
|
1598
|
+
completions: `Usage:
|
|
1599
|
+
pify completions <bash|zsh|fish|powershell>
|
|
1600
|
+
|
|
1601
|
+
Print a shell completion script generated from the live command registry \u2014
|
|
1602
|
+
command names, aliases, flags, and package names all tab-complete.
|
|
1603
|
+
|
|
1604
|
+
Examples:
|
|
1605
|
+
# bash / zsh \u2014 evaluate at shell startup (add to your rc file)
|
|
1606
|
+
eval "$(pify completions bash)"
|
|
1607
|
+
eval "$(pify completions zsh)"
|
|
1608
|
+
|
|
1609
|
+
# fish
|
|
1610
|
+
pify completions fish > ~/.config/fish/completions/pify.fish
|
|
1611
|
+
|
|
1612
|
+
# PowerShell \u2014 add to $PROFILE
|
|
1613
|
+
pify completions powershell | Out-String | Invoke-Expression
|
|
1358
1614
|
`,
|
|
1359
1615
|
init: `Usage:
|
|
1360
1616
|
pify init [dir] [--name <package-name>] [--description <text>]
|
|
@@ -1381,90 +1637,40 @@ function commandHelp(command) {
|
|
|
1381
1637
|
}
|
|
1382
1638
|
|
|
1383
1639
|
// src/cli.ts
|
|
1384
|
-
var
|
|
1385
|
-
{
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
},
|
|
1400
|
-
{
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
})
|
|
1414
|
-
},
|
|
1415
|
-
{
|
|
1416
|
-
name: "remove",
|
|
1417
|
-
aliases: ["rm", "uninstall"],
|
|
1418
|
-
options: {
|
|
1419
|
-
local: { type: "boolean", short: "l", default: false },
|
|
1420
|
-
approve: { type: "boolean", short: "a", default: false },
|
|
1421
|
-
"dry-run": { type: "boolean", default: false }
|
|
1422
|
-
},
|
|
1423
|
-
allowPositionals: true,
|
|
1424
|
-
run: (p, v) => remove(p, {
|
|
1425
|
-
local: Boolean(v.local),
|
|
1426
|
-
approve: Boolean(v.approve),
|
|
1427
|
-
dryRun: Boolean(v["dry-run"])
|
|
1428
|
-
})
|
|
1429
|
-
},
|
|
1430
|
-
{
|
|
1431
|
-
name: "update",
|
|
1432
|
-
aliases: ["up"],
|
|
1433
|
-
options: {
|
|
1434
|
-
catalog: { type: "boolean", default: false },
|
|
1435
|
-
"dry-run": { type: "boolean", default: false }
|
|
1436
|
-
},
|
|
1437
|
-
allowPositionals: true,
|
|
1438
|
-
run: (p, v) => update(p, { catalogOnly: Boolean(v.catalog), dryRun: Boolean(v["dry-run"]) })
|
|
1439
|
-
},
|
|
1440
|
-
{
|
|
1441
|
-
name: "list",
|
|
1442
|
-
aliases: ["ls"],
|
|
1443
|
-
options: { json: { type: "boolean", default: false } },
|
|
1444
|
-
allowPositionals: false,
|
|
1445
|
-
run: (_p, v) => list({ json: Boolean(v.json) })
|
|
1446
|
-
},
|
|
1447
|
-
{
|
|
1448
|
-
name: "doctor",
|
|
1449
|
-
aliases: [],
|
|
1450
|
-
options: { json: { type: "boolean", default: false } },
|
|
1451
|
-
allowPositionals: false,
|
|
1452
|
-
run: (_p, v) => doctor({ json: Boolean(v.json) })
|
|
1453
|
-
},
|
|
1454
|
-
{
|
|
1455
|
-
name: "init",
|
|
1456
|
-
aliases: [],
|
|
1457
|
-
options: {
|
|
1458
|
-
name: { type: "string" },
|
|
1459
|
-
description: { type: "string" }
|
|
1460
|
-
},
|
|
1461
|
-
allowPositionals: true,
|
|
1462
|
-
run: (p, v) => init(p[0], {
|
|
1463
|
-
name: v.name,
|
|
1464
|
-
description: v.description
|
|
1465
|
-
})
|
|
1640
|
+
var RUNNERS = {
|
|
1641
|
+
setup: (_p, v) => setup({
|
|
1642
|
+
force: Boolean(v.force),
|
|
1643
|
+
piVersion: v["pi-version"],
|
|
1644
|
+
installer: Boolean(v.installer)
|
|
1645
|
+
}),
|
|
1646
|
+
install: (p, v) => install(p, {
|
|
1647
|
+
local: Boolean(v.local),
|
|
1648
|
+
approve: Boolean(v.approve),
|
|
1649
|
+
dryRun: Boolean(v["dry-run"])
|
|
1650
|
+
}),
|
|
1651
|
+
remove: (p, v) => remove(p, {
|
|
1652
|
+
local: Boolean(v.local),
|
|
1653
|
+
approve: Boolean(v.approve),
|
|
1654
|
+
dryRun: Boolean(v["dry-run"])
|
|
1655
|
+
}),
|
|
1656
|
+
update: (p, v) => update(p, { catalogOnly: Boolean(v.catalog), dryRun: Boolean(v["dry-run"]) }),
|
|
1657
|
+
list: (_p, v) => list({ json: Boolean(v.json) }),
|
|
1658
|
+
doctor: (_p, v) => doctor({ json: Boolean(v.json) }),
|
|
1659
|
+
init: (p, v) => init(p[0], {
|
|
1660
|
+
name: v.name,
|
|
1661
|
+
description: v.description
|
|
1662
|
+
}),
|
|
1663
|
+
completions: async (p) => {
|
|
1664
|
+
if (!p[0]) {
|
|
1665
|
+
throw usageError("Missing shell.", "Usage: pify completions <bash|zsh|fish|powershell>");
|
|
1666
|
+
}
|
|
1667
|
+
out(generateCompletion(p[0]));
|
|
1668
|
+
return 0;
|
|
1466
1669
|
}
|
|
1467
|
-
|
|
1670
|
+
};
|
|
1671
|
+
function findSpec(name) {
|
|
1672
|
+
return COMMAND_SPECS.find((c) => c.name === name || c.aliases.includes(name));
|
|
1673
|
+
}
|
|
1468
1674
|
async function main(argv) {
|
|
1469
1675
|
const args = argv.filter((a) => a !== "--no-color");
|
|
1470
1676
|
if (args.length !== argv.length) setColor(false);
|
|
@@ -1484,8 +1690,8 @@ async function main(argv) {
|
|
|
1484
1690
|
if (first.startsWith("-")) {
|
|
1485
1691
|
throw usageError(`Unknown option ${first}.`, 'Use "pify --help".');
|
|
1486
1692
|
}
|
|
1487
|
-
const
|
|
1488
|
-
if (!
|
|
1693
|
+
const spec = findSpec(first);
|
|
1694
|
+
if (!spec) {
|
|
1489
1695
|
throw usageError(
|
|
1490
1696
|
`Unknown command: ${JSON.stringify(first)}`,
|
|
1491
1697
|
'Run "pify --help" to see available commands.'
|
|
@@ -1493,26 +1699,26 @@ async function main(argv) {
|
|
|
1493
1699
|
}
|
|
1494
1700
|
const rest = args.slice(1);
|
|
1495
1701
|
if (rest.includes("-h") || rest.includes("--help")) {
|
|
1496
|
-
out(commandHelp(
|
|
1702
|
+
out(commandHelp(spec.name));
|
|
1497
1703
|
return 0;
|
|
1498
1704
|
}
|
|
1499
1705
|
let parsed;
|
|
1500
1706
|
try {
|
|
1501
1707
|
parsed = parseArgs({
|
|
1502
1708
|
args: rest,
|
|
1503
|
-
options:
|
|
1504
|
-
allowPositionals:
|
|
1709
|
+
options: parseArgsOptionsFor(spec),
|
|
1710
|
+
allowPositionals: spec.positional !== "none",
|
|
1505
1711
|
strict: true
|
|
1506
1712
|
});
|
|
1507
1713
|
} catch (err) {
|
|
1508
1714
|
const message = err instanceof Error ? err.message : String(err);
|
|
1509
1715
|
const unknown = /Unknown option '(--?[^']+)'/.exec(message)?.[1];
|
|
1510
1716
|
throw usageError(
|
|
1511
|
-
unknown ? `Unknown option ${unknown} for "${
|
|
1512
|
-
`Use "pify --help" or "pify ${
|
|
1717
|
+
unknown ? `Unknown option ${unknown} for "${spec.name}".` : message,
|
|
1718
|
+
`Use "pify --help" or "pify ${spec.name} --help".`
|
|
1513
1719
|
);
|
|
1514
1720
|
}
|
|
1515
|
-
return
|
|
1721
|
+
return RUNNERS[spec.name](parsed.positionals, parsed.values);
|
|
1516
1722
|
}
|
|
1517
1723
|
main(process.argv.slice(2)).then((code) => {
|
|
1518
1724
|
process.exitCode = code;
|
package/dist/index.d.ts
CHANGED
|
@@ -167,6 +167,39 @@ declare function assertSafeArg(arg: string, what?: string): void;
|
|
|
167
167
|
*/
|
|
168
168
|
declare function which(command: string): string | null;
|
|
169
169
|
|
|
170
|
+
declare function generateCompletion(shell: string): string;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Declarative command surface — the single source of truth consumed by BOTH
|
|
174
|
+
* the argument parser (cli.ts) and the completion generator (completions.ts),
|
|
175
|
+
* so completions can never drift from the real CLI (oh-my-pi's principle).
|
|
176
|
+
*/
|
|
177
|
+
interface FlagSpec {
|
|
178
|
+
name: string;
|
|
179
|
+
short?: string;
|
|
180
|
+
/** Flag takes a value (e.g. --pi-version <version>). */
|
|
181
|
+
takesValue?: boolean;
|
|
182
|
+
description: string;
|
|
183
|
+
}
|
|
184
|
+
type PositionalKind = "none" | "packages" | "updateTargets" | "directory" | "shell";
|
|
185
|
+
interface CommandSpec {
|
|
186
|
+
name: string;
|
|
187
|
+
aliases: string[];
|
|
188
|
+
description: string;
|
|
189
|
+
positional: PositionalKind;
|
|
190
|
+
flags: FlagSpec[];
|
|
191
|
+
}
|
|
192
|
+
declare const GLOBAL_FLAGS: FlagSpec[];
|
|
193
|
+
declare const COMMAND_SPECS: CommandSpec[];
|
|
194
|
+
declare const COMPLETION_SHELLS: readonly ["bash", "zsh", "fish", "powershell"];
|
|
195
|
+
type CompletionShell = (typeof COMPLETION_SHELLS)[number];
|
|
196
|
+
/** Build the strict parseArgs options table for one command. */
|
|
197
|
+
declare function parseArgsOptionsFor(spec: CommandSpec): Record<string, {
|
|
198
|
+
type: "boolean" | "string";
|
|
199
|
+
short?: string;
|
|
200
|
+
default?: boolean;
|
|
201
|
+
}>;
|
|
202
|
+
|
|
170
203
|
/**
|
|
171
204
|
* The five `pify init` file templates, embedded as string literals so the
|
|
172
205
|
* tarball ships nothing extra. Every line of the package.json template
|
|
@@ -211,4 +244,4 @@ declare class PifyError extends Error {
|
|
|
211
244
|
constructor(message: string, code?: ExitCodeValue, hint?: string);
|
|
212
245
|
}
|
|
213
246
|
|
|
214
|
-
export { type Catalog, type CatalogPackage, ExitCode, 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, installPi, installedPifyPackages, installedVersionOnDisk, interpolate, isOffline, loadBundledCatalog, loadCatalog, npmInstallPiArgs, officialInstallerCommand, packageJsonTemplate, parseInstallSpec, piStatus, readSettings, refreshCatalog, resolveInstallTarget, resolvePifyName, updatePi, validateCatalog, which };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -551,6 +551,244 @@ function installedVersionOnDisk(name, scope) {
|
|
|
551
551
|
}
|
|
552
552
|
}
|
|
553
553
|
|
|
554
|
+
// src/registry.ts
|
|
555
|
+
var GLOBAL_FLAGS = [
|
|
556
|
+
{ name: "help", short: "h", description: "Show help" },
|
|
557
|
+
{ name: "version", short: "v", description: "Print the pify version" },
|
|
558
|
+
{ name: "no-color", description: "Disable colored output" }
|
|
559
|
+
];
|
|
560
|
+
var COMMAND_SPECS = [
|
|
561
|
+
{
|
|
562
|
+
name: "setup",
|
|
563
|
+
aliases: [],
|
|
564
|
+
description: "Install the pi coding agent (safe to re-run)",
|
|
565
|
+
positional: "none",
|
|
566
|
+
flags: [
|
|
567
|
+
{ name: "force", description: "Reinstall pi even if present" },
|
|
568
|
+
{ name: "pi-version", takesValue: true, description: "Install an exact pi version" },
|
|
569
|
+
{ name: "installer", description: "Run pi's official interactive installer" }
|
|
570
|
+
]
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
name: "install",
|
|
574
|
+
aliases: ["i", "add"],
|
|
575
|
+
description: "Install @pify packages by short name",
|
|
576
|
+
positional: "packages",
|
|
577
|
+
flags: [
|
|
578
|
+
{ name: "local", short: "l", description: "Project scope" },
|
|
579
|
+
{ name: "approve", short: "a", description: "Forward --approve to pi" },
|
|
580
|
+
{ name: "dry-run", description: "Print the pi commands without running them" }
|
|
581
|
+
]
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
name: "remove",
|
|
585
|
+
aliases: ["rm", "uninstall"],
|
|
586
|
+
description: "Remove installed @pify packages",
|
|
587
|
+
positional: "packages",
|
|
588
|
+
flags: [
|
|
589
|
+
{ name: "local", short: "l", description: "Project scope" },
|
|
590
|
+
{ name: "approve", short: "a", description: "Forward --approve to pi" },
|
|
591
|
+
{ name: "dry-run", description: "Print the pi commands without running them" }
|
|
592
|
+
]
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: "update",
|
|
596
|
+
aliases: ["up"],
|
|
597
|
+
description: "Update pi and installed @pify packages",
|
|
598
|
+
positional: "updateTargets",
|
|
599
|
+
flags: [
|
|
600
|
+
{ name: "catalog", description: "Refresh the catalog cache only" },
|
|
601
|
+
{ name: "dry-run", description: "Print the update plan without executing" }
|
|
602
|
+
]
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
name: "list",
|
|
606
|
+
aliases: ["ls"],
|
|
607
|
+
description: "Show the @pify catalog with install state",
|
|
608
|
+
positional: "none",
|
|
609
|
+
flags: [{ name: "json", description: "Machine-readable output" }]
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
name: "doctor",
|
|
613
|
+
aliases: [],
|
|
614
|
+
description: "Diagnose the local pi/pify environment",
|
|
615
|
+
positional: "none",
|
|
616
|
+
flags: [{ name: "json", description: "Machine-readable output" }]
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
name: "init",
|
|
620
|
+
aliases: [],
|
|
621
|
+
description: "Scaffold a new Pi Package",
|
|
622
|
+
positional: "directory",
|
|
623
|
+
flags: [
|
|
624
|
+
{ name: "name", takesValue: true, description: "npm package name" },
|
|
625
|
+
{ name: "description", takesValue: true, description: "Package description" }
|
|
626
|
+
]
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
name: "completions",
|
|
630
|
+
aliases: [],
|
|
631
|
+
description: "Print a shell completion script",
|
|
632
|
+
positional: "shell",
|
|
633
|
+
flags: []
|
|
634
|
+
}
|
|
635
|
+
];
|
|
636
|
+
var COMPLETION_SHELLS = ["bash", "zsh", "fish", "powershell"];
|
|
637
|
+
function parseArgsOptionsFor(spec) {
|
|
638
|
+
const options = {};
|
|
639
|
+
for (const flag of spec.flags) {
|
|
640
|
+
options[flag.name] = flag.takesValue ? { type: "string", ...flag.short ? { short: flag.short } : {} } : { type: "boolean", default: false, ...flag.short ? { short: flag.short } : {} };
|
|
641
|
+
}
|
|
642
|
+
return options;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/completions.ts
|
|
646
|
+
function packageNames() {
|
|
647
|
+
return loadBundledCatalog().packages.map((p) => p.name);
|
|
648
|
+
}
|
|
649
|
+
function allNames(spec) {
|
|
650
|
+
return [spec.name, ...spec.aliases];
|
|
651
|
+
}
|
|
652
|
+
function flagTokens(spec) {
|
|
653
|
+
return spec.flags.flatMap((f) => [`--${f.name}`, ...f.short ? [`-${f.short}`] : []]);
|
|
654
|
+
}
|
|
655
|
+
function positionalWords(spec) {
|
|
656
|
+
switch (spec.positional) {
|
|
657
|
+
case "packages":
|
|
658
|
+
return packageNames();
|
|
659
|
+
case "updateTargets":
|
|
660
|
+
return ["pi", ...packageNames()];
|
|
661
|
+
case "shell":
|
|
662
|
+
return [...COMPLETION_SHELLS];
|
|
663
|
+
default:
|
|
664
|
+
return [];
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
function bashScript() {
|
|
668
|
+
const commands = COMMAND_SPECS.flatMap(allNames).join(" ");
|
|
669
|
+
const cases = COMMAND_SPECS.map((spec) => {
|
|
670
|
+
const words = [...positionalWords(spec), ...flagTokens(spec)].join(" ");
|
|
671
|
+
return ` ${allNames(spec).join("|")}) words="${words}" ;;`;
|
|
672
|
+
}).join("\n");
|
|
673
|
+
return `# bash completion for pify \u2014 generated by \`pify completions bash\`
|
|
674
|
+
# Install: eval "$(pify completions bash)" (or save to a bash-completion dir)
|
|
675
|
+
_pify_completions() {
|
|
676
|
+
local cur words
|
|
677
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
678
|
+
if [ "$COMP_CWORD" -eq 1 ]; then
|
|
679
|
+
COMPREPLY=($(compgen -W "${commands} ${GLOBAL_FLAGS.map((f) => `--${f.name}`).join(" ")}" -- "$cur"))
|
|
680
|
+
return
|
|
681
|
+
fi
|
|
682
|
+
case "\${COMP_WORDS[1]}" in
|
|
683
|
+
${cases}
|
|
684
|
+
*) words="" ;;
|
|
685
|
+
esac
|
|
686
|
+
COMPREPLY=($(compgen -W "$words" -- "$cur"))
|
|
687
|
+
}
|
|
688
|
+
complete -F _pify_completions pify
|
|
689
|
+
`;
|
|
690
|
+
}
|
|
691
|
+
function zshScript() {
|
|
692
|
+
const commandDescs = COMMAND_SPECS.flatMap(
|
|
693
|
+
(spec) => allNames(spec).map((n) => ` '${n}:${spec.description.replace(/'/g, "")}'`)
|
|
694
|
+
).join("\n");
|
|
695
|
+
const cases = COMMAND_SPECS.map((spec) => {
|
|
696
|
+
const words = [...positionalWords(spec), ...flagTokens(spec)].map((w) => `'${w}'`).join(" ");
|
|
697
|
+
return ` ${allNames(spec).join("|")}) _values 'value' ${words || "''"} ;;`;
|
|
698
|
+
}).join("\n");
|
|
699
|
+
return `#compdef pify
|
|
700
|
+
# zsh completion for pify \u2014 generated by \`pify completions zsh\`
|
|
701
|
+
# Install: eval "$(pify completions zsh)" (or save into a directory on $fpath)
|
|
702
|
+
_pify() {
|
|
703
|
+
local -a commands
|
|
704
|
+
commands=(
|
|
705
|
+
${commandDescs}
|
|
706
|
+
)
|
|
707
|
+
if (( CURRENT == 2 )); then
|
|
708
|
+
_describe 'command' commands
|
|
709
|
+
return
|
|
710
|
+
fi
|
|
711
|
+
case "$words[2]" in
|
|
712
|
+
${cases}
|
|
713
|
+
esac
|
|
714
|
+
}
|
|
715
|
+
_pify "$@"
|
|
716
|
+
`;
|
|
717
|
+
}
|
|
718
|
+
function fishScript() {
|
|
719
|
+
const lines = [
|
|
720
|
+
`# fish completion for pify \u2014 generated by \`pify completions fish\``,
|
|
721
|
+
`# Install: pify completions fish > ~/.config/fish/completions/pify.fish`,
|
|
722
|
+
`complete -c pify -f`
|
|
723
|
+
];
|
|
724
|
+
for (const spec of COMMAND_SPECS) {
|
|
725
|
+
for (const name of allNames(spec)) {
|
|
726
|
+
lines.push(
|
|
727
|
+
`complete -c pify -n __fish_use_subcommand -a ${name} -d '${spec.description.replace(/'/g, "")}'`
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
const condition = `__fish_seen_subcommand_from ${allNames(spec).join(" ")}`;
|
|
731
|
+
const words = positionalWords(spec);
|
|
732
|
+
if (words.length > 0) {
|
|
733
|
+
lines.push(`complete -c pify -n '${condition}' -a '${words.join(" ")}'`);
|
|
734
|
+
}
|
|
735
|
+
for (const flag of spec.flags) {
|
|
736
|
+
const short = flag.short ? ` -s ${flag.short}` : "";
|
|
737
|
+
lines.push(
|
|
738
|
+
`complete -c pify -n '${condition}' -l ${flag.name}${short} -d '${flag.description.replace(/'/g, "")}'`
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
for (const flag of GLOBAL_FLAGS) {
|
|
743
|
+
const short = flag.short ? ` -s ${flag.short}` : "";
|
|
744
|
+
lines.push(`complete -c pify -l ${flag.name}${short} -d '${flag.description.replace(/'/g, "")}'`);
|
|
745
|
+
}
|
|
746
|
+
return `${lines.join("\n")}
|
|
747
|
+
`;
|
|
748
|
+
}
|
|
749
|
+
function powershellScript() {
|
|
750
|
+
const commands = COMMAND_SPECS.flatMap(allNames);
|
|
751
|
+
const caseBlocks = COMMAND_SPECS.map((spec) => {
|
|
752
|
+
const words = [...positionalWords(spec), ...flagTokens(spec)].map((w) => `'${w}'`).join(", ");
|
|
753
|
+
return ` ${allNames(spec).map((n) => `'${n}'`).join(" ")} { @(${words}) }`;
|
|
754
|
+
}).join("\n");
|
|
755
|
+
return `# PowerShell completion for pify \u2014 generated by \`pify completions powershell\`
|
|
756
|
+
# Install: pify completions powershell | Out-String | Invoke-Expression (add to $PROFILE)
|
|
757
|
+
Register-ArgumentCompleter -Native -CommandName pify -ScriptBlock {
|
|
758
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
759
|
+
$tokens = $commandAst.CommandElements | ForEach-Object { $_.ToString() }
|
|
760
|
+
$candidates = if ($tokens.Count -le 1 -or ($tokens.Count -eq 2 -and $wordToComplete)) {
|
|
761
|
+
@(${commands.map((c) => `'${c}'`).join(", ")})
|
|
762
|
+
} else {
|
|
763
|
+
switch ($tokens[1]) {
|
|
764
|
+
${caseBlocks}
|
|
765
|
+
default { @() }
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
$candidates | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
769
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
`;
|
|
773
|
+
}
|
|
774
|
+
function generateCompletion(shell) {
|
|
775
|
+
switch (shell) {
|
|
776
|
+
case "bash":
|
|
777
|
+
return bashScript();
|
|
778
|
+
case "zsh":
|
|
779
|
+
return zshScript();
|
|
780
|
+
case "fish":
|
|
781
|
+
return fishScript();
|
|
782
|
+
case "powershell":
|
|
783
|
+
return powershellScript();
|
|
784
|
+
default:
|
|
785
|
+
throw usageError(
|
|
786
|
+
`Unknown shell ${JSON.stringify(shell)}.`,
|
|
787
|
+
`Usage: pify completions <${COMPLETION_SHELLS.join("|")}>`
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
554
792
|
// src/templates.ts
|
|
555
793
|
function interpolate(template, vars) {
|
|
556
794
|
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
|
@@ -669,7 +907,10 @@ Then install anywhere with: pi install npm:{{name}}
|
|
|
669
907
|
Tested with pi {{piVersion}}.
|
|
670
908
|
`;
|
|
671
909
|
export {
|
|
910
|
+
COMMAND_SPECS,
|
|
911
|
+
COMPLETION_SHELLS,
|
|
672
912
|
ExitCode,
|
|
913
|
+
GLOBAL_FLAGS,
|
|
673
914
|
INDEX_TS,
|
|
674
915
|
NODE_FLOOR,
|
|
675
916
|
PI_PACKAGE,
|
|
@@ -681,6 +922,7 @@ export {
|
|
|
681
922
|
assertSafeArg,
|
|
682
923
|
compareSemver,
|
|
683
924
|
fetchLatestPiVersion,
|
|
925
|
+
generateCompletion,
|
|
684
926
|
installPi,
|
|
685
927
|
installedPifyPackages,
|
|
686
928
|
installedVersionOnDisk,
|
|
@@ -691,6 +933,7 @@ export {
|
|
|
691
933
|
npmInstallPiArgs,
|
|
692
934
|
officialInstallerCommand,
|
|
693
935
|
packageJsonTemplate,
|
|
936
|
+
parseArgsOptionsFor,
|
|
694
937
|
parseInstallSpec,
|
|
695
938
|
piStatus,
|
|
696
939
|
readSettings,
|