@pify/cli 0.1.2 → 0.2.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/dist/index.d.ts CHANGED
@@ -92,10 +92,29 @@ declare function piStatus(): PiStatus;
92
92
  /** Latest pi version from pi's own release endpoint. Null offline/on error. */
93
93
  declare function fetchLatestPiVersion(): Promise<string | null>;
94
94
  declare function compareSemver(a: string, b: string): number;
95
+ /** The official installer one-liner for this OS, for hints and --installer. */
96
+ declare function officialInstallerCommand(): string;
95
97
  /**
96
- * Install pi globally via npm. This is the one operation pi cannot do for
97
- * itself — `pi update --self` requires a working pi. `--ignore-scripts`
98
- * matches pi's own self-update invocation (supply-chain-safe default).
98
+ * The npm install argv pi's official installers use:
99
+ * `--ignore-scripts` (supply-chain-safe; matches pi's self-update),
100
+ * `--min-release-age=0` (pi ships npm-shrinkwrap.json, so bypassing npm's
101
+ * release-age gate does not reopen transitive ranges; npm >= 11 only),
102
+ * `--no-fund --no-audit` (noise). Exported for tests.
103
+ */
104
+ declare function npmInstallPiArgs(spec: string, npmMajor: number, prefix?: string): string[];
105
+ /**
106
+ * Install pi with the same decisions as pi's official installers, minus the
107
+ * interactive parts. This is the one operation pi cannot do for itself —
108
+ * `pi update` requires a working pi.
109
+ *
110
+ * Per-OS behavior (ported from install.sh / install.ps1):
111
+ * - POSIX, npm prefix unwritable, no global pi there: fall back to
112
+ * `--prefix ~/.local` — never sudo.
113
+ * - POSIX, npm prefix unwritable but an old global pi lives there: stop
114
+ * (a ~/.local copy would be shadowed by the stale global one).
115
+ * - No npm but bun on PATH: `bun add -g --ignore-scripts` (documented
116
+ * alternative channel).
117
+ * - Neither: point at the official installer, which can bootstrap Node too.
99
118
  */
100
119
  declare function installPi(version?: string): Promise<void>;
101
120
  /**
@@ -148,6 +167,39 @@ declare function assertSafeArg(arg: string, what?: string): void;
148
167
  */
149
168
  declare function which(command: string): string | null;
150
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
+
151
203
  /**
152
204
  * The five `pify init` file templates, embedded as string literals so the
153
205
  * tarball ships nothing extra. Every line of the package.json template
@@ -192,4 +244,4 @@ declare class PifyError extends Error {
192
244
  constructor(message: string, code?: ExitCodeValue, hint?: string);
193
245
  }
194
246
 
195
- 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, 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
@@ -34,21 +34,21 @@ var PifyError = class extends Error {
34
34
  code;
35
35
  /** Optional follow-up shown after the message, e.g. a command to run. */
36
36
  hint;
37
- constructor(message, code = ExitCode.FAILURE, hint) {
37
+ constructor(message, code = ExitCode.FAILURE, hint2) {
38
38
  super(message);
39
39
  this.name = "PifyError";
40
40
  this.code = code;
41
- this.hint = hint;
41
+ this.hint = hint2;
42
42
  }
43
43
  };
