@growthub/create-growthub-local 0.2.2 → 0.2.4

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 CHANGED
@@ -2,46 +2,72 @@
2
2
 
3
3
  `create-growthub-local` is the guided installer for Growthub Local.
4
4
 
5
+ It now handles three lanes in one command, so a fresh user can get either the
6
+ Paperclip Local App or a Custom Workspace Starter workspace in a single step —
7
+ no second `growthub starter init` call required.
8
+
5
9
  ## Quickstart
6
10
 
7
11
  ```bash
8
- # Interactive (recommended)
12
+ # Interactive discovery hub (kits / templates / workflows / agent harness / settings)
9
13
  npm create growthub-local@latest
10
14
 
11
- # Direct profile install
15
+ # Paperclip Local App profiles
12
16
  npm create growthub-local@latest -- --profile gtm
13
17
  npm create growthub-local@latest -- --profile dx
18
+
19
+ # Custom Workspace Starter (zero second step — scaffolds + registers as a fork)
20
+ npm create growthub-local@latest -- --profile workspace --out ./my-workspace
14
21
  ```
15
22
 
16
23
  ## Installer Paths
17
24
 
18
- - **Profile mode** (`--profile gtm|dx`)
19
- - runs direct onboarding for the selected local app profile
25
+ - **Paperclip Local App** (`--profile gtm|dx`)
26
+ - runs `growthub onboard --yes` for the selected surface profile
27
+ - **Custom Workspace Starter** (`--profile workspace`)
28
+ - runs `growthub starter init --out <path>` against the bundled
29
+ `growthub-custom-workspace-starter-v1` kit
30
+ - composes the already-shipping primitives — `copyBundledKitSource`,
31
+ `registerKitFork`, `writeKitForkPolicy`, `appendKitForkTraceEvent`,
32
+ and (optionally) `createFork` via the first-party GitHub integration
33
+ - no cross-package coupling beyond the existing `@growthub/cli` dep pin
20
34
  - **Discovery mode** (no profile)
21
- - opens `growthub discover` so users can choose:
22
- - Agent Harness
23
- - Worker Kits
24
- - Templates
35
+ - opens `growthub discover` so users can pick Worker Kits, Templates,
36
+ Workflows, Local Intelligence, Agent Harness, Settings, or Help
25
37
 
26
38
  ## Options
27
39
 
28
- | Flag | Description |
29
- |---|---|
30
- | `--profile gtm\|dx` | Optional direct install path for local app profiles |
31
- | `--run` | Start local runtime immediately after onboarding |
32
- | `--data-dir <path>` | Override install directory (default: `./growthub-local`) |
33
- | `--config <path>` | Use a custom config path |
34
-
35
- ## After Install
40
+ | Flag | Applies to | Description |
41
+ |---|---|---|
42
+ | `--profile gtm\|dx\|workspace` | all | Pick an install lane |
43
+ | `--run` | `dx`, `gtm`, discovery | Start Growthub immediately after saving config |
44
+ | `--data-dir <path>` | `dx`, `gtm`, discovery | Override install directory (default: `./growthub-local`) |
45
+ | `--config <path>` | `dx`, `gtm`, discovery | Use a custom config path |
46
+ | `--out <path>` | `workspace` | Destination directory for the new workspace |
47
+ | `--kit <kit-id>` | `workspace` | Source kit id (default: `growthub-custom-workspace-starter-v1`) |
48
+ | `--name <label>` | `workspace` | Human label for the fork |
49
+ | `--upstream <owner/repo>` | `workspace` | When set, also creates a remote GitHub fork |
50
+ | `--destination-org <org>` | `workspace` | Create the GitHub fork under an org |
51
+ | `--fork-name <name>` | `workspace` | Override the GitHub fork name |
52
+ | `--remote-sync-mode <mode>` | `workspace` | Initial `policy.remoteSyncMode` — `off` (default), `branch`, `pr` |
53
+ | `--json` | `workspace` | Emit machine-readable output from `growthub starter init` |
54
+
55
+ ## After install — Paperclip Local App
36
56
 
37
57
  ```bash
38
58
  cd growthub-local
39
59
  npx growthub run
40
60
  ```
