@growthub/cli 0.3.46 → 0.3.48

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.
Files changed (3) hide show
  1. package/README.md +25 -3
  2. package/dist/index.js +1511 -466
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6268,9 +6268,9 @@ async function runDatabaseBackup(opts) {
6268
6268
  WHERE n.nspname = ${schema_name} AND t.relname = ${tablename} AND c.contype = 'p'
6269
6269
  GROUP BY c.conname
6270
6270
  `;
6271
- for (const p19 of pk) {
6272
- const cols = p19.column_names.map((c) => `"${c}"`).join(", ");
6273
- colDefs.push(` CONSTRAINT "${p19.constraint_name}" PRIMARY KEY (${cols})`);
6271
+ for (const p20 of pk) {
6272
+ const cols = p20.column_names.map((c) => `"${c}"`).join(", ");
6273
+ colDefs.push(` CONSTRAINT "${p20.constraint_name}" PRIMARY KEY (${cols})`);
6274
6274
  }
6275
6275
  emit(`CREATE TABLE ${qualifiedTableName} (`);
6276
6276
  emit(colDefs.join(",\n"));
@@ -8016,9 +8016,9 @@ var init_onboard = __esm({
8016
8016
  init_onboard();
8017
8017
  init_doctor();
8018
8018
  import { Command } from "commander";
8019
- import * as p18 from "@clack/prompts";
8020
- import fs16 from "node:fs";
8021
- import path23 from "node:path";
8019
+ import * as p19 from "@clack/prompts";
8020
+ import fs19 from "node:fs";
8021
+ import path27 from "node:path";
8022
8022
 
8023
8023
  // src/commands/env.ts
8024
8024
  init_store();
@@ -8677,8 +8677,8 @@ function printItemCompleted(item) {
8677
8677
  const changes = Array.isArray(item.changes) ? item.changes : [];
8678
8678
  const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
8679
8679
  const kind = asString(change.kind, "update");
8680
- const path24 = asString(change.path, "unknown");
8681
- return `${kind} ${path24}`;
8680
+ const path28 = asString(change.path, "unknown");
8681
+ return `${kind} ${path28}`;
8682
8682
  });
8683
8683
  const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
8684
8684
  const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
@@ -9696,26 +9696,26 @@ var PaperclipApiClient = class {
9696
9696
  this.apiKey = opts.apiKey?.trim() || void 0;
9697
9697
  this.runId = opts.runId?.trim() || void 0;
9698
9698
  }
9699
- get(path24, opts) {
9700
- return this.request(path24, { method: "GET" }, opts);
9699
+ get(path28, opts) {
9700
+ return this.request(path28, { method: "GET" }, opts);
9701
9701
  }
9702
- post(path24, body, opts) {
9703
- return this.request(path24, {
9702
+ post(path28, body, opts) {
9703
+ return this.request(path28, {
9704
9704
  method: "POST",
9705
9705
  body: body === void 0 ? void 0 : JSON.stringify(body)
9706
9706
  }, opts);
9707
9707
  }
9708
- patch(path24, body, opts) {
9709
- return this.request(path24, {
9708
+ patch(path28, body, opts) {
9709
+ return this.request(path28, {
9710
9710
  method: "PATCH",
9711
9711
  body: body === void 0 ? void 0 : JSON.stringify(body)
9712
9712
  }, opts);
9713
9713
  }
9714
- delete(path24, opts) {
9715
- return this.request(path24, { method: "DELETE" }, opts);
9714
+ delete(path28, opts) {
9715
+ return this.request(path28, { method: "DELETE" }, opts);
9716
9716
  }
9717
- async request(path24, init, opts) {
9718
- const url = buildUrl(this.apiBase, path24);
9717
+ async request(path28, init, opts) {
9718
+ const url = buildUrl(this.apiBase, path28);
9719
9719
  const headers = {
9720
9720
  accept: "application/json",
9721
9721
  ...toStringRecord(init.headers)
@@ -9749,8 +9749,8 @@ var PaperclipApiClient = class {
9749
9749
  return safeParseJson(text58);
9750
9750
  }
9751
9751
  };
9752
- function buildUrl(apiBase, path24) {
9753
- const normalizedPath = path24.startsWith("/") ? path24 : `/${path24}`;
9752
+ function buildUrl(apiBase, path28) {
9753
+ const normalizedPath = path28.startsWith("/") ? path28 : `/${path28}`;
9754
9754
  const [pathname, query] = normalizedPath.split("?");
9755
9755
  const url = new URL2(apiBase);
9756
9756
  url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
@@ -10165,14 +10165,1026 @@ function safeParseLogLine(line) {
10165
10165
  init_run();
10166
10166
  init_auth_bootstrap_ceo();
10167
10167
 
10168
+ // src/commands/auth-login.ts
10169
+ init_store();
10170
+ init_env();
10171
+ import os3 from "node:os";
10172
+ import * as p14 from "@clack/prompts";
10173
+ import pc18 from "picocolors";
10174
+ import open from "open";
10175
+
10176
+ // src/auth/login-flow.ts
10177
+ import { createServer } from "node:http";
10178
+ import { randomBytes as randomBytes5 } from "node:crypto";
10179
+ import os2 from "node:os";
10180
+ import { URL as URL3 } from "node:url";
10181
+ var DEFAULT_HOSTED_LOGIN_PATH = "/cli/login";
10182
+ var CALLBACK_PATH = "/cli-callback";
10183
+ function randomState() {
10184
+ return randomBytes5(16).toString("hex");
10185
+ }
10186
+ function trimSlashes(value) {
10187
+ return value.replace(/\/+$/, "");
10188
+ }
10189
+ function pickParam(url, name) {
10190
+ const value = url.searchParams.get(name);
10191
+ if (value === null) return void 0;
10192
+ const trimmed = value.trim();
10193
+ return trimmed.length > 0 ? trimmed : void 0;
10194
+ }
10195
+ function renderSuccessPage(hostedBaseUrl) {
10196
+ const safeBase = hostedBaseUrl.replace(/"/g, """);
10197
+ return `<!doctype html>
10198
+ <html lang="en">
10199
+ <head>
10200
+ <meta charset="utf-8" />
10201
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
10202
+ <title>Growthub CLI connected</title>
10203
+ <style>
10204
+ body { font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; background: #0b0f14; color: #f5f7fa; }
10205
+ main { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
10206
+ section { width: min(560px, 100%); background: #121821; border: 1px solid #263244; border-radius: 16px; padding: 24px; box-sizing: border-box; }
10207
+ h1 { margin: 0 0 12px; font-size: 22px; }
10208
+ p { margin: 0 0 12px; line-height: 1.5; color: #c7d2e0; }
10209
+ a { color: #7dd3fc; }
10210
+ </style>
10211
+ </head>
10212
+ <body>
10213
+ <main>
10214
+ <section>
10215
+ <h1>Growthub CLI connected</h1>
10216
+ <p>Your local CLI now has a hosted session token. You can close this tab and return to your terminal.</p>
10217
+ <p>Hosted app: <a href="${safeBase}">${safeBase}</a></p>
10218
+ </section>
10219
+ </main>
10220
+ <script>window.setTimeout(() => { try { window.close(); } catch {} }, 1200);</script>
10221
+ </body>
10222
+ </html>`;
10223
+ }
10224
+ function renderErrorPage(message) {
10225
+ const safeMessage = message.replace(/</g, "&lt;");
10226
+ return `<!doctype html>
10227
+ <html lang="en"><head><meta charset="utf-8" /><title>Growthub CLI login error</title></head>
10228
+ <body style="font-family: ui-sans-serif, system-ui, sans-serif; background: #0b0f14; color: #f5f7fa; padding: 24px;">
10229
+ <h1>Login error</h1>
10230
+ <p>${safeMessage}</p>
10231
+ <p>Return to your terminal and try again.</p>
10232
+ </body></html>`;
10233
+ }
10234
+ function listenOnEphemeralLoopback(server) {
10235
+ return new Promise((resolve2, reject) => {
10236
+ server.once("error", reject);
10237
+ server.listen({ host: "127.0.0.1", port: 0 }, () => {
10238
+ server.off("error", reject);
10239
+ const address = server.address();
10240
+ if (!address || typeof address === "string") {
10241
+ reject(new Error("Failed to bind loopback port for CLI auth callback."));
10242
+ return;
10243
+ }
10244
+ resolve2(address.port);
10245
+ });
10246
+ });
10247
+ }
10248
+ async function startLoginFlow(opts) {
10249
+ const hostedBaseUrl = trimSlashes(opts.hostedBaseUrl);
10250
+ if (!hostedBaseUrl) {
10251
+ throw new Error("hostedBaseUrl is required to start the CLI login flow.");
10252
+ }
10253
+ try {
10254
+ new URL3(hostedBaseUrl);
10255
+ } catch {
10256
+ throw new Error(`Invalid hosted base URL: ${opts.hostedBaseUrl}`);
10257
+ }
10258
+ const state = randomState();
10259
+ const machineLabel = opts.machineLabel?.trim() || os2.hostname();
10260
+ const workspaceLabel = opts.workspaceLabel?.trim();
10261
+ const timeoutMs = Math.max(3e4, opts.timeoutMs ?? 5 * 6e4);
10262
+ let resolver = null;
10263
+ let rejecter = null;
10264
+ const waitPromise = new Promise((resolve2, reject) => {
10265
+ resolver = resolve2;
10266
+ rejecter = reject;
10267
+ });
10268
+ const server = createServer((req, res) => {
10269
+ try {
10270
+ const host = req.headers.host ?? "127.0.0.1";
10271
+ const requestUrl = new URL3(req.url ?? "/", `http://${host}`);
10272
+ if (requestUrl.pathname !== CALLBACK_PATH) {
10273
+ res.statusCode = 404;
10274
+ res.end("Not found");
10275
+ return;
10276
+ }
10277
+ const incomingState = pickParam(requestUrl, "state");
10278
+ if (!incomingState || incomingState !== state) {
10279
+ res.statusCode = 400;
10280
+ res.setHeader("content-type", "text/html; charset=utf-8");
10281
+ res.end(renderErrorPage("State token mismatch. Restart `growthub auth login`."));
10282
+ rejecter?.(new Error("CLI auth callback rejected \u2014 state mismatch."));
10283
+ return;
10284
+ }
10285
+ const error = pickParam(requestUrl, "error");
10286
+ if (error) {
10287
+ res.statusCode = 400;
10288
+ res.setHeader("content-type", "text/html; charset=utf-8");
10289
+ res.end(renderErrorPage(error));
10290
+ rejecter?.(new Error(`Hosted app reported login error: ${error}`));
10291
+ return;
10292
+ }
10293
+ const token = pickParam(requestUrl, "token");
10294
+ if (!token) {
10295
+ res.statusCode = 400;
10296
+ res.setHeader("content-type", "text/html; charset=utf-8");
10297
+ res.end(renderErrorPage("Missing token in callback."));
10298
+ rejecter?.(new Error("CLI auth callback missing token."));
10299
+ return;
10300
+ }
10301
+ const result = {
10302
+ state,
10303
+ token,
10304
+ hostedBaseUrl,
10305
+ expiresAt: pickParam(requestUrl, "expiresAt"),
10306
+ userId: pickParam(requestUrl, "userId"),
10307
+ email: pickParam(requestUrl, "email"),
10308
+ orgId: pickParam(requestUrl, "orgId"),
10309
+ orgName: pickParam(requestUrl, "orgName"),
10310
+ machineLabel: pickParam(requestUrl, "machineLabel") ?? machineLabel
10311
+ };
10312
+ res.statusCode = 200;
10313
+ res.setHeader("content-type", "text/html; charset=utf-8");
10314
+ res.end(renderSuccessPage(hostedBaseUrl));
10315
+ resolver?.(result);
10316
+ } catch (err) {
10317
+ const message = err instanceof Error ? err.message : String(err);
10318
+ res.statusCode = 500;
10319
+ res.end(message);
10320
+ rejecter?.(err instanceof Error ? err : new Error(message));
10321
+ }
10322
+ });
10323
+ const port = await listenOnEphemeralLoopback(server);
10324
+ const callbackUrl = `http://127.0.0.1:${port}${CALLBACK_PATH}`;
10325
+ const hostedLoginPath = opts.hostedLoginPath ?? DEFAULT_HOSTED_LOGIN_PATH;
10326
+ const loginUrl = (() => {
10327
+ const url = new URL3(hostedLoginPath, `${hostedBaseUrl}/`);
10328
+ url.searchParams.set("state", state);
10329
+ url.searchParams.set("callback", callbackUrl);
10330
+ url.searchParams.set("machineLabel", machineLabel);
10331
+ if (workspaceLabel) url.searchParams.set("workspaceLabel", workspaceLabel);
10332
+ url.searchParams.set("source", "cli");
10333
+ return url.toString();
10334
+ })();
10335
+ let timeoutHandle = setTimeout(() => {
10336
+ rejecter?.(new Error(`CLI login timed out after ${Math.round(timeoutMs / 1e3)}s.`));
10337
+ }, timeoutMs);
10338
+ if (typeof timeoutHandle.unref === "function") timeoutHandle.unref();
10339
+ const close = () => {
10340
+ if (timeoutHandle) {
10341
+ clearTimeout(timeoutHandle);
10342
+ timeoutHandle = null;
10343
+ }
10344
+ server.close();
10345
+ };
10346
+ const waitForCallback = async () => {
10347
+ try {
10348
+ const result = await waitPromise;
10349
+ return result;
10350
+ } finally {
10351
+ close();
10352
+ }
10353
+ };
10354
+ return {
10355
+ state,
10356
+ callbackUrl,
10357
+ loginUrl,
10358
+ waitForCallback,
10359
+ close
10360
+ };
10361
+ }
10362
+
10363
+ // src/auth/session-store.ts
10364
+ import fs11 from "node:fs";
10365
+ import path11 from "node:path";
10366
+
10367
+ // src/auth/paths.ts
10368
+ init_home();
10369
+ import path10 from "node:path";
10370
+ function resolveAuthDir() {
10371
+ return path10.resolve(resolvePaperclipHomeDir(), "auth");
10372
+ }
10373
+ function resolveProfilesDir() {
10374
+ return path10.resolve(resolvePaperclipHomeDir(), "profiles");
10375
+ }
10376
+ function resolveSessionPath() {
10377
+ return path10.resolve(resolveAuthDir(), "session.json");
10378
+ }
10379
+ function resolveHostedOverlayPath() {
10380
+ return path10.resolve(resolveProfilesDir(), "hosted-overlay.json");
10381
+ }
10382
+ function resolveEffectiveProfilePath() {
10383
+ return path10.resolve(resolveProfilesDir(), "effective-profile.json");
10384
+ }
10385
+
10386
+ // src/auth/session-store.ts
10387
+ function parseJson3(filePath) {
10388
+ try {
10389
+ return JSON.parse(fs11.readFileSync(filePath, "utf-8"));
10390
+ } catch (err) {
10391
+ throw new Error(
10392
+ `Failed to parse auth session at ${filePath}: ${err instanceof Error ? err.message : String(err)}`
10393
+ );
10394
+ }
10395
+ }
10396
+ function toStringOrUndefined2(value) {
10397
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
10398
+ }
10399
+ function normalizeSession(raw) {
10400
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
10401
+ const record = raw;
10402
+ const accessToken = toStringOrUndefined2(record.accessToken);
10403
+ const hostedBaseUrl = toStringOrUndefined2(record.hostedBaseUrl);
10404
+ if (!accessToken || !hostedBaseUrl) return null;
10405
+ const issuedAt = toStringOrUndefined2(record.issuedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
10406
+ return {
10407
+ version: 1,
10408
+ hostedBaseUrl,
10409
+ accessToken,
10410
+ expiresAt: toStringOrUndefined2(record.expiresAt),
10411
+ userId: toStringOrUndefined2(record.userId),
10412
+ email: toStringOrUndefined2(record.email),
10413
+ orgId: toStringOrUndefined2(record.orgId),
10414
+ orgName: toStringOrUndefined2(record.orgName),
10415
+ machineLabel: toStringOrUndefined2(record.machineLabel),
10416
+ issuedAt
10417
+ };
10418
+ }
10419
+ function readSession() {
10420
+ const filePath = resolveSessionPath();
10421
+ if (!fs11.existsSync(filePath)) return null;
10422
+ const raw = parseJson3(filePath);
10423
+ return normalizeSession(raw);
10424
+ }
10425
+ function writeSession(session) {
10426
+ const filePath = resolveSessionPath();
10427
+ fs11.mkdirSync(resolveAuthDir(), { recursive: true });
10428
+ fs11.writeFileSync(filePath, `${JSON.stringify(session, null, 2)}
10429
+ `, { mode: 384 });
10430
+ try {
10431
+ fs11.chmodSync(filePath, 384);
10432
+ } catch {
10433
+ }
10434
+ }
10435
+ function clearSession() {
10436
+ const filePath = resolveSessionPath();
10437
+ if (!fs11.existsSync(filePath)) return false;
10438
+ fs11.rmSync(filePath, { force: true });
10439
+ return true;
10440
+ }
10441
+ function isSessionExpired(session, now = /* @__PURE__ */ new Date()) {
10442
+ if (!session.expiresAt) return false;
10443
+ const expires = Date.parse(session.expiresAt);
10444
+ if (Number.isNaN(expires)) return false;
10445
+ return expires <= now.getTime();
10446
+ }
10447
+ function describeSessionPath() {
10448
+ return path11.resolve(resolveSessionPath());
10449
+ }
10450
+
10451
+ // src/auth/overlay-store.ts
10452
+ import fs12 from "node:fs";
10453
+ import path12 from "node:path";
10454
+ function parseJson4(filePath) {
10455
+ try {
10456
+ return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
10457
+ } catch (err) {
10458
+ throw new Error(
10459
+ `Failed to parse hosted overlay at ${filePath}: ${err instanceof Error ? err.message : String(err)}`
10460
+ );
10461
+ }
10462
+ }
10463
+ function toStringOrUndefined3(value) {
10464
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
10465
+ }
10466
+ function toStringArray(value) {
10467
+ if (!Array.isArray(value)) return [];
10468
+ const out = [];
10469
+ for (const item of value) {
10470
+ const normalized = toStringOrUndefined3(item);
10471
+ if (normalized && !out.includes(normalized)) out.push(normalized);
10472
+ }
10473
+ return out;
10474
+ }
10475
+ function toRecord(value) {
10476
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
10477
+ return value;
10478
+ }
10479
+ function normalizeExecutionDefaults(value) {
10480
+ const record = toRecord(value) ?? {};
10481
+ const modeRaw = toStringOrUndefined3(record.preferredMode);
10482
+ const preferredMode = modeRaw === "local" || modeRaw === "serverless" || modeRaw === "browser" || modeRaw === "auto" ? modeRaw : "local";
10483
+ return {
10484
+ preferredMode,
10485
+ allowServerlessFallback: typeof record.allowServerlessFallback === "boolean" ? record.allowServerlessFallback : false,
10486
+ allowBrowserBridge: typeof record.allowBrowserBridge === "boolean" ? record.allowBrowserBridge : false
10487
+ };
10488
+ }
10489
+ function defaultExecutionPreferences() {
10490
+ return {
10491
+ preferredMode: "local",
10492
+ allowServerlessFallback: false,
10493
+ allowBrowserBridge: false
10494
+ };
10495
+ }
10496
+ function normalizeOverlay(raw) {
10497
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
10498
+ const record = raw;
10499
+ const hostedBaseUrl = toStringOrUndefined3(record.hostedBaseUrl);
10500
+ if (!hostedBaseUrl) return null;
10501
+ return {
10502
+ version: 1,
10503
+ hostedBaseUrl,
10504
+ userId: toStringOrUndefined3(record.userId),
10505
+ email: toStringOrUndefined3(record.email),
10506
+ displayName: toStringOrUndefined3(record.displayName),
10507
+ orgId: toStringOrUndefined3(record.orgId),
10508
+ orgName: toStringOrUndefined3(record.orgName),
10509
+ entitlements: toStringArray(record.entitlements),
10510
+ gatedKitSlugs: toStringArray(record.gatedKitSlugs),
10511
+ executionDefaults: normalizeExecutionDefaults(record.executionDefaults),
10512
+ linkedInstanceId: toStringOrUndefined3(record.linkedInstanceId),
10513
+ lastPulledAt: toStringOrUndefined3(record.lastPulledAt),
10514
+ lastPushedAt: toStringOrUndefined3(record.lastPushedAt),
10515
+ extra: toRecord(record.extra)
10516
+ };
10517
+ }
10518
+ function readHostedOverlay() {
10519
+ const filePath = resolveHostedOverlayPath();
10520
+ if (!fs12.existsSync(filePath)) return null;
10521
+ return normalizeOverlay(parseJson4(filePath));
10522
+ }
10523
+ function writeHostedOverlay(overlay) {
10524
+ const filePath = resolveHostedOverlayPath();
10525
+ fs12.mkdirSync(resolveProfilesDir(), { recursive: true });
10526
+ fs12.writeFileSync(filePath, `${JSON.stringify(overlay, null, 2)}
10527
+ `, { mode: 384 });
10528
+ try {
10529
+ fs12.chmodSync(filePath, 384);
10530
+ } catch {
10531
+ }
10532
+ }
10533
+ function clearHostedOverlay() {
10534
+ const filePath = resolveHostedOverlayPath();
10535
+ if (!fs12.existsSync(filePath)) return false;
10536
+ fs12.rmSync(filePath, { force: true });
10537
+ return true;
10538
+ }
10539
+ function describeHostedOverlayPath() {
10540
+ return path12.resolve(resolveHostedOverlayPath());
10541
+ }
10542
+ function seedHostedOverlayFromSession(input) {
10543
+ return {
10544
+ version: 1,
10545
+ hostedBaseUrl: input.hostedBaseUrl,
10546
+ userId: input.userId,
10547
+ email: input.email,
10548
+ displayName: input.email,
10549
+ orgId: input.orgId,
10550
+ orgName: input.orgName,
10551
+ entitlements: [],
10552
+ gatedKitSlugs: [],
10553
+ executionDefaults: defaultExecutionPreferences(),
10554
+ linkedInstanceId: input.linkedInstanceId,
10555
+ lastPulledAt: void 0,
10556
+ lastPushedAt: void 0,
10557
+ extra: input.machineLabel ? { machineLabel: input.machineLabel } : void 0
10558
+ };
10559
+ }
10560
+
10561
+ // src/auth/effective-profile.ts
10562
+ init_store();
10563
+ init_home();
10564
+ import fs13 from "node:fs";
10565
+ import path13 from "node:path";
10566
+ function toLocalWorkspaceView(configPath, config) {
10567
+ return {
10568
+ instanceId: resolvePaperclipInstanceId(),
10569
+ configPath,
10570
+ surfaceProfile: config?.surface?.profile === "dx" || config?.surface?.profile === "gtm" ? config.surface.profile : null,
10571
+ serverPort: typeof config?.server?.port === "number" ? config.server.port : null,
10572
+ serverHost: typeof config?.server?.host === "string" ? config.server.host : null,
10573
+ hasConfiguredToken: Boolean(config?.auth?.token?.trim()),
10574
+ growthubBaseUrl: config?.auth?.growthubBaseUrl?.trim() || null,
10575
+ growthubPortalBaseUrl: config?.auth?.growthubPortalBaseUrl?.trim() || null,
10576
+ machineLabel: config?.auth?.growthubMachineLabel?.trim() || null,
10577
+ workspaceLabel: config?.auth?.growthubWorkspaceLabel?.trim() || null
10578
+ };
10579
+ }
10580
+ function toHostedOverlayView(overlay) {
10581
+ if (!overlay) {
10582
+ return {
10583
+ present: false,
10584
+ hostedBaseUrl: null,
10585
+ userId: null,
10586
+ email: null,
10587
+ displayName: null,
10588
+ orgId: null,
10589
+ orgName: null,
10590
+ entitlements: [],
10591
+ gatedKitSlugs: [],
10592
+ executionDefaults: {
10593
+ preferredMode: "local",
10594
+ allowServerlessFallback: false,
10595
+ allowBrowserBridge: false
10596
+ },
10597
+ linkedInstanceId: null,
10598
+ lastPulledAt: null,
10599
+ lastPushedAt: null
10600
+ };
10601
+ }
10602
+ return {
10603
+ present: true,
10604
+ hostedBaseUrl: overlay.hostedBaseUrl || null,
10605
+ userId: overlay.userId ?? null,
10606
+ email: overlay.email ?? null,
10607
+ displayName: overlay.displayName ?? null,
10608
+ orgId: overlay.orgId ?? null,
10609
+ orgName: overlay.orgName ?? null,
10610
+ entitlements: overlay.entitlements,
10611
+ gatedKitSlugs: overlay.gatedKitSlugs,
10612
+ executionDefaults: overlay.executionDefaults,
10613
+ linkedInstanceId: overlay.linkedInstanceId ?? null,
10614
+ lastPulledAt: overlay.lastPulledAt ?? null,
10615
+ lastPushedAt: overlay.lastPushedAt ?? null
10616
+ };
10617
+ }
10618
+ function toSessionView(session, now) {
10619
+ if (!session) {
10620
+ return {
10621
+ present: false,
10622
+ expired: false,
10623
+ expiresAt: null,
10624
+ userId: null,
10625
+ hostedBaseUrl: null
10626
+ };
10627
+ }
10628
+ let expired = false;
10629
+ if (session.expiresAt) {
10630
+ const expires = Date.parse(session.expiresAt);
10631
+ if (!Number.isNaN(expires)) {
10632
+ expired = expires <= now.getTime();
10633
+ }
10634
+ }
10635
+ return {
10636
+ present: true,
10637
+ expired,
10638
+ expiresAt: session.expiresAt ?? null,
10639
+ userId: session.userId ?? null,
10640
+ hostedBaseUrl: session.hostedBaseUrl
10641
+ };
10642
+ }
10643
+ function computeEffectiveProfile(opts = {}) {
10644
+ const configPath = resolveConfigPath(opts.configPath);
10645
+ let config = null;
10646
+ try {
10647
+ config = readConfig(opts.configPath);
10648
+ } catch {
10649
+ config = null;
10650
+ }
10651
+ const overlay = readHostedOverlay();
10652
+ const session = readSession();
10653
+ const now = opts.now ?? /* @__PURE__ */ new Date();
10654
+ const sessionView = toSessionView(session, now);
10655
+ const hostedView = toHostedOverlayView(overlay);
10656
+ const localView = toLocalWorkspaceView(configPath, config);
10657
+ return {
10658
+ version: 1,
10659
+ generatedAt: now.toISOString(),
10660
+ authenticated: sessionView.present && !sessionView.expired,
10661
+ local: localView,
10662
+ hosted: hostedView,
10663
+ session: sessionView,
10664
+ executionDefaults: hostedView.present ? hostedView.executionDefaults : {
10665
+ preferredMode: "local",
10666
+ allowServerlessFallback: false,
10667
+ allowBrowserBridge: false
10668
+ }
10669
+ };
10670
+ }
10671
+ function writeEffectiveProfileSnapshot(profile) {
10672
+ const filePath = resolveEffectiveProfilePath();
10673
+ fs13.mkdirSync(resolveProfilesDir(), { recursive: true });
10674
+ fs13.writeFileSync(filePath, `${JSON.stringify(profile, null, 2)}
10675
+ `, { mode: 384 });
10676
+ return path13.resolve(filePath);
10677
+ }
10678
+
10679
+ // src/commands/auth-login.ts
10680
+ init_home();
10681
+ var DEFAULT_HOSTED_BASE_URL = "https://www.growthub.ai";
10682
+ function trimSlashes2(value) {
10683
+ return value.replace(/\/+$/, "");
10684
+ }
10685
+ function resolveHostedBaseUrl(opts) {
10686
+ const explicit = opts.baseUrl?.trim();
10687
+ if (explicit) return trimSlashes2(explicit);
10688
+ const envBase = process.env.GROWTHUB_BASE_URL?.trim();
10689
+ if (envBase) return trimSlashes2(envBase);
10690
+ try {
10691
+ const config = readConfig(opts.configPath);
10692
+ const configuredBase = config?.auth?.growthubBaseUrl?.trim();
10693
+ if (configuredBase) return trimSlashes2(configuredBase);
10694
+ const portalBase = config?.auth?.growthubPortalBaseUrl?.trim();
10695
+ if (portalBase) return trimSlashes2(portalBase);
10696
+ } catch {
10697
+ }
10698
+ return DEFAULT_HOSTED_BASE_URL;
10699
+ }
10700
+ async function authLogin(opts) {
10701
+ const configPath = resolveConfigPath(opts.config);
10702
+ loadPaperclipEnvFile(configPath);
10703
+ const hostedBaseUrl = resolveHostedBaseUrl({ baseUrl: opts.baseUrl, configPath: opts.config });
10704
+ const machineLabel = opts.machineLabel?.trim() || os3.hostname();
10705
+ const workspaceLabel = opts.workspaceLabel?.trim();
10706
+ const linkedInstanceId = resolvePaperclipInstanceId();
10707
+ const existingSession = readSession();
10708
+ if (!opts.token && existingSession && !isSessionExpired(existingSession) && trimSlashes2(existingSession.hostedBaseUrl) === hostedBaseUrl) {
10709
+ if (opts.json) {
10710
+ console.log(
10711
+ JSON.stringify(
10712
+ {
10713
+ status: "ok",
10714
+ hostedBaseUrl,
10715
+ userId: existingSession.userId ?? null,
10716
+ email: existingSession.email ?? null,
10717
+ reusedSession: true
10718
+ },
10719
+ null,
10720
+ 2
10721
+ )
10722
+ );
10723
+ return;
10724
+ }
10725
+ p14.log.success(
10726
+ `Already connected${existingSession.email ? ` as ${existingSession.email}` : ""}.`
10727
+ );
10728
+ if (existingSession.hostedBaseUrl) {
10729
+ p14.log.message(pc18.dim(`Hosted: ${existingSession.hostedBaseUrl}`));
10730
+ }
10731
+ return;
10732
+ }
10733
+ if (opts.token) {
10734
+ const now = (/* @__PURE__ */ new Date()).toISOString();
10735
+ writeSession({
10736
+ version: 1,
10737
+ hostedBaseUrl,
10738
+ accessToken: opts.token.trim(),
10739
+ issuedAt: now,
10740
+ machineLabel
10741
+ });
10742
+ const existingOverlay = readHostedOverlay();
10743
+ const overlay = existingOverlay ?? seedHostedOverlayFromSession({
10744
+ hostedBaseUrl,
10745
+ machineLabel,
10746
+ linkedInstanceId
10747
+ });
10748
+ writeHostedOverlay({
10749
+ ...overlay,
10750
+ hostedBaseUrl,
10751
+ linkedInstanceId: overlay.linkedInstanceId ?? linkedInstanceId
10752
+ });
10753
+ const effective = computeEffectiveProfile({ configPath });
10754
+ writeEffectiveProfileSnapshot(effective);
10755
+ if (opts.json) {
10756
+ console.log(JSON.stringify({ status: "ok", hostedBaseUrl, mode: "token" }, null, 2));
10757
+ } else {
10758
+ p14.log.success("Saved hosted session from --token.");
10759
+ p14.log.message(pc18.dim(`Session: ${describeSessionPath()}`));
10760
+ p14.log.message(pc18.dim(`Overlay: ${describeHostedOverlayPath()}`));
10761
+ }
10762
+ return;
10763
+ }
10764
+ p14.intro(pc18.bgCyan(pc18.black(" growthub auth login ")));
10765
+ p14.log.message(pc18.dim(`Hosted app: ${hostedBaseUrl}`));
10766
+ const flow = await startLoginFlow({
10767
+ hostedBaseUrl,
10768
+ machineLabel,
10769
+ workspaceLabel,
10770
+ timeoutMs: opts.timeoutMs
10771
+ });
10772
+ if (!opts.noBrowser) {
10773
+ try {
10774
+ p14.log.message("Opening browser to complete sign-in\u2026");
10775
+ await open(flow.loginUrl);
10776
+ } catch (err) {
10777
+ p14.log.warn(`Could not launch browser automatically: ${err instanceof Error ? err.message : String(err)}`);
10778
+ p14.log.message(pc18.dim("Paste this URL into a browser:"));
10779
+ p14.log.message(pc18.cyan(flow.loginUrl));
10780
+ }
10781
+ } else {
10782
+ p14.log.message(pc18.dim("Paste this URL into a browser:"));
10783
+ p14.log.message(pc18.cyan(flow.loginUrl));
10784
+ }
10785
+ const spinner5 = p14.spinner();
10786
+ spinner5.start("Waiting for hosted app to complete the exchange\u2026");
10787
+ try {
10788
+ const result = await flow.waitForCallback();
10789
+ spinner5.stop("Received hosted session token.");
10790
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
10791
+ writeSession({
10792
+ version: 1,
10793
+ hostedBaseUrl: result.hostedBaseUrl,
10794
+ accessToken: result.token,
10795
+ expiresAt: result.expiresAt,
10796
+ userId: result.userId,
10797
+ email: result.email,
10798
+ orgId: result.orgId,
10799
+ orgName: result.orgName,
10800
+ machineLabel: result.machineLabel,
10801
+ issuedAt: nowIso
10802
+ });
10803
+ const existingOverlay = readHostedOverlay();
10804
+ const overlay = existingOverlay ? {
10805
+ ...existingOverlay,
10806
+ hostedBaseUrl: result.hostedBaseUrl,
10807
+ userId: result.userId ?? existingOverlay.userId,
10808
+ email: result.email ?? existingOverlay.email,
10809
+ displayName: result.email ?? existingOverlay.displayName,
10810
+ orgId: result.orgId ?? existingOverlay.orgId,
10811
+ orgName: result.orgName ?? existingOverlay.orgName,
10812
+ linkedInstanceId: existingOverlay.linkedInstanceId ?? linkedInstanceId
10813
+ } : seedHostedOverlayFromSession({
10814
+ hostedBaseUrl: result.hostedBaseUrl,
10815
+ userId: result.userId,
10816
+ email: result.email,
10817
+ orgId: result.orgId,
10818
+ orgName: result.orgName,
10819
+ machineLabel: result.machineLabel,
10820
+ linkedInstanceId
10821
+ });
10822
+ writeHostedOverlay(overlay);
10823
+ const effective = computeEffectiveProfile({ configPath });
10824
+ writeEffectiveProfileSnapshot(effective);
10825
+ if (opts.json) {
10826
+ console.log(
10827
+ JSON.stringify(
10828
+ {
10829
+ status: "ok",
10830
+ hostedBaseUrl: result.hostedBaseUrl,
10831
+ userId: result.userId ?? null,
10832
+ email: result.email ?? null,
10833
+ orgId: result.orgId ?? null
10834
+ },
10835
+ null,
10836
+ 2
10837
+ )
10838
+ );
10839
+ } else {
10840
+ p14.log.success(`Signed in${result.email ? ` as ${result.email}` : ""}.`);
10841
+ p14.log.message(pc18.dim(`Session: ${describeSessionPath()}`));
10842
+ }
10843
+ p14.outro("Done");
10844
+ } catch (err) {
10845
+ spinner5.stop("Login failed.");
10846
+ p14.log.error(err instanceof Error ? err.message : String(err));
10847
+ process.exit(1);
10848
+ } finally {
10849
+ flow.close();
10850
+ }
10851
+ }
10852
+ async function authLogout(opts) {
10853
+ const sessionCleared = clearSession();
10854
+ const overlayCleared = opts.keepOverlay ? false : clearHostedOverlay();
10855
+ const effective = computeEffectiveProfile({ configPath: resolveConfigPath(opts.config) });
10856
+ writeEffectiveProfileSnapshot(effective);
10857
+ if (opts.json) {
10858
+ console.log(
10859
+ JSON.stringify(
10860
+ {
10861
+ status: "ok",
10862
+ sessionCleared,
10863
+ overlayCleared
10864
+ },
10865
+ null,
10866
+ 2
10867
+ )
10868
+ );
10869
+ return;
10870
+ }
10871
+ if (!sessionCleared && !overlayCleared) {
10872
+ console.log(pc18.dim("No hosted session or overlay present. Local workspace profile is untouched."));
10873
+ return;
10874
+ }
10875
+ if (sessionCleared) console.log(pc18.green("Cleared hosted session."));
10876
+ if (overlayCleared) console.log(pc18.green("Cleared hosted overlay."));
10877
+ console.log(pc18.dim("Local workspace profile is untouched."));
10878
+ }
10879
+ async function authWhoami(opts) {
10880
+ const session = readSession();
10881
+ const overlay = readHostedOverlay();
10882
+ const effective = computeEffectiveProfile({ configPath: resolveConfigPath(opts.config) });
10883
+ const payload = {
10884
+ authenticated: effective.authenticated,
10885
+ hostedBaseUrl: session?.hostedBaseUrl ?? overlay?.hostedBaseUrl ?? null,
10886
+ userId: overlay?.userId ?? session?.userId ?? null,
10887
+ email: overlay?.email ?? session?.email ?? null,
10888
+ displayName: overlay?.displayName ?? null,
10889
+ orgId: overlay?.orgId ?? session?.orgId ?? null,
10890
+ orgName: overlay?.orgName ?? session?.orgName ?? null,
10891
+ entitlements: overlay?.entitlements ?? [],
10892
+ linkedInstanceId: overlay?.linkedInstanceId ?? null,
10893
+ session: {
10894
+ present: Boolean(session),
10895
+ expired: effective.session.expired,
10896
+ expiresAt: effective.session.expiresAt
10897
+ }
10898
+ };
10899
+ if (opts.json) {
10900
+ console.log(JSON.stringify(payload, null, 2));
10901
+ return;
10902
+ }
10903
+ if (!payload.authenticated) {
10904
+ console.log(pc18.yellow("Not signed in."));
10905
+ if (effective.session.present && effective.session.expired) {
10906
+ console.log(pc18.dim("Hosted session exists but is expired. Run `growthub auth login` to refresh."));
10907
+ } else {
10908
+ console.log(pc18.dim("Run `growthub auth login` to connect this CLI to hosted Growthub."));
10909
+ }
10910
+ console.log(pc18.dim("Local workspace profile continues to work without authentication."));
10911
+ return;
10912
+ }
10913
+ console.log(pc18.bold(`Signed in${payload.email ? ` as ${payload.email}` : payload.userId ? ` as ${payload.userId}` : ""}.`));
10914
+ if (payload.hostedBaseUrl) console.log(pc18.dim(`Hosted: ${payload.hostedBaseUrl}`));
10915
+ if (payload.orgName || payload.orgId) {
10916
+ console.log(pc18.dim(`Org: ${payload.orgName ?? payload.orgId}`));
10917
+ }
10918
+ if (payload.linkedInstanceId) {
10919
+ console.log(pc18.dim(`Linked local instance: ${payload.linkedInstanceId}`));
10920
+ }
10921
+ if (payload.entitlements.length > 0) {
10922
+ console.log(pc18.dim(`Entitlements: ${payload.entitlements.join(", ")}`));
10923
+ }
10924
+ if (payload.session.expiresAt) {
10925
+ console.log(pc18.dim(`Session expires: ${payload.session.expiresAt}`));
10926
+ }
10927
+ }
10928
+
10929
+ // src/commands/profile.ts
10930
+ init_store();
10931
+ init_env();
10932
+ import pc19 from "picocolors";
10933
+
10934
+ // src/auth/hosted-client.ts
10935
+ var DEFAULT_PULL_PATH = "/api/cli/profile";
10936
+ var DEFAULT_PUSH_PATH = "/api/cli/profile";
10937
+ function toApiClient(session) {
10938
+ return new PaperclipApiClient({
10939
+ apiBase: session.hostedBaseUrl,
10940
+ apiKey: session.accessToken
10941
+ });
10942
+ }
10943
+ var HostedEndpointUnavailableError = class extends Error {
10944
+ status;
10945
+ constructor(status, message) {
10946
+ super(message);
10947
+ this.status = status;
10948
+ }
10949
+ };
10950
+ async function fetchHostedProfile(session) {
10951
+ const client = toApiClient(session);
10952
+ try {
10953
+ return await client.get(DEFAULT_PULL_PATH, { ignoreNotFound: true });
10954
+ } catch (err) {
10955
+ if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
10956
+ throw new HostedEndpointUnavailableError(err.status, err.message);
10957
+ }
10958
+ throw err;
10959
+ }
10960
+ }
10961
+ async function pushHostedProfile(session, payload) {
10962
+ const client = toApiClient(session);
10963
+ try {
10964
+ return await client.post(DEFAULT_PUSH_PATH, payload, { ignoreNotFound: true });
10965
+ } catch (err) {
10966
+ if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
10967
+ throw new HostedEndpointUnavailableError(err.status, err.message);
10968
+ }
10969
+ throw err;
10970
+ }
10971
+ }
10972
+
10973
+ // src/commands/profile.ts
10974
+ init_home();
10975
+ function printEffectiveProfileHuman(effective) {
10976
+ console.log(pc19.bold("Effective profile"));
10977
+ console.log(
10978
+ ` Authenticated: ${effective.authenticated ? pc19.green("yes") : pc19.yellow("no")}${effective.session.expired ? pc19.yellow(" (session expired)") : ""}`
10979
+ );
10980
+ console.log(pc19.bold("Local workspace (base layer)"));
10981
+ console.log(` Instance: ${effective.local.instanceId}`);
10982
+ console.log(` Config: ${pc19.dim(effective.local.configPath)}`);
10983
+ console.log(
10984
+ ` Surface: ${effective.local.surfaceProfile ?? pc19.dim("(unset)")} Host: ${effective.local.serverHost ?? pc19.dim("(unset)")} Port: ${effective.local.serverPort ?? pc19.dim("(unset)")}`
10985
+ );
10986
+ console.log(
10987
+ ` Local-linked hosted token: ${effective.local.hasConfiguredToken ? pc19.green("set") : pc19.dim("none")}`
10988
+ );
10989
+ if (effective.local.growthubBaseUrl) {
10990
+ console.log(` Growthub base: ${effective.local.growthubBaseUrl}`);
10991
+ }
10992
+ console.log(pc19.bold("Hosted overlay"));
10993
+ if (!effective.hosted.present) {
10994
+ console.log(pc19.dim(" No hosted overlay present. Run `growthub auth login` to attach one."));
10995
+ } else {
10996
+ if (effective.hosted.email || effective.hosted.userId) {
10997
+ console.log(` User: ${effective.hosted.email ?? effective.hosted.userId}`);
10998
+ }
10999
+ if (effective.hosted.orgName || effective.hosted.orgId) {
11000
+ console.log(` Org: ${effective.hosted.orgName ?? effective.hosted.orgId}`);
11001
+ }
11002
+ if (effective.hosted.hostedBaseUrl) console.log(` Hosted: ${effective.hosted.hostedBaseUrl}`);
11003
+ if (effective.hosted.linkedInstanceId) {
11004
+ console.log(` Linked instance: ${effective.hosted.linkedInstanceId}`);
11005
+ }
11006
+ if (effective.hosted.entitlements.length > 0) {
11007
+ console.log(` Entitlements: ${effective.hosted.entitlements.join(", ")}`);
11008
+ }
11009
+ if (effective.hosted.gatedKitSlugs.length > 0) {
11010
+ console.log(` Gated kits: ${effective.hosted.gatedKitSlugs.join(", ")}`);
11011
+ }
11012
+ if (effective.hosted.lastPulledAt) console.log(pc19.dim(` Last pulled: ${effective.hosted.lastPulledAt}`));
11013
+ if (effective.hosted.lastPushedAt) console.log(pc19.dim(` Last pushed: ${effective.hosted.lastPushedAt}`));
11014
+ }
11015
+ console.log(pc19.bold("Execution defaults"));
11016
+ console.log(
11017
+ ` preferredMode=${effective.executionDefaults.preferredMode} serverlessFallback=${effective.executionDefaults.allowServerlessFallback} browserBridge=${effective.executionDefaults.allowBrowserBridge}`
11018
+ );
11019
+ }
11020
+ async function runProfileStatus(opts) {
11021
+ const configPath = resolveConfigPath(opts.config);
11022
+ loadPaperclipEnvFile(configPath);
11023
+ const effective = computeEffectiveProfile({ configPath });
11024
+ writeEffectiveProfileSnapshot(effective);
11025
+ if (opts.json) {
11026
+ console.log(JSON.stringify(effective, null, 2));
11027
+ return;
11028
+ }
11029
+ printEffectiveProfileHuman(effective);
11030
+ }
11031
+ function normalizeExecutionPrefs(value, fallback) {
11032
+ if (!value) return fallback;
11033
+ const preferredMode = value.preferredMode === "local" || value.preferredMode === "serverless" || value.preferredMode === "browser" || value.preferredMode === "auto" ? value.preferredMode : fallback.preferredMode;
11034
+ return {
11035
+ preferredMode,
11036
+ allowServerlessFallback: typeof value.allowServerlessFallback === "boolean" ? value.allowServerlessFallback : fallback.allowServerlessFallback,
11037
+ allowBrowserBridge: typeof value.allowBrowserBridge === "boolean" ? value.allowBrowserBridge : fallback.allowBrowserBridge
11038
+ };
11039
+ }
11040
+ async function runProfilePull(opts) {
11041
+ const configPath = resolveConfigPath(opts.config);
11042
+ loadPaperclipEnvFile(configPath);
11043
+ const session = readSession();
11044
+ if (!session) {
11045
+ console.error(pc19.red("No hosted session. Run `growthub auth login` first."));
11046
+ process.exit(1);
11047
+ }
11048
+ if (isSessionExpired(session)) {
11049
+ console.error(pc19.red("Hosted session is expired. Run `growthub auth login` to refresh."));
11050
+ process.exit(1);
11051
+ }
11052
+ const existingOverlay = readHostedOverlay() ?? seedHostedOverlayFromSession({
11053
+ hostedBaseUrl: session.hostedBaseUrl,
11054
+ userId: session.userId,
11055
+ email: session.email,
11056
+ orgId: session.orgId,
11057
+ orgName: session.orgName,
11058
+ machineLabel: session.machineLabel,
11059
+ linkedInstanceId: resolvePaperclipInstanceId()
11060
+ });
11061
+ let remote = null;
11062
+ let usedFallback = false;
11063
+ try {
11064
+ remote = await fetchHostedProfile(session);
11065
+ } catch (err) {
11066
+ if (err instanceof HostedEndpointUnavailableError) {
11067
+ usedFallback = true;
11068
+ if (!opts.json) {
11069
+ console.log(
11070
+ pc19.yellow(
11071
+ "Hosted profile endpoint not yet available \u2014 keeping current overlay. (This is expected while gh-app is still shipping its CLI API.)"
11072
+ )
11073
+ );
11074
+ }
11075
+ } else {
11076
+ throw err;
11077
+ }
11078
+ }
11079
+ const merged = {
11080
+ ...existingOverlay,
11081
+ hostedBaseUrl: session.hostedBaseUrl,
11082
+ userId: remote?.userId ?? existingOverlay.userId,
11083
+ email: remote?.email ?? existingOverlay.email,
11084
+ displayName: remote?.displayName ?? existingOverlay.displayName ?? existingOverlay.email,
11085
+ orgId: remote?.orgId ?? existingOverlay.orgId,
11086
+ orgName: remote?.orgName ?? existingOverlay.orgName,
11087
+ entitlements: remote?.entitlements ?? existingOverlay.entitlements,
11088
+ gatedKitSlugs: remote?.gatedKitSlugs ?? existingOverlay.gatedKitSlugs,
11089
+ executionDefaults: normalizeExecutionPrefs(remote?.executionDefaults, existingOverlay.executionDefaults),
11090
+ lastPulledAt: remote ? (/* @__PURE__ */ new Date()).toISOString() : existingOverlay.lastPulledAt,
11091
+ extra: remote?.extra ?? existingOverlay.extra
11092
+ };
11093
+ writeHostedOverlay(merged);
11094
+ const effective = computeEffectiveProfile({ configPath });
11095
+ writeEffectiveProfileSnapshot(effective);
11096
+ if (opts.json) {
11097
+ console.log(JSON.stringify({ status: "ok", usedFallback, overlay: merged }, null, 2));
11098
+ return;
11099
+ }
11100
+ if (!usedFallback) {
11101
+ console.log(pc19.green("Hosted profile pulled and overlay updated."));
11102
+ }
11103
+ console.log(pc19.dim(`Entitlements: ${merged.entitlements.length === 0 ? "(none)" : merged.entitlements.join(", ")}`));
11104
+ console.log(pc19.dim(`Gated kits: ${merged.gatedKitSlugs.length === 0 ? "(none)" : merged.gatedKitSlugs.join(", ")}`));
11105
+ }
11106
+ async function runProfilePush(opts) {
11107
+ const configPath = resolveConfigPath(opts.config);
11108
+ loadPaperclipEnvFile(configPath);
11109
+ const session = readSession();
11110
+ if (!session) {
11111
+ console.error(pc19.red("No hosted session. Run `growthub auth login` first."));
11112
+ process.exit(1);
11113
+ }
11114
+ if (isSessionExpired(session)) {
11115
+ console.error(pc19.red("Hosted session is expired. Run `growthub auth login` to refresh."));
11116
+ process.exit(1);
11117
+ }
11118
+ const effective = computeEffectiveProfile({ configPath });
11119
+ let acknowledged = false;
11120
+ let usedFallback = false;
11121
+ try {
11122
+ await pushHostedProfile(session, {
11123
+ linkedInstanceId: effective.local.instanceId,
11124
+ surfaceProfile: effective.local.surfaceProfile,
11125
+ machineLabel: effective.local.machineLabel,
11126
+ workspaceLabel: effective.local.workspaceLabel
11127
+ });
11128
+ acknowledged = true;
11129
+ } catch (err) {
11130
+ if (err instanceof HostedEndpointUnavailableError) {
11131
+ usedFallback = true;
11132
+ } else {
11133
+ throw err;
11134
+ }
11135
+ }
11136
+ const existingOverlay = readHostedOverlay() ?? seedHostedOverlayFromSession({
11137
+ hostedBaseUrl: session.hostedBaseUrl,
11138
+ userId: session.userId,
11139
+ email: session.email,
11140
+ orgId: session.orgId,
11141
+ orgName: session.orgName,
11142
+ machineLabel: session.machineLabel,
11143
+ linkedInstanceId: effective.local.instanceId
11144
+ });
11145
+ const updatedOverlay = {
11146
+ ...existingOverlay,
11147
+ linkedInstanceId: existingOverlay.linkedInstanceId ?? effective.local.instanceId,
11148
+ lastPushedAt: acknowledged ? (/* @__PURE__ */ new Date()).toISOString() : existingOverlay.lastPushedAt
11149
+ };
11150
+ writeHostedOverlay(updatedOverlay);
11151
+ writeEffectiveProfileSnapshot(computeEffectiveProfile({ configPath }));
11152
+ if (opts.json) {
11153
+ console.log(JSON.stringify({ status: "ok", acknowledged, usedFallback, overlay: updatedOverlay }, null, 2));
11154
+ return;
11155
+ }
11156
+ if (usedFallback) {
11157
+ console.log(
11158
+ pc19.yellow(
11159
+ "Hosted push endpoint not yet available \u2014 linkage recorded locally only. (This is expected while gh-app is still shipping its CLI API.)"
11160
+ )
11161
+ );
11162
+ return;
11163
+ }
11164
+ console.log(pc19.green("Hosted profile push acknowledged."));
11165
+ console.log(pc19.dim(`Linked instance: ${effective.local.instanceId}`));
11166
+ }
11167
+ function registerProfileCommands(program2) {
11168
+ const profile = program2.command("profile").description("Inspect and sync the effective Growthub profile (local workspace + hosted overlay)");
11169
+ profile.command("status").description("Show the merged local + hosted profile the CLI will use at runtime").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", "Growthub data directory root (isolates local instance state)").option("--json", "Output raw JSON").action(async (opts) => {
11170
+ await runProfileStatus(opts);
11171
+ });
11172
+ profile.command("pull").description("Pull hosted Growthub profile metadata into the local overlay").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", "Growthub data directory root (isolates local instance state)").option("--json", "Output raw JSON").action(async (opts) => {
11173
+ await runProfilePull(opts);
11174
+ });
11175
+ profile.command("push").description("Push safe local profile metadata (workspace linkage, labels) upward").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", "Growthub data directory root (isolates local instance state)").option("--json", "Output raw JSON").action(async (opts) => {
11176
+ await runProfilePush(opts);
11177
+ });
11178
+ }
11179
+
10168
11180
  // src/commands/db-backup.ts