44
- function usageError(message, hint) {
45
- return new PifyError(message, ExitCode.USAGE, hint);
44
+ function usageError(message, hint2) {
45
+ return new PifyError(message, ExitCode.USAGE, hint2);
46
46
  }
47
- function environmentError(message, hint) {
48
- return new PifyError(message, ExitCode.ENVIRONMENT, hint);
47
+ function environmentError(message, hint2) {
48
+ return new PifyError(message, ExitCode.ENVIRONMENT, hint2);
49
49
  }
50
- function notFoundError(message, hint) {
51
- return new PifyError(message, ExitCode.NOT_FOUND, hint);
50
+ function notFoundError(message, hint2) {
51
+ return new PifyError(message, ExitCode.NOT_FOUND, hint2);
52
52
  }
53
53
 
54
54
  // src/exec.ts
@@ -305,9 +305,9 @@ function resolvePifyName(input) {
305
305
  }
306
306
 
307
307
  // src/pi.ts
308
- import { readFileSync as readFileSync2, realpathSync } from "fs";
308
+ import { accessSync as accessSync2, constants as constants2, readFileSync as readFileSync2, realpathSync, statSync as statSync2 } from "fs";
309
309
  import { homedir as homedir2 } from "os";
310
- import { join as join3 } from "path";
310
+ import { dirname as dirname2, join as join3 } from "path";
311
311
 
312
312
  // src/ui.ts
313
313
  function detectColor(stream) {
@@ -340,6 +340,10 @@ function success(msg) {
340
340
  process.stdout.write(`${style.green(msg)}
341
341
  `);
342
342
  }
343
+ function hint(msg) {
344
+ process.stdout.write(`${style.dim(msg)}
345
+ `);
346
+ }
343
347
 
344
348
  // src/pi.ts
345
349
  var PI_PACKAGE = "@earendil-works/pi-coding-agent";
@@ -388,38 +392,107 @@ function assertNodeVersion() {
388
392
  );
389
393
  }
390
394
  }
395
+ function officialInstallerCommand() {
396
+ return isWindows ? 'powershell -c "irm https://pi.dev/install.ps1 | iex"' : "curl -fsSL https://pi.dev/install.sh | sh";
397
+ }
398
+ function writableOrCreatable(path) {
399
+ let current = path;
400
+ for (; ; ) {
401
+ try {
402
+ statSync2(current);
403
+ break;
404
+ } catch {
405
+ const parent = dirname2(current);
406
+ if (parent === current) return false;
407
+ current = parent;
408
+ }
409
+ }
410
+ try {
411
+ if (!statSync2(current).isDirectory()) return false;
412
+ accessSync2(current, constants2.W_OK);
413
+ return true;
414
+ } catch {
415
+ return false;
416
+ }
417
+ }
418
+ function npmInstallPiArgs(spec, npmMajor, prefix) {
419
+ const args = ["install", "-g", "--ignore-scripts"];
420
+ if (npmMajor >= 11) args.push("--min-release-age=0");
421
+ args.push("--no-fund", "--no-audit");
422
+ if (prefix) args.push("--prefix", prefix);
423
+ args.push(spec);
424
+ return args;
425
+ }
426
+ function npmMajorVersion() {
427
+ const res = runCapture("npm", ["--version"]);
428
+ const major = Number.parseInt(res.stdout.trim().split(".")[0] ?? "", 10);
429
+ return Number.isNaN(major) ? 0 : major;
430
+ }
391
431
  async function installPi(version) {
392
432
  assertNodeVersion();
393
- if (!which("npm")) {
394
- throw environmentError(
395
- "npm was not found on PATH.",
396
- "Install Node.js (which bundles npm) from https://nodejs.org and retry."
397
- );
398
- }
399
433
  if (process.env.PI_MANAGED_INSTALL_ROOT && !piStatus().installed) {
400
434
  throw environmentError(
401
435
  "PI_MANAGED_INSTALL_ROOT is set but pi is not installed.",
402
- "Use the managed installer: curl -fsSL https://pi.dev/install.sh | sh (Linux/macOS)."
436
+ `Use the official installer: ${officialInstallerCommand()}`
403
437
  );
404
438
  }
405
439
  const spec = version ? `${PI_PACKAGE}@${version}` : PI_PACKAGE;
406
- step(`npm install -g --ignore-scripts ${spec}`);
407
- const code = await runInherit("npm", ["install", "-g", "--ignore-scripts", spec]);
440
+ const npmPath = which("npm");
441
+ const bunPath = which("bun");
442
+ if (!npmPath && !bunPath) {
443
+ throw environmentError(
444
+ "Neither npm nor bun was found on PATH.",
445
+ `Run the official installer (it can install Node too): ${officialInstallerCommand()}`
446
+ );
447
+ }
448
+ let command;
449
+ let args;
450
+ let localPrefix = null;
451
+ if (npmPath) {
452
+ command = "npm";
453
+ if (!isWindows) {
454
+ const prefixRes = runCapture("npm", ["prefix", "-g"]);
455
+ const prefix = prefixRes.status === 0 ? prefixRes.stdout.trim() : "";
456
+ const prefixWritable = prefix !== "" && writableOrCreatable(join3(prefix, "lib", "node_modules")) && writableOrCreatable(join3(prefix, "bin"));
457
+ if (prefix && !prefixWritable) {
458
+ let staleGlobalPi = false;
459
+ try {
460
+ statSync2(join3(prefix, "bin", "pi"));
461
+ staleGlobalPi = true;
462
+ } catch {
463
+ }
464
+ if (staleGlobalPi) {
465
+ throw environmentError(
466
+ `npm's global directory is not writable (${prefix}) and pi is already installed there.`,
467
+ `Update or remove it first: sudo npm install -g --ignore-scripts ${PI_PACKAGE}`
468
+ );
469
+ }
470
+ localPrefix = join3(homedir2(), ".local");
471
+ }
472
+ }
473
+ args = npmInstallPiArgs(spec, npmMajorVersion(), localPrefix ?? void 0);
474
+ } else {
475
+ command = "bun";
476
+ args = ["add", "-g", "--ignore-scripts", spec];
477
+ }
478
+ step(`${command} ${args.join(" ")}`);
479
+ const code = await runInherit(command, args);
408
480
  if (code !== 0) {
409
481
  throw new PifyError(
410
- `npm install -g ${spec} exited with code ${code}.`,
482
+ `${command} ${args[0]} exited with code ${code}.`,
411
483
  ExitCode.SUBPROCESS,
412
- isWindows ? "If this is a permissions or file-lock error, close running pi sessions and retry from an elevated terminal." : "If this is an EACCES error, configure a user-writable npm prefix (https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) or use a Node version manager."
484
+ isWindows ? "If this is a permissions or file-lock error, close running pi sessions and retry from an elevated terminal." : `If the problem persists, try the official installer: ${officialInstallerCommand()}`
413
485
  );
414
486
  }
415
487
  const st = piStatus();
416
488
  if (!st.installed) {
417
- throw environmentError(
418
- "pi was installed but is not resolvable on PATH.",
419
- "Add npm's global prefix to PATH (npm prefix -g) and open a new terminal."
420
- );
489
+ const pathHint = localPrefix ? `Add ${join3(localPrefix, "bin")} to PATH and open a new terminal.` : "Add the global bin directory to PATH (npm prefix -g) and open a new terminal.";
490
+ throw environmentError("pi was installed but is not resolvable on PATH.", pathHint);
421
491
  }
422
492
  success(`pi ${st.version ?? ""} installed.`.replace(" ", " "));
493
+ if (localPrefix) {
494
+ hint(`Installed under ${localPrefix} because npm's global prefix is not writable.`);
495
+ }
423
496
  }
424
497
  async function updatePi() {
425
498
  const st = piStatus();
@@ -478,6 +551,244 @@ function installedVersionOnDisk(name, scope) {
478
551
  }
479
552
  }
480
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
+
481
792
  // src/templates.ts
482
793
  function interpolate(template, vars) {
483
794
  return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
@@ -596,7 +907,10 @@ Then install anywhere with: pi install npm:{{name}}
596
907
  Tested with pi {{piVersion}}.
597
908
  `;
598
909
  export {
910
+ COMMAND_SPECS,
911
+ COMPLETION_SHELLS,
599
912
  ExitCode,
913
+ GLOBAL_FLAGS,
600
914
  INDEX_TS,
601
915
  NODE_FLOOR,
602
916
  PI_PACKAGE,
@@ -608,6 +922,7 @@ export {
608
922
  assertSafeArg,
609
923
  compareSemver,
610
924
  fetchLatestPiVersion,
925
+ generateCompletion,
611
926
  installPi,
612
927
  installedPifyPackages,
613
928
  installedVersionOnDisk,
@@ -615,7 +930,10 @@ export {
615
930
  isOffline,
616
931
  loadBundledCatalog,
617
932
  loadCatalog,
933
+ npmInstallPiArgs,
934
+ officialInstallerCommand,
618
935
  packageJsonTemplate,
936
+ parseArgsOptionsFor,
619
937
  parseInstallSpec,
620
938
  piStatus,
621
939
  readSettings,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pify/cli",
3
- "version": "0.1.2",
3
+ "version": "0.2.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",