41
61
 
42
- Open CLI discovery again:
62
+ ## After install — Custom Workspace Starter
43
63
 
44
64
  ```bash
65
+ cd my-workspace
66
+
67
+ # Inspect your new fork (registration + policy + trace)
68
+ npx growthub kit fork status <fork-id>
69
+
70
+ # Re-open the discovery hub any time
45
71
  npx growthub
46
72
  ```
47
73
 
@@ -9,50 +9,157 @@ import { fileURLToPath } from "node:url";
9
9
  const require = createRequire(import.meta.url);
10
10
  const __filename = fileURLToPath(import.meta.url);
11
11
 
12
+ const VALID_PROFILES = new Set(["dx", "gtm", "workspace"]);
13
+ const VALID_REMOTE_SYNC_MODES = new Set(["off", "branch", "pr"]);
14
+
12
15
  function printUsage() {
13
- console.log("Usage: create-growthub-local [--profile <dx|gtm>] [--run] [--data-dir <path>] [--config <path>]");
16
+ console.log(
17
+ [
18
+ "Usage:",
19
+ " create-growthub-local [--profile <dx|gtm|workspace>] [--out <path>]",
20
+ " [--data-dir <path>] [--config <path>]",
21
+ " [--run]",
22
+ "",
23
+ "Paperclip Local App profiles (dx | gtm):",
24
+ " create-growthub-local --profile gtm",
25
+ " create-growthub-local --profile dx --data-dir ./my-growthub",
26
+ "",
27
+ "Custom Workspace Starter profile (workspace):",
28
+ " create-growthub-local --profile workspace --out ./my-workspace",
29
+ " create-growthub-local --profile workspace --out ./my-workspace --name \"My Workspace\"",
30
+ " create-growthub-local --profile workspace --out ./my-workspace \\",
31
+ " --upstream Growthub-ai/growthub-custom-workspace-starter-v1 --remote-sync-mode off",
32
+ "",
33
+ "Discovery mode (no profile):",
34
+ " create-growthub-local # opens `growthub discover` picker",
35
+ ].join("\n"),
36
+ );
14
37
  }
15
38
 