10169
11181
  init_src2();
10170
11182
  init_home();
10171
11183
  init_store();
10172
11184
  init_banner();
10173
- import path10 from "node:path";
10174
- import * as p14 from "@clack/prompts";
10175
- import pc18 from "picocolors";
11185
+ import path14 from "node:path";
11186
+ import * as p15 from "@clack/prompts";
11187
+ import pc20 from "picocolors";
10176
11188
  function resolveConnectionString(configPath) {
10177
11189
  const envUrl = process.env.DATABASE_URL?.trim();
10178
11190
  if (envUrl) return { value: envUrl, source: "DATABASE_URL" };
@@ -10194,11 +11206,11 @@ function normalizeRetentionDays(value, fallback) {
10194
11206
  return candidate;
10195
11207
  }
10196
11208
  function resolveBackupDir(raw) {
10197
- return path10.resolve(expandHomePrefix(raw.trim()));
11209
+ return path14.resolve(expandHomePrefix(raw.trim()));
10198
11210
  }
10199
11211
  async function dbBackupCommand(opts) {
10200
11212
  printPaperclipCliBanner();
10201
- p14.intro(pc18.bgCyan(pc18.black(" paperclip db:backup ")));
11213
+ p15.intro(pc20.bgCyan(pc20.black(" paperclip db:backup ")));
10202
11214
  const configPath = resolveConfigPath(opts.config);
10203
11215
  const config = readConfig(opts.config);
10204
11216
  const connection = resolveConnectionString(opts.config);
@@ -10210,12 +11222,12 @@ async function dbBackupCommand(opts) {
10210
11222
  config?.database.backup.retentionDays ?? 30
10211
11223
  );
10212
11224
  const filenamePrefix = opts.filenamePrefix?.trim() || "paperclip";
10213
- p14.log.message(pc18.dim(`Config: ${configPath}`));
10214
- p14.log.message(pc18.dim(`Connection source: ${connection.source}`));
10215
- p14.log.message(pc18.dim(`Backup dir: ${backupDir}`));
10216
- p14.log.message(pc18.dim(`Retention: ${retentionDays} day(s)`));
10217
- const spinner4 = p14.spinner();
10218
- spinner4.start("Creating database backup...");
11225
+ p15.log.message(pc20.dim(`Config: ${configPath}`));
11226
+ p15.log.message(pc20.dim(`Connection source: ${connection.source}`));
11227
+ p15.log.message(pc20.dim(`Backup dir: ${backupDir}`));
11228
+ p15.log.message(pc20.dim(`Retention: ${retentionDays} day(s)`));
11229
+ const spinner5 = p15.spinner();
11230
+ spinner5.start("Creating database backup...");
10219
11231
  try {
10220
11232
  const result = await runDatabaseBackup({
10221
11233
  connectionString: connection.value,
@@ -10223,7 +11235,7 @@ async function dbBackupCommand(opts) {
10223
11235
  retentionDays,
10224
11236
  filenamePrefix
10225
11237
  });
10226
- spinner4.stop(`Backup saved: ${formatDatabaseBackupResult(result)}`);
11238
+ spinner5.stop(`Backup saved: ${formatDatabaseBackupResult(result)}`);
10227
11239
  if (opts.json) {
10228
11240
  console.log(
10229
11241
  JSON.stringify(
@@ -10240,15 +11252,15 @@ async function dbBackupCommand(opts) {
10240
11252
  )
10241
11253
  );
10242
11254
  }
10243
- p14.outro(pc18.green("Backup completed."));
11255
+ p15.outro(pc20.green("Backup completed."));
10244
11256
  } catch (err) {
10245
- spinner4.stop(pc18.red("Backup failed."));
11257
+ spinner5.stop(pc20.red("Backup failed."));
10246
11258
  throw err;
10247
11259
  }
10248
11260
  }
10249
11261
 
10250
11262
  // src/commands/client/context.ts
10251
- import pc19 from "picocolors";
11263
+ import pc21 from "picocolors";
10252
11264
  function registerContextCommands(program2) {
10253
11265
  const context = program2.command("context").description("Manage CLI client context profiles");
10254
11266
  context.command("show").description("Show current context and active profile").option("-d, --data-dir <path>", "Growthub data directory root (isolates local instance state)").option("--context <path>", "Path to CLI context file").option("--profile <name>", "Profile to inspect").option("--json", "Output raw JSON").action((opts) => {
@@ -10277,7 +11289,7 @@ function registerContextCommands(program2) {
10277
11289
  });
10278
11290
  context.command("use").description("Set active context profile").argument("<profile>", "Profile name").option("-d, --data-dir <path>", "Growthub data directory root (isolates local instance state)").option("--context <path>", "Path to CLI context file").action((profile, opts) => {
10279
11291
  setCurrentProfile(profile, opts.context);
10280
- console.log(pc19.green(`Active profile set to '${profile}'.`));
11292
+ console.log(pc21.green(`Active profile set to '${profile}'.`));
10281
11293
  });
10282
11294
  context.command("set").description("Set values on a profile").option("-d, --data-dir <path>", "Growthub data directory root (isolates local instance state)").option("--context <path>", "Path to CLI context file").option("--profile <name>", "Profile name (default: current profile)").option("--api-base <url>", "Default API base URL").option("--company-id <id>", "Default company ID").option("--api-key-env-var-name <name>", "Env var containing API key (recommended)").option("--use", "Set this profile as active").option("--json", "Output raw JSON").action((opts) => {
10283
11295
  const existing = readContext(opts.context);
@@ -10303,9 +11315,9 @@ function registerContextCommands(program2) {
10303
11315
  profile: resolved.profile
10304
11316
  };
10305
11317
  if (!opts.json) {
10306
- console.log(pc19.green(`Updated profile '${targetProfile}'.`));
11318
+ console.log(pc21.green(`Updated profile '${targetProfile}'.`));
10307
11319
  if (opts.use) {
10308
- console.log(pc19.green(`Set '${targetProfile}' as active profile.`));
11320
+ console.log(pc21.green(`Set '${targetProfile}' as active profile.`));
10309
11321
  }
10310
11322
  }
10311
11323
  printOutput(payload, { json: opts.json });
@@ -10314,7 +11326,7 @@ function registerContextCommands(program2) {
10314
11326
 
10315
11327
  // src/commands/client/company.ts
10316
11328
  import { mkdir, readFile as readFile3, stat, writeFile as writeFile2 } from "node:fs/promises";
10317
- import path11 from "node:path";
11329
+ import path15 from "node:path";
10318
11330
  function isUuidLike2(value) {
10319
11331
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
10320
11332
  }
@@ -10348,32 +11360,32 @@ function isGithubUrl(input) {
10348
11360
  return /^https?:\/\/github\.com\//i.test(input.trim());
10349
11361
  }
10350
11362
  async function resolveInlineSourceFromPath(inputPath) {
10351
- const resolved = path11.resolve(inputPath);
11363
+ const resolved = path15.resolve(inputPath);
10352
11364
  const resolvedStat = await stat(resolved);
10353
- const manifestPath = resolvedStat.isDirectory() ? path11.join(resolved, "paperclip.manifest.json") : resolved;
10354
- const manifestBaseDir = path11.dirname(manifestPath);
11365
+ const manifestPath = resolvedStat.isDirectory() ? path15.join(resolved, "paperclip.manifest.json") : resolved;
11366
+ const manifestBaseDir = path15.dirname(manifestPath);
10355
11367
  const manifestRaw = await readFile3(manifestPath, "utf8");
10356
11368
  const manifest = JSON.parse(manifestRaw);
10357
11369
  const files = {};
10358
11370
  if (manifest.company?.path) {
10359
11371
  const companyPath = manifest.company.path.replace(/\\/g, "/");
10360
- files[companyPath] = await readFile3(path11.join(manifestBaseDir, companyPath), "utf8");
11372
+ files[companyPath] = await readFile3(path15.join(manifestBaseDir, companyPath), "utf8");
10361
11373
  }
10362
11374
  for (const agent of manifest.agents ?? []) {
10363
11375
  const agentPath = agent.path.replace(/\\/g, "/");
10364
- files[agentPath] = await readFile3(path11.join(manifestBaseDir, agentPath), "utf8");
11376
+ files[agentPath] = await readFile3(path15.join(manifestBaseDir, agentPath), "utf8");
10365
11377
  }
10366
11378
  return { manifest, files };
10367
11379
  }
10368
11380
  async function writeExportToFolder(outDir, exported) {
10369
- const root = path11.resolve(outDir);
11381
+ const root = path15.resolve(outDir);
10370
11382
  await mkdir(root, { recursive: true });
10371
- const manifestPath = path11.join(root, "paperclip.manifest.json");
11383
+ const manifestPath = path15.join(root, "paperclip.manifest.json");
10372
11384
  await writeFile2(manifestPath, JSON.stringify(exported.manifest, null, 2), "utf8");
10373
11385
  for (const [relativePath, content] of Object.entries(exported.files)) {
10374
11386
  const normalized = relativePath.replace(/\\/g, "/");
10375
- const filePath = path11.join(root, normalized);
10376
- await mkdir(path11.dirname(filePath), { recursive: true });
11387
+ const filePath = path15.join(root, normalized);
11388
+ await mkdir(path15.dirname(filePath), { recursive: true });
10377
11389
  await writeFile2(filePath, content, "utf8");
10378
11390
  }
10379
11391
  }
@@ -10496,7 +11508,7 @@ function registerCompanyCommands(program2) {
10496
11508
  printOutput(
10497
11509
  {
10498
11510
  ok: true,
10499
- out: path11.resolve(opts.out),
11511
+ out: path15.resolve(opts.out),
10500
11512
  filesWritten: Object.keys(exported.files).length + 1,
10501
11513
  warningCount: exported.warnings.length
10502
11514
  },
@@ -10658,8 +11670,8 @@ function registerIssueCommands(program2) {
10658
11670
  if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
10659
11671
  if (opts.projectId) params.set("projectId", opts.projectId);
10660
11672
  const query = params.toString();
10661
- const path24 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
10662
- const rows = await ctx.api.get(path24) ?? [];
11673
+ const path28 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
11674
+ const rows = await ctx.api.get(path28) ?? [];
10663
11675
  const filtered = filterIssueRows(rows, opts.match);
10664
11676
  if (ctx.json) {
10665
11677
  printOutput(filtered, { json: true });
@@ -10821,8 +11833,8 @@ function filterIssueRows(rows, match) {
10821
11833
  }
10822
11834
 
10823
11835
  // ../packages/adapter-utils/src/server-utils.ts
10824
- import { constants as fsConstants, promises as fs11 } from "node:fs";
10825
- import path12 from "node:path";
11836
+ import { constants as fsConstants, promises as fs14 } from "node:fs";
11837
+ import path16 from "node:path";
10826
11838
  var MAX_CAPTURE_BYTES = 4 * 1024 * 1024;
10827
11839
  var MAX_EXCERPT_BYTES = 32 * 1024;
10828
11840
  var PAPERCLIP_SKILL_ROOT_RELATIVE_CANDIDATES = [
@@ -10837,14 +11849,14 @@ function isMaintainerOnlySkillTarget(candidate) {
10837
11849
  }
10838
11850
  async function resolvePaperclipSkillsDir(moduleDir, additionalCandidates = []) {
10839
11851
  const candidates = [
10840
- ...PAPERCLIP_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) => path12.resolve(moduleDir, relativePath)),
10841
- ...additionalCandidates.map((candidate) => path12.resolve(candidate))
11852
+ ...PAPERCLIP_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) => path16.resolve(moduleDir, relativePath)),
11853
+ ...additionalCandidates.map((candidate) => path16.resolve(candidate))
10842
11854
  ];
10843
11855
  const seenRoots = /* @__PURE__ */ new Set();
10844
11856
  for (const root of candidates) {
10845
11857
  if (seenRoots.has(root)) continue;
10846
11858
  seenRoots.add(root);
10847
- const isDirectory = await fs11.stat(root).then((stats) => stats.isDirectory()).catch(() => false);
11859
+ const isDirectory = await fs14.stat(root).then((stats) => stats.isDirectory()).catch(() => false);
10848
11860
  if (isDirectory) return root;
10849
11861
  }
10850
11862
  return null;
@@ -10852,20 +11864,20 @@ async function resolvePaperclipSkillsDir(moduleDir, additionalCandidates = []) {
10852
11864
  async function removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames) {
10853
11865
  const allowed = new Set(Array.from(allowedSkillNames));
10854
11866
  try {
10855
- const entries = await fs11.readdir(skillsHome, { withFileTypes: true });
11867
+ const entries = await fs14.readdir(skillsHome, { withFileTypes: true });
10856
11868
  const removed = [];
10857
11869
  for (const entry of entries) {
10858
11870
  if (allowed.has(entry.name)) continue;
10859
- const target = path12.join(skillsHome, entry.name);
10860
- const existing = await fs11.lstat(target).catch(() => null);
11871
+ const target = path16.join(skillsHome, entry.name);
11872
+ const existing = await fs14.lstat(target).catch(() => null);
10861
11873
  if (!existing?.isSymbolicLink()) continue;
10862
- const linkedPath = await fs11.readlink(target).catch(() => null);
11874
+ const linkedPath = await fs14.readlink(target).catch(() => null);
10863
11875
  if (!linkedPath) continue;
10864
- const resolvedLinkedPath = path12.isAbsolute(linkedPath) ? linkedPath : path12.resolve(path12.dirname(target), linkedPath);
11876
+ const resolvedLinkedPath = path16.isAbsolute(linkedPath) ? linkedPath : path16.resolve(path16.dirname(target), linkedPath);
10865
11877
  if (!isMaintainerOnlySkillTarget(linkedPath) && !isMaintainerOnlySkillTarget(resolvedLinkedPath)) {
10866
11878
  continue;
10867
11879
  }
10868
- await fs11.unlink(target);
11880
+ await fs14.unlink(target);
10869
11881
  removed.push(entry.name);
10870
11882
  }
10871
11883
  return removed;
@@ -10875,20 +11887,20 @@ async function removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames)
10875
11887
  }
10876
11888
 
10877
11889
  // src/commands/client/agent.ts
10878
- import fs12 from "node:fs/promises";
10879
- import os2 from "node:os";
10880
- import path13 from "node:path";
11890
+ import fs15 from "node:fs/promises";
11891
+ import os4 from "node:os";
11892
+ import path17 from "node:path";
10881
11893
  import { fileURLToPath as fileURLToPath3 } from "node:url";
10882
- var __moduleDir = path13.dirname(fileURLToPath3(import.meta.url));
11894
+ var __moduleDir = path17.dirname(fileURLToPath3(import.meta.url));
10883
11895
  function codexSkillsHome() {
10884
11896
  const fromEnv = process.env.CODEX_HOME?.trim();
10885
- const base = fromEnv && fromEnv.length > 0 ? fromEnv : path13.join(os2.homedir(), ".codex");
10886
- return path13.join(base, "skills");
11897
+ const base = fromEnv && fromEnv.length > 0 ? fromEnv : path17.join(os4.homedir(), ".codex");
11898
+ return path17.join(base, "skills");
10887
11899
  }
10888
11900
  function claudeSkillsHome() {
10889
11901
  const fromEnv = process.env.CLAUDE_HOME?.trim();
10890
- const base = fromEnv && fromEnv.length > 0 ? fromEnv : path13.join(os2.homedir(), ".claude");
10891
- return path13.join(base, "skills");
11902
+ const base = fromEnv && fromEnv.length > 0 ? fromEnv : path17.join(os4.homedir(), ".claude");
11903
+ return path17.join(base, "skills");
10892
11904
  }
10893
11905
  async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
10894
11906
  const summary = {
@@ -10899,26 +11911,26 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
10899
11911
  skipped: [],
10900
11912
  failed: []
10901
11913
  };
10902
- await fs12.mkdir(targetSkillsDir, { recursive: true });
10903
- const entries = await fs12.readdir(sourceSkillsDir, { withFileTypes: true });
11914
+ await fs15.mkdir(targetSkillsDir, { recursive: true });
11915
+ const entries = await fs15.readdir(sourceSkillsDir, { withFileTypes: true });
10904
11916
  summary.removed = await removeMaintainerOnlySkillSymlinks(
10905
11917
  targetSkillsDir,
10906
11918
  entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name)
10907
11919
  );
10908
11920
  for (const entry of entries) {
10909
11921
  if (!entry.isDirectory()) continue;
10910
- const source = path13.join(sourceSkillsDir, entry.name);
10911
- const target = path13.join(targetSkillsDir, entry.name);
10912
- const existing = await fs12.lstat(target).catch(() => null);
11922
+ const source = path17.join(sourceSkillsDir, entry.name);
11923
+ const target = path17.join(targetSkillsDir, entry.name);
11924
+ const existing = await fs15.lstat(target).catch(() => null);
10913
11925
  if (existing) {
10914
11926
  if (existing.isSymbolicLink()) {
10915
11927
  let linkedPath = null;
10916
11928
  try {
10917
- linkedPath = await fs12.readlink(target);
11929
+ linkedPath = await fs15.readlink(target);
10918
11930
  } catch (err) {
10919
- await fs12.unlink(target);
11931
+ await fs15.unlink(target);
10920
11932
  try {
10921
- await fs12.symlink(source, target);
11933
+ await fs15.symlink(source, target);
10922
11934
  summary.linked.push(entry.name);
10923
11935
  continue;
10924
11936
  } catch (linkErr) {
@@ -10929,10 +11941,10 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
10929
11941
  continue;
10930
11942
  }
10931
11943
  }
10932
- const resolvedLinkedPath = path13.isAbsolute(linkedPath) ? linkedPath : path13.resolve(path13.dirname(target), linkedPath);
10933
- const linkedTargetExists = await fs12.stat(resolvedLinkedPath).then(() => true).catch(() => false);
11944
+ const resolvedLinkedPath = path17.isAbsolute(linkedPath) ? linkedPath : path17.resolve(path17.dirname(target), linkedPath);
11945
+ const linkedTargetExists = await fs15.stat(resolvedLinkedPath).then(() => true).catch(() => false);
10934
11946
  if (!linkedTargetExists) {
10935
- await fs12.unlink(target);
11947
+ await fs15.unlink(target);
10936
11948
  } else {
10937
11949
  summary.skipped.push(entry.name);
10938
11950
  continue;
@@ -10943,7 +11955,7 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool) {
10943
11955
  }
10944
11956
  }
10945
11957
  try {
10946
- await fs12.symlink(source, target);
11958
+ await fs15.symlink(source, target);
10947
11959
  summary.linked.push(entry.name);
10948
11960
  } catch (err) {
10949
11961
  summary.failed.push({
@@ -11032,7 +12044,7 @@ function registerAgentCommands(program2) {
11032
12044
  }
11033
12045
  const installSummaries = [];
11034
12046
  if (opts.installSkills !== false) {
11035
- const skillsDir = await resolvePaperclipSkillsDir(__moduleDir, [path13.resolve(process.cwd(), "skills")]);
12047
+ const skillsDir = await resolvePaperclipSkillsDir(__moduleDir, [path17.resolve(process.cwd(), "skills")]);
11036
12048
  if (!skillsDir) {
11037
12049
  throw new Error(
11038
12050
  "Could not locate local Paperclip skills directory. Expected ./skills in the repo checkout."
@@ -11265,8 +12277,8 @@ function registerActivityCommands(program2) {
11265
12277
  if (opts.entityType) params.set("entityType", opts.entityType);
11266
12278
  if (opts.entityId) params.set("entityId", opts.entityId);
11267
12279
  const query = params.toString();
11268
- const path24 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
11269
- const rows = await ctx.api.get(path24) ?? [];
12280
+ const path28 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
12281
+ const rows = await ctx.api.get(path28) ?? [];
11270
12282
  if (ctx.json) {
11271
12283
  printOutput(rows, { json: true });
11272
12284
  return;
@@ -11315,11 +12327,11 @@ function registerDashboardCommands(program2) {
11315
12327
 
11316
12328
  // src/config/data-dir.ts
11317
12329
  init_home();
11318
- import path14 from "node:path";
12330
+ import path18 from "node:path";
11319
12331
  function applyDataDirOverride(options, support = {}) {
11320
12332
  const rawDataDir = options.dataDir?.trim();
11321
12333
  if (!rawDataDir) return null;
11322
- const resolvedDataDir = path14.resolve(expandHomePrefix(rawDataDir));
12334
+ const resolvedDataDir = path18.resolve(expandHomePrefix(rawDataDir));
11323
12335
  process.env.PAPERCLIP_HOME = resolvedDataDir;
11324
12336
  if (support.hasConfigOption) {
11325
12337
  const hasConfigOverride = Boolean(options.config?.trim()) || Boolean(process.env.PAPERCLIP_CONFIG?.trim());
@@ -11346,35 +12358,35 @@ init_store();
11346
12358
  // src/commands/gtm.ts
11347
12359
  init_src();
11348
12360
  init_home();
11349
- import fs13 from "node:fs";
11350
- import path15 from "node:path";
12361
+ import fs16 from "node:fs";
12362
+ import path19 from "node:path";
11351
12363
  import { spawn } from "node:child_process";
11352
- import pc20 from "picocolors";
12364
+ import pc22 from "picocolors";
11353
12365
  function resolveGtmStatePath() {
11354
- return path15.resolve(resolvePaperclipHomeDir(), "gtm", "state.json");
12366
+ return path19.resolve(resolvePaperclipHomeDir(), "gtm", "state.json");
11355
12367
  }
11356
12368
  function readState() {
11357
12369
  const filePath = resolveGtmStatePath();
11358
- if (!fs13.existsSync(filePath)) return createDefaultGtmState();
11359
- return coerceGtmState(JSON.parse(fs13.readFileSync(filePath, "utf-8")));
12370
+ if (!fs16.existsSync(filePath)) return createDefaultGtmState();
12371
+ return coerceGtmState(JSON.parse(fs16.readFileSync(filePath, "utf-8")));
11360
12372
  }
11361
12373
  function writeState(state) {
11362
12374
  const filePath = resolveGtmStatePath();
11363
- fs13.mkdirSync(path15.dirname(filePath), { recursive: true });
11364
- fs13.writeFileSync(filePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
12375
+ fs16.mkdirSync(path19.dirname(filePath), { recursive: true });
12376
+ fs16.writeFileSync(filePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
11365
12377
  }
11366
12378
  function launchWorkflow(state) {
11367
12379
  const runnerPath = state.workflow.runnerPath?.trim();
11368
12380
  if (!runnerPath) {
11369
12381
  throw new Error("No local SDR runner configured.");
11370
12382
  }
11371
- if (!fs13.existsSync(runnerPath)) {
12383
+ if (!fs16.existsSync(runnerPath)) {
11372
12384
  throw new Error(`Runner not found at ${runnerPath}`);
11373
12385
  }
11374
12386
  const args = runnerPath.endsWith(".mjs") || runnerPath.endsWith(".js") ? [runnerPath] : [];
11375
12387
  const command = args.length > 0 ? process.execPath : runnerPath;
11376
12388
  const child = spawn(command, args, {
11377
- cwd: path15.dirname(runnerPath),
12389
+ cwd: path19.dirname(runnerPath),
11378
12390
  detached: true,
11379
12391
  stdio: "ignore"
11380
12392
  });
@@ -11399,7 +12411,7 @@ function printJsonOrMessage(payload, json, message) {
11399
12411
  console.log(JSON.stringify(payload, null, 2));
11400
12412
  return;
11401
12413
  }
11402
- if (message) console.log(pc20.green(message));
12414
+ if (message) console.log(pc22.green(message));
11403
12415
  console.log(payload);
11404
12416
  }
11405
12417
  function registerGtmCommands(program2) {
@@ -11412,7 +12424,7 @@ function registerGtmCommands(program2) {
11412
12424
  if (opts.internalSocialsPath) state.workflow.referenceInterfaces.internalSocialsPath = opts.internalSocialsPath.trim();
11413
12425
  if (opts.localSdrPath) {
11414
12426
  state.workflow.referenceInterfaces.localSdrPath = opts.localSdrPath.trim();
11415
- state.workflow.runnerPath = path15.resolve(opts.localSdrPath.trim(), "sdr-bot.mjs");
12427
+ state.workflow.runnerPath = path19.resolve(opts.localSdrPath.trim(), "sdr-bot.mjs");
11416
12428
  }
11417
12429
  writeState(state);
11418
12430
  const view = toGtmViewModel(state);
@@ -11464,18 +12476,18 @@ import {
11464
12476
  symlinkSync,
11465
12477
  writeFileSync
11466
12478
  } from "node:fs";
11467
- import os3 from "node:os";
11468
- import path17 from "node:path";
12479
+ import os5 from "node:os";
12480
+ import path21 from "node:path";
11469
12481
  import { execFileSync } from "node:child_process";
11470
- import { createServer } from "node:net";
11471
- import * as p15 from "@clack/prompts";
11472
- import pc21 from "picocolors";
12482
+ import { createServer as createServer2 } from "node:net";
12483
+ import * as p16 from "@clack/prompts";
12484
+ import pc23 from "picocolors";
11473
12485
  import { eq as eq2 } from "drizzle-orm";
11474
12486
 
11475
12487
  // src/commands/worktree-lib.ts
11476
12488
  init_home();
11477
12489
  import { randomInt } from "node:crypto";
11478
- import path16 from "node:path";
12490
+ import path20 from "node:path";
11479
12491
  var DEFAULT_WORKTREE_HOME = "~/.paperclip-worktrees";
11480
12492
  var WORKTREE_SEED_MODES = ["minimal", "full"];
11481
12493
  var MINIMAL_WORKTREE_EXCLUDED_TABLES = [
@@ -11523,7 +12535,7 @@ function sanitizeWorktreeInstanceId(rawValue) {
11523
12535
  return normalized || "worktree";
11524
12536
  }
11525
12537
  function resolveSuggestedWorktreeName(cwd, explicitName) {
11526
- return nonEmpty(explicitName) ?? path16.basename(path16.resolve(cwd));
12538
+ return nonEmpty(explicitName) ?? path20.basename(path20.resolve(cwd));
11527
12539
  }
11528
12540
  function hslComponentToHex(n) {
11529
12541
  return Math.round(Math.max(0, Math.min(255, n))).toString(16).padStart(2, "0");
@@ -11563,24 +12575,24 @@ function generateWorktreeColor() {
11563
12575
  return hslToHex(randomInt(0, 360), 68, 56);
11564
12576
  }
11565
12577
  function resolveWorktreeLocalPaths(opts) {
11566
- const cwd = path16.resolve(opts.cwd);
11567
- const homeDir = path16.resolve(expandHomePrefix(opts.homeDir ?? DEFAULT_WORKTREE_HOME));
11568
- const instanceRoot = path16.resolve(homeDir, "instances", opts.instanceId);
11569
- const repoConfigDir = path16.resolve(cwd, ".paperclip");
12578
+ const cwd = path20.resolve(opts.cwd);
12579
+ const homeDir = path20.resolve(expandHomePrefix(opts.homeDir ?? DEFAULT_WORKTREE_HOME));
12580
+ const instanceRoot = path20.resolve(homeDir, "instances", opts.instanceId);
12581
+ const repoConfigDir = path20.resolve(cwd, ".paperclip");
11570
12582
  return {
11571
12583
  cwd,
11572
12584
  repoConfigDir,
11573
- configPath: path16.resolve(repoConfigDir, "config.json"),
11574
- envPath: path16.resolve(repoConfigDir, ".env"),
12585
+ configPath: path20.resolve(repoConfigDir, "config.json"),
12586
+ envPath: path20.resolve(repoConfigDir, ".env"),
11575
12587
  homeDir,
11576
12588
  instanceId: opts.instanceId,
11577
12589
  instanceRoot,
11578
- contextPath: path16.resolve(homeDir, "context.json"),
11579
- embeddedPostgresDataDir: path16.resolve(instanceRoot, "db"),
11580
- backupDir: path16.resolve(instanceRoot, "data", "backups"),
11581
- logDir: path16.resolve(instanceRoot, "logs"),
11582
- secretsKeyFilePath: path16.resolve(instanceRoot, "secrets", "master.key"),
11583
- storageDir: path16.resolve(instanceRoot, "data", "storage")
12590
+ contextPath: path20.resolve(homeDir, "context.json"),
12591
+ embeddedPostgresDataDir: path20.resolve(instanceRoot, "db"),
12592
+ backupDir: path20.resolve(instanceRoot, "data", "backups"),
12593
+ logDir: path20.resolve(instanceRoot, "logs"),
12594
+ secretsKeyFilePath: path20.resolve(instanceRoot, "secrets", "master.key"),
12595
+ storageDir: path20.resolve(instanceRoot, "data", "storage")
11584
12596
  };
11585
12597
  }
11586
12598
  function rewriteLocalUrlPort(rawUrl, port) {
@@ -11686,7 +12698,7 @@ function isCurrentSourceConfigPath(sourceConfigPath) {
11686
12698
  if (!currentConfigPath || currentConfigPath.trim().length === 0) {
11687
12699
  return false;
11688
12700
  }
11689
- return path17.resolve(currentConfigPath) === path17.resolve(sourceConfigPath);
12701
+ return path21.resolve(currentConfigPath) === path21.resolve(sourceConfigPath);
11690
12702
  }
11691
12703
  var WORKTREE_NAME_PREFIX = "paperclip-";
11692
12704
  function resolveWorktreeMakeName(name) {
@@ -11708,7 +12720,7 @@ function resolveWorktreeStartPoint(explicit) {
11708
12720
  return explicit ?? nonEmpty2(process.env.PAPERCLIP_WORKTREE_START_POINT) ?? void 0;
11709
12721
  }
11710
12722
  function resolveWorktreeMakeTargetPath(name) {
11711
- return path17.resolve(os3.homedir(), resolveWorktreeMakeName(name));
12723
+ return path21.resolve(os5.homedir(), resolveWorktreeMakeName(name));
11712
12724
  }
11713
12725
  function extractExecSyncErrorMessage(error) {
11714
12726
  if (!error || typeof error !== "object") {
@@ -11764,7 +12776,7 @@ function readRunningPostmasterPid(postmasterPidFile) {
11764
12776
  }
11765
12777
  async function isPortAvailable(port) {
11766
12778
  return await new Promise((resolve2) => {
11767
- const server = createServer();
12779
+ const server = createServer2();
11768
12780
  server.unref();
11769
12781
  server.once("error", () => resolve2(false));
11770
12782
  server.listen(port, "127.0.0.1", () => {
@@ -11814,10 +12826,10 @@ function detectGitWorkspaceInfo(cwd) {
11814
12826
  stdio: ["ignore", "pipe", "ignore"]
11815
12827
  }).trim();
11816
12828
  return {
11817
- root: path17.resolve(root),
11818
- commonDir: path17.resolve(root, commonDirRaw),
11819
- gitDir: path17.resolve(root, gitDirRaw),
11820
- hooksPath: path17.resolve(root, hooksPathRaw)
12829
+ root: path21.resolve(root),
12830
+ commonDir: path21.resolve(root, commonDirRaw),
12831
+ gitDir: path21.resolve(root, gitDirRaw),
12832
+ hooksPath: path21.resolve(root, hooksPathRaw)
11821
12833
  };
11822
12834
  } catch {
11823
12835
  return null;
@@ -11830,8 +12842,8 @@ function copyDirectoryContents(sourceDir, targetDir) {
11830
12842
  mkdirSync2(targetDir, { recursive: true });
11831
12843
  let copied = false;
11832
12844
  for (const entry of entries) {
11833
- const sourcePath = path17.resolve(sourceDir, entry.name);
11834
- const targetPath = path17.resolve(targetDir, entry.name);
12845
+ const sourcePath = path21.resolve(sourceDir, entry.name);
12846
+ const targetPath = path21.resolve(targetDir, entry.name);
11835
12847
  if (entry.isDirectory()) {
11836
12848
  mkdirSync2(targetPath, { recursive: true });
11837
12849
  copyDirectoryContents(sourcePath, targetPath);
@@ -11857,7 +12869,7 @@ function copyGitHooksToWorktreeGitDir(cwd) {
11857
12869
  const workspace = detectGitWorkspaceInfo(cwd);
11858
12870
  if (!workspace) return null;
11859
12871
  const sourceHooksPath = workspace.hooksPath;
11860
- const targetHooksPath = path17.resolve(workspace.gitDir, "hooks");
12872
+ const targetHooksPath = path21.resolve(workspace.gitDir, "hooks");
11861
12873
  if (sourceHooksPath === targetHooksPath) {
11862
12874
  return {
11863
12875
  sourceHooksPath,
@@ -11872,17 +12884,17 @@ function copyGitHooksToWorktreeGitDir(cwd) {
11872
12884
  };
11873
12885
  }
11874
12886
  function rebindWorkspaceCwd(input) {
11875
- const sourceRepoRoot = path17.resolve(input.sourceRepoRoot);
11876
- const targetRepoRoot = path17.resolve(input.targetRepoRoot);
11877
- const workspaceCwd = path17.resolve(input.workspaceCwd);
11878
- const relative = path17.relative(sourceRepoRoot, workspaceCwd);
12887
+ const sourceRepoRoot = path21.resolve(input.sourceRepoRoot);
12888
+ const targetRepoRoot = path21.resolve(input.targetRepoRoot);
12889
+ const workspaceCwd = path21.resolve(input.workspaceCwd);
12890
+ const relative = path21.relative(sourceRepoRoot, workspaceCwd);
11879
12891
  if (!relative || relative === "") {
11880
12892
  return targetRepoRoot;
11881
12893
  }
11882
- if (relative.startsWith("..") || path17.isAbsolute(relative)) {
12894
+ if (relative.startsWith("..") || path21.isAbsolute(relative)) {
11883
12895
  return null;
11884
12896
  }
11885
- return path17.resolve(targetRepoRoot, relative);
12897
+ return path21.resolve(targetRepoRoot, relative);
11886
12898
  }
11887
12899
  async function rebindSeededProjectWorkspaces(input) {
11888
12900
  const targetRepo = detectGitWorkspaceInfo(input.currentCwd);
@@ -11908,7 +12920,7 @@ async function rebindSeededProjectWorkspaces(input) {
11908
12920
  workspaceCwd
11909
12921
  });
11910
12922
  if (!reboundCwd) continue;
11911
- const normalizedCurrent = path17.resolve(workspaceCwd);
12923
+ const normalizedCurrent = path21.resolve(workspaceCwd);
11912
12924
  if (reboundCwd === normalizedCurrent) continue;
11913
12925
  if (!existsSync2(reboundCwd)) continue;
11914
12926
  await db.update(projectWorkspaces).set({
@@ -11927,14 +12939,14 @@ async function rebindSeededProjectWorkspaces(input) {
11927
12939
  }
11928
12940
  }
11929
12941
  function resolveSourceConfigPath(opts) {
11930
- if (opts.sourceConfigPathOverride) return path17.resolve(opts.sourceConfigPathOverride);
11931
- if (opts.fromConfig) return path17.resolve(opts.fromConfig);
12942
+ if (opts.sourceConfigPathOverride) return path21.resolve(opts.sourceConfigPathOverride);
12943
+ if (opts.fromConfig) return path21.resolve(opts.fromConfig);
11932
12944
  if (!opts.fromDataDir && !opts.fromInstance) {
11933
12945
  return resolveConfigPath();
11934
12946
  }
11935
- const sourceHome = path17.resolve(expandHomePrefix(opts.fromDataDir ?? "~/.paperclip"));
12947
+ const sourceHome = path21.resolve(expandHomePrefix(opts.fromDataDir ?? "~/.paperclip"));
11936
12948
  const sourceInstanceId = sanitizeWorktreeInstanceId(opts.fromInstance ?? "default");
11937
- return path17.resolve(sourceHome, "instances", sourceInstanceId, "config.json");
12949
+ return path21.resolve(sourceHome, "instances", sourceInstanceId, "config.json");
11938
12950
  }
11939
12951
  function resolveSourceConnectionString(config, envEntries, portOverride) {
11940
12952
  if (config.database.mode === "postgres") {
@@ -11953,7 +12965,7 @@ function copySeededSecretsKey(input) {
11953
12965
  if (input.sourceConfig.secrets.provider !== "local_encrypted") {
11954
12966
  return;
11955
12967
  }
11956
- mkdirSync2(path17.dirname(input.targetKeyFilePath), { recursive: true });
12968
+ mkdirSync2(path21.dirname(input.targetKeyFilePath), { recursive: true });
11957
12969
  const allowProcessEnvFallback = isCurrentSourceConfigPath(input.sourceConfigPath);
11958
12970
  const sourceInlineMasterKey = nonEmpty2(input.sourceEnvEntries.PAPERCLIP_SECRETS_MASTER_KEY) ?? (allowProcessEnvFallback ? nonEmpty2(process.env.PAPERCLIP_SECRETS_MASTER_KEY) : null);
11959
12971
  if (sourceInlineMasterKey) {
@@ -11992,7 +13004,7 @@ async function ensureEmbeddedPostgres(dataDir, preferredPort) {
11992
13004
  "Embedded PostgreSQL support requires dependency `embedded-postgres`. Reinstall dependencies and try again."
11993
13005
  );
11994
13006
  }
11995
- const postmasterPidFile = path17.resolve(dataDir, "postmaster.pid");
13007
+ const postmasterPidFile = path21.resolve(dataDir, "postmaster.pid");
11996
13008
  const runningPid = readRunningPostmasterPid(postmasterPidFile);
11997
13009
  if (runningPid) {
11998
13010
  return {
@@ -12015,7 +13027,7 @@ async function ensureEmbeddedPostgres(dataDir, preferredPort) {
12015
13027
  onError: () => {
12016
13028
  }
12017
13029
  });
12018
- if (!existsSync2(path17.resolve(dataDir, "PG_VERSION"))) {
13030
+ if (!existsSync2(path21.resolve(dataDir, "PG_VERSION"))) {
12019
13031
  await instance.initialise();
12020
13032
  }
12021
13033
  if (existsSync2(postmasterPidFile)) {
@@ -12056,7 +13068,7 @@ async function seedWorktreeDatabase(input) {
12056
13068
  );
12057
13069
  const backup = await runDatabaseBackup({
12058
13070
  connectionString: sourceConnectionString,
12059
- backupDir: path17.resolve(input.targetPaths.backupDir, "seed"),
13071
+ backupDir: path21.resolve(input.targetPaths.backupDir, "seed"),
12060
13072
  retentionDays: 7,
12061
13073
  filenamePrefix: `${input.instanceId}-seed`,
12062
13074
  includeMigrationJournal: true,
@@ -12154,8 +13166,8 @@ async function runWorktreeInit(opts) {
12154
13166
  `Cannot seed worktree database because source config was not found at ${sourceConfigPath}. Use --no-seed or provide --from-config.`
12155
13167
  );
12156
13168
  }
12157
- const spinner4 = p15.spinner();
12158
- spinner4.start(`Seeding isolated worktree database from source instance (${seedMode})...`);
13169
+ const spinner5 = p16.spinner();
13170
+ spinner5.start(`Seeding isolated worktree database from source instance (${seedMode})...`);
12159
13171
  try {
12160
13172
  const seeded = await seedWorktreeDatabase({
12161
13173
  sourceConfigPath,
@@ -12167,46 +13179,46 @@ async function runWorktreeInit(opts) {
12167
13179
  });
12168
13180
  seedSummary = seeded.backupSummary;
12169
13181
  reboundWorkspaceSummary = seeded.reboundWorkspaces;
12170
- spinner4.stop(`Seeded isolated worktree database (${seedMode}).`);
13182
+ spinner5.stop(`Seeded isolated worktree database (${seedMode}).`);
12171
13183
  } catch (error) {
12172
- spinner4.stop(pc21.red("Failed to seed worktree database."));
13184
+ spinner5.stop(pc23.red("Failed to seed worktree database."));
12173
13185
  throw error;
12174
13186
  }
12175
13187
  }
12176
- p15.log.message(pc21.dim(`Repo config: ${paths.configPath}`));
12177
- p15.log.message(pc21.dim(`Repo env: ${paths.envPath}`));
12178
- p15.log.message(pc21.dim(`Isolated home: ${paths.homeDir}`));
12179
- p15.log.message(pc21.dim(`Instance: ${paths.instanceId}`));
12180
- p15.log.message(pc21.dim(`Worktree badge: ${branding.name} (${branding.color})`));
12181
- p15.log.message(pc21.dim(`Server port: ${serverPort} | DB port: ${databasePort}`));
13188
+ p16.log.message(pc23.dim(`Repo config: ${paths.configPath}`));
13189
+ p16.log.message(pc23.dim(`Repo env: ${paths.envPath}`));
13190
+ p16.log.message(pc23.dim(`Isolated home: ${paths.homeDir}`));
13191
+ p16.log.message(pc23.dim(`Instance: ${paths.instanceId}`));
13192
+ p16.log.message(pc23.dim(`Worktree badge: ${branding.name} (${branding.color})`));
13193
+ p16.log.message(pc23.dim(`Server port: ${serverPort} | DB port: ${databasePort}`));
12182
13194
  if (copiedGitHooks?.copied) {
12183
- p15.log.message(
12184
- pc21.dim(`Mirrored git hooks: ${copiedGitHooks.sourceHooksPath} -> ${copiedGitHooks.targetHooksPath}`)
13195
+ p16.log.message(
13196
+ pc23.dim(`Mirrored git hooks: ${copiedGitHooks.sourceHooksPath} -> ${copiedGitHooks.targetHooksPath}`)
12185
13197
  );
12186
13198
  }
12187
13199
  if (seedSummary) {
12188
- p15.log.message(pc21.dim(`Seed mode: ${seedMode}`));
12189
- p15.log.message(pc21.dim(`Seed snapshot: ${seedSummary}`));
13200
+ p16.log.message(pc23.dim(`Seed mode: ${seedMode}`));
13201
+ p16.log.message(pc23.dim(`Seed snapshot: ${seedSummary}`));
12190
13202
  for (const rebound of reboundWorkspaceSummary) {
12191
- p15.log.message(
12192
- pc21.dim(`Rebound workspace ${rebound.name}: ${rebound.fromCwd} -> ${rebound.toCwd}`)
13203
+ p16.log.message(
13204
+ pc23.dim(`Rebound workspace ${rebound.name}: ${rebound.fromCwd} -> ${rebound.toCwd}`)
12193
13205
  );
12194
13206
  }
12195
13207
  }
12196
- p15.outro(
12197
- pc21.green(
13208
+ p16.outro(
13209
+ pc23.green(
12198
13210
  `Worktree ready. Run Paperclip inside this repo and the CLI/server will use ${paths.instanceId} automatically.`
12199
13211
  )
12200
13212
  );
12201
13213
  }
12202
13214
  async function worktreeInitCommand(opts) {
12203
13215
  printPaperclipCliBanner();
12204
- p15.intro(pc21.bgCyan(pc21.black(" paperclipai worktree init ")));
13216
+ p16.intro(pc23.bgCyan(pc23.black(" paperclipai worktree init ")));
12205
13217
  await runWorktreeInit(opts);
12206
13218
  }
12207
13219
  async function worktreeMakeCommand(nameArg, opts) {
12208
13220
  printPaperclipCliBanner();
12209
- p15.intro(pc21.bgCyan(pc21.black(" paperclipai worktree:make ")));
13221
+ p16.intro(pc23.bgCyan(pc23.black(" paperclipai worktree:make ")));
12210
13222
  const name = resolveWorktreeMakeName(nameArg);
12211
13223
  const startPoint = resolveWorktreeStartPoint(opts.startPoint);
12212
13224
  const sourceCwd = process.cwd();
@@ -12215,7 +13227,7 @@ async function worktreeMakeCommand(nameArg, opts) {
12215
13227
  if (existsSync2(targetPath)) {
12216
13228
  throw new Error(`Target path already exists: ${targetPath}`);
12217
13229
  }
12218
- mkdirSync2(path17.dirname(targetPath), { recursive: true });
13230
+ mkdirSync2(path21.dirname(targetPath), { recursive: true });
12219
13231
  if (startPoint) {
12220
13232
  const [remote] = startPoint.split("/", 1);
12221
13233
  try {
@@ -12235,19 +13247,19 @@ async function worktreeMakeCommand(nameArg, opts) {
12235
13247
  branchExists: !startPoint && localBranchExists(sourceCwd, name),
12236
13248
  startPoint
12237
13249
  });
12238
- const spinner4 = p15.spinner();
12239
- spinner4.start(`Creating git worktree at ${targetPath}...`);
13250
+ const spinner5 = p16.spinner();
13251
+ spinner5.start(`Creating git worktree at ${targetPath}...`);
12240
13252
  try {
12241
13253
  execFileSync("git", worktreeArgs, {
12242
13254
  cwd: sourceCwd,
12243
13255
  stdio: ["ignore", "pipe", "pipe"]
12244
13256
  });
12245
- spinner4.stop(`Created git worktree at ${targetPath}.`);
13257
+ spinner5.stop(`Created git worktree at ${targetPath}.`);
12246
13258
  } catch (error) {
12247
- spinner4.stop(pc21.red("Failed to create git worktree."));
13259
+ spinner5.stop(pc23.red("Failed to create git worktree."));
12248
13260
  throw new Error(extractExecSyncErrorMessage(error) ?? String(error));
12249
13261
  }
12250
- const installSpinner = p15.spinner();
13262
+ const installSpinner = p16.spinner();
12251
13263
  installSpinner.start("Installing dependencies...");
12252
13264
  try {
12253
13265
  execFileSync("pnpm", ["install"], {
@@ -12256,8 +13268,8 @@ async function worktreeMakeCommand(nameArg, opts) {
12256
13268
  });
12257
13269
  installSpinner.stop("Installed dependencies.");
12258
13270
  } catch (error) {
12259
- installSpinner.stop(pc21.yellow("Failed to install dependencies (continuing anyway)."));
12260
- p15.log.warning(extractExecSyncErrorMessage(error) ?? String(error));
13271
+ installSpinner.stop(pc23.yellow("Failed to install dependencies (continuing anyway)."));
13272
+ p16.log.warning(extractExecSyncErrorMessage(error) ?? String(error));
12261
13273
  }
12262
13274
  const originalCwd = process.cwd();
12263
13275
  try {
@@ -12272,13 +13284,13 @@ async function worktreeMakeCommand(nameArg, opts) {
12272
13284
  } finally {
12273
13285
  process.chdir(originalCwd);
12274
13286
  }
12275
- const bootstrapScript = path17.resolve(sourceCwd, "scripts/worktree-bootstrap.mjs");
13287
+ const bootstrapScript = path21.resolve(sourceCwd, "scripts/worktree-bootstrap.mjs");
12276
13288
  if (existsSync2(bootstrapScript)) {
12277
- p15.log.message(pc21.dim(`Running worktree bootstrap in ${targetPath}...`));
13289
+ p16.log.message(pc23.dim(`Running worktree bootstrap in ${targetPath}...`));
12278
13290
  try {
12279
13291
  execFileSync("node", [bootstrapScript], { cwd: targetPath, stdio: "inherit" });
12280
13292
  } catch (error) {
12281
- p15.log.warning(`Bootstrap failed: ${extractExecSyncErrorMessage(error) ?? String(error)}`);
13293
+ p16.log.warning(`Bootstrap failed: ${extractExecSyncErrorMessage(error) ?? String(error)}`);
12282
13294
  }
12283
13295
  }
12284
13296
  }
@@ -12357,30 +13369,30 @@ function worktreePathHasUncommittedChanges(worktreePath) {
12357
13369
  }
12358
13370
  async function worktreeCleanupCommand(nameArg, opts) {
12359
13371
  printPaperclipCliBanner();
12360
- p15.intro(pc21.bgCyan(pc21.black(" paperclipai worktree:cleanup ")));
13372
+ p16.intro(pc23.bgCyan(pc23.black(" paperclipai worktree:cleanup ")));
12361
13373
  const name = resolveWorktreeMakeName(nameArg);
12362
13374
  const sourceCwd = process.cwd();
12363
13375
  const targetPath = resolveWorktreeMakeTargetPath(name);
12364
13376
  const instanceId = sanitizeWorktreeInstanceId(opts.instance ?? name);
12365
- const homeDir = path17.resolve(expandHomePrefix(resolveWorktreeHome(opts.home)));
12366
- const instanceRoot = path17.resolve(homeDir, "instances", instanceId);
13377
+ const homeDir = path21.resolve(expandHomePrefix(resolveWorktreeHome(opts.home)));
13378
+ const instanceRoot = path21.resolve(homeDir, "instances", instanceId);
12367
13379
  const hasBranch = localBranchExists(sourceCwd, name);
12368
13380
  const hasTargetDir = existsSync2(targetPath);
12369
13381
  const hasInstanceData = existsSync2(instanceRoot);
12370
13382
  const worktrees = parseGitWorktreeList(sourceCwd);
12371
13383
  const linkedWorktree = worktrees.find(
12372
- (wt) => wt.branch === `refs/heads/${name}` || path17.resolve(wt.worktree) === path17.resolve(targetPath)
13384
+ (wt) => wt.branch === `refs/heads/${name}` || path21.resolve(wt.worktree) === path21.resolve(targetPath)
12373
13385
  );
12374
13386
  if (!hasBranch && !hasTargetDir && !hasInstanceData && !linkedWorktree) {
12375
- p15.log.info("Nothing to clean up \u2014 no branch, worktree directory, or instance data found.");
12376
- p15.outro(pc21.green("Already clean."));
13387
+ p16.log.info("Nothing to clean up \u2014 no branch, worktree directory, or instance data found.");
13388
+ p16.outro(pc23.green("Already clean."));
12377
13389
  return;
12378
13390
  }
12379
13391
  const problems = [];
12380
13392
  if (hasBranch && branchHasUniqueCommits(sourceCwd, name)) {
12381
13393
  const onRemote = branchExistsOnAnyRemote(sourceCwd, name);
12382
13394
  if (onRemote) {
12383
- p15.log.info(
13395
+ p16.log.info(
12384
13396
  `Branch "${name}" has unique local commits, but the branch also exists on a remote \u2014 safe to delete locally.`
12385
13397
  );
12386
13398
  } else {
@@ -12396,20 +13408,20 @@ async function worktreeCleanupCommand(nameArg, opts) {
12396
13408
  }
12397
13409
  if (problems.length > 0 && !opts.force) {
12398
13410
  for (const problem of problems) {
12399
- p15.log.error(problem);
13411
+ p16.log.error(problem);
12400
13412
  }
12401
13413
  throw new Error("Safety checks failed. Resolve the issues above or re-run with --force.");
12402
13414
  }
12403
13415
  if (problems.length > 0 && opts.force) {
12404
13416
  for (const problem of problems) {
12405
- p15.log.warning(`Overridden by --force: ${problem}`);
13417
+ p16.log.warning(`Overridden by --force: ${problem}`);
12406
13418
  }
12407
13419
  }
12408
13420
  if (linkedWorktree) {
12409
13421
  const worktreeDirExists = existsSync2(linkedWorktree.worktree);
12410
- const spinner4 = p15.spinner();
13422
+ const spinner5 = p16.spinner();
12411
13423
  if (worktreeDirExists) {
12412
- spinner4.start(`Removing git worktree at ${linkedWorktree.worktree}...`);
13424
+ spinner5.start(`Removing git worktree at ${linkedWorktree.worktree}...`);
12413
13425
  try {
12414
13426
  const removeArgs = ["worktree", "remove", linkedWorktree.worktree];
12415
13427
  if (opts.force) removeArgs.push("--force");
@@ -12417,18 +13429,18 @@ async function worktreeCleanupCommand(nameArg, opts) {
12417
13429
  cwd: sourceCwd,
12418
13430
  stdio: ["ignore", "pipe", "pipe"]
12419
13431
  });
12420
- spinner4.stop(`Removed git worktree at ${linkedWorktree.worktree}.`);
13432
+ spinner5.stop(`Removed git worktree at ${linkedWorktree.worktree}.`);
12421
13433
  } catch (error) {
12422
- spinner4.stop(pc21.yellow(`Could not remove worktree cleanly, will prune instead.`));
12423
- p15.log.warning(extractExecSyncErrorMessage(error) ?? String(error));
13434
+ spinner5.stop(pc23.yellow(`Could not remove worktree cleanly, will prune instead.`));
13435
+ p16.log.warning(extractExecSyncErrorMessage(error) ?? String(error));
12424
13436
  }
12425
13437
  } else {
12426
- spinner4.start("Pruning stale worktree entry...");
13438
+ spinner5.start("Pruning stale worktree entry...");
12427
13439
  execFileSync("git", ["worktree", "prune"], {
12428
13440
  cwd: sourceCwd,
12429
13441
  stdio: ["ignore", "pipe", "pipe"]
12430
13442
  });
12431
- spinner4.stop("Pruned stale worktree entry.");
13443
+ spinner5.stop("Pruned stale worktree entry.");
12432
13444
  }
12433
13445
  } else {
12434
13446
  execFileSync("git", ["worktree", "prune"], {
@@ -12437,33 +13449,33 @@ async function worktreeCleanupCommand(nameArg, opts) {
12437
13449
  });
12438
13450
  }
12439
13451
  if (existsSync2(targetPath)) {
12440
- const spinner4 = p15.spinner();
12441
- spinner4.start(`Removing worktree directory ${targetPath}...`);
13452
+ const spinner5 = p16.spinner();
13453
+ spinner5.start(`Removing worktree directory ${targetPath}...`);
12442
13454
  rmSync(targetPath, { recursive: true, force: true });
12443
- spinner4.stop(`Removed worktree directory ${targetPath}.`);
13455
+ spinner5.stop(`Removed worktree directory ${targetPath}.`);
12444
13456
  }
12445
13457
  if (localBranchExists(sourceCwd, name)) {
12446
- const spinner4 = p15.spinner();
12447
- spinner4.start(`Deleting local branch "${name}"...`);
13458
+ const spinner5 = p16.spinner();
13459
+ spinner5.start(`Deleting local branch "${name}"...`);
12448
13460
  try {
12449
13461
  const deleteFlag = opts.force ? "-D" : "-d";
12450
13462
  execFileSync("git", ["branch", deleteFlag, name], {
12451
13463
  cwd: sourceCwd,
12452
13464
  stdio: ["ignore", "pipe", "pipe"]
12453
13465
  });
12454
- spinner4.stop(`Deleted local branch "${name}".`);
13466
+ spinner5.stop(`Deleted local branch "${name}".`);
12455
13467
  } catch (error) {
12456
- spinner4.stop(pc21.yellow(`Could not delete branch "${name}".`));
12457
- p15.log.warning(extractExecSyncErrorMessage(error) ?? String(error));
13468
+ spinner5.stop(pc23.yellow(`Could not delete branch "${name}".`));
13469
+ p16.log.warning(extractExecSyncErrorMessage(error) ?? String(error));
12458
13470
  }
12459
13471
  }
12460
13472
  if (existsSync2(instanceRoot)) {
12461
- const spinner4 = p15.spinner();
12462
- spinner4.start(`Removing instance data at ${instanceRoot}...`);
13473
+ const spinner5 = p16.spinner();
13474
+ spinner5.start(`Removing instance data at ${instanceRoot}...`);
12463
13475
  rmSync(instanceRoot, { recursive: true, force: true });
12464
- spinner4.stop(`Removed instance data at ${instanceRoot}.`);
13476
+ spinner5.stop(`Removed instance data at ${instanceRoot}.`);
12465
13477
  }
12466
- p15.outro(pc21.green("Cleanup complete."));
13478
+ p16.outro(pc23.green("Cleanup complete."));
12467
13479
  }
12468
13480
  async function worktreeEnvCommand(opts) {
12469
13481
  const configPath = resolveConfigPath(opts.config);
@@ -12491,27 +13503,27 @@ function registerWorktreeCommands(program2) {
12491
13503
  }
12492
13504
 
12493
13505
  // src/commands/client/plugin.ts
12494
- import path18 from "node:path";
12495
- import pc22 from "picocolors";
13506
+ import path22 from "node:path";
13507
+ import pc24 from "picocolors";
12496
13508
  function resolvePackageArg(packageArg, isLocal) {
12497
13509
  if (!isLocal) return packageArg;
12498
- if (path18.isAbsolute(packageArg)) return packageArg;
13510
+ if (path22.isAbsolute(packageArg)) return packageArg;
12499
13511
  if (packageArg.startsWith("~")) {
12500
13512
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12501
- return path18.resolve(home, packageArg.slice(1).replace(/^[\\/]/, ""));
13513
+ return path22.resolve(home, packageArg.slice(1).replace(/^[\\/]/, ""));
12502
13514
  }
12503
- return path18.resolve(process.cwd(), packageArg);
13515
+ return path22.resolve(process.cwd(), packageArg);
12504
13516
  }
12505
- function formatPlugin(p19) {
12506
- const statusColor = p19.status === "ready" ? pc22.green(p19.status) : p19.status === "error" ? pc22.red(p19.status) : p19.status === "disabled" ? pc22.dim(p19.status) : pc22.yellow(p19.status);
13517
+ function formatPlugin(p20) {
13518
+ const statusColor = p20.status === "ready" ? pc24.green(p20.status) : p20.status === "error" ? pc24.red(p20.status) : p20.status === "disabled" ? pc24.dim(p20.status) : pc24.yellow(p20.status);
12507
13519
  const parts = [
12508
- `key=${pc22.bold(p19.pluginKey)}`,
13520
+ `key=${pc24.bold(p20.pluginKey)}`,
12509
13521
  `status=${statusColor}`,
12510
- `version=${p19.version}`,
12511
- `id=${pc22.dim(p19.id)}`
13522
+ `version=${p20.version}`,
13523
+ `id=${pc24.dim(p20.id)}`
12512
13524
  ];
12513
- if (p19.lastError) {
12514
- parts.push(`error=${pc22.red(p19.lastError.slice(0, 80))}`);
13525
+ if (p20.lastError) {
13526
+ parts.push(`error=${pc24.red(p20.lastError.slice(0, 80))}`);
12515
13527
  }
12516
13528
  return parts.join(" ");
12517
13529
  }
@@ -12529,11 +13541,11 @@ function registerPluginCommands(program2) {
12529
13541
  }
12530
13542
  const rows = plugins2 ?? [];
12531
13543
  if (rows.length === 0) {
12532
- console.log(pc22.dim("No plugins installed."));
13544
+ console.log(pc24.dim("No plugins installed."));
12533
13545
  return;
12534
13546
  }
12535
- for (const p19 of rows) {
12536
- console.log(formatPlugin(p19));
13547
+ for (const p20 of rows) {
13548
+ console.log(formatPlugin(p20));
12537
13549
  }
12538
13550
  } catch (err) {
12539
13551
  handleCommandError(err);
@@ -12550,7 +13562,7 @@ function registerPluginCommands(program2) {
12550
13562
  const resolvedPackage = resolvePackageArg(packageArg, isLocal);
12551
13563
  if (!ctx.json) {
12552
13564
  console.log(
12553
- pc22.dim(
13565
+ pc24.dim(
12554
13566
  isLocal ? `Installing plugin from local path: ${resolvedPackage}` : `Installing plugin: ${resolvedPackage}${opts.version ? `@${opts.version}` : ""}`
12555
13567
  )
12556
13568
  );
@@ -12565,16 +13577,16 @@ function registerPluginCommands(program2) {
12565
13577
  return;
12566
13578
  }
12567
13579
  if (!installedPlugin) {
12568
- console.log(pc22.dim("Install returned no plugin record."));
13580
+ console.log(pc24.dim("Install returned no plugin record."));
12569
13581
  return;
12570
13582
  }
12571
13583
  console.log(
12572
- pc22.green(
12573
- `\u2713 Installed ${pc22.bold(installedPlugin.pluginKey)} v${installedPlugin.version} (${installedPlugin.status})`
13584
+ pc24.green(
13585
+ `\u2713 Installed ${pc24.bold(installedPlugin.pluginKey)} v${installedPlugin.version} (${installedPlugin.status})`
12574
13586
  )
12575
13587
  );
12576
13588
  if (installedPlugin.lastError) {
12577
- console.log(pc22.red(` Warning: ${installedPlugin.lastError}`));
13589
+ console.log(pc24.red(` Warning: ${installedPlugin.lastError}`));
12578
13590
  }
12579
13591
  } catch (err) {
12580
13592
  handleCommandError(err);
@@ -12591,7 +13603,7 @@ function registerPluginCommands(program2) {
12591
13603
  const qs = purge ? "?purge=true" : "";
12592
13604
  if (!ctx.json) {
12593
13605
  console.log(
12594
- pc22.dim(
13606
+ pc24.dim(
12595
13607
  purge ? `Uninstalling and purging plugin: ${pluginKey}` : `Uninstalling plugin: ${pluginKey}`
12596
13608
  )
12597
13609
  );
@@ -12603,7 +13615,7 @@ function registerPluginCommands(program2) {
12603
13615
  printOutput(result, { json: true });
12604
13616
  return;
12605
13617
  }
12606
- console.log(pc22.green(`\u2713 Uninstalled ${pc22.bold(pluginKey)}${purge ? " (purged)" : ""}`));
13618
+ console.log(pc24.green(`\u2713 Uninstalled ${pc24.bold(pluginKey)}${purge ? " (purged)" : ""}`));
12607
13619
  } catch (err) {
12608
13620
  handleCommandError(err);
12609
13621
  }
@@ -12620,7 +13632,7 @@ function registerPluginCommands(program2) {
12620
13632
  printOutput(result, { json: true });
12621
13633
  return;
12622
13634
  }
12623
- console.log(pc22.green(`\u2713 Enabled ${pc22.bold(pluginKey)} \u2014 status: ${result?.status ?? "unknown"}`));
13635
+ console.log(pc24.green(`\u2713 Enabled ${pc24.bold(pluginKey)} \u2014 status: ${result?.status ?? "unknown"}`));
12624
13636
  } catch (err) {
12625
13637
  handleCommandError(err);
12626
13638
  }
@@ -12637,7 +13649,7 @@ function registerPluginCommands(program2) {
12637
13649
  printOutput(result, { json: true });
12638
13650
  return;
12639
13651
  }
12640
- console.log(pc22.dim(`Disabled ${pc22.bold(pluginKey)} \u2014 status: ${result?.status ?? "unknown"}`));
13652
+ console.log(pc24.dim(`Disabled ${pc24.bold(pluginKey)} \u2014 status: ${result?.status ?? "unknown"}`));
12641
13653
  } catch (err) {
12642
13654
  handleCommandError(err);
12643
13655
  }
@@ -12655,13 +13667,13 @@ function registerPluginCommands(program2) {
12655
13667
  return;
12656
13668
  }
12657
13669
  if (!result) {
12658
- console.log(pc22.red(`Plugin not found: ${pluginKey}`));
13670
+ console.log(pc24.red(`Plugin not found: ${pluginKey}`));
12659
13671
  process.exit(1);
12660
13672
  }
12661
13673
  console.log(formatPlugin(result));
12662
13674
  if (result.lastError) {
12663
13675
  console.log(`
12664
- ${pc22.red("Last error:")}
13676
+ ${pc24.red("Last error:")}
12665
13677
  ${result.lastError}`);
12666
13678
  }
12667
13679
  } catch (err) {
@@ -12680,14 +13692,14 @@ ${result.lastError}`);
12680
13692
  }
12681
13693
  const rows = examples ?? [];
12682
13694
  if (rows.length === 0) {
12683
- console.log(pc22.dim("No bundled examples available."));
13695
+ console.log(pc24.dim("No bundled examples available."));
12684
13696
  return;
12685
13697
  }
12686
13698
  for (const ex of rows) {
12687
13699
  console.log(
12688
- `${pc22.bold(ex.displayName)} ${pc22.dim(ex.pluginKey)}
13700
+ `${pc24.bold(ex.displayName)} ${pc24.dim(ex.pluginKey)}
12689
13701
  ${ex.description}
12690
- ${pc22.cyan(`paperclipai plugin install ${ex.localPath}`)}`
13702
+ ${pc24.cyan(`paperclipai plugin install ${ex.localPath}`)}`
12691
13703
  );
12692
13704
  }
12693
13705
  } catch (err) {
@@ -12698,15 +13710,15 @@ ${result.lastError}`);
12698
13710
  }
12699
13711
 
12700
13712
  // src/commands/kit.ts
12701
- import path20 from "node:path";
13713
+ import path24 from "node:path";
12702
13714
  import { pathToFileURL as pathToFileURL2 } from "node:url";
12703
- import * as p16 from "@clack/prompts";
12704
- import pc23 from "picocolors";
13715
+ import * as p17 from "@clack/prompts";
13716
+ import pc25 from "picocolors";
12705
13717
 
12706
13718
  // src/kits/service.ts
12707
13719
  init_home();
12708
- import fs14 from "node:fs";
12709
- import path19 from "node:path";
13720
+ import fs17 from "node:fs";
13721
+ import path23 from "node:path";
12710
13722
  import { fileURLToPath as fileURLToPath4 } from "node:url";
12711
13723
 
12712
13724
  // src/kits/catalog.ts
@@ -12799,48 +13811,48 @@ var KIT_ACTIVATION_MODES = [
12799
13811
  // src/kits/service.ts
12800
13812
  var ZIP_TIMESTAMP = /* @__PURE__ */ new Date("2026-04-09T00:00:00.000Z");
12801
13813
  function resolveBundledKitAssetsRoot() {
12802
- const moduleDir = path19.dirname(fileURLToPath4(import.meta.url));
13814
+ const moduleDir = path23.dirname(fileURLToPath4(import.meta.url));
12803
13815
  const candidates = [
12804
- path19.resolve(moduleDir, "../../assets/worker-kits"),
12805
- path19.resolve(moduleDir, "../assets/worker-kits")
13816
+ path23.resolve(moduleDir, "../../assets/worker-kits"),
13817
+ path23.resolve(moduleDir, "../assets/worker-kits")
12806
13818
  ];
12807
13819
  for (const candidate of candidates) {
12808
- if (fs14.existsSync(candidate)) return candidate;
13820
+ if (fs17.existsSync(candidate)) return candidate;
12809
13821
  }
12810
13822
  throw new Error("Could not locate bundled worker kit assets.");
12811
13823
  }
12812
13824
  function resolveRequestedOutputRoot(outDir) {
12813
13825
  if (outDir?.trim()) {
12814
- return path19.resolve(expandHomePrefix(outDir.trim()));
13826
+ return path23.resolve(expandHomePrefix(outDir.trim()));
12815
13827
  }
12816
- return path19.resolve(resolvePaperclipHomeDir(), "kits", "exports");
13828
+ return path23.resolve(resolvePaperclipHomeDir(), "kits", "exports");
12817
13829
  }
12818
13830
  function readJsonFile(filePath) {
12819
- return JSON.parse(fs14.readFileSync(filePath, "utf8"));
13831
+ return JSON.parse(fs17.readFileSync(filePath, "utf8"));
12820
13832
  }
12821
13833
  function assertRelativePathExists(assetRoot, relativePath, label) {
12822
- const fullPath = path19.resolve(assetRoot, relativePath);
12823
- if (!fs14.existsSync(fullPath)) {
13834
+ const fullPath = path23.resolve(assetRoot, relativePath);
13835
+ if (!fs17.existsSync(fullPath)) {
12824
13836
  throw new Error(`${label} is missing required path: ${relativePath}`);
12825
13837
  }
12826
13838
  }
12827
13839
  function listRelativeFiles(rootDir) {
12828
13840
  const files = [];
12829
13841
  const walk = (currentDir) => {
12830
- for (const entry of fs14.readdirSync(currentDir, { withFileTypes: true })) {
12831
- const fullPath = path19.join(currentDir, entry.name);
13842
+ for (const entry of fs17.readdirSync(currentDir, { withFileTypes: true })) {
13843
+ const fullPath = path23.join(currentDir, entry.name);
12832
13844
  if (entry.isDirectory()) {
12833
13845
  walk(fullPath);
12834
13846
  continue;
12835
13847
  }
12836
- files.push(path19.relative(rootDir, fullPath).split(path19.sep).join("/"));
13848
+ files.push(path23.relative(rootDir, fullPath).split(path23.sep).join("/"));
12837
13849
  }
12838
13850
  };
12839
13851
  walk(rootDir);
12840
13852
  return files.sort();
12841
13853
  }
12842
13854
  function parseManifest(assetRoot) {
12843
- const raw = readJsonFile(path19.resolve(assetRoot, "kit.json"));
13855
+ const raw = readJsonFile(path23.resolve(assetRoot, "kit.json"));
12844
13856
  if (!SUPPORTED_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
12845
13857
  throw new Error(`Unsupported kit schema version for ${assetRoot}: ${raw.schemaVersion}`);
12846
13858
  }
@@ -12851,7 +13863,7 @@ function parseBundleManifest(assetRoot, manifest, bundleId) {
12851
13863
  if (!bundleRef) {
12852
13864
  throw new Error(`Kit ${manifest.kit.id} does not declare bundle ${bundleId}.`);
12853
13865
  }
12854
- const raw = readJsonFile(path19.resolve(assetRoot, bundleRef.path));
13866
+ const raw = readJsonFile(path23.resolve(assetRoot, bundleRef.path));
12855
13867
  if (!SUPPORTED_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
12856
13868
  throw new Error(
12857
13869
  `Unsupported bundle schema version for ${bundleRef.path}: ${raw.schemaVersion}`
@@ -12916,14 +13928,14 @@ function validateKitDirectory(kitPath) {
12916
13928
  const warnings = [];
12917
13929
  let schemaVersion = 0;
12918
13930
  let kitId = "<unknown>";
12919
- const kitJsonPath = path19.resolve(kitPath, "kit.json");
12920
- if (!fs14.existsSync(kitJsonPath)) {
13931
+ const kitJsonPath = path23.resolve(kitPath, "kit.json");
13932
+ if (!fs17.existsSync(kitJsonPath)) {
12921
13933
  errors.push({ field: "kit.json", message: "kit.json not found in kit directory" });
12922
13934
  return { valid: false, schemaVersion, kitId, errors, warnings };
12923
13935
  }
12924
13936
  let raw;
12925
13937
  try {
12926
- raw = JSON.parse(fs14.readFileSync(kitJsonPath, "utf8"));
13938
+ raw = JSON.parse(fs17.readFileSync(kitJsonPath, "utf8"));
12927
13939
  } catch {
12928
13940
  errors.push({ field: "kit.json", message: "kit.json is not valid JSON" });
12929
13941
  return { valid: false, schemaVersion, kitId, errors, warnings };
@@ -12990,8 +14002,8 @@ function validateKitDirectory(kitPath) {
12990
14002
  if (typeof entrypoint.path !== "string") {
12991
14003
  errors.push({ field: "entrypoint.path", message: "Missing required field 'entrypoint.path'" });
12992
14004
  } else {
12993
- const fullPath = path19.resolve(kitPath, entrypoint.path);
12994
- if (!fs14.existsSync(fullPath)) {
14005
+ const fullPath = path23.resolve(kitPath, entrypoint.path);
14006
+ if (!fs17.existsSync(fullPath)) {
12995
14007
  errors.push({ field: "entrypoint.path", message: `Entrypoint file not found: ${entrypoint.path}` });
12996
14008
  }
12997
14009
  }
@@ -12999,16 +14011,16 @@ function validateKitDirectory(kitPath) {
12999
14011
  if (typeof raw.agentContractPath !== "string") {
13000
14012
  errors.push({ field: "agentContractPath", message: "Missing required field 'agentContractPath'" });
13001
14013
  } else {
13002
- const fullPath = path19.resolve(kitPath, raw.agentContractPath);
13003
- if (!fs14.existsSync(fullPath)) {
14014
+ const fullPath = path23.resolve(kitPath, raw.agentContractPath);
14015
+ if (!fs17.existsSync(fullPath)) {
13004
14016
  errors.push({ field: "agentContractPath", message: `Agent contract not found: ${raw.agentContractPath}` });
13005
14017
  }
13006
14018
  }
13007
14019
  if (typeof raw.brandTemplatePath !== "string") {
13008
14020
  errors.push({ field: "brandTemplatePath", message: "Missing required field 'brandTemplatePath'" });
13009
14021
  } else {
13010
- const fullPath = path19.resolve(kitPath, raw.brandTemplatePath);
13011
- if (!fs14.existsSync(fullPath)) {
14022
+ const fullPath = path23.resolve(kitPath, raw.brandTemplatePath);
14023
+ if (!fs17.existsSync(fullPath)) {
13012
14024
  errors.push({ field: "brandTemplatePath", message: `Brand template not found: ${raw.brandTemplatePath}` });
13013
14025
  }
13014
14026
  }
@@ -13018,8 +14030,8 @@ function validateKitDirectory(kitPath) {
13018
14030
  } else {
13019
14031
  for (const assetPath of frozenAssets) {
13020
14032
  if (typeof assetPath !== "string") continue;
13021
- const fullPath = path19.resolve(kitPath, assetPath);
13022
- if (!fs14.existsSync(fullPath)) {
14033
+ const fullPath = path23.resolve(kitPath, assetPath);
14034
+ if (!fs17.existsSync(fullPath)) {
13023
14035
  errors.push({ field: "frozenAssetPaths", message: `Frozen asset not found: ${assetPath}` });
13024
14036
  }
13025
14037
  }
@@ -13037,8 +14049,8 @@ function validateKitDirectory(kitPath) {
13037
14049
  } else {
13038
14050
  for (const reqPath of requiredPaths) {
13039
14051
  if (typeof reqPath !== "string") continue;
13040
- const fullPath = path19.resolve(kitPath, reqPath);
13041
- if (!fs14.existsSync(fullPath)) {
14052
+ const fullPath = path23.resolve(kitPath, reqPath);
14053
+ if (!fs17.existsSync(fullPath)) {
13042
14054
  errors.push({ field: "outputStandard.requiredPaths", message: `Required output path not found: ${reqPath}` });
13043
14055
  }
13044
14056
  }
@@ -13054,13 +14066,13 @@ function validateKitDirectory(kitPath) {
13054
14066
  errors.push({ field: "bundles[].path", message: "Bundle ref missing 'path' field" });
13055
14067
  continue;
13056
14068
  }
13057
- const bundlePath = path19.resolve(kitPath, ref.path);
13058
- if (!fs14.existsSync(bundlePath)) {
14069
+ const bundlePath = path23.resolve(kitPath, ref.path);
14070
+ if (!fs17.existsSync(bundlePath)) {
13059
14071
  errors.push({ field: "bundles[].path", message: `Bundle manifest not found: ${ref.path}` });
13060
14072
  continue;
13061
14073
  }
13062
14074
  try {
13063
- const bundleRaw = JSON.parse(fs14.readFileSync(bundlePath, "utf8"));
14075
+ const bundleRaw = JSON.parse(fs17.readFileSync(bundlePath, "utf8"));
13064
14076
  const bundleBlock = bundleRaw.bundle;
13065
14077
  if (!bundleBlock || typeof bundleBlock !== "object") {
13066
14078
  errors.push({ field: `bundle(${ref.id})`, message: "Bundle manifest missing 'bundle' block" });
@@ -13120,7 +14132,7 @@ Available: ${available}`
13120
14132
  );
13121
14133
  }
13122
14134
  const catalogEntry = BUNDLED_KIT_CATALOG.find((e) => e.id === resolvedId);
13123
- const assetRoot = path19.resolve(resolveBundledKitAssetsRoot(), catalogEntry.packageDirName);
14135
+ const assetRoot = path23.resolve(resolveBundledKitAssetsRoot(), catalogEntry.packageDirName);
13124
14136
  return loadResolvedBundledKit(assetRoot, catalogEntry);
13125
14137
  }
13126
14138
  function toListItem(resolved) {
@@ -13140,8 +14152,8 @@ function toListItem(resolved) {
13140
14152
  }
13141
14153
  function resolveOutputPaths(resolved, outDir) {
13142
14154
  const outputRoot = resolveRequestedOutputRoot(outDir);
13143
- const folderPath = path19.resolve(outputRoot, resolved.bundleManifest.export.folderName);
13144
- const zipPath = path19.resolve(outputRoot, resolved.bundleManifest.export.zipFileName);
14155
+ const folderPath = path23.resolve(outputRoot, resolved.bundleManifest.export.folderName);
14156
+ const zipPath = path23.resolve(outputRoot, resolved.bundleManifest.export.zipFileName);
13145
14157
  return { outputRoot, folderPath, zipPath };
13146
14158
  }
13147
14159
  function listBundledKits() {
@@ -13257,7 +14269,7 @@ function reportProgress(onProgress, progress) {
13257
14269
  function copyDirectoryWithProgress(sourceRoot, targetRoot, onProgress) {
13258
14270
  const files = listRelativeFiles(sourceRoot);
13259
14271
  const total = Math.max(files.length, 1);
13260
- fs14.mkdirSync(targetRoot, { recursive: true });
14272
+ fs17.mkdirSync(targetRoot, { recursive: true });
13261
14273
  reportProgress(onProgress, {
13262
14274
  phase: "copying",
13263
14275
  completed: 0,
@@ -13266,10 +14278,10 @@ function copyDirectoryWithProgress(sourceRoot, targetRoot, onProgress) {
13266
14278
  detail: "Preparing files"
13267
14279
  });
13268
14280
  files.forEach((relativePath, index51) => {
13269
- const sourcePath = path19.resolve(sourceRoot, relativePath);
13270
- const targetPath = path19.resolve(targetRoot, relativePath);
13271
- fs14.mkdirSync(path19.dirname(targetPath), { recursive: true });
13272
- fs14.copyFileSync(sourcePath, targetPath);
14281
+ const sourcePath = path23.resolve(sourceRoot, relativePath);
14282
+ const targetPath = path23.resolve(targetRoot, relativePath);
14283
+ fs17.mkdirSync(path23.dirname(targetPath), { recursive: true });
14284
+ fs17.copyFileSync(sourcePath, targetPath);
13273
14285
  const completed = index51 + 1;
13274
14286
  const percent = 10 + Math.round(completed / total * 55);
13275
14287
  reportProgress(onProgress, {
@@ -13295,8 +14307,8 @@ function buildZipEntriesWithProgress(sourceRoot, exportFolderName, onProgress) {
13295
14307
  detail: relativePath
13296
14308
  });
13297
14309
  return {
13298
- name: path19.posix.join(exportFolderName, relativePath),
13299
- data: fs14.readFileSync(path19.resolve(sourceRoot, relativePath))
14310
+ name: path23.posix.join(exportFolderName, relativePath),
14311
+ data: fs17.readFileSync(path23.resolve(sourceRoot, relativePath))
13300
14312
  };
13301
14313
  });
13302
14314
  }
@@ -13311,8 +14323,8 @@ function downloadBundledKit(kitId, outDir, options = {}) {
13311
14323
  percent: 0,
13312
14324
  detail: "Resolving export target"
13313
14325
  });
13314
- fs14.mkdirSync(outputPaths.outputRoot, { recursive: true });
13315
- fs14.rmSync(outputPaths.folderPath, { recursive: true, force: true });
14326
+ fs17.mkdirSync(outputPaths.outputRoot, { recursive: true });
14327
+ fs17.rmSync(outputPaths.folderPath, { recursive: true, force: true });
13316
14328
  copyDirectoryWithProgress(resolved.assetRoot, outputPaths.folderPath, onProgress);
13317
14329
  const zipBuffer = buildStoredZip(
13318
14330
  buildZipEntriesWithProgress(outputPaths.folderPath, resolved.bundleManifest.export.folderName, onProgress)
@@ -13322,9 +14334,9 @@ function downloadBundledKit(kitId, outDir, options = {}) {
13322
14334
  completed: 1,
13323
14335
  total: 1,
13324
14336
  percent: 98,
13325
- detail: path19.basename(outputPaths.zipPath)
14337
+ detail: path23.basename(outputPaths.zipPath)
13326
14338
  });
13327
- fs14.writeFileSync(outputPaths.zipPath, zipBuffer);
14339
+ fs17.writeFileSync(outputPaths.zipPath, zipBuffer);
13328
14340
  reportProgress(onProgress, {
13329
14341
  phase: "done",
13330
14342
  completed: 1,
@@ -13341,9 +14353,9 @@ function downloadBundledKit(kitId, outDir, options = {}) {
13341
14353
  // src/commands/kit.ts
13342
14354
  init_banner();
13343
14355
  var TYPE_CONFIG = {
13344
- studio: { color: pc23.cyan, emoji: "\u{1F6E0}\uFE0F", label: "Custom Workspaces" },
13345
- specialized_agents: { color: pc23.magenta, emoji: "\u{1F9E0}", label: "Specialized Agents" },
13346
- ops: { color: pc23.yellow, emoji: "\u2699\uFE0F ", label: "Ops" }
14356
+ studio: { color: pc25.cyan, emoji: "\u{1F6E0}\uFE0F", label: "Custom Workspaces" },
14357
+ specialized_agents: { color: pc25.magenta, emoji: "\u{1F9E0}", label: "Specialized Agents" },
14358
+ ops: { color: pc25.yellow, emoji: "\u2699\uFE0F ", label: "Ops" }
13347
14359
  };
13348
14360
  function displayTypeForFamily(family) {
13349
14361
  if (family === "workflow" || family === "operator") return "specialized_agents";
@@ -13368,16 +14380,16 @@ function displayKitName(name) {
13368
14380
  return name.replace(/^Growthub Agent Worker Kit\s+[—-]\s+/u, "").trim();
13369
14381
  }
13370
14382
  function hr(width = 72) {
13371
- return pc23.dim("\u2500".repeat(width));
14383
+ return pc25.dim("\u2500".repeat(width));
13372
14384
  }
13373
14385
  function box(lines) {
13374
14386
  const padded = lines.map((l) => " " + l);
13375
14387
  const width = Math.max(...padded.map((l) => stripAnsi(l).length)) + 4;
13376
- const top = pc23.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
13377
- const bottom = pc23.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
14388
+ const top = pc25.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
14389
+ const bottom = pc25.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
13378
14390
  const body = padded.map((l) => {
13379
14391
  const pad = width - stripAnsi(l).length;
13380
- return pc23.dim("\u2502") + l + " ".repeat(pad) + pc23.dim("\u2502");
14392
+ return pc25.dim("\u2502") + l + " ".repeat(pad) + pc25.dim("\u2502");
13381
14393
  });
13382
14394
  return [top, ...body, bottom].join("\n");
13383
14395
  }
@@ -13398,7 +14410,7 @@ function renderProgressBar(progress) {
13398
14410
  const filled = Math.max(0, Math.min(width, Math.round(progress.percent / 100 * width)));
13399
14411
  const bar = `${"=".repeat(filled)}${"-".repeat(width - filled)}`;
13400
14412
  const detail = truncate(progress.detail, 48);
13401
- const line = `\r${pc23.cyan("Exporting kit")} ${pc23.dim("[")}${pc23.green(bar)}${pc23.dim("]")} ${String(progress.percent).padStart(3)}% ${pc23.dim(detail)}`;
14413
+ const line = `\r${pc25.cyan("Exporting kit")} ${pc25.dim("[")}${pc25.green(bar)}${pc25.dim("]")} ${String(progress.percent).padStart(3)}% ${pc25.dim(detail)}`;
13402
14414
  process.stdout.write(line);
13403
14415
  if (progress.phase === "done") {
13404
14416
  process.stdout.write("\n");
@@ -13408,12 +14420,12 @@ function printKitCard(item) {
13408
14420
  const badge2 = typeBadge(item.family);
13409
14421
  console.log("");
13410
14422
  console.log(box([
13411
- `${pc23.bold(item.name)} ${pc23.dim("v" + item.version)}`,
13412
- `${badge2} ${pc23.dim(item.id)}`,
14423
+ `${pc25.bold(item.name)} ${pc25.dim("v" + item.version)}`,
14424
+ `${badge2} ${pc25.dim(item.id)}`,
13413
14425
  "",
13414
14426
  truncate(item.description, 62),
13415
14427
  "",
13416
- `${pc23.dim("Brief:")} ${pc23.dim(item.briefType)} ${pc23.dim("Mode:")} ${pc23.dim(item.executionMode)}`
14428
+ `${pc25.dim("Brief:")} ${pc25.dim(item.briefType)} ${pc25.dim("Mode:")} ${pc25.dim(item.executionMode)}`
13417
14429
  ]));
13418
14430
  }
13419
14431
  function getActionLabel(action) {
@@ -13427,20 +14439,20 @@ async function confirmKitActions(input) {
13427
14439
  return getActionLabel(action);
13428
14440
  });
13429
14441
  const summaryLines = [
13430
- pc23.bold("Selected kits"),
14442
+ pc25.bold("Selected kits"),
13431
14443
  ...input.kits.map((kit) => `${typeBadge(kit.family)} ${displayKitName(kit.name)}`),
13432
14444
  "",
13433
- pc23.bold("Selected actions"),
14445
+ pc25.bold("Selected actions"),
13434
14446
  actionLabels.join(", ")
13435
14447
  ];
13436
14448
  console.log("");
13437
14449
  console.log(box(summaryLines));
13438
- const confirmed = await p16.confirm({
14450
+ const confirmed = await p17.confirm({
13439
14451
  message: "Continue with these worker kit actions?",
13440
14452
  initialValue: false
13441
14453
  });
13442
- if (p16.isCancel(confirmed)) {
13443
- p16.cancel("Cancelled.");
14454
+ if (p17.isCancel(confirmed)) {
14455
+ p17.cancel("Cancelled.");
13444
14456
  process.exit(0);
13445
14457
  }
13446
14458
  return Boolean(confirmed);
@@ -13455,39 +14467,39 @@ function printGroupedList(kits) {
13455
14467
  const totalTypes = types.length;
13456
14468
  console.log("");
13457
14469
  console.log(
13458
- pc23.bold("Growthub Agent Worker Kits") + pc23.dim(` ${kits.length} kit${kits.length !== 1 ? "s" : ""} \xB7 ${totalTypes} type${totalTypes !== 1 ? "s" : ""}`)
14470
+ pc25.bold("Growthub Agent Worker Kits") + pc25.dim(` ${kits.length} kit${kits.length !== 1 ? "s" : ""} \xB7 ${totalTypes} type${totalTypes !== 1 ? "s" : ""}`)
13459
14471
  );
13460
14472
  console.log(hr());
13461
14473
  for (const type of types) {
13462
14474
  const groupKits = byType[type];
13463
14475
  const header = typeBadge(type);
13464
14476
  console.log(`
13465
- ${header} ${pc23.dim("(" + groupKits.length + ")")}`);
14477
+ ${header} ${pc25.dim("(" + groupKits.length + ")")}`);
13466
14478
  for (const kit of groupKits) {
13467
- console.log(` ${typeColor(kit.family, pc23.bold(kit.id))} ${pc23.dim("v" + kit.version)}`);
13468
- console.log(` ${pc23.dim(truncate(kit.description, 62))}`);
13469
- console.log(` ${pc23.dim("\u2192")} ${pc23.cyan("growthub kit download " + kit.id)}`);
14479
+ console.log(` ${typeColor(kit.family, pc25.bold(kit.id))} ${pc25.dim("v" + kit.version)}`);
14480
+ console.log(` ${pc25.dim(truncate(kit.description, 62))}`);
14481
+ console.log(` ${pc25.dim("\u2192")} ${pc25.cyan("growthub kit download " + kit.id)}`);
13470
14482
  console.log("");
13471
14483
  }
13472
14484
  }
13473
14485
  console.log(hr());
13474
- console.log(pc23.dim(" growthub kit download <id> \xB7 growthub kit inspect <id> \xB7 growthub kit families"));
14486
+ console.log(pc25.dim(" growthub kit download <id> \xB7 growthub kit inspect <id> \xB7 growthub kit families"));
13475
14487
  console.log("");
13476
14488
  }
13477
14489
  async function runInteractivePicker(opts) {
13478
14490
  printPaperclipCliBanner();
13479
- p16.intro(pc23.bold("Growthub Agent Worker Kits"));
14491
+ p17.intro(pc25.bold("Growthub Agent Worker Kits"));
13480
14492
  let kits;
13481
14493
  try {
13482
14494
  kits = listBundledKits();
13483
14495
  } catch (err) {
13484
- p16.log.error("Failed to load kits: " + err.message);
14496
+ p17.log.error("Failed to load kits: " + err.message);
13485
14497
  process.exit(1);
13486
14498
  }
13487
14499
  const familiesAvailable = [...new Set(kits.map((k) => k.family))].sort();
13488
14500
  const typeOptions = Array.from(new Set(familiesAvailable.map((family) => displayTypeForFamily(family))));
13489
14501
  while (true) {
13490
- const typeChoice = await p16.select({
14502
+ const typeChoice = await p17.select({
13491
14503
  message: "Filter by type",
13492
14504
  options: [
13493
14505
  { value: "all", label: "All Types" },
@@ -13501,54 +14513,54 @@ async function runInteractivePicker(opts) {
13501
14513
  ...opts.allowBackToHub ? [{ value: "__back_to_hub", label: "\u2190 Back to main menu" }] : []
13502
14514
  ]
13503
14515
  });
13504
- if (p16.isCancel(typeChoice)) {
13505
- p16.cancel("Cancelled.");
14516
+ if (p17.isCancel(typeChoice)) {
14517
+ p17.cancel("Cancelled.");
13506
14518
  process.exit(0);
13507
14519
  }
13508
14520
  if (typeChoice === "__back_to_hub") return "back";
13509
14521
  const filtered = typeChoice === "all" ? kits : kits.filter((k) => displayTypeForFamily(k.family) === typeChoice);
13510
14522
  const showTypeBadgeInKitChoices = typeChoice === "all";
13511
14523
  if (filtered.length === 0) {
13512
- p16.note("No kits are available for that type yet.", "Nothing found");
14524
+ p17.note("No kits are available for that type yet.", "Nothing found");
13513
14525
  continue;
13514
14526
  }
13515
14527
  while (true) {
13516
- const kitChoice = await p16.select({
14528
+ const kitChoice = await p17.select({
13517
14529
  message: "Select kit",
13518
14530
  options: [
13519
14531
  ...filtered.map((k) => ({
13520
14532
  value: k.id,
13521
- label: (showTypeBadgeInKitChoices ? typeBadge(k.family) + " " : "") + pc23.bold(displayKitName(k.name)) + " " + pc23.dim("v" + k.version),
14533
+ label: (showTypeBadgeInKitChoices ? typeBadge(k.family) + " " : "") + pc25.bold(displayKitName(k.name)) + " " + pc25.dim("v" + k.version),
13522
14534
  hint: truncate(k.description, 55)
13523
14535
  })),
13524
14536
  { value: "__back_to_type", label: "\u2190 Back to type filter" }
13525
14537
  ]
13526
14538
  });
13527
- if (p16.isCancel(kitChoice)) {
13528
- p16.cancel("Cancelled.");
14539
+ if (p17.isCancel(kitChoice)) {
14540
+ p17.cancel("Cancelled.");
13529
14541
  process.exit(0);
13530
14542
  }
13531
14543
  if (kitChoice === "__back_to_type") break;
13532
14544
  const selected = filtered.find((kit) => kit.id === kitChoice);
13533
14545
  if (!selected) {
13534
- p16.cancel("Selected kit was not found.");
14546
+ p17.cancel("Selected kit was not found.");
13535
14547
  process.exit(1);
13536
14548
  }
13537
14549
  printKitCard(selected);
13538
- const nextStep = await p16.select({
14550
+ const nextStep = await p17.select({
13539
14551
  message: "Next step",
13540
14552
  options: [
13541
14553
  { value: "actions", label: "Choose action(s)" },
13542
14554
  { value: "back_to_kits", label: "\u2190 Back to kit list" }
13543
14555
  ]
13544
14556
  });
13545
- if (p16.isCancel(nextStep)) {
13546
- p16.cancel("Cancelled.");
14557
+ if (p17.isCancel(nextStep)) {
14558
+ p17.cancel("Cancelled.");
13547
14559
  process.exit(0);
13548
14560
  }
13549
14561
  if (nextStep === "back_to_kits") continue;
13550
14562
  while (true) {
13551
- const action = await p16.select({
14563
+ const action = await p17.select({
13552
14564
  message: "What would you like to do?",
13553
14565
  options: [
13554
14566
  { value: "download", label: "\u2B07\uFE0F Download kit", hint: "growthub kit download <id>" },
@@ -13557,8 +14569,8 @@ async function runInteractivePicker(opts) {
13557
14569
  { value: "back_to_kits", label: "\u2190 Back to kit list" }
13558
14570
  ]
13559
14571
  });
13560
- if (p16.isCancel(action)) {
13561
- p16.cancel("Cancelled.");
14572
+ if (p17.isCancel(action)) {
14573
+ p17.cancel("Cancelled.");
13562
14574
  process.exit(0);
13563
14575
  }
13564
14576
  if (action === "back_to_kits") break;
@@ -13567,15 +14579,15 @@ async function runInteractivePicker(opts) {
13567
14579
  actions: [action]
13568
14580
  });
13569
14581
  if (!confirmed) {
13570
- const reviewChoice = await p16.select({
14582
+ const reviewChoice = await p17.select({
13571
14583
  message: "Review selection",
13572
14584
  options: [
13573
14585
  { value: "actions", label: `Choose ${getActionLabel(action)} again` },
13574
14586
  { value: "back_to_kits", label: "\u2190 Back to kit list" }
13575
14587
  ]
13576
14588
  });
13577
- if (p16.isCancel(reviewChoice)) {
13578
- p16.cancel("Cancelled.");
14589
+ if (p17.isCancel(reviewChoice)) {
14590
+ p17.cancel("Cancelled.");
13579
14591
  process.exit(0);
13580
14592
  }
13581
14593
  if (reviewChoice === "back_to_kits") break;
@@ -13583,16 +14595,16 @@ async function runInteractivePicker(opts) {
13583
14595
  }
13584
14596
  if (action === "copy-id") {
13585
14597
  console.log(selected.id);
13586
- p16.outro(pc23.dim("Kit ID printed above."));
14598
+ p17.outro(pc25.dim("Kit ID printed above."));
13587
14599
  return "done";
13588
14600
  }
13589
14601
  if (action === "inspect") {
13590
14602
  runInspect(selected.id, opts.out);
13591
- p16.outro(pc23.dim("Done."));
14603
+ p17.outro(pc25.dim("Done."));
13592
14604
  return "done";
13593
14605
  }
13594
14606
  await runDownload(selected.id, opts);
13595
- p16.outro(pc23.green("Kit exported successfully."));
14607
+ p17.outro(pc25.green("Kit exported successfully."));
13596
14608
  return "done";
13597
14609
  }
13598
14610
  }
@@ -13601,19 +14613,19 @@ async function runInteractivePicker(opts) {
13601
14613
  async function runDownload(kitId, opts) {
13602
14614
  const resolvedId = fuzzyResolveKitId(kitId);
13603
14615
  if (!resolvedId) {
13604
- console.error(pc23.red("Unknown kit '" + kitId + "'.") + pc23.dim(" Run `growthub kit list` to browse."));
14616
+ console.error(pc25.red("Unknown kit '" + kitId + "'.") + pc25.dim(" Run `growthub kit list` to browse."));
13605
14617
  process.exit(1);
13606
14618
  }
13607
14619
  if (resolvedId !== kitId) {
13608
- console.log(pc23.dim("Resolved '" + kitId + "' \u2192 " + resolvedId));
14620
+ console.log(pc25.dim("Resolved '" + kitId + "' \u2192 " + resolvedId));
13609
14621
  }
13610
14622
  const kits = listBundledKits();
13611
14623
  const item = kits.find((k) => k.id === resolvedId);
13612
14624
  printKitCard(item);
13613
14625
  if (!opts.yes) {
13614
- const confirmed = await p16.confirm({ message: "Download " + pc23.bold(displayKitName(item.name)) + "?" });
13615
- if (p16.isCancel(confirmed) || !confirmed) {
13616
- p16.cancel("Cancelled.");
14626
+ const confirmed = await p17.confirm({ message: "Download " + pc25.bold(displayKitName(item.name)) + "?" });
14627
+ if (p17.isCancel(confirmed) || !confirmed) {
14628
+ p17.cancel("Cancelled.");
13617
14629
  process.exit(0);
13618
14630
  }
13619
14631
  }
@@ -13621,35 +14633,35 @@ async function runDownload(kitId, opts) {
13621
14633
  onProgress: renderProgressBar
13622
14634
  });
13623
14635
  console.log("");
13624
- console.log(pc23.green(pc23.bold("Kit exported successfully.")));
14636
+ console.log(pc25.green(pc25.bold("Kit exported successfully.")));
13625
14637
  console.log("");
13626
14638
  const nextSteps = [
13627
- pc23.bold("Next steps"),
14639
+ pc25.bold("Next steps"),
13628
14640
  "",
13629
- pc23.dim("1.") + " Point Working Directory at:",
13630
- " " + pc23.cyan(result.folderPath),
14641
+ pc25.dim("1.") + " Point Working Directory at:",
14642
+ " " + pc25.cyan(result.folderPath),
13631
14643
  "",
13632
- pc23.dim("2.") + " " + pc23.cyan("cp .env.example .env") + " \u2192 add your API key",
13633
- pc23.dim("3.") + " " + pc23.cyan("bash setup/clone-fork.sh") + " \u2192 boot local studio",
13634
- pc23.dim("4.") + " Open Growthub local \u2014 the agent loads automatically",
14644
+ pc25.dim("2.") + " " + pc25.cyan("cp .env.example .env") + " \u2192 add your API key",
14645
+ pc25.dim("3.") + " " + pc25.cyan("bash setup/clone-fork.sh") + " \u2192 boot local studio",
14646
+ pc25.dim("4.") + " Open Growthub local \u2014 the agent loads automatically",
13635
14647
  "",
13636
- pc23.dim("Docs: QUICKSTART.md \xB7 validation-checklist.md")
14648
+ pc25.dim("Docs: QUICKSTART.md \xB7 validation-checklist.md")
13637
14649
  ];
13638
14650
  console.log("");
13639
14651
  console.log(box(nextSteps));
13640
14652
  console.log("");
13641
- console.log(pc23.bold("Open folder: ") + folderOpenLabel(result.folderPath));
13642
- console.log(pc23.dim("Folder: ") + result.folderPath);
14653
+ console.log(pc25.bold("Open folder: ") + folderOpenLabel(result.folderPath));
14654
+ console.log(pc25.dim("Folder: ") + result.folderPath);
13643
14655
  console.log("");
13644
- console.log(pc23.dim("Zip: ") + result.zipPath);
14656
+ console.log(pc25.dim("Zip: ") + result.zipPath);
13645
14657
  console.log("");
13646
14658
  }
13647
14659
  function runInspect(kitId, outDir) {
13648
14660
  const info = inspectBundledKit(kitId, outDir);
13649
- const kv = (label, value) => console.log(" " + pc23.bold(label.padEnd(24)) + " " + value);
14661
+ const kv = (label, value) => console.log(" " + pc25.bold(label.padEnd(24)) + " " + value);
13650
14662
  console.log("");
13651
- console.log(pc23.bold("Kit: " + info.id) + pc23.dim(" v" + info.version));
13652
- console.log(typeBadge(info.family) + pc23.dim(" schema v" + info.schemaVersion));
14663
+ console.log(pc25.bold("Kit: " + info.id) + pc25.dim(" v" + info.version));
14664
+ console.log(typeBadge(info.family) + pc25.dim(" schema v" + info.schemaVersion));
13653
14665
  console.log(hr());
13654
14666
  kv("Name:", info.name);
13655
14667
  kv("Description:", truncate(info.description, 55));
@@ -13665,8 +14677,8 @@ function runInspect(kitId, outDir) {
13665
14677
  kv("Compatibility:", JSON.stringify(info.compatibility));
13666
14678
  }
13667
14679
  console.log(hr());
13668
- console.log(pc23.bold(" Required Paths:"));
13669
- for (const rp of info.requiredPaths) console.log(" " + pc23.dim("\xB7") + " " + rp);
14680
+ console.log(pc25.bold(" Required Paths:"));
14681
+ for (const rp of info.requiredPaths) console.log(" " + pc25.dim("\xB7") + " " + rp);
13670
14682
  console.log("");
13671
14683
  }
13672
14684
  function registerKitCommands(program2) {
@@ -13696,8 +14708,8 @@ Examples:
13696
14708
  const wanted = opts.family.split(",").map((f) => f.trim().toLowerCase());
13697
14709
  kits = kits.filter((k) => wanted.includes(k.family));
13698
14710
  if (kits.length === 0) {
13699
- console.error(pc23.yellow("No kits found for family: " + opts.family));
13700
- console.error(pc23.dim("Valid families: studio, workflow, operator, ops"));
14711
+ console.error(pc25.yellow("No kits found for family: " + opts.family));
14712
+ console.error(pc25.dim("Valid families: studio, workflow, operator, ops"));
13701
14713
  process.exitCode = 1;
13702
14714
  return;
13703
14715
  }
@@ -13715,7 +14727,7 @@ Examples:
13715
14727
  `).action((kitId, opts) => {
13716
14728
  const resolvedId = fuzzyResolveKitId(kitId);
13717
14729
  if (!resolvedId) {
13718
- console.error(pc23.red("Unknown kit '" + kitId + "'.") + pc23.dim(" Run `growthub kit list` to browse."));
14730
+ console.error(pc25.red("Unknown kit '" + kitId + "'.") + pc25.dim(" Run `growthub kit list` to browse."));
13719
14731
  process.exitCode = 1;
13720
14732
  return;
13721
14733
  }
@@ -13739,7 +14751,7 @@ Examples:
13739
14751
  }
13740
14752
  const resolvedId = fuzzyResolveKitId(kitId);
13741
14753
  if (!resolvedId) {
13742
- console.error(pc23.red("Unknown kit '" + kitId + "'.") + pc23.dim(" Run `growthub kit list` to browse."));
14754
+ console.error(pc25.red("Unknown kit '" + kitId + "'.") + pc25.dim(" Run `growthub kit list` to browse."));
13743
14755
  process.exitCode = 1;
13744
14756
  return;
13745
14757
  }
@@ -13748,14 +14760,14 @@ Examples:
13748
14760
  onProgress: renderProgressBar
13749
14761
  });
13750
14762
  console.log("");
13751
- console.log(pc23.bold("Exported folder:"), pc23.cyan(result.folderPath));
13752
- console.log(pc23.bold("Open folder: "), folderOpenLabel(result.folderPath));
13753
- console.log(pc23.bold("Zip: "), pc23.dim(result.zipPath));
14763
+ console.log(pc25.bold("Exported folder:"), pc25.cyan(result.folderPath));
14764
+ console.log(pc25.bold("Open folder: "), folderOpenLabel(result.folderPath));
14765
+ console.log(pc25.bold("Zip: "), pc25.dim(result.zipPath));
13754
14766
  console.log("");
13755
- console.log(pc23.bold("Next steps:"));
13756
- console.log(" 1. Point Working Directory at: " + pc23.cyan(result.folderPath));
13757
- console.log(" 2. " + pc23.cyan("cp .env.example .env") + " \u2192 add your API key");
13758
- console.log(" 3. " + pc23.cyan("bash setup/clone-fork.sh") + " \u2192 boot local studio");
14767
+ console.log(pc25.bold("Next steps:"));
14768
+ console.log(" 1. Point Working Directory at: " + pc25.cyan(result.folderPath));
14769
+ console.log(" 2. " + pc25.cyan("cp .env.example .env") + " \u2192 add your API key");
14770
+ console.log(" 3. " + pc25.cyan("bash setup/clone-fork.sh") + " \u2192 boot local studio");
13759
14771
  console.log(" 4. Open Growthub local \u2014 the agent loads automatically");
13760
14772
  console.log("");
13761
14773
  return;
@@ -13765,7 +14777,7 @@ Examples:
13765
14777
  kit.command("path").description("Resolve the expected export folder path without exporting").argument("<kit-id>", "Kit id or fuzzy slug").option("--out <path>", "Override the export root").action((kitId, opts) => {
13766
14778
  const resolvedId = fuzzyResolveKitId(kitId);
13767
14779
  if (!resolvedId) {
13768
- console.error(pc23.red("Unknown kit '" + kitId + "'."));
14780
+ console.error(pc25.red("Unknown kit '" + kitId + "'."));
13769
14781
  process.exitCode = 1;
13770
14782
  return;
13771
14783
  }
@@ -13776,23 +14788,23 @@ Examples:
13776
14788
  $ growthub kit validate ./my-kit
13777
14789
  $ growthub kit validate ~/kits/growthub-open-higgsfield-studio-v1
13778
14790
  `).action((kitPath) => {
13779
- const resolvedPath = path20.resolve(kitPath);
14791
+ const resolvedPath = path24.resolve(kitPath);
13780
14792
  const result = validateKitDirectory(resolvedPath);
13781
14793
  console.log("");
13782
- console.log(pc23.bold("Kit: " + result.kitId) + pc23.dim(" schema v" + result.schemaVersion));
14794
+ console.log(pc25.bold("Kit: " + result.kitId) + pc25.dim(" schema v" + result.schemaVersion));
13783
14795
  console.log(hr());
13784
14796
  for (const w of result.warnings) {
13785
- console.log(pc23.yellow(" WARN " + w.field + ": " + w.message));
14797
+ console.log(pc25.yellow(" WARN " + w.field + ": " + w.message));
13786
14798
  }
13787
14799
  for (const e of result.errors) {
13788
- console.log(pc23.red(" ERROR " + e.field + ": " + e.message));
14800
+ console.log(pc25.red(" ERROR " + e.field + ": " + e.message));
13789
14801
  }
13790
14802
  if (result.errors.length > 0) {
13791
14803
  console.log("");
13792
- console.log(pc23.red(pc23.bold(" Result: INVALID")) + pc23.dim(" (" + result.errors.length + " error" + (result.errors.length !== 1 ? "s" : "") + ")"));
14804
+ console.log(pc25.red(pc25.bold(" Result: INVALID")) + pc25.dim(" (" + result.errors.length + " error" + (result.errors.length !== 1 ? "s" : "") + ")"));
13793
14805
  process.exitCode = 1;
13794
14806
  } else {
13795
- console.log(pc23.green(pc23.bold(" Result: VALID")));
14807
+ console.log(pc25.green(pc25.bold(" Result: VALID")));
13796
14808
  }
13797
14809
  console.log("");
13798
14810
  });
@@ -13804,29 +14816,29 @@ Examples:
13804
14816
  { family: "ops", tagline: "Infrastructure / toolchain operator (provider optional)", surfaces: "local-fork (primary)", example: "(coming soon)" }
13805
14817
  ];
13806
14818
  console.log("");
13807
- console.log(pc23.bold("Kit Family Taxonomy"));
14819
+ console.log(pc25.bold("Kit Family Taxonomy"));
13808
14820
  console.log(hr());
13809
14821
  for (const def of defs) {
13810
14822
  console.log("\n " + typeBadge(def.family));
13811
- console.log(" " + pc23.dim(def.tagline));
13812
- console.log(" " + pc23.dim("Surfaces: ") + pc23.dim(def.surfaces));
13813
- console.log(" " + pc23.dim("Example: ") + pc23.cyan(def.example));
14823
+ console.log(" " + pc25.dim(def.tagline));
14824
+ console.log(" " + pc25.dim("Surfaces: ") + pc25.dim(def.surfaces));
14825
+ console.log(" " + pc25.dim("Example: ") + pc25.cyan(def.example));
13814
14826
  }
13815
14827
  console.log("");
13816
14828
  console.log(hr());
13817
- console.log(pc23.dim(" growthub kit list --family <family> to filter by internal family"));
14829
+ console.log(pc25.dim(" growthub kit list --family <family> to filter by internal family"));
13818
14830
  console.log("");
13819
14831
  });
13820
14832
  }
13821
14833
 
13822
14834
  // src/commands/template.ts
13823
- import path22 from "node:path";
13824
- import * as p17 from "@clack/prompts";
13825
- import pc24 from "picocolors";
14835
+ import path26 from "node:path";
14836
+ import * as p18 from "@clack/prompts";
14837
+ import pc26 from "picocolors";
13826
14838
 
13827
14839
  // src/templates/service.ts
13828
- import fs15 from "node:fs";
13829
- import path21 from "node:path";
14840
+ import fs18 from "node:fs";
14841
+ import path25 from "node:path";
13830
14842
  import { fileURLToPath as fileURLToPath5 } from "node:url";
13831
14843
 
13832
14844
  // src/templates/catalog.ts
@@ -14073,12 +15085,12 @@ var TEMPLATE_CATALOG = [
14073
15085
 
14074
15086
  // src/templates/service.ts
14075
15087
  function resolveSharedTemplatesRoot() {
14076
- const moduleDir = path21.dirname(fileURLToPath5(import.meta.url));
15088
+ const moduleDir = path25.dirname(fileURLToPath5(import.meta.url));
14077
15089
  for (const candidate of [
14078
- path21.resolve(moduleDir, "../../assets/shared-templates"),
14079
- path21.resolve(moduleDir, "../assets/shared-templates")
15090
+ path25.resolve(moduleDir, "../../assets/shared-templates"),
15091
+ path25.resolve(moduleDir, "../assets/shared-templates")
14080
15092
  ]) {
14081
- if (fs15.existsSync(candidate)) return candidate;
15093
+ if (fs18.existsSync(candidate)) return candidate;
14082
15094
  }
14083
15095
  throw new Error("Shared template assets not found at cli/assets/shared-templates/");
14084
15096
  }
@@ -14113,15 +15125,15 @@ function getArtifact(slugOrId) {
14113
15125
  const artifact = resolveSlug(slugOrId);
14114
15126
  if (!artifact) throw new Error(`Unknown template '${slugOrId}'. Run 'growthub template list' to browse.`);
14115
15127
  const root = resolveSharedTemplatesRoot();
14116
- const absolutePath = path21.resolve(root, artifact.path);
14117
- if (!fs15.existsSync(absolutePath)) throw new Error(`Template file missing: ${absolutePath}`);
14118
- return { artifact, content: fs15.readFileSync(absolutePath, "utf8"), absolutePath };
15128
+ const absolutePath = path25.resolve(root, artifact.path);
15129
+ if (!fs18.existsSync(absolutePath)) throw new Error(`Template file missing: ${absolutePath}`);
15130
+ return { artifact, content: fs18.readFileSync(absolutePath, "utf8"), absolutePath };
14119
15131
  }
14120
15132
  function copyArtifact(slugOrId, destDir) {
14121
15133
  const resolved = getArtifact(slugOrId);
14122
- fs15.mkdirSync(destDir, { recursive: true });
14123
- const destPath = path21.resolve(destDir, path21.basename(resolved.absolutePath));
14124
- fs15.copyFileSync(resolved.absolutePath, destPath);
15134
+ fs18.mkdirSync(destDir, { recursive: true });
15135
+ const destPath = path25.resolve(destDir, path25.basename(resolved.absolutePath));
15136
+ fs18.copyFileSync(resolved.absolutePath, destPath);
14125
15137
  return destPath;
14126
15138
  }
14127
15139
  var GROUP_ORDER = ["ad-formats", "scene-modules/hooks", "scene-modules/body", "scene-modules/cta"];
@@ -14171,7 +15183,7 @@ function stripAnsi2(s) {
14171
15183
  return s.replace(/\x1B\[[0-9;]*m/g, "");
14172
15184
  }
14173
15185
  function hr2(w = 72) {
14174
- return pc24.dim("\u2500".repeat(w));
15186
+ return pc26.dim("\u2500".repeat(w));
14175
15187
  }
14176
15188
  function truncate2(s, max) {
14177
15189
  return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
@@ -14179,32 +15191,32 @@ function truncate2(s, max) {
14179
15191
  function box2(lines) {
14180
15192
  const padded = lines.map((l) => " " + l);
14181
15193
  const width = Math.max(...padded.map((l) => stripAnsi2(l).length)) + 4;
14182
- const top = pc24.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
14183
- const bottom = pc24.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
14184
- const body = padded.map((l) => pc24.dim("\u2502") + l + " ".repeat(width - stripAnsi2(l).length) + pc24.dim("\u2502"));
15194
+ const top = pc26.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
15195
+ const bottom = pc26.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
15196
+ const body = padded.map((l) => pc26.dim("\u2502") + l + " ".repeat(width - stripAnsi2(l).length) + pc26.dim("\u2502"));
14185
15197
  return [top, ...body, bottom].join("\n");
14186
15198
  }
14187
15199
  function badge(a) {
14188
- if (a.type === "ad-format") return pc24.cyan("\u{1F3AC} Ad Format");
15200
+ if (a.type === "ad-format") return pc26.cyan("\u{1F3AC} Ad Format");
14189
15201
  if (a.type === "scene-module") {
14190
- if (a.subtype === "hook") return pc24.yellow("\u{1FA9D} Hook");
14191
- if (a.subtype === "body") return pc24.blue("\u{1F9E9} Body");
14192
- if (a.subtype === "cta") return pc24.green("\u{1F3AF} CTA");
15202
+ if (a.subtype === "hook") return pc26.yellow("\u{1FA9D} Hook");
15203
+ if (a.subtype === "body") return pc26.blue("\u{1F9E9} Body");
15204
+ if (a.subtype === "cta") return pc26.green("\u{1F3AF} CTA");
14193
15205
  }
14194
- return pc24.magenta("\u{1F9E9} Module");
15206
+ return pc26.magenta("\u{1F9E9} Module");
14195
15207
  }
14196
15208
  function printCard(a) {
14197
- const compat = a.compatibleFormats.length ? pc24.dim("Works with: ") + a.compatibleFormats.map((f) => pc24.cyan(f)).join(", ") : pc24.dim("Works with: any format");
15209
+ const compat = a.compatibleFormats.length ? pc26.dim("Works with: ") + a.compatibleFormats.map((f) => pc26.cyan(f)).join(", ") : pc26.dim("Works with: any format");
14198
15210
  const rows = [
14199
- pc24.bold(a.name),
14200
- `${badge(a)} ${pc24.dim(a.id)}`,
15211
+ pc26.bold(a.name),
15212
+ `${badge(a)} ${pc26.dim(a.id)}`,
14201
15213
  "",
14202
15214
  truncate2(a.category, 62),
14203
15215
  "",
14204
15216
  compat
14205
15217
  ];
14206
15218
  if (a.type === "ad-format" && a.scenes != null) {
14207
- rows.push(pc24.dim("Scenes: ") + a.scenes + (a.hookVariations ? pc24.dim(" \xB7 Hook variations: ") + a.hookVariations : ""));
15219
+ rows.push(pc26.dim("Scenes: ") + a.scenes + (a.hookVariations ? pc26.dim(" \xB7 Hook variations: ") + a.hookVariations : ""));
14208
15220
  }
14209
15221
  console.log("");
14210
15222
  console.log(box2(rows));
@@ -14212,32 +15224,32 @@ function printCard(a) {
14212
15224
  function printSummary2(filter) {
14213
15225
  const artifacts = listArtifacts(filter);
14214
15226
  if (!artifacts.length) {
14215
- console.log(pc24.yellow("No templates matched. Try: growthub template list"));
15227
+ console.log(pc26.yellow("No templates matched. Try: growthub template list"));
14216
15228
  return;
14217
15229
  }
14218
15230
  const stats = getCatalogStats();
14219
15231
  const groups = groupArtifacts(artifacts);
14220
15232
  console.log("");
14221
- console.log(pc24.bold("Growthub Shared Template Library") + pc24.dim(` ${artifacts.length} of ${stats.total} artifacts`));
14222
- console.log(pc24.dim(" " + Object.entries(stats.byFamily).map(([f, n]) => `${f} (${n})`).join(" \xB7 ")));
15233
+ console.log(pc26.bold("Growthub Shared Template Library") + pc26.dim(` ${artifacts.length} of ${stats.total} artifacts`));
15234
+ console.log(pc26.dim(" " + Object.entries(stats.byFamily).map(([f, n]) => `${f} (${n})`).join(" \xB7 ")));
14223
15235
  console.log(hr2());
14224
15236
  for (const g of groups) {
14225
15237
  console.log(`
14226
- ${pc24.bold(g.label)} ${pc24.dim("(" + g.count + ")")}`);
14227
- console.log(pc24.dim(" " + g.description));
15238
+ ${pc26.bold(g.label)} ${pc26.dim("(" + g.count + ")")}`);
15239
+ console.log(pc26.dim(" " + g.description));
14228
15240
  console.log("");
14229
15241
  for (const a of g.artifacts) {
14230
- const compat = a.compatibleFormats.length ? pc24.dim(" \xB7 " + a.compatibleFormats.join(", ")) : "";
14231
- console.log(` ${pc24.cyan(pc24.bold(a.name))}${compat}`);
14232
- console.log(` ${pc24.dim("growthub template get " + a.slug)}`);
15242
+ const compat = a.compatibleFormats.length ? pc26.dim(" \xB7 " + a.compatibleFormats.join(", ")) : "";
15243
+ console.log(` ${pc26.cyan(pc26.bold(a.name))}${compat}`);
15244
+ console.log(` ${pc26.dim("growthub template get " + a.slug)}`);
14233
15245
  console.log("");
14234
15246
  }
14235
15247
  }
14236
15248
  console.log(hr2());
14237
- console.log(pc24.dim(" growthub template get <slug>"));
14238
- console.log(pc24.dim(" growthub template list --type ad-formats"));
14239
- console.log(pc24.dim(" growthub template list --type scene-modules --subtype hooks"));
14240
- console.log(pc24.dim(" growthub template (interactive picker)"));
15249
+ console.log(pc26.dim(" growthub template get <slug>"));
15250
+ console.log(pc26.dim(" growthub template list --type ad-formats"));
15251
+ console.log(pc26.dim(" growthub template list --type scene-modules --subtype hooks"));
15252
+ console.log(pc26.dim(" growthub template (interactive picker)"));
14241
15253
  console.log("");
14242
15254
  }
14243
15255
  var TEMPLATE_FAMILY_META = {
@@ -14263,16 +15275,16 @@ var TEMPLATE_FAMILY_META = {
14263
15275
  }
14264
15276
  };
14265
15277
  async function runTemplatePicker(opts) {
14266
- p17.intro(pc24.bold("Growthub Shared Template Library"));
15278
+ p18.intro(pc26.bold("Growthub Shared Template Library"));
14267
15279
  let artifacts;
14268
15280
  try {
14269
15281
  artifacts = listArtifacts();
14270
15282
  } catch (err) {
14271
- p17.log.error(err.message);
15283
+ p18.log.error(err.message);
14272
15284
  process.exit(1);
14273
15285
  }
14274
15286
  const families = [...new Set(artifacts.map((artifact) => artifact.family))];
14275
- const familyChoice = await p17.select({
15287
+ const familyChoice = await p18.select({
14276
15288
  message: "What template type do you want to browse?",
14277
15289
  options: [
14278
15290
  ...families.map((family) => {
@@ -14291,14 +15303,14 @@ async function runTemplatePicker(opts) {
14291
15303
  ...opts?.allowBackToHub ? [{ value: "__back_to_hub", label: "\u2190 Back to main menu" }] : []
14292
15304
  ]
14293
15305
  });
14294
- if (p17.isCancel(familyChoice)) {
14295
- p17.cancel("Cancelled.");
15306
+ if (p18.isCancel(familyChoice)) {
15307
+ p18.cancel("Cancelled.");
14296
15308
  process.exit(0);
14297
15309
  }
14298
15310
  if (familyChoice === "__back_to_hub") return "back";
14299
15311
  const filteredArtifacts = artifacts.filter((artifact) => artifact.family === familyChoice);
14300
15312
  const groups = groupArtifacts(filteredArtifacts);
14301
- const groupChoice = await p17.select({
15313
+ const groupChoice = await p18.select({
14302
15314
  message: "What kind of template?",
14303
15315
  options: groups.map((g) => ({
14304
15316
  value: g.key,
@@ -14306,26 +15318,26 @@ async function runTemplatePicker(opts) {
14306
15318
  hint: `${g.count} available \xB7 ${g.description}`
14307
15319
  }))
14308
15320
  });
14309
- if (p17.isCancel(groupChoice)) {
14310
- p17.cancel("Cancelled.");
15321
+ if (p18.isCancel(groupChoice)) {
15322
+ p18.cancel("Cancelled.");
14311
15323
  process.exit(0);
14312
15324
  }
14313
15325
  const group = groups.find((g) => g.key === groupChoice);
14314
- const artifactChoice = await p17.select({
15326
+ const artifactChoice = await p18.select({
14315
15327
  message: `Select from: ${group.label}`,
14316
15328
  options: group.artifacts.map((a) => ({
14317
15329
  value: a.id,
14318
- label: pc24.bold(a.name),
15330
+ label: pc26.bold(a.name),
14319
15331
  hint: truncate2(a.category, 52)
14320
15332
  }))
14321
15333
  });
14322
- if (p17.isCancel(artifactChoice)) {
14323
- p17.cancel("Cancelled.");
15334
+ if (p18.isCancel(artifactChoice)) {
15335
+ p18.cancel("Cancelled.");
14324
15336
  process.exit(0);
14325
15337
  }
14326
15338
  const selected = filteredArtifacts.find((a) => a.id === artifactChoice);
14327
15339
  printCard(selected);
14328
- const action = await p17.select({
15340
+ const action = await p18.select({
14329
15341
  message: "What would you like to do?",
14330
15342
  options: [
14331
15343
  { value: "print", label: "\u{1F4C4} Print to terminal" },
@@ -14334,13 +15346,13 @@ async function runTemplatePicker(opts) {
14334
15346
  { value: "cancel", label: "Cancel" }
14335
15347
  ]
14336
15348
  });
14337
- if (p17.isCancel(action) || action === "cancel") {
14338
- p17.cancel("Cancelled.");
15349
+ if (p18.isCancel(action) || action === "cancel") {
15350
+ p18.cancel("Cancelled.");
14339
15351
  process.exit(0);
14340
15352
  }
14341
15353
  if (action === "slug") {
14342
15354
  console.log(selected.slug);
14343
- p17.outro(pc24.dim("Use with: growthub template get " + selected.slug));
15355
+ p18.outro(pc26.dim("Use with: growthub template get " + selected.slug));
14344
15356
  return "done";
14345
15357
  }
14346
15358
  if (action === "print") {
@@ -14348,22 +15360,22 @@ async function runTemplatePicker(opts) {
14348
15360
  console.log("\n" + hr2());
14349
15361
  console.log(r.content);
14350
15362
  console.log(hr2());
14351
- p17.outro(pc24.dim("Source: " + r.absolutePath));
15363
+ p18.outro(pc26.dim("Source: " + r.absolutePath));
14352
15364
  return "done";
14353
15365
  }
14354
15366
  if (action === "copy") {
14355
- const destInput = await p17.text({
15367
+ const destInput = await p18.text({
14356
15368
  message: "Output directory:",
14357
15369
  placeholder: "~/Downloads/templates",
14358
15370
  validate: (v) => !v?.trim() ? "Path is required" : void 0
14359
15371
  });
14360
- if (p17.isCancel(destInput)) {
14361
- p17.cancel("Cancelled.");
15372
+ if (p18.isCancel(destInput)) {
15373
+ p18.cancel("Cancelled.");
14362
15374
  process.exit(0);
14363
15375
  }
14364
- const destDir = path22.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
15376
+ const destDir = path26.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
14365
15377
  const destPath = copyArtifact(selected.id, destDir);
14366
- p17.outro(pc24.green("Copied \u2192 ") + destPath);
15378
+ p18.outro(pc26.green("Copied \u2192 ") + destPath);
14367
15379
  return "done";
14368
15380
  }
14369
15381
  return "done";
@@ -14390,7 +15402,7 @@ Any agent or kit resolves them by slug.
14390
15402
  if (opts.type) {
14391
15403
  const t = opts.type.replace(/s$/, "");
14392
15404
  if (t !== "ad-format" && t !== "scene-module") {
14393
- console.error(pc24.red(`Unknown --type '${opts.type}'.`) + pc24.dim(" Valid: ad-formats, scene-modules"));
15405
+ console.error(pc26.red(`Unknown --type '${opts.type}'.`) + pc26.dim(" Valid: ad-formats, scene-modules"));
14394
15406
  process.exitCode = 1;
14395
15407
  return;
14396
15408
  }
@@ -14399,7 +15411,7 @@ Any agent or kit resolves them by slug.
14399
15411
  if (opts.subtype) {
14400
15412
  const sub = opts.subtype.replace(/s$/, "");
14401
15413
  if (!["hook", "body", "cta"].includes(sub)) {
14402
- console.error(pc24.red(`Unknown --subtype '${opts.subtype}'.`) + pc24.dim(" Valid: hooks, body, cta"));
15414
+ console.error(pc26.red(`Unknown --subtype '${opts.subtype}'.`) + pc26.dim(" Valid: hooks, body, cta"));
14403
15415
  process.exitCode = 1;
14404
15416
  return;
14405
15417
  }
@@ -14415,18 +15427,18 @@ Any agent or kit resolves them by slug.
14415
15427
  cmd.command("get").description("Print or copy a template \u2014 fuzzy slug resolution").argument("<slug>", "Artifact slug (e.g. villain-animation, meme-overlay)").option("--out <path>", "Copy to this directory").option("--json", "Artifact metadata + content as JSON").action((slug, opts) => {
14416
15428
  const artifact = resolveSlug(slug);
14417
15429
  if (!artifact) {
14418
- console.error(pc24.red(`Unknown template '${slug}'.`) + pc24.dim(" Run `growthub template list` to browse."));
15430
+ console.error(pc26.red(`Unknown template '${slug}'.`) + pc26.dim(" Run `growthub template list` to browse."));
14419
15431
  process.exitCode = 1;
14420
15432
  return;
14421
15433
  }
14422
15434
  if (artifact.id !== slug && artifact.slug !== slug) {
14423
- console.error(pc24.dim(`Resolved '${slug}' \u2192 ${artifact.slug}`));
15435
+ console.error(pc26.dim(`Resolved '${slug}' \u2192 ${artifact.slug}`));
14424
15436
  }
14425
15437
  let resolved;
14426
15438
  try {
14427
15439
  resolved = getArtifact(artifact.id);
14428
15440
  } catch (err) {
14429
- console.error(pc24.red(err.message));
15441
+ console.error(pc26.red(err.message));
14430
15442
  process.exitCode = 1;
14431
15443
  return;
14432
15444
  }
@@ -14435,12 +15447,12 @@ Any agent or kit resolves them by slug.
14435
15447
  return;
14436
15448
  }
14437
15449
  if (opts.out) {
14438
- const destDir = path22.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
15450
+ const destDir = path26.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
14439
15451
  try {
14440
15452
  const dest = copyArtifact(artifact.id, destDir);
14441
- console.log(pc24.green("Copied \u2192 ") + dest);
15453
+ console.log(pc26.green("Copied \u2192 ") + dest);
14442
15454
  } catch (err) {
14443
- console.error(pc24.red(err.message));
15455
+ console.error(pc26.red(err.message));
14444
15456
  process.exitCode = 1;
14445
15457
  }
14446
15458
  return;
@@ -14449,7 +15461,7 @@ Any agent or kit resolves them by slug.
14449
15461
  console.log(hr2());
14450
15462
  console.log(resolved.content);
14451
15463
  console.log(hr2());
14452
- console.log(pc24.dim("Source: " + resolved.absolutePath));
15464
+ console.log(pc26.dim("Source: " + resolved.absolutePath));
14453
15465
  console.log("");
14454
15466
  });
14455
15467
  }
@@ -14501,12 +15513,31 @@ function registerSharedCommands(target) {
14501
15513
  registerTemplateCommands(target);
14502
15514
  const auth = target.command("auth").description("Authentication and bootstrap utilities");
14503
15515
  auth.command("bootstrap-ceo").description("Create a one-time bootstrap invite URL for first instance admin").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--force", "Create new invite even if admin already exists", false).option("--expires-hours <hours>", "Invite expiration window in hours", (value) => Number(value)).option("--base-url <url>", "Public base URL used to print invite link").action(bootstrapCeoInvite);
15516
+ auth.command("login").description("Sign in to hosted Growthub and save a CLI session (browser flow)").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--base-url <url>", "Hosted Growthub base URL (defaults to auth.growthubBaseUrl or GROWTHUB_BASE_URL)").option("--token <token>", "Skip the browser flow by providing a pre-issued hosted token (scripting/CI)").option("--machine-label <label>", "Label identifying this machine in the hosted app").option("--workspace-label <label>", "Label identifying this workspace in the hosted app").option("--timeout-ms <ms>", "How long to wait for the browser callback", (value) => Number(value)).option("--no-browser", "Do not try to launch a browser \u2014 print the URL and wait").option("--json", "Output raw JSON").action(async (opts) => {
15517
+ await authLogin({
15518
+ ...opts,
15519
+ noBrowser: opts.browser === false
15520
+ });
15521
+ });
15522
+ auth.command("logout").description("Clear the hosted CLI session (local workspace profile is preserved)").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--keep-overlay", "Keep cached hosted overlay metadata; only drop the session token").option("--json", "Output raw JSON").action(async (opts) => {
15523
+ await authLogout(opts);
15524
+ });
15525
+ auth.command("whoami").description("Print the authenticated hosted identity and linked local workspace").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--json", "Output raw JSON").action(async (opts) => {
15526
+ await authWhoami(opts);
15527
+ });
15528
+ registerProfileCommands(target);
15529
+ }
15530
+ async function runHostedBridgeEntry(opts) {
15531
+ await authLogin({
15532
+ config: opts?.config,
15533
+ dataDir: opts?.dataDir
15534
+ });
14504
15535
  }
14505
15536
  async function runDiscoveryHub(opts) {
14506
15537
  printPaperclipCliBanner();
14507
- p18.intro("Growthub Local");
15538
+ p19.intro("Growthub Local");
14508
15539
  while (true) {
14509
- const surfaceChoice = await p18.select({
15540
+ const surfaceChoice = await p19.select({
14510
15541
  message: "What do you want to do first?",
14511
15542
  options: [
14512
15543
  {
@@ -14524,6 +15555,11 @@ async function runDiscoveryHub(opts) {
14524
15555
  label: "\u{1F4DA} Templates",
14525
15556
  hint: "Artifact template library"
14526
15557
  },
15558
+ {
15559
+ value: "hosted-auth",
15560
+ label: "\u{1F510} Connect Growthub Account",
15561
+ hint: "Attach this CLI to the hosted Growthub user through the canonical browser flow"
15562
+ },
14527
15563
  {
14528
15564
  value: "help",
14529
15565
  label: "\u2753 Help CLI",
@@ -14531,23 +15567,23 @@ async function runDiscoveryHub(opts) {
14531
15567
  }
14532
15568
  ]
14533
15569
  });
14534
- if (p18.isCancel(surfaceChoice)) {
14535
- p18.cancel("Cancelled.");
15570
+ if (p19.isCancel(surfaceChoice)) {
15571
+ p19.cancel("Cancelled.");
14536
15572
  process.exit(0);
14537
15573
  }
14538
15574
  if (surfaceChoice === "help") {
14539
- p18.note(
15575
+ p19.note(
14540
15576
  [
14541
15577
  "\u{1F4E6} Full Local App: open an existing local surface or create a new GTM/DX profile.",
14542
15578
  "\u{1F9F0} Worker Kits: browse specialized agents and custom workspaces.",
14543
15579
  "\u{1F4DA} Templates: browse reusable artifact templates by library type.",
15580
+ "\u{1F510} Connect Growthub Account: open the canonical hosted auth flow for this CLI.",
14544
15581
  "",
14545
15582
  "Direct commands:",
14546
- "growthub run",
15583
+ "growthub auth login",
15584
+ "growthub auth whoami",
14547
15585
  "growthub kit",
14548
- "growthub template",
14549
- "growthub doctor",
14550
- "growthub configure"
15586
+ "growthub template"
14551
15587
  ].join("\n"),
14552
15588
  "Growthub CLI Help"
14553
15589
  );
@@ -14555,7 +15591,7 @@ async function runDiscoveryHub(opts) {
14555
15591
  }
14556
15592
  if (surfaceChoice === "app") {
14557
15593
  while (true) {
14558
- const appModeChoice = await p18.select({
15594
+ const appModeChoice = await p19.select({
14559
15595
  message: "How do you want to open Growthub Local?",
14560
15596
  options: [
14561
15597
  {
@@ -14574,18 +15610,18 @@ async function runDiscoveryHub(opts) {
14574
15610
  }
14575
15611
  ]
14576
15612
  });
14577
- if (p18.isCancel(appModeChoice)) {
14578
- p18.cancel("Cancelled.");
15613
+ if (p19.isCancel(appModeChoice)) {
15614
+ p19.cancel("Cancelled.");
14579
15615
  process.exit(0);
14580
15616
  }
14581
15617
  if (appModeChoice === "__back_to_hub") break;
14582
15618
  if (appModeChoice === "load") {
14583
15619
  const existingSurfaces = listLocalSurfaces();
14584
15620
  if (existingSurfaces.length === 0) {
14585
- p18.note("No existing local app profiles were found on this machine.", "Nothing found");
15621
+ p19.note("No existing local app profiles were found on this machine.", "Nothing found");
14586
15622
  continue;
14587
15623
  }
14588
- const existingChoice = await p18.select({
15624
+ const existingChoice = await p19.select({
14589
15625
  message: "Select an existing app surface",
14590
15626
  options: [
14591
15627
  ...existingSurfaces.map((surface) => ({
@@ -14596,8 +15632,8 @@ async function runDiscoveryHub(opts) {
14596
15632
  { value: "__back_to_app_mode", label: "\u2190 Back to app options" }
14597
15633
  ]
14598
15634
  });
14599
- if (p18.isCancel(existingChoice)) {
14600
- p18.cancel("Cancelled.");
15635
+ if (p19.isCancel(existingChoice)) {
15636
+ p19.cancel("Cancelled.");
14601
15637
  process.exit(0);
14602
15638
  }
14603
15639
  if (existingChoice === "__back_to_app_mode") {
@@ -14605,7 +15641,7 @@ async function runDiscoveryHub(opts) {
14605
15641
  }
14606
15642
  const selectedSurface = existingSurfaces.find((surface) => surface.instanceId === existingChoice);
14607
15643
  if (!selectedSurface) {
14608
- p18.cancel("Selected profile not found.");
15644
+ p19.cancel("Selected profile not found.");
14609
15645
  process.exit(1);
14610
15646
  }
14611
15647
  process.env.PAPERCLIP_SURFACE_PROFILE = selectedSurface.profile;
@@ -14617,7 +15653,7 @@ async function runDiscoveryHub(opts) {
14617
15653
  });
14618
15654
  return;
14619
15655
  }
14620
- const profileChoice = await p18.select({
15656
+ const profileChoice = await p19.select({
14621
15657
  message: "Which new app surface do you want to create?",
14622
15658
  options: [
14623
15659
  {
@@ -14636,8 +15672,8 @@ async function runDiscoveryHub(opts) {
14636
15672
  }
14637
15673
  ]
14638
15674
  });
14639
- if (p18.isCancel(profileChoice)) {
14640
- p18.cancel("Cancelled.");
15675
+ if (p19.isCancel(profileChoice)) {
15676
+ p19.cancel("Cancelled.");
14641
15677
  process.exit(0);
14642
15678
  }
14643
15679
  if (profileChoice === "__back_to_app_mode") {
@@ -14658,6 +15694,10 @@ async function runDiscoveryHub(opts) {
14658
15694
  if (result2 === "back") continue;
14659
15695
  return;
14660
15696
  }
15697
+ if (surfaceChoice === "hosted-auth") {
15698
+ await runHostedBridgeEntry({ config: opts?.config, dataDir: opts?.dataDir });
15699
+ continue;
15700
+ }
14661
15701
  const result = await runTemplatePicker({ allowBackToHub: true });
14662
15702
  if (result === "back") continue;
14663
15703
  return;
@@ -14668,12 +15708,12 @@ function isInstallerMode() {
14668
15708
  }
14669
15709
  function listLocalSurfaces() {
14670
15710
  const homeDir = resolvePaperclipHomeDir();
14671
- const instancesDir = path23.resolve(homeDir, "instances");
14672
- if (!fs16.existsSync(instancesDir)) return [];
14673
- return fs16.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
15711
+ const instancesDir = path27.resolve(homeDir, "instances");
15712
+ if (!fs19.existsSync(instancesDir)) return [];
15713
+ return fs19.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
14674
15714
  const instanceId = entry.name;
14675
- const configPath = path23.resolve(instancesDir, instanceId, "config.json");
14676
- if (!fs16.existsSync(configPath)) return null;
15715
+ const configPath = path27.resolve(instancesDir, instanceId, "config.json");
15716
+ if (!fs19.existsSync(configPath)) return null;
14677
15717
  try {
14678
15718
  const config = readConfig(configPath);
14679
15719
  if (!config) return null;
@@ -14714,7 +15754,7 @@ applyDataDirOverride(bootstrapOptions, {
14714
15754
  loadPaperclipEnvFile(bootstrapOptions.config);
14715
15755
  var bootstrapConfig = readConfig(resolveConfigPath(bootstrapOptions.config));
14716
15756
  var surfaceRuntime = initializeSurfaceRuntimeContract(resolveSurfaceProfile(bootstrapConfig) ?? void 0);
14717
- program.name("growthub").description("Growthub CLI \u2014 setup, configure, and run your local Growthub instance").version("0.3.46").addHelpText("after", `
15757
+ program.name("growthub").description("Growthub CLI \u2014 setup, configure, and run your local Growthub instance").version("0.3.48").addHelpText("after", `
14718
15758
  Worker Kits (agent execution environments):
14719
15759
 
14720
15760
  Discovery:
@@ -14745,6 +15785,11 @@ Instance setup:
14745
15785
  $ growthub doctor Diagnose and optionally repair
14746
15786
  $ growthub configure Update config sections
14747
15787
  $ growthub Interactive discovery hub
15788
+
15789
+ Hosted account bridge:
15790
+ $ growthub auth login Sign in via the hosted app (browser flow)
15791
+ $ growthub auth whoami Show signed-in identity + linked local workspace
15792
+ $ growthub auth logout Clear the hosted session (local workspace preserved)
14748
15793
  `);
14749
15794
  program.action(async () => {
14750
15795
  await runDiscoveryHub();