@penvhq/cli 0.10.0 → 0.12.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.
@@ -0,0 +1,126 @@
1
+ import { PenvError } from '@penvhq/core';
2
+
3
+ /**
4
+ * The runtime dependencies an adopted project takes, and how they get there.
5
+ *
6
+ * PRD §3: an adopted project depends on `@penvhq/penv` at the engine's own
7
+ * version — the typed `@env` surface, not a CLI distribution. It also depends on
8
+ * zod, because the `penv.schema.ts` init scaffolds imports it: zod is a *peer* of
9
+ * `@penvhq/penv`, and a peer is a package the project supplies. Under pnpm's
10
+ * strict layout nothing hoists it to the project root, so an install that named
11
+ * only `@penvhq/penv` left the very schema init had just written unable to
12
+ * resolve `zod` — and adoption could never finish.
13
+ *
14
+ * Both are installed with the package manager the project already uses, and only
15
+ * after showing the exact `package.json` and lockfile change: an install is the
16
+ * one step of adoption that reaches outside the repository, so it is the one step
17
+ * that is shown before it happens rather than reported after.
18
+ *
19
+ * The install itself is a seam. It shells out to a package manager, which the
20
+ * tests must never do — and a fake here is not a weaker test, because what init
21
+ * has to get right is the plan, the consent, and the refusal when the install
22
+ * does not happen.
23
+ *
24
+ * A plan is a list of steps, because in a workspace "the project's dependency"
25
+ * is plural. pnpm refuses a bare `add` at a workspace root (`-w` is how you say
26
+ * you meant the root), and a workspace package that declares `@penvhq/penv`
27
+ * itself is a second copy of the very version the manifest pins — one repository
28
+ * ran the 0.8 bridge under a 0.11 pin for three releases because nothing looked
29
+ * below the root. So every `package.json` that declares it moves, under one
30
+ * consent, and the commands shown are the ones that run.
31
+ *
32
+ * Two commands write that dependency line: `penv init`, which is the engine's,
33
+ * and `penv upgrade`, which is the launcher's. This module is published at
34
+ * `@penvhq/cli/install` so the launcher reaches it without loading the command
35
+ * surface — one answer to "which package manager, which diff, which spawn",
36
+ * rather than a second copy on the other side of the launcher/engine split.
37
+ */
38
+
39
+ /** The package an adopted project depends on. The CLI engine is not one of its dependencies. */
40
+ declare const RUNTIME_PACKAGE = "@penvhq/penv";
41
+ /** The peer `penv.schema.ts` imports, which the project supplies because a peer is not hoisted. */
42
+ declare const SCHEMA_PACKAGE = "zod";
43
+ type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
44
+ /** One package the adopted project needs, and what its `package.json` says today. */
45
+ interface InstallPackage {
46
+ readonly name: string;
47
+ readonly version: string;
48
+ /** What `package.json` already says about it, when it says anything. */
49
+ readonly declared?: string;
50
+ /** True when this project already has it — nothing to install for this one. */
51
+ readonly satisfied: boolean;
52
+ }
53
+ /** One `package.json` the install rewrites, and the command that rewrites it. */
54
+ interface InstallStep {
55
+ /** The file the diff names — `package.json`, or a workspace package's path to it. */
56
+ readonly manifest: string;
57
+ /** Everything this file needs, in the order the diff shows them. */
58
+ readonly packages: readonly InstallPackage[];
59
+ /** The command, argv-shaped — run from the project root, whichever file it writes. */
60
+ readonly command: readonly string[];
61
+ /** True when this file already declares every one of them. */
62
+ readonly satisfied: boolean;
63
+ }
64
+ interface InstallPlan {
65
+ readonly root: string;
66
+ readonly manager: PackageManager;
67
+ /** The root's `package.json` first, then every workspace package that declares the runtime one. */
68
+ readonly steps: readonly InstallStep[];
69
+ /** The lockfile the manager will rewrite, when the project has one. */
70
+ readonly lockfile?: string;
71
+ /** True when every step is already satisfied — nothing to install. */
72
+ readonly satisfied: boolean;
73
+ }
74
+ /** Runs an install plan, or throws. Replaced in tests; never spawns there. */
75
+ type InstallRuntime = (plan: InstallPlan) => Promise<void>;
76
+ /**
77
+ * The engine's own version, read from its manifest rather than restated in the
78
+ * source: `@penvhq/penv` must match the engine exactly, and a constant beside
79
+ * the version a release bumps is a second answer waiting to drift.
80
+ */
81
+ declare function engineVersion(): string;
82
+ /**
83
+ * The zod an adopted project installs: the floor of the peer range the engine
84
+ * and `@penvhq/penv` both declare, which is the version penv is built and tested
85
+ * against.
86
+ *
87
+ * The floor rather than the range, because the diff shown before the install has
88
+ * to be the line that actually lands — `--save-exact` on `^4.4.3` would write
89
+ * whatever the registry resolved that day, which is not something a reader can
90
+ * consent to in advance.
91
+ */
92
+ declare function schemaPackageVersion(): string;
93
+ /** The package manager this project already uses: its lockfile, then what it declares, then npm. */
94
+ declare function detectPackageManager(root: string): PackageManager;
95
+ /** True when `root` is the root of a pnpm workspace, which is what `-w` is for. */
96
+ declare function isPnpmWorkspaceRoot(root: string): boolean;
97
+ declare function planInstall(root: string, version?: string): InstallPlan;
98
+ /** What this plan actually installs, once per package however many files declare it. */
99
+ declare function installedPackages(plan: InstallPlan): readonly InstallPackage[];
100
+ /**
101
+ * The change, as it will appear in the diff — the whole point of showing it is
102
+ * that the reader recognises their own file, so these are the `package.json`
103
+ * lines that land and the lockfile that gets rewritten, not a summary of both.
104
+ *
105
+ * In a workspace that is more than one file, and the commands underneath are the
106
+ * ones that run: a "Run with:" line the reader cannot paste is worse than none.
107
+ */
108
+ declare function renderInstallPlan(plan: InstallPlan): string[];
109
+ /**
110
+ * The real install: the project's own package manager, started the way any other
111
+ * child is (`.cmd` shims on Windows included), with its output the user's to see.
112
+ *
113
+ * Every step runs from the project root — `-w` and `--filter` are how a workspace
114
+ * says which `package.json` it means, so the directory never changes.
115
+ */
116
+ declare const installWithPackageManager: InstallRuntime;
117
+ /**
118
+ * What to do about a package manager that refused.
119
+ *
120
+ * Never the command that just failed: the one remediation guaranteed not to work
121
+ * is the one the reader already ran. The manager said why, on their screen, and
122
+ * nothing was migrated — so the answer is that line and a second `penv init`.
123
+ */
124
+ declare function installFailed(plan: InstallPlan, step: InstallStep): PenvError;
125
+
126
+ export { type InstallPackage, type InstallPlan, type InstallRuntime, type InstallStep, type PackageManager, RUNTIME_PACKAGE, SCHEMA_PACKAGE, detectPackageManager, engineVersion, installFailed, installWithPackageManager, installedPackages, isPnpmWorkspaceRoot, planInstall, renderInstallPlan, schemaPackageVersion };
@@ -0,0 +1,126 @@
1
+ import { PenvError } from '@penvhq/core';
2
+
3
+ /**
4
+ * The runtime dependencies an adopted project takes, and how they get there.
5
+ *
6
+ * PRD §3: an adopted project depends on `@penvhq/penv` at the engine's own
7
+ * version — the typed `@env` surface, not a CLI distribution. It also depends on
8
+ * zod, because the `penv.schema.ts` init scaffolds imports it: zod is a *peer* of
9
+ * `@penvhq/penv`, and a peer is a package the project supplies. Under pnpm's
10
+ * strict layout nothing hoists it to the project root, so an install that named
11
+ * only `@penvhq/penv` left the very schema init had just written unable to
12
+ * resolve `zod` — and adoption could never finish.
13
+ *
14
+ * Both are installed with the package manager the project already uses, and only
15
+ * after showing the exact `package.json` and lockfile change: an install is the
16
+ * one step of adoption that reaches outside the repository, so it is the one step
17
+ * that is shown before it happens rather than reported after.
18
+ *
19
+ * The install itself is a seam. It shells out to a package manager, which the
20
+ * tests must never do — and a fake here is not a weaker test, because what init
21
+ * has to get right is the plan, the consent, and the refusal when the install
22
+ * does not happen.
23
+ *
24
+ * A plan is a list of steps, because in a workspace "the project's dependency"
25
+ * is plural. pnpm refuses a bare `add` at a workspace root (`-w` is how you say
26
+ * you meant the root), and a workspace package that declares `@penvhq/penv`
27
+ * itself is a second copy of the very version the manifest pins — one repository
28
+ * ran the 0.8 bridge under a 0.11 pin for three releases because nothing looked
29
+ * below the root. So every `package.json` that declares it moves, under one
30
+ * consent, and the commands shown are the ones that run.
31
+ *
32
+ * Two commands write that dependency line: `penv init`, which is the engine's,
33
+ * and `penv upgrade`, which is the launcher's. This module is published at
34
+ * `@penvhq/cli/install` so the launcher reaches it without loading the command
35
+ * surface — one answer to "which package manager, which diff, which spawn",
36
+ * rather than a second copy on the other side of the launcher/engine split.
37
+ */
38
+
39
+ /** The package an adopted project depends on. The CLI engine is not one of its dependencies. */
40
+ declare const RUNTIME_PACKAGE = "@penvhq/penv";
41
+ /** The peer `penv.schema.ts` imports, which the project supplies because a peer is not hoisted. */
42
+ declare const SCHEMA_PACKAGE = "zod";
43
+ type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
44
+ /** One package the adopted project needs, and what its `package.json` says today. */
45
+ interface InstallPackage {
46
+ readonly name: string;
47
+ readonly version: string;
48
+ /** What `package.json` already says about it, when it says anything. */
49
+ readonly declared?: string;
50
+ /** True when this project already has it — nothing to install for this one. */
51
+ readonly satisfied: boolean;
52
+ }
53
+ /** One `package.json` the install rewrites, and the command that rewrites it. */
54
+ interface InstallStep {
55
+ /** The file the diff names — `package.json`, or a workspace package's path to it. */
56
+ readonly manifest: string;
57
+ /** Everything this file needs, in the order the diff shows them. */
58
+ readonly packages: readonly InstallPackage[];
59
+ /** The command, argv-shaped — run from the project root, whichever file it writes. */
60
+ readonly command: readonly string[];
61
+ /** True when this file already declares every one of them. */
62
+ readonly satisfied: boolean;
63
+ }
64
+ interface InstallPlan {
65
+ readonly root: string;
66
+ readonly manager: PackageManager;
67
+ /** The root's `package.json` first, then every workspace package that declares the runtime one. */
68
+ readonly steps: readonly InstallStep[];
69
+ /** The lockfile the manager will rewrite, when the project has one. */
70
+ readonly lockfile?: string;
71
+ /** True when every step is already satisfied — nothing to install. */
72
+ readonly satisfied: boolean;
73
+ }
74
+ /** Runs an install plan, or throws. Replaced in tests; never spawns there. */
75
+ type InstallRuntime = (plan: InstallPlan) => Promise<void>;
76
+ /**
77
+ * The engine's own version, read from its manifest rather than restated in the
78
+ * source: `@penvhq/penv` must match the engine exactly, and a constant beside
79
+ * the version a release bumps is a second answer waiting to drift.
80
+ */
81
+ declare function engineVersion(): string;
82
+ /**
83
+ * The zod an adopted project installs: the floor of the peer range the engine
84
+ * and `@penvhq/penv` both declare, which is the version penv is built and tested
85
+ * against.
86
+ *
87
+ * The floor rather than the range, because the diff shown before the install has
88
+ * to be the line that actually lands — `--save-exact` on `^4.4.3` would write
89
+ * whatever the registry resolved that day, which is not something a reader can
90
+ * consent to in advance.
91
+ */
92
+ declare function schemaPackageVersion(): string;
93
+ /** The package manager this project already uses: its lockfile, then what it declares, then npm. */
94
+ declare function detectPackageManager(root: string): PackageManager;
95
+ /** True when `root` is the root of a pnpm workspace, which is what `-w` is for. */
96
+ declare function isPnpmWorkspaceRoot(root: string): boolean;
97
+ declare function planInstall(root: string, version?: string): InstallPlan;
98
+ /** What this plan actually installs, once per package however many files declare it. */
99
+ declare function installedPackages(plan: InstallPlan): readonly InstallPackage[];
100
+ /**
101
+ * The change, as it will appear in the diff — the whole point of showing it is
102
+ * that the reader recognises their own file, so these are the `package.json`
103
+ * lines that land and the lockfile that gets rewritten, not a summary of both.
104
+ *
105
+ * In a workspace that is more than one file, and the commands underneath are the
106
+ * ones that run: a "Run with:" line the reader cannot paste is worse than none.
107
+ */
108
+ declare function renderInstallPlan(plan: InstallPlan): string[];
109
+ /**
110
+ * The real install: the project's own package manager, started the way any other
111
+ * child is (`.cmd` shims on Windows included), with its output the user's to see.
112
+ *
113
+ * Every step runs from the project root — `-w` and `--filter` are how a workspace
114
+ * says which `package.json` it means, so the directory never changes.
115
+ */
116
+ declare const installWithPackageManager: InstallRuntime;
117
+ /**
118
+ * What to do about a package manager that refused.
119
+ *
120
+ * Never the command that just failed: the one remediation guaranteed not to work
121
+ * is the one the reader already ran. The manager said why, on their screen, and
122
+ * nothing was migrated — so the answer is that line and a second `penv init`.
123
+ */
124
+ declare function installFailed(plan: InstallPlan, step: InstallStep): PenvError;
125
+
126
+ export { type InstallPackage, type InstallPlan, type InstallRuntime, type InstallStep, type PackageManager, RUNTIME_PACKAGE, SCHEMA_PACKAGE, detectPackageManager, engineVersion, installFailed, installWithPackageManager, installedPackages, isPnpmWorkspaceRoot, planInstall, renderInstallPlan, schemaPackageVersion };
@@ -0,0 +1,27 @@
1
+ import {
2
+ RUNTIME_PACKAGE,
3
+ SCHEMA_PACKAGE,
4
+ detectPackageManager,
5
+ engineVersion,
6
+ installFailed,
7
+ installWithPackageManager,
8
+ installedPackages,
9
+ isPnpmWorkspaceRoot,
10
+ planInstall,
11
+ renderInstallPlan,
12
+ schemaPackageVersion
13
+ } from "./chunk-NHTDIZZ2.js";
14
+ export {
15
+ RUNTIME_PACKAGE,
16
+ SCHEMA_PACKAGE,
17
+ detectPackageManager,
18
+ engineVersion,
19
+ installFailed,
20
+ installWithPackageManager,
21
+ installedPackages,
22
+ isPnpmWorkspaceRoot,
23
+ planInstall,
24
+ renderInstallPlan,
25
+ schemaPackageVersion
26
+ };
27
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@penvhq/cli",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "penv's command line. Dependency budget is deliberately looser than the runtime's.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Itzfeminisce/penvhq.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "homepage": "https://github.com/Itzfeminisce/penvhq#readme",
6
12
  "type": "module",