16
39
  function parseArgs(argv) {
17
- let profile = null;
18
- let run = false;
19
- let dataDir = null;
20
- let config = null;
40
+ const opts = {
41
+ profile: null,
42
+ run: false,
43
+ dataDir: null,
44
+ config: null,
45
+ out: null,
46
+ kit: null,
47
+ name: null,
48
+ upstream: null,
49
+ destinationOrg: null,
50
+ forkName: null,
51
+ remoteSyncMode: null,
52
+ json: false,
53
+ };
21
54
 
22
55
  for (let index = 0; index < argv.length; index += 1) {
23
56
  const value = argv[index];
57
+
24
58
  if (value === "--profile" && argv[index + 1]) {
25
- profile = argv[index + 1];
59
+ opts.profile = String(argv[index + 1]).trim();
26
60
  index += 1;
27
61
  continue;
28
62
  }
29
63
  if (value === "--run") {
30
- run = true;
64
+ opts.run = true;
31
65
  continue;
32
66
  }
33
67
  if ((value === "-d" || value === "--data-dir") && argv[index + 1]) {
34
- dataDir = argv[index + 1];
68
+ opts.dataDir = argv[index + 1];
35
69
  index += 1;
36
70
  continue;
37
71
  }
38
72
  if ((value === "-c" || value === "--config") && argv[index + 1]) {
39
- config = argv[index + 1];
73
+ opts.config = argv[index + 1];
74
+ index += 1;
75
+ continue;
76
+ }
77
+ if (value === "--out" && argv[index + 1]) {
78
+ opts.out = argv[index + 1];
79
+ index += 1;
80
+ continue;
81
+ }
82
+ if (value === "--kit" && argv[index + 1]) {
83
+ opts.kit = argv[index + 1];
84
+ index += 1;
85
+ continue;
86
+ }
87
+ if (value === "--name" && argv[index + 1]) {
88
+ opts.name = argv[index + 1];
89
+ index += 1;
90
+ continue;
91
+ }
92
+ if (value === "--upstream" && argv[index + 1]) {
93
+ opts.upstream = argv[index + 1];
40
94
  index += 1;
41
95
  continue;
42
96
  }
97
+ if (value === "--destination-org" && argv[index + 1]) {
98
+ opts.destinationOrg = argv[index + 1];
99
+ index += 1;
100
+ continue;
101
+ }
102
+ if (value === "--fork-name" && argv[index + 1]) {
103
+ opts.forkName = argv[index + 1];
104
+ index += 1;
105
+ continue;
106
+ }
107
+ if (value === "--remote-sync-mode" && argv[index + 1]) {
108
+ opts.remoteSyncMode = String(argv[index + 1]).trim();
109
+ index += 1;
110
+ continue;
111
+ }
112
+ if (value === "--json") {
113
+ opts.json = true;
114
+ continue;
115
+ }
43
116
  if (value === "-h" || value === "--help") {
44
117
  printUsage();
45
118
  process.exit(0);
46
119
  }
47
120
  }
48
121
 
49
- if (profile !== null && profile !== "dx" && profile !== "gtm") {
122
+ if (opts.profile !== null && !VALID_PROFILES.has(opts.profile)) {
123
+ printUsage();
124
+ console.error(
125
+ `create-growthub-local only accepts --profile dx | gtm | workspace (got: ${opts.profile})`,
126
+ );
127
+ process.exit(1);
128
+ }
129
+
130
+ if (
131
+ opts.remoteSyncMode !== null
132
+ && !VALID_REMOTE_SYNC_MODES.has(opts.remoteSyncMode)
133
+ ) {
50
134
  printUsage();
51
- console.error("create-growthub-local only accepts --profile dx or --profile gtm");
135
+ console.error(
136
+ `--remote-sync-mode must be one of off | branch | pr (got: ${opts.remoteSyncMode})`,
137
+ );
52
138
  process.exit(1);
53
139
  }
54
140
 
55
- return { profile, run, dataDir, config };
141
+ const workspaceOnlyFlags = [
142
+ ["--out", opts.out],
143
+ ["--kit", opts.kit],
144
+ ["--name", opts.name],
145
+ ["--upstream", opts.upstream],
146
+ ["--destination-org", opts.destinationOrg],
147
+ ["--fork-name", opts.forkName],
148
+ ["--remote-sync-mode", opts.remoteSyncMode],
149
+ ];
150
+ if (opts.profile !== "workspace") {
151
+ for (const [flag, value] of workspaceOnlyFlags) {
152
+ if (value !== null) {
153
+ printUsage();
154
+ console.error(
155
+ `${flag} is only valid with --profile workspace (the Custom Workspace Starter path).`,
156
+ );
157
+ process.exit(1);
158
+ }
159
+ }
160
+ }
161
+
162
+ return opts;
56
163
  }
57
164
 
58
165
  function resolveGrowthubCliEntrypoint() {
@@ -78,10 +185,71 @@ function resolveGrowthubCliEntrypoint() {
78
185
  return path.resolve(cliPackageDir, growthubBin);
79
186
  }
80
187
 
81
- const { profile, run, dataDir, config } = parseArgs(process.argv.slice(2));
82
- const effectiveDataDir = dataDir ? path.resolve(process.cwd(), dataDir) : path.resolve(process.cwd(), "growthub-local");
83
- let growthubCli;
188
+ function buildCliArgs(opts, effectiveDataDir, growthubCli) {
189
+ // --profile workspace forward into `growthub starter init`
190
+ // (the Custom Workspace Starter surface, which composes
191
+ // copyBundledKitSource + registerKitFork + writeKitForkPolicy
192
+ // + appendKitForkTraceEvent and, optionally, createFork).
193
+ if (opts.profile === "workspace") {
194
+ const outArg = opts.out ?? "./my-workspace";
195
+ const absOut = path.resolve(process.cwd(), outArg);
196
+ const args = [growthubCli, "starter", "init", "--out", absOut];
197
+ if (opts.kit) args.push("--kit", opts.kit);
198
+ if (opts.name) args.push("--name", opts.name);
199
+ if (opts.upstream) args.push("--upstream", opts.upstream);
200
+ if (opts.destinationOrg) args.push("--destination-org", opts.destinationOrg);
201
+ if (opts.forkName) args.push("--fork-name", opts.forkName);
202
+ if (opts.remoteSyncMode) args.push("--remote-sync-mode", opts.remoteSyncMode);
203
+ if (opts.json) args.push("--json");
204
+ return args;
205
+ }
206
+
207
+ // --profile dx | gtm → existing Paperclip Local App onboarding flow.
208
+ if (opts.profile) {
209
+ return [
210
+ growthubCli,
211
+ "onboard",
212
+ "--yes",
213
+ ...(opts.run ? ["--run"] : []),
214
+ "--data-dir",
215
+ effectiveDataDir,
216
+ ...(opts.config ? ["--config", opts.config] : []),
217
+ ];
218
+ }
219
+
220
+ // No profile → open the shared discovery hub (kits / templates /
221
+ // workflows / agent harness / settings).
222
+ return [
223
+ growthubCli,
224
+ "discover",
225
+ ...(opts.run ? ["--run"] : []),
226
+ "--data-dir",
227
+ effectiveDataDir,
228
+ ...(opts.config ? ["--config", opts.config] : []),
229
+ ];
230
+ }
84
231
 
232
+ function buildCliEnv(opts) {
233
+ const env = {
234
+ ...process.env,
235
+ GROWTHUB_INSTALLER_MODE: "true",
236
+ };
237
+ // PAPERCLIP_SURFACE_PROFILE is only meaningful for the Paperclip
238
+ // Local App lanes (dx | gtm). The workspace profile scaffolds a
239
+ // Self-Healing Fork Sync workspace that has nothing to do with
240
+ // Paperclip surface selection, so we intentionally do not set it.
241
+ if (opts.profile === "dx" || opts.profile === "gtm") {
242
+ env.PAPERCLIP_SURFACE_PROFILE = opts.profile;
243
+ }
244
+ return env;
245
+ }
246
+
247
+ const opts = parseArgs(process.argv.slice(2));
248
+ const effectiveDataDir = opts.dataDir
249
+ ? path.resolve(process.cwd(), opts.dataDir)
250
+ : path.resolve(process.cwd(), "growthub-local");
251
+
252
+ let growthubCli;
85
253
  try {
86
254
  growthubCli = resolveGrowthubCliEntrypoint();
87
255
  } catch (error) {
@@ -93,35 +261,13 @@ try {
93
261
  process.exit(1);
94
262
  }
95
263
 
96
- const result = spawnSync(
97
- process.execPath,
98
- profile
99
- ? [
100
- growthubCli,
101
- "onboard",
102
- "--yes",
103
- ...(run ? ["--run"] : []),
104
- "--data-dir",
105
- effectiveDataDir,
106
- ...(config ? ["--config", config] : []),
107
- ]
108
- : [
109
- growthubCli,
110
- "discover",
111
- ...(run ? ["--run"] : []),
112
- "--data-dir",
113
- effectiveDataDir,
114
- ...(config ? ["--config", config] : []),
115
- ],
116
- {
117
- cwd: process.cwd(),
118
- stdio: "inherit",
119
- env: {
120
- ...process.env,
121
- GROWTHUB_INSTALLER_MODE: "true",
122
- ...(profile ? { PAPERCLIP_SURFACE_PROFILE: profile } : {}),
123
- },
124
- },
125
- );
264
+ const cliArgs = buildCliArgs(opts, effectiveDataDir, growthubCli);
265
+ const cliEnv = buildCliEnv(opts);
266
+
267
+ const result = spawnSync(process.execPath, cliArgs, {
268
+ cwd: process.cwd(),
269
+ stdio: "inherit",
270
+ env: cliEnv,
271
+ });
126
272
 
127
273
  process.exit(result.status ?? 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growthub/create-growthub-local",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Growthub local installer for DX and GTM profiles",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "node": ">=20"
24
24
  },
25
25
  "dependencies": {
26
- "@growthub/cli": "0.4.2"
26
+ "@growthub/cli": "0.5.0"
27
27
  },
28
28
  "keywords": [
29
29
  "growthub",