7
13
  "main": "./dist/index.cjs",
8
14
  "module": "./dist/index.js",
@@ -16,6 +22,11 @@
16
22
  "import": "./dist/index.js",
17
23
  "require": "./dist/index.cjs"
18
24
  },
25
+ "./install": {
26
+ "types": "./dist/install.d.ts",
27
+ "import": "./dist/install.js",
28
+ "require": "./dist/install.cjs"
29
+ },
19
30
  "./package.json": "./package.json"
20
31
  },
21
32
  "files": [
@@ -25,10 +36,10 @@
25
36
  "@napi-rs/keyring": "1.3.0",
26
37
  "citty": "0.2.2",
27
38
  "jiti": "2.7.0",
28
- "@penvhq/core": "0.10.0",
29
- "@penvhq/provider-filesystem": "0.10.0",
30
- "@penvhq/provider-mock": "0.10.0",
31
- "@penvhq/runtime": "0.10.0"
39
+ "@penvhq/core": "0.12.0",
40
+ "@penvhq/provider-filesystem": "0.12.0",
41
+ "@penvhq/provider-mock": "0.12.0",
42
+ "@penvhq/runtime": "0.12.0"
32
43
  },
33
44
  "peerDependencies": {
34
45
  "zod": "^4.4.3"
@@ -36,8 +47,8 @@
36
47
  "devDependencies": {
37
48
  "server-only": "0.0.1",
38
49
  "zod": "4.4.3",
39
- "@penvhq/provider-github": "0.10.0",
40
- "@penvhq/provider-vault": "0.10.0"
50
+ "@penvhq/provider-github": "0.12.0",
51
+ "@penvhq/provider-vault": "0.12.0"
41
52
  },
42
53
  "scripts": {
43
54
  "build": "tsup"