@growthub/cli 0.6.8 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/assets/shared-templates/marketing-frameworks/competitor-positioning.md +46 -0
  2. package/assets/shared-templates/marketing-frameworks/cro-7-dimensions.md +48 -0
  3. package/assets/shared-templates/marketing-frameworks/email-sequence-architecture.md +50 -0
  4. package/assets/shared-templates/marketing-frameworks/launch-playbook.md +62 -0
  5. package/assets/shared-templates/marketing-frameworks/product-marketing-context.md +62 -0
  6. package/assets/shared-templates/marketing-frameworks/seo-audit-eeat.md +44 -0
  7. package/assets/worker-kits/growthub-hyperframes-studio-v1/.env.example +7 -0
  8. package/assets/worker-kits/growthub-hyperframes-studio-v1/QUICKSTART.md +21 -0
  9. package/assets/worker-kits/growthub-hyperframes-studio-v1/brands/NEW-CLIENT.md +3 -0
  10. package/assets/worker-kits/growthub-hyperframes-studio-v1/brands/_template/brand-kit.md +7 -0
  11. package/assets/worker-kits/growthub-hyperframes-studio-v1/brands/growthub/brand-kit.md +6 -0
  12. package/assets/worker-kits/growthub-hyperframes-studio-v1/bundles/growthub-hyperframes-studio-v1.json +47 -0
  13. package/assets/worker-kits/growthub-hyperframes-studio-v1/docs/hyperframes-discovery-path.md +12 -0
  14. package/assets/worker-kits/growthub-hyperframes-studio-v1/docs/hyperframes-fork-integration.md +11 -0
  15. package/assets/worker-kits/growthub-hyperframes-studio-v1/docs/provider-adapter-layer.md +9 -0
  16. package/assets/worker-kits/growthub-hyperframes-studio-v1/examples/video-brief-sample.md +15 -0
  17. package/assets/worker-kits/growthub-hyperframes-studio-v1/growthub-meta/README.md +3 -0
  18. package/assets/worker-kits/growthub-hyperframes-studio-v1/growthub-meta/kit-standard.md +6 -0
  19. package/assets/worker-kits/growthub-hyperframes-studio-v1/kit.json +97 -0
  20. package/assets/worker-kits/growthub-hyperframes-studio-v1/output/README.md +5 -0
  21. package/assets/worker-kits/growthub-hyperframes-studio-v1/output-standards.md +12 -0
  22. package/assets/worker-kits/growthub-hyperframes-studio-v1/runtime-assumptions.md +6 -0
  23. package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/check-deps.sh +14 -0
  24. package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/clone-fork.sh +12 -0
  25. package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/verify-env.mjs +16 -0
  26. package/assets/worker-kits/growthub-hyperframes-studio-v1/skills.md +12 -0
  27. package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/composition-spec.md +6 -0
  28. package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/qa-checklist.md +6 -0
  29. package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/render-plan.md +13 -0
  30. package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/video-brief.md +7 -0
  31. package/assets/worker-kits/growthub-hyperframes-studio-v1/validation-checklist.md +6 -0
  32. package/assets/worker-kits/growthub-hyperframes-studio-v1/workers/hyperframes-studio-operator/CLAUDE.md +18 -0
  33. package/assets/worker-kits/growthub-marketing-skills-v1/.env.example +7 -0
  34. package/assets/worker-kits/growthub-marketing-skills-v1/QUICKSTART.md +111 -0
  35. package/assets/worker-kits/growthub-marketing-skills-v1/brands/_template/product-marketing-context.md +147 -0
  36. package/assets/worker-kits/growthub-marketing-skills-v1/brands/growthub/product-marketing-context.md +145 -0
  37. package/assets/worker-kits/growthub-marketing-skills-v1/bundles/growthub-marketing-skills-v1.json +43 -0
  38. package/assets/worker-kits/growthub-marketing-skills-v1/docs/evaluation-frameworks.md +53 -0
  39. package/assets/worker-kits/growthub-marketing-skills-v1/docs/fork-integration.md +43 -0
  40. package/assets/worker-kits/growthub-marketing-skills-v1/docs/skill-dispatch-methodology.md +50 -0
  41. package/assets/worker-kits/growthub-marketing-skills-v1/examples/email-sequence-sample.md +75 -0
  42. package/assets/worker-kits/growthub-marketing-skills-v1/examples/landing-page-cro-sample.md +128 -0
  43. package/assets/worker-kits/growthub-marketing-skills-v1/examples/seo-audit-sample.md +102 -0
  44. package/assets/worker-kits/growthub-marketing-skills-v1/growthub-meta/README.md +5 -0
  45. package/assets/worker-kits/growthub-marketing-skills-v1/growthub-meta/kit-standard.md +25 -0
  46. package/assets/worker-kits/growthub-marketing-skills-v1/kit.json +87 -0
  47. package/assets/worker-kits/growthub-marketing-skills-v1/output/README.md +32 -0
  48. package/assets/worker-kits/growthub-marketing-skills-v1/output-standards.md +51 -0
  49. package/assets/worker-kits/growthub-marketing-skills-v1/runtime-assumptions.md +46 -0
  50. package/assets/worker-kits/growthub-marketing-skills-v1/setup/verify-env.mjs +81 -0
  51. package/assets/worker-kits/growthub-marketing-skills-v1/skills.md +157 -0
  52. package/assets/worker-kits/growthub-marketing-skills-v1/templates/competitor-analysis.md +81 -0
  53. package/assets/worker-kits/growthub-marketing-skills-v1/templates/content-strategy-plan.md +75 -0
  54. package/assets/worker-kits/growthub-marketing-skills-v1/templates/cro-audit-brief.md +138 -0
  55. package/assets/worker-kits/growthub-marketing-skills-v1/templates/email-sequence-plan.md +80 -0
  56. package/assets/worker-kits/growthub-marketing-skills-v1/templates/launch-checklist.md +83 -0
  57. package/assets/worker-kits/growthub-marketing-skills-v1/templates/seo-audit-report.md +119 -0
  58. package/assets/worker-kits/growthub-marketing-skills-v1/workers/marketing-operator/CLAUDE.md +198 -0
  59. package/dist/index.js +694 -268
  60. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8015,8 +8015,8 @@ var init_onboard = __esm({
8015
8015
 
8016
8016
  // src/client/http.ts
8017
8017
  import { URL as URL2 } from "node:url";
8018
- function buildUrl(apiBase, path56) {
8019
- const normalizedPath = path56.startsWith("/") ? path56 : `/${path56}`;
8018
+ function buildUrl(apiBase, path57) {
8019
+ const normalizedPath = path57.startsWith("/") ? path57 : `/${path57}`;
8020
8020
  const [pathname, query] = normalizedPath.split("?");
8021
8021
  const url = new URL2(apiBase);
8022
8022
  url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
@@ -8078,26 +8078,26 @@ var init_http = __esm({
8078
8078
  this.runId = opts.runId?.trim() || void 0;
8079
8079
  this.userId = opts.userId?.trim() || void 0;
8080
8080
  }
8081
- get(path56, opts) {
8082
- return this.request(path56, { method: "GET" }, opts);
8081
+ get(path57, opts) {
8082
+ return this.request(path57, { method: "GET" }, opts);
8083
8083
  }
8084
- post(path56, body, opts) {
8085
- return this.request(path56, {
8084
+ post(path57, body, opts) {
8085
+ return this.request(path57, {
8086
8086
  method: "POST",
8087
8087
  body: body === void 0 ? void 0 : JSON.stringify(body)
8088
8088
  }, opts);
8089
8089
  }
8090
- patch(path56, body, opts) {
8091
- return this.request(path56, {
8090
+ patch(path57, body, opts) {
8091
+ return this.request(path57, {
8092
8092
  method: "PATCH",
8093
8093
  body: body === void 0 ? void 0 : JSON.stringify(body)
8094
8094
  }, opts);
8095
8095
  }
8096
- delete(path56, opts) {
8097
- return this.request(path56, { method: "DELETE" }, opts);
8096
+ delete(path57, opts) {
8097
+ return this.request(path57, { method: "DELETE" }, opts);
8098
8098
  }
8099
- async request(path56, init, opts) {
8100
- const url = buildUrl(this.apiBase, path56);
8099
+ async request(path57, init, opts) {
8100
+ const url = buildUrl(this.apiBase, path57);
8101
8101
  const headers = {
8102
8102
  accept: "application/json",
8103
8103
  ...toStringRecord(init.headers)
@@ -9622,9 +9622,9 @@ async function fetchHostedIntegrations(session) {
9622
9622
  }
9623
9623
  async function fetchHostedIntegrationCredential(session, providerId) {
9624
9624
  const client = toApiClient2(session);
9625
- const path56 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
9625
+ const path57 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
9626
9626
  try {
9627
- return await client.get(path56, { ignoreNotFound: true });
9627
+ return await client.get(path57, { ignoreNotFound: true });
9628
9628
  } catch (err) {
9629
9629
  if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
9630
9630
  throw new HostedEndpointUnavailableError(err.status, err.message);
@@ -10199,12 +10199,12 @@ var init_github = __esm({
10199
10199
  });
10200
10200
 
10201
10201
  // src/starter/init.ts
10202
- import fs36 from "node:fs";
10203
- import path45 from "node:path";
10202
+ import fs38 from "node:fs";
10203
+ import path46 from "node:path";
10204
10204
  async function initStarterWorkspace(opts) {
10205
10205
  const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
10206
- const absOut = path45.resolve(opts.out);
10207
- if (fs36.existsSync(absOut) && fs36.readdirSync(absOut).length > 0) {
10206
+ const absOut = path46.resolve(opts.out);
10207
+ if (fs38.existsSync(absOut) && fs38.readdirSync(absOut).length > 0) {
10208
10208
  throw new Error(`Destination ${absOut} already exists and is not empty.`);
10209
10209
  }
10210
10210
  const info = getBundledKitSourceInfo(kitId);
@@ -10213,7 +10213,7 @@ async function initStarterWorkspace(opts) {
10213
10213
  forkPath: absOut,
10214
10214
  kitId: info.id,
10215
10215
  baseVersion: info.version,
10216
- label: opts.name?.trim() || path45.basename(absOut)
10216
+ label: opts.name?.trim() || path46.basename(absOut)
10217
10217
  });
10218
10218
  const policy = {
10219
10219
  ...makeDefaultKitForkPolicy(),
@@ -10306,8 +10306,8 @@ var init_types2 = __esm({
10306
10306
  });
10307
10307
 
10308
10308
  // src/starter/source-import/github-source.ts
10309
- import fs37 from "node:fs";
10310
- import path46 from "node:path";
10309
+ import fs39 from "node:fs";
10310
+ import path47 from "node:path";
10311
10311
  import { spawnSync as spawnSync4 } from "node:child_process";
10312
10312
  function baseHeaders() {
10313
10313
  return {
@@ -10438,12 +10438,12 @@ function cloneGithubRepo(input) {
10438
10438
  if (!gitAvailable()) {
10439
10439
  throw new Error("`git` is not available on PATH \u2014 cannot clone.");
10440
10440
  }
10441
- if (fs37.existsSync(input.destination)) {
10441
+ if (fs39.existsSync(input.destination)) {
10442
10442
  throw new Error(`Clone destination already exists: ${input.destination}`);
10443
10443
  }
10444
10444
  const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
10445
- const parent = path46.dirname(input.destination);
10446
- fs37.mkdirSync(parent, { recursive: true });
10445
+ const parent = path47.dirname(input.destination);
10446
+ fs39.mkdirSync(parent, { recursive: true });
10447
10447
  const depth = input.depth ?? 1;
10448
10448
  const branch = input.branch ?? input.probe.defaultBranch;
10449
10449
  const args = ["clone"];
@@ -10470,17 +10470,17 @@ function cloneGithubRepo(input) {
10470
10470
  function narrowToSubdirectory(rootDir, subdirectory) {
10471
10471
  const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
10472
10472
  if (!normalizedSub) return;
10473
- const abs = path46.resolve(rootDir, normalizedSub);
10474
- if (!fs37.existsSync(abs) || !fs37.statSync(abs).isDirectory()) {
10473
+ const abs = path47.resolve(rootDir, normalizedSub);
10474
+ if (!fs39.existsSync(abs) || !fs39.statSync(abs).isDirectory()) {
10475
10475
  throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
10476
10476
  }
10477
- const tmp = path46.resolve(
10478
- path46.dirname(rootDir),
10479
- `.${path46.basename(rootDir)}-narrow-${Date.now().toString(36)}`
10477
+ const tmp = path47.resolve(
10478
+ path47.dirname(rootDir),
10479
+ `.${path47.basename(rootDir)}-narrow-${Date.now().toString(36)}`
10480
10480
  );
10481
- fs37.renameSync(abs, tmp);
10482
- fs37.rmSync(rootDir, { recursive: true, force: true });
10483
- fs37.renameSync(tmp, rootDir);
10481
+ fs39.renameSync(abs, tmp);
10482
+ fs39.rmSync(rootDir, { recursive: true, force: true });
10483
+ fs39.renameSync(tmp, rootDir);
10484
10484
  }
10485
10485
  var GITHUB_API_BASE2;
10486
10486
  var init_github_source = __esm({
@@ -10494,9 +10494,9 @@ var init_github_source = __esm({
10494
10494
  });
10495
10495
 
10496
10496
  // src/starter/source-import/skills-source.ts
10497
- import fs38 from "node:fs";
10498
- import os9 from "node:os";
10499
- import path47 from "node:path";
10497
+ import fs40 from "node:fs";
10498
+ import os10 from "node:os";
10499
+ import path48 from "node:path";
10500
10500
  import { spawnSync as spawnSync5 } from "node:child_process";
10501
10501
  function resolveBase() {
10502
10502
  const raw = process.env.SKILLS_SH_BASE?.trim();
@@ -10762,9 +10762,9 @@ async function probeSkillsSource(input) {
10762
10762
  };
10763
10763
  }
10764
10764
  function assertInsidePayloadRoot(root, candidate) {
10765
- const abs = path47.resolve(candidate);
10766
- const rootAbs = path47.resolve(root);
10767
- if (!abs.startsWith(rootAbs + path47.sep) && abs !== rootAbs) {
10765
+ const abs = path48.resolve(candidate);
10766
+ const rootAbs = path48.resolve(root);
10767
+ if (!abs.startsWith(rootAbs + path48.sep) && abs !== rootAbs) {
10768
10768
  throw new Error(`Refusing to write outside payload root: ${candidate}`);
10769
10769
  }
10770
10770
  }
@@ -10780,24 +10780,24 @@ function runGit3(args, cwd) {
10780
10780
  };
10781
10781
  }
10782
10782
  function skillDirectoryMatches(dir, skillSlug) {
10783
- const skillFile = path47.resolve(dir, "SKILL.md");
10784
- if (!fs38.existsSync(skillFile) || !fs38.statSync(skillFile).isFile()) {
10783
+ const skillFile = path48.resolve(dir, "SKILL.md");
10784
+ if (!fs40.existsSync(skillFile) || !fs40.statSync(skillFile).isFile()) {
10785
10785
  return false;
10786
10786
  }
10787
- if (path47.basename(dir) === skillSlug) {
10787
+ if (path48.basename(dir) === skillSlug) {
10788
10788
  return true;
10789
10789
  }
10790
- const content = fs38.readFileSync(skillFile, "utf8");
10790
+ const content = fs40.readFileSync(skillFile, "utf8");
10791
10791
  const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
10792
10792
  return nameMatch?.[1] === skillSlug;
10793
10793
  }
10794
10794
  function locateSkillDirectory(root, skillSlug) {
10795
10795
  const preferred = [
10796
- path47.resolve(root, "skills", skillSlug),
10797
- path47.resolve(root, skillSlug)
10796
+ path48.resolve(root, "skills", skillSlug),
10797
+ path48.resolve(root, skillSlug)
10798
10798
  ];
10799
10799
  for (const candidate of preferred) {
10800
- if (fs38.existsSync(candidate) && fs38.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
10800
+ if (fs40.existsSync(candidate) && fs40.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
10801
10801
  return candidate;
10802
10802
  }
10803
10803
  }
@@ -10807,12 +10807,12 @@ function locateSkillDirectory(root, skillSlug) {
10807
10807
  if (skillDirectoryMatches(current, skillSlug)) {
10808
10808
  return current;
10809
10809
  }
10810
- for (const entry of fs38.readdirSync(current, { withFileTypes: true })) {
10810
+ for (const entry of fs40.readdirSync(current, { withFileTypes: true })) {
10811
10811
  if (!entry.isDirectory()) continue;
10812
10812
  if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
10813
10813
  continue;
10814
10814
  }
10815
- queue.push(path47.resolve(current, entry.name));
10815
+ queue.push(path48.resolve(current, entry.name));
10816
10816
  }
10817
10817
  }
10818
10818
  return null;
@@ -10822,18 +10822,18 @@ function copySkillTree(sourceDir, destination) {
10822
10822
  const stack = [{ from: sourceDir, to: destination }];
10823
10823
  while (stack.length > 0) {
10824
10824
  const current = stack.pop();
10825
- fs38.mkdirSync(current.to, { recursive: true });
10826
- for (const entry of fs38.readdirSync(current.from, { withFileTypes: true })) {
10827
- const fromPath = path47.resolve(current.from, entry.name);
10828
- const toPath = path47.resolve(current.to, entry.name);
10825
+ fs40.mkdirSync(current.to, { recursive: true });
10826
+ for (const entry of fs40.readdirSync(current.from, { withFileTypes: true })) {
10827
+ const fromPath = path48.resolve(current.from, entry.name);
10828
+ const toPath = path48.resolve(current.to, entry.name);
10829
10829
  assertInsidePayloadRoot(destination, toPath);
10830
10830
  if (entry.isDirectory()) {
10831
10831
  stack.push({ from: fromPath, to: toPath });
10832
10832
  continue;
10833
10833
  }
10834
- const data = fs38.readFileSync(fromPath);
10835
- fs38.mkdirSync(path47.dirname(toPath), { recursive: true });
10836
- fs38.writeFileSync(toPath, data, { mode: 420 });
10834
+ const data = fs40.readFileSync(fromPath);
10835
+ fs40.mkdirSync(path48.dirname(toPath), { recursive: true });
10836
+ fs40.writeFileSync(toPath, data, { mode: 420 });
10837
10837
  written += 1;
10838
10838
  }
10839
10839
  }
@@ -10844,7 +10844,7 @@ async function fetchSkillPayload(input) {
10844
10844
  if (!gitAvailable()) {
10845
10845
  throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
10846
10846
  }
10847
- if (fs38.existsSync(destination)) {
10847
+ if (fs40.existsSync(destination)) {
10848
10848
  throw new Error(`Skill payload destination already exists: ${destination}`);
10849
10849
  }
10850
10850
  const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
@@ -10852,11 +10852,11 @@ async function fetchSkillPayload(input) {
10852
10852
  if (!repoSource || !skillSlug) {
10853
10853
  throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
10854
10854
  }
10855
- const cloneRoot = fs38.mkdtempSync(
10856
- path47.join(os9.tmpdir(), "growthub-skills-source-")
10855
+ const cloneRoot = fs40.mkdtempSync(
10856
+ path48.join(os10.tmpdir(), "growthub-skills-source-")
10857
10857
  );
10858
10858
  try {
10859
- const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path47.dirname(cloneRoot));
10859
+ const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path48.dirname(cloneRoot));
10860
10860
  if (!cloneRes.ok) {
10861
10861
  throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
10862
10862
  }
@@ -10869,7 +10869,7 @@ async function fetchSkillPayload(input) {
10869
10869
  const fileCount = copySkillTree(skillDir, destination);
10870
10870
  return { destination, fileCount };
10871
10871
  } finally {
10872
- fs38.rmSync(cloneRoot, { recursive: true, force: true });
10872
+ fs40.rmSync(cloneRoot, { recursive: true, force: true });
10873
10873
  }
10874
10874
  }
10875
10875
  var DEFAULT_BASE, COMMENT_PATTERN;
@@ -10883,13 +10883,13 @@ var init_skills_source = __esm({
10883
10883
  });
10884
10884
 
10885
10885
  // src/starter/source-import/detect.ts
10886
- import fs39 from "node:fs";
10887
- import path48 from "node:path";
10886
+ import fs41 from "node:fs";
10887
+ import path49 from "node:path";
10888
10888
  function safeReadPackageJson(dir) {
10889
- const p33 = path48.resolve(dir, "package.json");
10890
- if (!fs39.existsSync(p33)) return null;
10889
+ const p33 = path49.resolve(dir, "package.json");
10890
+ if (!fs41.existsSync(p33)) return null;
10891
10891
  try {
10892
- return JSON.parse(fs39.readFileSync(p33, "utf8"));
10892
+ return JSON.parse(fs41.readFileSync(p33, "utf8"));
10893
10893
  } catch {
10894
10894
  return null;
10895
10895
  }
@@ -10899,10 +10899,10 @@ function detectPackageManager(dir, pkg) {
10899
10899
  if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
10900
10900
  if (pkg?.packageManager?.startsWith("npm")) return "npm";
10901
10901
  if (pkg?.packageManager?.startsWith("bun")) return "bun";
10902
- if (fs39.existsSync(path48.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
10903
- if (fs39.existsSync(path48.resolve(dir, "yarn.lock"))) return "yarn";
10904
- if (fs39.existsSync(path48.resolve(dir, "bun.lockb"))) return "bun";
10905
- if (fs39.existsSync(path48.resolve(dir, "package-lock.json"))) return "npm";
10902
+ if (fs41.existsSync(path49.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
10903
+ if (fs41.existsSync(path49.resolve(dir, "yarn.lock"))) return "yarn";
10904
+ if (fs41.existsSync(path49.resolve(dir, "bun.lockb"))) return "bun";
10905
+ if (fs41.existsSync(path49.resolve(dir, "package-lock.json"))) return "npm";
10906
10906
  return "unknown";
10907
10907
  }
10908
10908
  function collectDeps(pkg) {
@@ -10916,12 +10916,12 @@ function collectDeps(pkg) {
10916
10916
  }
10917
10917
  function looksLikeSkillPayload(rootDir) {
10918
10918
  const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
10919
- return markers.some((name) => fs39.existsSync(path48.resolve(rootDir, name)));
10919
+ return markers.some((name) => fs41.existsSync(path49.resolve(rootDir, name)));
10920
10920
  }
10921
10921
  function detectFramework(rootDir, pkg) {
10922
10922
  if (!pkg) {
10923
10923
  if (looksLikeSkillPayload(rootDir)) return "skill";
10924
- if (fs39.existsSync(path48.resolve(rootDir, "docs"))) return "docs";
10924
+ if (fs41.existsSync(path49.resolve(rootDir, "docs"))) return "docs";
10925
10925
  return "unknown";
10926
10926
  }
10927
10927
  const deps = collectDeps(pkg);
@@ -10930,8 +10930,8 @@ function detectFramework(rootDir, pkg) {
10930
10930
  "vite.config.ts",
10931
10931
  "vite.config.mjs",
10932
10932
  "vite.config.cjs"
10933
- ].some((name) => fs39.existsSync(path48.resolve(rootDir, name)));
10934
- if (deps.has("next") || fs39.existsSync(path48.resolve(rootDir, "next.config.js")) || fs39.existsSync(path48.resolve(rootDir, "next.config.mjs"))) {
10933
+ ].some((name) => fs41.existsSync(path49.resolve(rootDir, name)));
10934
+ if (deps.has("next") || fs41.existsSync(path49.resolve(rootDir, "next.config.js")) || fs41.existsSync(path49.resolve(rootDir, "next.config.mjs"))) {
10935
10935
  return "next";
10936
10936
  }
10937
10937
  if (deps.has("vite") || hasViteConfig) return "vite";
@@ -10957,15 +10957,15 @@ function pickScripts(pkg) {
10957
10957
  return out;
10958
10958
  }
10959
10959
  function listEnvFiles(dir) {
10960
- if (!fs39.existsSync(dir)) return [];
10961
- return fs39.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
10960
+ if (!fs41.existsSync(dir)) return [];
10961
+ return fs41.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
10962
10962
  }
10963
10963
  function findAppRoot(rootDir, pkg) {
10964
10964
  if (pkg) return ".";
10965
10965
  const candidates = ["app", "src", "apps", "packages"];
10966
10966
  for (const candidate of candidates) {
10967
- const abs = path48.resolve(rootDir, candidate);
10968
- if (fs39.existsSync(abs) && fs39.statSync(abs).isDirectory()) {
10967
+ const abs = path49.resolve(rootDir, candidate);
10968
+ if (fs41.existsSync(abs) && fs41.statSync(abs).isDirectory()) {
10969
10969
  const child = safeReadPackageJson(abs);
10970
10970
  if (child) return candidate;
10971
10971
  }
@@ -10981,12 +10981,12 @@ function computeConfidence(framework, manager, pkg) {
10981
10981
  return Math.min(1, Number(score.toFixed(2)));
10982
10982
  }
10983
10983
  function detectSourceShape(rootDir) {
10984
- if (!fs39.existsSync(rootDir) || !fs39.statSync(rootDir).isDirectory()) {
10984
+ if (!fs41.existsSync(rootDir) || !fs41.statSync(rootDir).isDirectory()) {
10985
10985
  throw new Error(`Detection target is not a directory: ${rootDir}`);
10986
10986
  }
10987
10987
  const rootPkg = safeReadPackageJson(rootDir);
10988
10988
  const appRootRel = findAppRoot(rootDir, rootPkg);
10989
- const appRootAbs = path48.resolve(rootDir, appRootRel);
10989
+ const appRootAbs = path49.resolve(rootDir, appRootRel);
10990
10990
  const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
10991
10991
  const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
10992
10992
  const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
@@ -11026,18 +11026,18 @@ var init_detect = __esm({
11026
11026
  });
11027
11027
 
11028
11028
  // src/starter/source-import/security.ts
11029
- import fs40 from "node:fs";
11030
- import path49 from "node:path";
11029
+ import fs42 from "node:fs";
11030
+ import path50 from "node:path";
11031
11031
  function isLikelyTextFile(filename) {
11032
- const ext = path49.extname(filename).toLowerCase();
11032
+ const ext = path50.extname(filename).toLowerCase();
11033
11033
  if (!ext) return true;
11034
11034
  return TEXT_EXTENSIONS.has(ext);
11035
11035
  }
11036
11036
  function isSuspiciousBinary(filename) {
11037
- return SUSPICIOUS_BINARY_EXTENSIONS.has(path49.extname(filename).toLowerCase());
11037
+ return SUSPICIOUS_BINARY_EXTENSIONS.has(path50.extname(filename).toLowerCase());
11038
11038
  }
11039
11039
  function isUnexpectedArchive(filename) {
11040
- const ext = path49.extname(filename).toLowerCase();
11040
+ const ext = path50.extname(filename).toLowerCase();
11041
11041
  return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
11042
11042
  }
11043
11043
  function shortExcerpt(line) {
@@ -11092,19 +11092,19 @@ function walkPayload(root, onFile, limits) {
11092
11092
  if (!current) break;
11093
11093
  let entries;
11094
11094
  try {
11095
- entries = fs40.readdirSync(current, { withFileTypes: true });
11095
+ entries = fs42.readdirSync(current, { withFileTypes: true });
11096
11096
  } catch {
11097
11097
  continue;
11098
11098
  }
11099
11099
  for (const entry of entries) {
11100
- const abs = path49.resolve(current, entry.name);
11100
+ const abs = path50.resolve(current, entry.name);
11101
11101
  if (entry.isDirectory()) {
11102
11102
  if (entry.name === ".git" || entry.name === "node_modules") continue;
11103
11103
  stack.push(abs);
11104
11104
  continue;
11105
11105
  }
11106
11106
  if (!entry.isFile()) continue;
11107
- const rel = path49.relative(root, abs);
11107
+ const rel = path50.relative(root, abs);
11108
11108
  onFile(abs, rel);
11109
11109
  visited += 1;
11110
11110
  if (visited >= limits.maxFiles) break;
@@ -11114,7 +11114,7 @@ function walkPayload(root, onFile, limits) {
11114
11114
  }
11115
11115
  function inspectSourcePayload(input) {
11116
11116
  const { payloadRoot } = input;
11117
- if (!fs40.existsSync(payloadRoot) || !fs40.statSync(payloadRoot).isDirectory()) {
11117
+ if (!fs42.existsSync(payloadRoot) || !fs42.statSync(payloadRoot).isDirectory()) {
11118
11118
  throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
11119
11119
  }
11120
11120
  const findings = [];
@@ -11124,7 +11124,7 @@ function inspectSourcePayload(input) {
11124
11124
  (abs, rel) => {
11125
11125
  let size = 0;
11126
11126
  try {
11127
- size = fs40.statSync(abs).size;
11127
+ size = fs42.statSync(abs).size;
11128
11128
  } catch {
11129
11129
  return;
11130
11130
  }
@@ -11133,7 +11133,7 @@ function inspectSourcePayload(input) {
11133
11133
  category: "suspicious-binary",
11134
11134
  severity: "high-risk",
11135
11135
  path: rel,
11136
- message: `Payload ships a precompiled binary (${path49.extname(rel)}). Review provenance before use.`
11136
+ message: `Payload ships a precompiled binary (${path50.extname(rel)}). Review provenance before use.`
11137
11137
  });
11138
11138
  return;
11139
11139
  }
@@ -11142,7 +11142,7 @@ function inspectSourcePayload(input) {
11142
11142
  category: "unexpected-archive",
11143
11143
  severity: "caution",
11144
11144
  path: rel,
11145
- message: `Payload ships an archive (${path49.extname(rel)}) \u2014 expand and review contents before use.`
11145
+ message: `Payload ships an archive (${path50.extname(rel)}) \u2014 expand and review contents before use.`
11146
11146
  });
11147
11147
  return;
11148
11148
  }
@@ -11150,12 +11150,12 @@ function inspectSourcePayload(input) {
11150
11150
  if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
11151
11151
  let buf;
11152
11152
  try {
11153
- const handle = fs40.openSync(abs, "r");
11153
+ const handle = fs42.openSync(abs, "r");
11154
11154
  try {
11155
11155
  buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
11156
- fs40.readSync(handle, buf, 0, buf.length, 0);
11156
+ fs42.readSync(handle, buf, 0, buf.length, 0);
11157
11157
  } finally {
11158
- fs40.closeSync(handle);
11158
+ fs42.closeSync(handle);
11159
11159
  }
11160
11160
  } catch {
11161
11161
  return;
@@ -11364,18 +11364,18 @@ var init_security = __esm({
11364
11364
  });
11365
11365
 
11366
11366
  // src/starter/source-import/plan.ts
11367
- import fs41 from "node:fs";
11368
- import path50 from "node:path";
11367
+ import fs43 from "node:fs";
11368
+ import path51 from "node:path";
11369
11369
  function generateImportId() {
11370
11370
  return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
11371
11371
  }
11372
11372
  function destinationState(absDest) {
11373
- if (!fs41.existsSync(absDest)) return { exists: false, nonEmpty: false };
11374
- const stats = fs41.statSync(absDest);
11373
+ if (!fs43.existsSync(absDest)) return { exists: false, nonEmpty: false };
11374
+ const stats = fs43.statSync(absDest);
11375
11375
  if (!stats.isDirectory()) {
11376
11376
  throw new Error(`Destination is not a directory: ${absDest}`);
11377
11377
  }
11378
- const entries = fs41.readdirSync(absDest);
11378
+ const entries = fs43.readdirSync(absDest);
11379
11379
  return { exists: true, nonEmpty: entries.length > 0 };
11380
11380
  }
11381
11381
  function describeSource(probe) {
@@ -11385,7 +11385,7 @@ function describeSource(probe) {
11385
11385
  return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
11386
11386
  }
11387
11387
  function buildSourceImportPlan(input) {
11388
- const absDest = path50.resolve(input.destination);
11388
+ const absDest = path51.resolve(input.destination);
11389
11389
  const state = destinationState(absDest);
11390
11390
  const payloadPath = "imported";
11391
11391
  const warnings = [...input.probe.warnings];
@@ -11498,8 +11498,8 @@ var init_plan = __esm({
11498
11498
  });
11499
11499
 
11500
11500
  // src/starter/source-import/summarize.ts
11501
- import fs42 from "node:fs";
11502
- import path51 from "node:path";
11501
+ import fs44 from "node:fs";
11502
+ import path52 from "node:path";
11503
11503
  function sourceHeading(manifest) {
11504
11504
  const src = manifest.source;
11505
11505
  if (src.kind === "github-repo") {
@@ -11554,7 +11554,7 @@ function nextStepsSection(manifest) {
11554
11554
  }
11555
11555
  function writeImportSummary(input) {
11556
11556
  const { forkPath, summaryRelativePath, manifest } = input;
11557
- const summaryPath = path51.resolve(forkPath, summaryRelativePath);
11557
+ const summaryPath = path52.resolve(forkPath, summaryRelativePath);
11558
11558
  const body = [
11559
11559
  `# Source Import Summary`,
11560
11560
  ``,
@@ -11584,8 +11584,8 @@ function writeImportSummary(input) {
11584
11584
  `Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
11585
11585
  ``
11586
11586
  ].join("\n");
11587
- fs42.mkdirSync(path51.dirname(summaryPath), { recursive: true });
11588
- fs42.writeFileSync(summaryPath, body, "utf8");
11587
+ fs44.mkdirSync(path52.dirname(summaryPath), { recursive: true });
11588
+ fs44.writeFileSync(summaryPath, body, "utf8");
11589
11589
  return summaryPath;
11590
11590
  }
11591
11591
  var init_summarize = __esm({
@@ -11595,16 +11595,16 @@ var init_summarize = __esm({
11595
11595
  });
11596
11596
 
11597
11597
  // src/starter/source-import/materialize.ts
11598
- import fs43 from "node:fs";
11599
- import os10 from "node:os";
11600
- import path52 from "node:path";
11598
+ import fs45 from "node:fs";
11599
+ import os11 from "node:os";
11600
+ import path53 from "node:path";
11601
11601
  function resolveSourceKind(probe) {
11602
11602
  return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
11603
11603
  }
11604
11604
  function stagingDirFor(forkPath) {
11605
- return path52.join(
11606
- os10.tmpdir(),
11607
- `growthub-source-import-${path52.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
11605
+ return path53.join(
11606
+ os11.tmpdir(),
11607
+ `growthub-source-import-${path53.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
11608
11608
  );
11609
11609
  }
11610
11610
  async function fetchPayload(probe, stagingDir, opts) {
@@ -11636,18 +11636,18 @@ async function fetchPayload(probe, stagingDir, opts) {
11636
11636
  return { payloadRoot: stagingDir };
11637
11637
  }
11638
11638
  function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
11639
- const target = path52.resolve(forkPath, payloadRelativePath);
11640
- if (fs43.existsSync(target)) {
11641
- fs43.rmSync(target, { recursive: true, force: true });
11639
+ const target = path53.resolve(forkPath, payloadRelativePath);
11640
+ if (fs45.existsSync(target)) {
11641
+ fs45.rmSync(target, { recursive: true, force: true });
11642
11642
  }
11643
- fs43.mkdirSync(path52.dirname(target), { recursive: true });
11644
- fs43.renameSync(payloadRoot, target);
11643
+ fs45.mkdirSync(path53.dirname(target), { recursive: true });
11644
+ fs45.renameSync(payloadRoot, target);
11645
11645
  return target;
11646
11646
  }
11647
11647
  function writeManifest(forkPath, manifest) {
11648
- const p33 = path52.resolve(forkPath, MANIFEST_RELATIVE_PATH);
11649
- fs43.mkdirSync(path52.dirname(p33), { recursive: true });
11650
- fs43.writeFileSync(p33, JSON.stringify(manifest, null, 2) + "\n", "utf8");
11648
+ const p33 = path53.resolve(forkPath, MANIFEST_RELATIVE_PATH);
11649
+ fs45.mkdirSync(path53.dirname(p33), { recursive: true });
11650
+ fs45.writeFileSync(p33, JSON.stringify(manifest, null, 2) + "\n", "utf8");
11651
11651
  return p33;
11652
11652
  }
11653
11653
  function assertConfirmationsSatisfied(plan, confirmations) {
@@ -11687,7 +11687,7 @@ async function materializeImportPlan(input) {
11687
11687
  requireSkillAcknowledgement: sourceKind === "skills-skill"
11688
11688
  });
11689
11689
  if (security.blocked) {
11690
- fs43.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
11690
+ fs45.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
11691
11691
  throw new Error(
11692
11692
  `Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
11693
11693
  );
@@ -11818,26 +11818,26 @@ var init_materialize = __esm({
11818
11818
  });
11819
11819
 
11820
11820
  // src/starter/source-import/agent.ts
11821
- import fs44 from "node:fs";
11822
- import path53 from "node:path";
11821
+ import fs46 from "node:fs";
11822
+ import path54 from "node:path";
11823
11823
  function resolveJobsDir() {
11824
- return path53.resolve(resolveKitForksHomeDir(), "source-import-jobs");
11824
+ return path54.resolve(resolveKitForksHomeDir(), "source-import-jobs");
11825
11825
  }
11826
11826
  function resolveJobPath2(jobId) {
11827
- return path53.resolve(resolveJobsDir(), `${jobId}.json`);
11827
+ return path54.resolve(resolveJobsDir(), `${jobId}.json`);
11828
11828
  }
11829
11829
  function generateJobId2() {
11830
11830
  return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
11831
11831
  }
11832
11832
  function writeJob2(job) {
11833
11833
  const p33 = resolveJobPath2(job.jobId);
11834
- fs44.mkdirSync(path53.dirname(p33), { recursive: true });
11835
- fs44.writeFileSync(p33, JSON.stringify(job, null, 2) + "\n", "utf8");
11834
+ fs46.mkdirSync(path54.dirname(p33), { recursive: true });
11835
+ fs46.writeFileSync(p33, JSON.stringify(job, null, 2) + "\n", "utf8");
11836
11836
  }
11837
11837
  function readJobFile(p33) {
11838
- if (!fs44.existsSync(p33)) return null;
11838
+ if (!fs46.existsSync(p33)) return null;
11839
11839
  try {
11840
- return JSON.parse(fs44.readFileSync(p33, "utf8"));
11840
+ return JSON.parse(fs46.readFileSync(p33, "utf8"));
11841
11841
  } catch {
11842
11842
  return null;
11843
11843
  }
@@ -11847,7 +11847,7 @@ function patchJob2(jobId, status, patch = {}) {
11847
11847
  const job = readJobFile(p33);
11848
11848
  if (!job) return null;
11849
11849
  const updated = { ...job, ...patch, status };
11850
- fs44.writeFileSync(p33, JSON.stringify(updated, null, 2) + "\n", "utf8");
11850
+ fs46.writeFileSync(p33, JSON.stringify(updated, null, 2) + "\n", "utf8");
11851
11851
  return updated;
11852
11852
  }
11853
11853
  function getSourceImportJob(jobId) {
@@ -11866,7 +11866,7 @@ async function probeAndPlan(input, destination) {
11866
11866
  }
11867
11867
  async function runSourceImportJob(input) {
11868
11868
  const jobId = generateJobId2();
11869
- const destination = path53.resolve(input.out);
11869
+ const destination = path54.resolve(input.out);
11870
11870
  const sourceKind = input.source.kind;
11871
11871
  const initial = {
11872
11872
  jobId,
@@ -12023,8 +12023,8 @@ __export(source_import_discovery_exports, {
12023
12023
  });
12024
12024
  import * as p31 from "@clack/prompts";
12025
12025
  import pc45 from "picocolors";
12026
- import fs46 from "node:fs";
12027
- import path54 from "node:path";
12026
+ import fs48 from "node:fs";
12027
+ import path55 from "node:path";
12028
12028
  import { pathToFileURL as pathToFileURL4 } from "node:url";
12029
12029
  function slugifyWorkspaceName(input) {
12030
12030
  const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -12052,10 +12052,10 @@ async function promptForInteractiveWorkspacePath(input) {
12052
12052
  });
12053
12053
  if (p31.isCancel(raw) || !raw) return null;
12054
12054
  const trimmed = String(raw).trim();
12055
- const expanded = trimmed.startsWith("~/") ? path54.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
12056
- const resolved = path54.resolve(expanded);
12057
- if (fs46.existsSync(resolved) && fs46.statSync(resolved).isDirectory()) {
12058
- const finalPath = path54.join(resolved, suggestedName);
12055
+ const expanded = trimmed.startsWith("~/") ? path55.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
12056
+ const resolved = path55.resolve(expanded);
12057
+ if (fs48.existsSync(resolved) && fs48.statSync(resolved).isDirectory()) {
12058
+ const finalPath = path55.join(resolved, suggestedName);
12059
12059
  p31.note(
12060
12060
  [
12061
12061
  `You selected an existing folder: ${resolved}`,
@@ -12328,8 +12328,8 @@ init_doctor();
12328
12328
  import { Command } from "commander";
12329
12329
  import * as p32 from "@clack/prompts";
12330
12330
  import pc46 from "picocolors";
12331
- import fs47 from "node:fs";
12332
- import path55 from "node:path";
12331
+ import fs49 from "node:fs";
12332
+ import path56 from "node:path";
12333
12333
  import { spawnSync as spawnSync6 } from "node:child_process";
12334
12334
  import { fileURLToPath as fileURLToPath7 } from "node:url";
12335
12335
 
@@ -12990,8 +12990,8 @@ function printItemCompleted(item) {
12990
12990
  const changes = Array.isArray(item.changes) ? item.changes : [];
12991
12991
  const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
12992
12992
  const kind = asString(change.kind, "update");
12993
- const path56 = asString(change.path, "unknown");
12994
- return `${kind} ${path56}`;
12993
+ const path57 = asString(change.path, "unknown");
12994
+ return `${kind} ${path57}`;
12995
12995
  });
12996
12996
  const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
12997
12997
  const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
@@ -15823,8 +15823,8 @@ function registerIssueCommands(program2) {
15823
15823
  if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
15824
15824
  if (opts.projectId) params.set("projectId", opts.projectId);
15825
15825
  const query = params.toString();
15826
- const path56 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
15827
- const rows = await ctx.api.get(path56) ?? [];
15826
+ const path57 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
15827
+ const rows = await ctx.api.get(path57) ?? [];
15828
15828
  const filtered = filterIssueRows(rows, opts.match);
15829
15829
  if (ctx.json) {
15830
15830
  printOutput(filtered, { json: true });
@@ -16430,8 +16430,8 @@ function registerActivityCommands(program2) {
16430
16430
  if (opts.entityType) params.set("entityType", opts.entityType);
16431
16431
  if (opts.entityId) params.set("entityId", opts.entityId);
16432
16432
  const query = params.toString();
16433
- const path56 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
16434
- const rows = await ctx.api.get(path56) ?? [];
16433
+ const path57 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
16434
+ const rows = await ctx.api.get(path57) ?? [];
16435
16435
  if (ctx.json) {
16436
16436
  printOutput(rows, { json: true });
16437
16437
  return;
@@ -17865,12 +17865,13 @@ ${result.lastError}`);
17865
17865
  // src/commands/kit.ts
17866
17866
  init_service();
17867
17867
  init_banner();
17868
- import path33 from "node:path";
17868
+ import path34 from "node:path";
17869
17869
  import { pathToFileURL as pathToFileURL2 } from "node:url";
17870
17870
  import * as p19 from "@clack/prompts";
17871
17871
  import pc30 from "picocolors";
17872
17872
 
17873
17873
  // src/commands/kit-fork.ts
17874
+ import fs27 from "node:fs";
17874
17875
  import * as p18 from "@clack/prompts";
17875
17876
 
17876
17877
  // src/commands/kit-fork-remote.ts
@@ -19219,6 +19220,243 @@ init_fork_registry();
19219
19220
  init_service();
19220
19221
  init_fork_policy();
19221
19222
  init_fork_trace();
19223
+
19224
+ // src/kits/fork-authority.ts
19225
+ init_home();
19226
+ init_kit_forks_home();
19227
+ import crypto from "node:crypto";
19228
+ import fs26 from "node:fs";
19229
+ import os8 from "node:os";
19230
+ import path33 from "node:path";
19231
+ function resolveAuthorityHomeDir() {
19232
+ const env = process.env.GROWTHUB_AUTHORITY_HOME?.trim();
19233
+ if (env) return path33.resolve(expandHomePrefix(env));
19234
+ return path33.resolve(os8.homedir(), ".growthub", "authority");
19235
+ }
19236
+ function resolveAuthorityIssuersPath() {
19237
+ return path33.resolve(resolveAuthorityHomeDir(), "issuers.json");
19238
+ }
19239
+ function resolveInForkAuthorityPath(forkPath) {
19240
+ return path33.resolve(resolveInForkStateDir(forkPath), "authority.json");
19241
+ }
19242
+ function canonicalize(value) {
19243
+ if (value === null) return "null";
19244
+ if (typeof value === "number") {
19245
+ if (!Number.isFinite(value)) {
19246
+ throw new Error("Cannot canonicalize non-finite number");
19247
+ }
19248
+ return JSON.stringify(value);
19249
+ }
19250
+ if (typeof value === "string" || typeof value === "boolean") {
19251
+ return JSON.stringify(value);
19252
+ }
19253
+ if (Array.isArray(value)) {
19254
+ return "[" + value.map(canonicalize).join(",") + "]";
19255
+ }
19256
+ if (typeof value === "object") {
19257
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
19258
+ return "{" + entries.map(([k, v]) => JSON.stringify(k) + ":" + canonicalize(v)).join(",") + "}";
19259
+ }
19260
+ throw new Error(`Unsupported value type for canonicalize: ${typeof value}`);
19261
+ }
19262
+ function buildSigningPayload(envelope) {
19263
+ return canonicalize({
19264
+ version: envelope.version,
19265
+ envelopeId: envelope.envelopeId,
19266
+ issuerId: envelope.issuerId,
19267
+ algorithm: envelope.algorithm,
19268
+ subject: envelope.subject,
19269
+ grants: envelope.grants,
19270
+ issuedAt: envelope.issuedAt,
19271
+ expiresAt: envelope.expiresAt,
19272
+ nonce: envelope.nonce
19273
+ });
19274
+ }
19275
+ function computePolicyHash(policy) {
19276
+ const shape = {
19277
+ version: policy.version,
19278
+ untouchablePaths: [...policy.untouchablePaths].sort(),
19279
+ confirmBeforeChange: [...policy.confirmBeforeChange].sort(),
19280
+ autoApprove: policy.autoApprove,
19281
+ autoApproveDepUpdates: policy.autoApproveDepUpdates,
19282
+ remoteSyncMode: policy.remoteSyncMode,
19283
+ interactiveConflicts: policy.interactiveConflicts,
19284
+ allowedScripts: [...policy.allowedScripts].sort()
19285
+ };
19286
+ return crypto.createHash("sha256").update(canonicalize(shape), "utf8").digest("hex");
19287
+ }
19288
+ function readIssuerRegistry() {
19289
+ const p33 = resolveAuthorityIssuersPath();
19290
+ if (!fs26.existsSync(p33)) return { version: 1, issuers: [] };
19291
+ try {
19292
+ const parsed = JSON.parse(fs26.readFileSync(p33, "utf8"));
19293
+ if (!parsed || !Array.isArray(parsed.issuers)) return { version: 1, issuers: [] };
19294
+ return { version: 1, issuers: parsed.issuers.filter(isValidIssuer) };
19295
+ } catch {
19296
+ return { version: 1, issuers: [] };
19297
+ }
19298
+ }
19299
+ function isValidIssuer(x) {
19300
+ if (!x || typeof x !== "object") return false;
19301
+ const o = x;
19302
+ return typeof o.id === "string" && typeof o.publicKeyPem === "string" && (o.kind === "growthub-hosted" || o.kind === "self-signed" || o.kind === "enterprise");
19303
+ }
19304
+ function writeIssuerRegistry(registry) {
19305
+ const p33 = resolveAuthorityIssuersPath();
19306
+ fs26.mkdirSync(path33.dirname(p33), { recursive: true });
19307
+ fs26.writeFileSync(p33, JSON.stringify({ version: 1, issuers: registry.issuers }, null, 2) + "\n", "utf8");
19308
+ }
19309
+ function upsertIssuer(issuer) {
19310
+ if (!isValidIssuer(issuer)) {
19311
+ throw new Error("Invalid issuer: id, publicKeyPem, and kind are required.");
19312
+ }
19313
+ const reg = readIssuerRegistry();
19314
+ const idx = reg.issuers.findIndex((i) => i.id === issuer.id);
19315
+ const stamped = { ...issuer, addedAt: issuer.addedAt ?? (/* @__PURE__ */ new Date()).toISOString() };
19316
+ if (idx >= 0) reg.issuers[idx] = stamped;
19317
+ else reg.issuers.push(stamped);
19318
+ writeIssuerRegistry(reg);
19319
+ return reg;
19320
+ }
19321
+ function removeIssuer(issuerId) {
19322
+ const reg = readIssuerRegistry();
19323
+ const next = reg.issuers.filter((i) => i.id !== issuerId);
19324
+ if (next.length === reg.issuers.length) return false;
19325
+ writeIssuerRegistry({ version: 1, issuers: next });
19326
+ return true;
19327
+ }
19328
+ function findIssuer(issuerId) {
19329
+ return readIssuerRegistry().issuers.find((i) => i.id === issuerId) ?? null;
19330
+ }
19331
+ function verifyAuthorityEnvelope(envelope, options = {}) {
19332
+ if (!isWellFormedEnvelope(envelope)) {
19333
+ return { ok: false, reason: "malformed", detail: "envelope shape invalid" };
19334
+ }
19335
+ if (options.expectedKitId && envelope.subject.kitId !== options.expectedKitId) {
19336
+ return { ok: false, reason: "subject-mismatch", detail: `kitId expected ${options.expectedKitId}` };
19337
+ }
19338
+ if (options.expectedForkId && envelope.subject.forkId !== options.expectedForkId) {
19339
+ return { ok: false, reason: "subject-mismatch", detail: `forkId expected ${options.expectedForkId}` };
19340
+ }
19341
+ if (options.expectedForkPath && envelope.subject.forkPath && envelope.subject.forkPath !== options.expectedForkPath) {
19342
+ return { ok: false, reason: "subject-mismatch", detail: `forkPath expected ${options.expectedForkPath}` };
19343
+ }
19344
+ const now = options.now ?? /* @__PURE__ */ new Date();
19345
+ if (envelope.expiresAt) {
19346
+ const exp = Date.parse(envelope.expiresAt);
19347
+ if (!Number.isFinite(exp) || exp <= now.getTime()) {
19348
+ return { ok: false, reason: "expired", detail: envelope.expiresAt };
19349
+ }
19350
+ }
19351
+ const issuer = options.issuerOverride ?? findIssuer(envelope.issuerId);
19352
+ if (!issuer) {
19353
+ return { ok: false, reason: "unknown-issuer", detail: envelope.issuerId };
19354
+ }
19355
+ let signatureBytes;
19356
+ try {
19357
+ signatureBytes = Buffer.from(envelope.signature, "base64");
19358
+ } catch {
19359
+ return { ok: false, reason: "bad-signature", detail: "signature is not base64" };
19360
+ }
19361
+ let ok = false;
19362
+ try {
19363
+ const { signature: _omit, ...unsigned } = envelope;
19364
+ const payload = buildSigningPayload(unsigned);
19365
+ const pubKey = crypto.createPublicKey({ key: issuer.publicKeyPem, format: "pem" });
19366
+ ok = crypto.verify(null, Buffer.from(payload, "utf8"), pubKey, signatureBytes);
19367
+ } catch (err) {
19368
+ return { ok: false, reason: "bad-signature", detail: err.message };
19369
+ }
19370
+ if (!ok) return { ok: false, reason: "bad-signature" };
19371
+ return { ok: true, issuer };
19372
+ }
19373
+ function isWellFormedEnvelope(x) {
19374
+ if (!x || typeof x !== "object") return false;
19375
+ const e = x;
19376
+ return e.version === 1 && typeof e.envelopeId === "string" && typeof e.issuerId === "string" && e.algorithm === "ed25519" && typeof e.signature === "string" && typeof e.issuedAt === "string" && typeof e.nonce === "string" && !!e.subject && typeof e.subject.kitId === "string" && typeof e.subject.forkId === "string" && !!e.grants && Array.isArray(e.grants.capabilities) && typeof e.grants.policyAttested === "boolean";
19377
+ }
19378
+ function readForkAuthorityState(forkPath) {
19379
+ const p33 = resolveInForkAuthorityPath(forkPath);
19380
+ if (!fs26.existsSync(p33)) {
19381
+ return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
19382
+ }
19383
+ try {
19384
+ const parsed = JSON.parse(fs26.readFileSync(p33, "utf8"));
19385
+ if (!parsed || parsed.version !== 1) {
19386
+ return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
19387
+ }
19388
+ return parsed;
19389
+ } catch {
19390
+ return { state: "none", version: 1, updatedAt: (/* @__PURE__ */ new Date(0)).toISOString() };
19391
+ }
19392
+ }
19393
+ function writeForkAuthorityState(forkPath, state) {
19394
+ const p33 = resolveInForkAuthorityPath(forkPath);
19395
+ fs26.mkdirSync(path33.dirname(p33), { recursive: true });
19396
+ fs26.writeFileSync(p33, JSON.stringify(state, null, 2) + "\n", "utf8");
19397
+ }
19398
+ function attachAuthorityEnvelope(forkPath, envelope, options = {}) {
19399
+ const verification = verifyAuthorityEnvelope(envelope, options);
19400
+ if (!verification.ok) {
19401
+ throw new Error(
19402
+ `Authority envelope rejected (${verification.reason}${verification.detail ? `: ${verification.detail}` : ""}).`
19403
+ );
19404
+ }
19405
+ const state = {
19406
+ state: "attested",
19407
+ version: 1,
19408
+ envelope,
19409
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
19410
+ };
19411
+ writeForkAuthorityState(forkPath, state);
19412
+ return { state, verification };
19413
+ }
19414
+ function revokeForkAuthority(forkPath, reason) {
19415
+ const current = readForkAuthorityState(forkPath);
19416
+ if (current.state !== "attested") {
19417
+ throw new Error("No active authority envelope to revoke.");
19418
+ }
19419
+ const next = {
19420
+ state: "revoked",
19421
+ version: 1,
19422
+ envelope: current.envelope,
19423
+ revocation: { reason, revokedAt: (/* @__PURE__ */ new Date()).toISOString() },
19424
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
19425
+ };
19426
+ writeForkAuthorityState(forkPath, next);
19427
+ return next;
19428
+ }
19429
+ function describePolicyAttestation(forkPath, policy, options = {}) {
19430
+ const stateRecord = readForkAuthorityState(forkPath);
19431
+ if (stateRecord.state === "none") {
19432
+ return { origin: "operator-local" };
19433
+ }
19434
+ if (stateRecord.state === "revoked") {
19435
+ return {
19436
+ origin: "authority-revoked",
19437
+ envelope: stateRecord.envelope,
19438
+ revokedReason: stateRecord.revocation.reason
19439
+ };
19440
+ }
19441
+ const envelope = stateRecord.envelope;
19442
+ const verification = verifyAuthorityEnvelope(envelope, {
19443
+ expectedForkId: options.expectedForkId,
19444
+ expectedKitId: options.expectedKitId,
19445
+ now: options.now
19446
+ });
19447
+ if (!verification.ok) {
19448
+ return { origin: "operator-local", envelope, verification };
19449
+ }
19450
+ const policyHashMatches = envelope.subject.policyHash ? envelope.subject.policyHash === computePolicyHash(policy) : void 0;
19451
+ return {
19452
+ origin: "authority-attested",
19453
+ envelope,
19454
+ verification,
19455
+ policyHashMatches
19456
+ };
19457
+ }
19458
+
19459
+ // src/commands/kit-fork.ts
19222
19460
  function hr(width = 72) {
19223
19461
  return pc29.dim("\u2500".repeat(width));
19224
19462
  }
@@ -20367,6 +20605,7 @@ function addForkSubcommands(parentCmd) {
20367
20605
  console.log(hr());
20368
20606
  console.log("");
20369
20607
  });
20608
+ registerAuthoritySubcommands(parentCmd);
20370
20609
  parentCmd.command("deregister").description("Remove a fork registration (does not delete your fork directory)").argument("<fork-id>", "Fork ID to deregister").action(async (forkId) => {
20371
20610
  const allForks = listKitForkRegistrations();
20372
20611
  const reg = allForks.find((f) => f.forkId === forkId);
@@ -20392,6 +20631,193 @@ function addForkSubcommands(parentCmd) {
20392
20631
  }
20393
20632
  });
20394
20633
  }
20634
+ function registerAuthoritySubcommands(parentCmd) {
20635
+ const authorityCmd = parentCmd.command("authority").description("Inspect and manage hosted-authority attestations attached to a fork");
20636
+ authorityCmd.command("status").description("Show current authority attestation state for a fork").argument("<fork-id>", "Fork ID from list").option("--json", "Emit machine-readable JSON").action((forkId, opts) => {
20637
+ const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
20638
+ if (!reg) {
20639
+ console.error(pc29.red(`Fork not found: ${forkId}`));
20640
+ process.exitCode = 1;
20641
+ return;
20642
+ }
20643
+ const policy = readKitForkPolicy(reg.forkPath);
20644
+ const summary = describePolicyAttestation(reg.forkPath, policy, {
20645
+ expectedForkId: reg.forkId,
20646
+ expectedKitId: reg.kitId
20647
+ });
20648
+ const state = readForkAuthorityState(reg.forkPath);
20649
+ if (opts.json) {
20650
+ console.log(JSON.stringify({ forkId: reg.forkId, kitId: reg.kitId, state, summary }, null, 2));
20651
+ return;
20652
+ }
20653
+ console.log("");
20654
+ console.log(pc29.bold(`Authority: ${reg.forkId}`));
20655
+ console.log(hr());
20656
+ console.log(` ${pc29.dim("Origin:")} ${originLabel(summary.origin)}`);
20657
+ if (summary.envelope) {
20658
+ const env = summary.envelope;
20659
+ console.log(` ${pc29.dim("Issuer:")} ${env.issuerId}`);
20660
+ console.log(` ${pc29.dim("Envelope ID:")} ${env.envelopeId}`);
20661
+ console.log(` ${pc29.dim("Issued at:")} ${env.issuedAt}`);
20662
+ if (env.expiresAt) console.log(` ${pc29.dim("Expires at:")} ${env.expiresAt}`);
20663
+ console.log(` ${pc29.dim("Capabilities:")} ${env.grants.capabilities.join(", ") || pc29.dim("(none)")}`);
20664
+ console.log(` ${pc29.dim("Policy attested:")} ${env.grants.policyAttested ? "yes" : "no"}`);
20665
+ if (summary.policyHashMatches === true) {
20666
+ console.log(` ${pc29.dim("Policy hash:")} ${pc29.green("matches on-disk policy")}`);
20667
+ } else if (summary.policyHashMatches === false) {
20668
+ console.log(` ${pc29.dim("Policy hash:")} ${pc29.yellow("MISMATCH \u2014 policy changed since attestation")}`);
20669
+ }
20670
+ if (summary.verification && !summary.verification.ok) {
20671
+ console.log(` ${pc29.dim("Verification:")} ${pc29.red(summary.verification.reason)}${summary.verification.detail ? pc29.dim(` (${summary.verification.detail})`) : ""}`);
20672
+ }
20673
+ if (summary.origin === "authority-revoked") {
20674
+ console.log(` ${pc29.dim("Revoked reason:")} ${summary.revokedReason ?? pc29.dim("(none)")}`);
20675
+ }
20676
+ } else {
20677
+ console.log(pc29.dim(" No authority envelope attached. Operator-local policy in effect."));
20678
+ }
20679
+ console.log("");
20680
+ });
20681
+ authorityCmd.command("attest").description("Attach a signed authority envelope to a fork").argument("<fork-id>", "Fork ID from list").requiredOption("--file <path>", "Path to a JSON file containing the envelope").action((forkId, opts) => {
20682
+ const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
20683
+ if (!reg) {
20684
+ console.error(pc29.red(`Fork not found: ${forkId}`));
20685
+ process.exitCode = 1;
20686
+ return;
20687
+ }
20688
+ let raw;
20689
+ try {
20690
+ raw = fs27.readFileSync(opts.file, "utf8");
20691
+ } catch (err) {
20692
+ console.error(pc29.red(`Cannot read envelope file: ${err.message}`));
20693
+ process.exitCode = 1;
20694
+ return;
20695
+ }
20696
+ let envelope;
20697
+ try {
20698
+ envelope = JSON.parse(raw);
20699
+ } catch (err) {
20700
+ console.error(pc29.red(`Invalid JSON in envelope file: ${err.message}`));
20701
+ process.exitCode = 1;
20702
+ return;
20703
+ }
20704
+ try {
20705
+ const result = attachAuthorityEnvelope(reg.forkPath, envelope, {
20706
+ expectedForkId: reg.forkId,
20707
+ expectedKitId: reg.kitId
20708
+ });
20709
+ appendKitForkTraceEvent(reg.forkPath, {
20710
+ forkId: reg.forkId,
20711
+ kitId: reg.kitId,
20712
+ type: "authority_attested",
20713
+ summary: `Authority envelope ${envelope.envelopeId} attached (issuer ${envelope.issuerId})`,
20714
+ detail: {
20715
+ envelopeId: envelope.envelopeId,
20716
+ issuerId: envelope.issuerId,
20717
+ capabilities: envelope.grants.capabilities,
20718
+ expiresAt: envelope.expiresAt ?? null
20719
+ }
20720
+ });
20721
+ console.log(pc29.green("Authority envelope attached:"), result.state.state);
20722
+ if (result.verification.ok) {
20723
+ console.log(pc29.dim(" Issuer: "), result.verification.issuer.id);
20724
+ console.log(pc29.dim(" Capabilities:"), envelope.grants.capabilities.join(", ") || pc29.dim("(none)"));
20725
+ }
20726
+ } catch (err) {
20727
+ console.error(pc29.red(err.message));
20728
+ process.exitCode = 1;
20729
+ }
20730
+ });
20731
+ authorityCmd.command("revoke").description("Revoke the local authority attestation for a fork").argument("<fork-id>", "Fork ID from list").option("--reason <text>", "Why the attestation is being revoked").action((forkId, opts) => {
20732
+ const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
20733
+ if (!reg) {
20734
+ console.error(pc29.red(`Fork not found: ${forkId}`));
20735
+ process.exitCode = 1;
20736
+ return;
20737
+ }
20738
+ try {
20739
+ const next = revokeForkAuthority(reg.forkPath, opts.reason);
20740
+ appendKitForkTraceEvent(reg.forkPath, {
20741
+ forkId: reg.forkId,
20742
+ kitId: reg.kitId,
20743
+ type: "authority_revoked",
20744
+ summary: `Authority envelope ${next.state === "revoked" ? next.envelope.envelopeId : ""} revoked`,
20745
+ detail: { reason: opts.reason ?? null }
20746
+ });
20747
+ console.log(pc29.yellow("Authority attestation revoked."));
20748
+ if (opts.reason) console.log(pc29.dim(" Reason:"), opts.reason);
20749
+ } catch (err) {
20750
+ console.error(pc29.red(err.message));
20751
+ process.exitCode = 1;
20752
+ }
20753
+ });
20754
+ const issuerCmd = authorityCmd.command("issuer").description("Manage trusted authority issuers (local trust root)");
20755
+ issuerCmd.command("list").description("List trusted issuers from the local registry").option("--json", "Emit machine-readable JSON").action((opts) => {
20756
+ const reg = readIssuerRegistry();
20757
+ if (opts.json) {
20758
+ console.log(JSON.stringify(reg, null, 2));
20759
+ return;
20760
+ }
20761
+ console.log("");
20762
+ console.log(pc29.bold("Trusted Authority Issuers"));
20763
+ console.log(hr());
20764
+ if (reg.issuers.length === 0) {
20765
+ console.log(pc29.dim(" No issuers trusted. Add one with `growthub kit fork authority issuer add`."));
20766
+ } else {
20767
+ for (const i of reg.issuers) {
20768
+ console.log(` ${pc29.cyan(i.id)} ${pc29.dim(`[${i.kind}]`)}${i.label ? ` ${i.label}` : ""}`);
20769
+ if (i.addedAt) console.log(` ${pc29.dim("added:")} ${i.addedAt}`);
20770
+ }
20771
+ }
20772
+ console.log("");
20773
+ });
20774
+ issuerCmd.command("add").description("Add or replace a trusted issuer in the local registry").requiredOption("--id <id>", "Issuer ID (must match envelope.issuerId)").requiredOption("--kind <kind>", "Issuer kind: growthub-hosted | self-signed | enterprise").requiredOption("--key <path>", "Path to a PEM-encoded ed25519 public key file").option("--label <label>", "Human-readable label").action((opts) => {
20775
+ const kinds = ["growthub-hosted", "self-signed", "enterprise"];
20776
+ if (!kinds.includes(opts.kind)) {
20777
+ console.error(pc29.red(`Invalid --kind: ${opts.kind}. Expected one of ${kinds.join(", ")}.`));
20778
+ process.exitCode = 1;
20779
+ return;
20780
+ }
20781
+ let pem;
20782
+ try {
20783
+ pem = fs27.readFileSync(opts.key, "utf8");
20784
+ } catch (err) {
20785
+ console.error(pc29.red(`Cannot read key file: ${err.message}`));
20786
+ process.exitCode = 1;
20787
+ return;
20788
+ }
20789
+ try {
20790
+ upsertIssuer({
20791
+ id: opts.id,
20792
+ kind: opts.kind,
20793
+ publicKeyPem: pem,
20794
+ label: opts.label
20795
+ });
20796
+ console.log(pc29.green("Issuer added:"), opts.id);
20797
+ } catch (err) {
20798
+ console.error(pc29.red(err.message));
20799
+ process.exitCode = 1;
20800
+ }
20801
+ });
20802
+ issuerCmd.command("remove").description("Remove a trusted issuer from the local registry").requiredOption("--id <id>", "Issuer ID to remove").action((opts) => {
20803
+ const ok = removeIssuer(opts.id);
20804
+ if (ok) console.log(pc29.green("Issuer removed:"), opts.id);
20805
+ else {
20806
+ console.error(pc29.red(`No issuer with id: ${opts.id}`));
20807
+ process.exitCode = 1;
20808
+ }
20809
+ });
20810
+ }
20811
+ function originLabel(origin) {
20812
+ switch (origin) {
20813
+ case "authority-attested":
20814
+ return pc29.green("authority-attested");
20815
+ case "authority-revoked":
20816
+ return pc29.yellow("authority-revoked");
20817
+ default:
20818
+ return pc29.dim("operator-local");
20819
+ }
20820
+ }
20395
20821
 
20396
20822
  // src/commands/kit.ts
20397
20823
  var TYPE_CONFIG = {
@@ -20836,7 +21262,7 @@ Examples:
20836
21262
  $ growthub kit validate ./my-kit
20837
21263
  $ growthub kit validate ~/kits/growthub-open-higgsfield-studio-v1
20838
21264
  `).action((kitPath) => {
20839
- const resolvedPath = path33.resolve(kitPath);
21265
+ const resolvedPath = path34.resolve(kitPath);
20840
21266
  const result = validateKitDirectory(resolvedPath);
20841
21267
  console.log("");
20842
21268
  console.log(pc30.bold("Kit: " + result.kitId) + pc30.dim(" schema v" + result.schemaVersion));
@@ -20881,13 +21307,13 @@ Examples:
20881
21307
  }
20882
21308
 
20883
21309
  // src/commands/template.ts
20884
- import path35 from "node:path";
21310
+ import path36 from "node:path";
20885
21311
  import * as p20 from "@clack/prompts";
20886
21312
  import pc31 from "picocolors";
20887
21313
 
20888
21314
  // src/templates/service.ts
20889
- import fs26 from "node:fs";
20890
- import path34 from "node:path";
21315
+ import fs28 from "node:fs";
21316
+ import path35 from "node:path";
20891
21317
  import { fileURLToPath as fileURLToPath6 } from "node:url";
20892
21318
 
20893
21319
  // src/templates/catalog.ts
@@ -21134,12 +21560,12 @@ var TEMPLATE_CATALOG = [
21134
21560
 
21135
21561
  // src/templates/service.ts
21136
21562
  function resolveSharedTemplatesRoot() {
21137
- const moduleDir = path34.dirname(fileURLToPath6(import.meta.url));
21563
+ const moduleDir = path35.dirname(fileURLToPath6(import.meta.url));
21138
21564
  for (const candidate of [
21139
- path34.resolve(moduleDir, "../../assets/shared-templates"),
21140
- path34.resolve(moduleDir, "../assets/shared-templates")
21565
+ path35.resolve(moduleDir, "../../assets/shared-templates"),
21566
+ path35.resolve(moduleDir, "../assets/shared-templates")
21141
21567
  ]) {
21142
- if (fs26.existsSync(candidate)) return candidate;
21568
+ if (fs28.existsSync(candidate)) return candidate;
21143
21569
  }
21144
21570
  throw new Error("Shared template assets not found at cli/assets/shared-templates/");
21145
21571
  }
@@ -21174,15 +21600,15 @@ function getArtifact(slugOrId) {
21174
21600
  const artifact = resolveSlug(slugOrId);
21175
21601
  if (!artifact) throw new Error(`Unknown template '${slugOrId}'. Run 'growthub template list' to browse.`);
21176
21602
  const root = resolveSharedTemplatesRoot();
21177
- const absolutePath = path34.resolve(root, artifact.path);
21178
- if (!fs26.existsSync(absolutePath)) throw new Error(`Template file missing: ${absolutePath}`);
21179
- return { artifact, content: fs26.readFileSync(absolutePath, "utf8"), absolutePath };
21603
+ const absolutePath = path35.resolve(root, artifact.path);
21604
+ if (!fs28.existsSync(absolutePath)) throw new Error(`Template file missing: ${absolutePath}`);
21605
+ return { artifact, content: fs28.readFileSync(absolutePath, "utf8"), absolutePath };
21180
21606
  }
21181
21607
  function copyArtifact(slugOrId, destDir) {
21182
21608
  const resolved = getArtifact(slugOrId);
21183
- fs26.mkdirSync(destDir, { recursive: true });
21184
- const destPath = path34.resolve(destDir, path34.basename(resolved.absolutePath));
21185
- fs26.copyFileSync(resolved.absolutePath, destPath);
21609
+ fs28.mkdirSync(destDir, { recursive: true });
21610
+ const destPath = path35.resolve(destDir, path35.basename(resolved.absolutePath));
21611
+ fs28.copyFileSync(resolved.absolutePath, destPath);
21186
21612
  return destPath;
21187
21613
  }
21188
21614
  var GROUP_ORDER = ["ad-formats", "scene-modules/hooks", "scene-modules/body", "scene-modules/cta"];
@@ -21422,7 +21848,7 @@ async function runTemplatePicker(opts) {
21422
21848
  p20.cancel("Cancelled.");
21423
21849
  process.exit(0);
21424
21850
  }
21425
- const destDir = path35.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
21851
+ const destDir = path36.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
21426
21852
  const destPath = copyArtifact(selected.id, destDir);
21427
21853
  p20.outro(pc31.green("Copied \u2192 ") + destPath);
21428
21854
  return "done";
@@ -21496,7 +21922,7 @@ Any agent or kit resolves them by slug.
21496
21922
  return;
21497
21923
  }
21498
21924
  if (opts.out) {
21499
- const destDir = path35.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
21925
+ const destDir = path36.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
21500
21926
  try {
21501
21927
  const dest = copyArtifact(artifact.id, destDir);
21502
21928
  console.log(pc31.green("Copied \u2192 ") + dest);
@@ -22158,10 +22584,10 @@ function createCmsCapabilityRegistryClient() {
22158
22584
  }
22159
22585
 
22160
22586
  // src/runtime/machine-capability-resolver/index.ts
22161
- import os8 from "node:os";
22587
+ import os9 from "node:os";
22162
22588
  function buildMachineContext(profile) {
22163
22589
  return {
22164
- hostname: os8.hostname(),
22590
+ hostname: os9.hostname(),
22165
22591
  machineLabel: profile.local.machineLabel ?? void 0,
22166
22592
  workspaceLabel: profile.local.workspaceLabel ?? void 0,
22167
22593
  instanceId: profile.local.instanceId,
@@ -22580,8 +23006,8 @@ Examples:
22580
23006
  // src/commands/pipeline.ts
22581
23007
  init_session_store();
22582
23008
  init_hosted_client();
22583
- import fs29 from "node:fs";
22584
- import path38 from "node:path";
23009
+ import fs31 from "node:fs";
23010
+ import path39 from "node:path";
22585
23011
  import * as p22 from "@clack/prompts";
22586
23012
  import pc34 from "picocolors";
22587
23013
 
@@ -23091,40 +23517,40 @@ function renderPreSaveReview(input) {
23091
23517
 
23092
23518
  // src/runtime/artifact-contracts/index.ts
23093
23519
  init_home();
23094
- import fs27 from "node:fs";
23095
- import path36 from "node:path";
23520
+ import fs29 from "node:fs";
23521
+ import path37 from "node:path";
23096
23522
  import { randomBytes as randomBytes7 } from "node:crypto";
23097
23523
  function generateArtifactId() {
23098
23524
  return `art_${randomBytes7(8).toString("hex")}`;
23099
23525
  }
23100
23526
  function resolveArtifactsDir() {
23101
- return path36.resolve(resolvePaperclipHomeDir(), "artifacts");
23527
+ return path37.resolve(resolvePaperclipHomeDir(), "artifacts");
23102
23528
  }
23103
23529
  function resolveArtifactManifestPath(artifactId) {
23104
- return path36.resolve(resolveArtifactsDir(), `${artifactId}.json`);
23530
+ return path37.resolve(resolveArtifactsDir(), `${artifactId}.json`);
23105
23531
  }
23106
23532
  function readLocalManifest(artifactId) {
23107
23533
  const filePath = resolveArtifactManifestPath(artifactId);
23108
- if (!fs27.existsSync(filePath)) return null;
23534
+ if (!fs29.existsSync(filePath)) return null;
23109
23535
  try {
23110
- return JSON.parse(fs27.readFileSync(filePath, "utf-8"));
23536
+ return JSON.parse(fs29.readFileSync(filePath, "utf-8"));
23111
23537
  } catch {
23112
23538
  return null;
23113
23539
  }
23114
23540
  }
23115
23541
  function writeLocalManifest(manifest) {
23116
23542
  const dir = resolveArtifactsDir();
23117
- fs27.mkdirSync(dir, { recursive: true });
23543
+ fs29.mkdirSync(dir, { recursive: true });
23118
23544
  const filePath = resolveArtifactManifestPath(manifest.id);
23119
- fs27.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
23545
+ fs29.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
23120
23546
  `, { mode: 384 });
23121
23547
  }
23122
23548
  function listLocalManifests() {
23123
23549
  const dir = resolveArtifactsDir();
23124
- if (!fs27.existsSync(dir)) return [];
23125
- return fs27.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => {
23550
+ if (!fs29.existsSync(dir)) return [];
23551
+ return fs29.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => {
23126
23552
  try {
23127
- const content = fs27.readFileSync(path36.resolve(dir, entry.name), "utf-8");
23553
+ const content = fs29.readFileSync(path37.resolve(dir, entry.name), "utf-8");
23128
23554
  return JSON.parse(content);
23129
23555
  } catch {
23130
23556
  return null;
@@ -23202,8 +23628,8 @@ function createArtifactStore() {
23202
23628
 
23203
23629
  // src/runtime/native-intelligence/index.ts
23204
23630
  init_home();
23205
- import fs28 from "node:fs";
23206
- import path37 from "node:path";
23631
+ import fs30 from "node:fs";
23632
+ import path38 from "node:path";
23207
23633
 
23208
23634
  // src/runtime/native-intelligence/contract.ts
23209
23635
  var DEFAULT_INTELLIGENCE_CONFIG = {
@@ -24313,15 +24739,15 @@ function parseJsonSafe4(text66) {
24313
24739
 
24314
24740
  // src/runtime/native-intelligence/index.ts
24315
24741
  function resolveConfigPath2() {
24316
- return path37.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
24742
+ return path38.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
24317
24743
  }
24318
24744
  function readIntelligenceConfig() {
24319
24745
  const configPath = resolveConfigPath2();
24320
- if (!fs28.existsSync(configPath)) {
24746
+ if (!fs30.existsSync(configPath)) {
24321
24747
  return { ...DEFAULT_INTELLIGENCE_CONFIG };
24322
24748
  }
24323
24749
  try {
24324
- const raw = JSON.parse(fs28.readFileSync(configPath, "utf-8"));
24750
+ const raw = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
24325
24751
  return {
24326
24752
  modelId: validateModelId(raw.modelId),
24327
24753
  backendType: raw.backendType === "hosted" ? "hosted" : "local",
@@ -24338,8 +24764,8 @@ function readIntelligenceConfig() {
24338
24764
  }
24339
24765
  function writeIntelligenceConfig(config) {
24340
24766
  const configPath = resolveConfigPath2();
24341
- fs28.mkdirSync(path37.dirname(configPath), { recursive: true });
24342
- fs28.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
24767
+ fs30.mkdirSync(path38.dirname(configPath), { recursive: true });
24768
+ fs30.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
24343
24769
  `, "utf-8");
24344
24770
  }
24345
24771
  function validateModelId(id) {
@@ -24695,9 +25121,9 @@ async function runPipelineAssembler(opts) {
24695
25121
  }
24696
25122
  }
24697
25123
  function loadPipelineFromFileOrJson(input) {
24698
- const resolvedPath = path38.resolve(input);
24699
- if (fs29.existsSync(resolvedPath)) {
24700
- const content = fs29.readFileSync(resolvedPath, "utf-8");
25124
+ const resolvedPath = path39.resolve(input);
25125
+ if (fs31.existsSync(resolvedPath)) {
25126
+ const content = fs31.readFileSync(resolvedPath, "utf-8");
24701
25127
  return deserializePipeline(JSON.parse(content));
24702
25128
  }
24703
25129
  try {
@@ -25242,8 +25668,8 @@ Examples:
25242
25668
  }
25243
25669
 
25244
25670
  // src/commands/workflow.ts
25245
- import fs31 from "node:fs";
25246
- import path40 from "node:path";
25671
+ import fs33 from "node:fs";
25672
+ import path41 from "node:path";
25247
25673
  import * as p23 from "@clack/prompts";
25248
25674
  import pc37 from "picocolors";
25249
25675
  init_session_store();
@@ -25251,15 +25677,15 @@ init_hosted_client();
25251
25677
 
25252
25678
  // src/runtime/workflow-hygiene/labels.ts
25253
25679
  init_home();
25254
- import fs30 from "node:fs";
25255
- import path39 from "node:path";
25680
+ import fs32 from "node:fs";
25681
+ import path40 from "node:path";
25256
25682
  function resolveStorePath() {
25257
- return path39.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
25683
+ return path40.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
25258
25684
  }
25259
25685
  function readStoreFile(filePath) {
25260
- if (!fs30.existsSync(filePath)) return { records: [] };
25686
+ if (!fs32.existsSync(filePath)) return { records: [] };
25261
25687
  try {
25262
- const raw = JSON.parse(fs30.readFileSync(filePath, "utf-8"));
25688
+ const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
25263
25689
  if (!Array.isArray(raw.records)) return { records: [] };
25264
25690
  return raw;
25265
25691
  } catch {
@@ -25267,8 +25693,8 @@ function readStoreFile(filePath) {
25267
25693
  }
25268
25694
  }
25269
25695
  function writeStoreFile(filePath, data) {
25270
- fs30.mkdirSync(path39.dirname(filePath), { recursive: true });
25271
- fs30.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
25696
+ fs32.mkdirSync(path40.dirname(filePath), { recursive: true });
25697
+ fs32.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
25272
25698
  `, "utf-8");
25273
25699
  }
25274
25700
  function inferDefaultLabel(name, createdAt, versionCount) {
@@ -25369,16 +25795,16 @@ function box5(lines) {
25369
25795
  return [top, ...body, bottom].join("\n");
25370
25796
  }
25371
25797
  function resolveSavedWorkflowsDir() {
25372
- return path40.resolve(resolvePaperclipHomeDir(), "workflows");
25798
+ return path41.resolve(resolvePaperclipHomeDir(), "workflows");
25373
25799
  }
25374
25800
  function resolveDeletedWorkflowIdsPath() {
25375
- return path40.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
25801
+ return path41.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
25376
25802
  }
25377
25803
  function readDeletedWorkflowIds() {
25378
25804
  const filePath = resolveDeletedWorkflowIdsPath();
25379
- if (!fs31.existsSync(filePath)) return /* @__PURE__ */ new Set();
25805
+ if (!fs33.existsSync(filePath)) return /* @__PURE__ */ new Set();
25380
25806
  try {
25381
- const raw = JSON.parse(fs31.readFileSync(filePath, "utf-8"));
25807
+ const raw = JSON.parse(fs33.readFileSync(filePath, "utf-8"));
25382
25808
  if (!Array.isArray(raw?.workflowIds)) return /* @__PURE__ */ new Set();
25383
25809
  return new Set(raw.workflowIds.filter((value) => typeof value === "string"));
25384
25810
  } catch {
@@ -25387,8 +25813,8 @@ function readDeletedWorkflowIds() {
25387
25813
  }
25388
25814
  function writeDeletedWorkflowIds(ids) {
25389
25815
  const filePath = resolveDeletedWorkflowIdsPath();
25390
- fs31.mkdirSync(path40.dirname(filePath), { recursive: true });
25391
- fs31.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
25816
+ fs33.mkdirSync(path41.dirname(filePath), { recursive: true });
25817
+ fs33.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
25392
25818
  `, "utf-8");
25393
25819
  }
25394
25820
  function markWorkflowDeletedLocally(workflowId) {
@@ -25414,10 +25840,10 @@ function filterLocallyDeletedWorkflows(entries) {
25414
25840
  }
25415
25841
  function listLocalSavedWorkflows() {
25416
25842
  const dir = resolveSavedWorkflowsDir();
25417
- if (!fs31.existsSync(dir)) return [];
25418
- const entries = fs31.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
25843
+ if (!fs33.existsSync(dir)) return [];
25844
+ const entries = fs33.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
25419
25845
  try {
25420
- const raw = JSON.parse(fs31.readFileSync(path40.resolve(dir, e.name), "utf-8"));
25846
+ const raw = JSON.parse(fs33.readFileSync(path41.resolve(dir, e.name), "utf-8"));
25421
25847
  const pipeline = raw.pipeline ?? raw;
25422
25848
  return {
25423
25849
  filename: e.name,
@@ -25478,11 +25904,11 @@ async function archiveSavedWorkflow(entry) {
25478
25904
  throw new Error("Local workflow entry is missing filename.");
25479
25905
  }
25480
25906
  const dir = resolveSavedWorkflowsDir();
25481
- const archiveDir = path40.resolve(dir, "archived");
25482
- fs31.mkdirSync(archiveDir, { recursive: true });
25483
- fs31.renameSync(
25484
- path40.resolve(dir, entry.filename),
25485
- path40.resolve(archiveDir, entry.filename)
25907
+ const archiveDir = path41.resolve(dir, "archived");
25908
+ fs33.mkdirSync(archiveDir, { recursive: true });
25909
+ fs33.renameSync(
25910
+ path41.resolve(dir, entry.filename),
25911
+ path41.resolve(archiveDir, entry.filename)
25486
25912
  );
25487
25913
  }
25488
25914
  async function deleteSavedWorkflow(entry) {
@@ -25506,7 +25932,7 @@ async function deleteSavedWorkflow(entry) {
25506
25932
  if (!entry.filename) {
25507
25933
  throw new Error("Local workflow entry is missing filename.");
25508
25934
  }
25509
- fs31.rmSync(path40.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
25935
+ fs33.rmSync(path41.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
25510
25936
  markWorkflowDeletedLocally(entry.workflowId);
25511
25937
  }
25512
25938
  async function loadSavedWorkflowDetail(entry) {
@@ -25525,7 +25951,7 @@ async function loadSavedWorkflowDetail(entry) {
25525
25951
  };
25526
25952
  }
25527
25953
  const dir = resolveSavedWorkflowsDir();
25528
- const content = fs31.readFileSync(path40.resolve(dir, entry.filename), "utf-8");
25954
+ const content = fs33.readFileSync(path41.resolve(dir, entry.filename), "utf-8");
25529
25955
  const raw = JSON.parse(content);
25530
25956
  return {
25531
25957
  pipeline: raw.pipeline ?? raw,
@@ -26509,36 +26935,36 @@ import pc38 from "picocolors";
26509
26935
 
26510
26936
  // src/runtime/agent-harness/auth-store.ts
26511
26937
  init_home();
26512
- import fs32 from "node:fs";
26513
- import path41 from "node:path";
26938
+ import fs34 from "node:fs";
26939
+ import path42 from "node:path";
26514
26940
  function resolveHarnessAuthDir() {
26515
- return path41.resolve(resolvePaperclipHomeDir(), "harness-auth");
26941
+ return path42.resolve(resolvePaperclipHomeDir(), "harness-auth");
26516
26942
  }
26517
26943
  function resolveHarnessAuthFile(harnessId) {
26518
- return path41.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
26944
+ return path42.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
26519
26945
  }
26520
26946
  function normalizeSecret(value) {
26521
26947
  const trimmed = value?.trim();
26522
26948
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
26523
26949
  }
26524
26950
  function ensureSecureDir(dirPath) {
26525
- fs32.mkdirSync(dirPath, { recursive: true });
26951
+ fs34.mkdirSync(dirPath, { recursive: true });
26526
26952
  try {
26527
- fs32.chmodSync(dirPath, 448);
26953
+ fs34.chmodSync(dirPath, 448);
26528
26954
  } catch {
26529
26955
  }
26530
26956
  }
26531
26957
  function ensureSecureFile(filePath) {
26532
26958
  try {
26533
- fs32.chmodSync(filePath, 384);
26959
+ fs34.chmodSync(filePath, 384);
26534
26960
  } catch {
26535
26961
  }
26536
26962
  }
26537
26963
  function readHarnessCredentials(harnessId) {
26538
26964
  const filePath = resolveHarnessAuthFile(harnessId);
26539
- if (!fs32.existsSync(filePath)) return {};
26965
+ if (!fs34.existsSync(filePath)) return {};
26540
26966
  try {
26541
- const parsed = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
26967
+ const parsed = JSON.parse(fs34.readFileSync(filePath, "utf-8"));
26542
26968
  const creds = {};
26543
26969
  for (const [key, value] of Object.entries(parsed)) {
26544
26970
  if (typeof value === "string" && value.trim().length > 0) {
@@ -26565,7 +26991,7 @@ function setHarnessCredential(harnessId, key, value) {
26565
26991
  const dirPath = resolveHarnessAuthDir();
26566
26992
  ensureSecureDir(dirPath);
26567
26993
  const filePath = resolveHarnessAuthFile(harnessId);
26568
- fs32.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
26994
+ fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
26569
26995
  `, "utf-8");
26570
26996
  ensureSecureFile(filePath);
26571
26997
  }
@@ -26582,7 +27008,7 @@ function setHarnessCredentials(harnessId, updates) {
26582
27008
  const dirPath = resolveHarnessAuthDir();
26583
27009
  ensureSecureDir(dirPath);
26584
27010
  const filePath = resolveHarnessAuthFile(harnessId);
26585
- fs32.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
27011
+ fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
26586
27012
  `, "utf-8");
26587
27013
  ensureSecureFile(filePath);
26588
27014
  }
@@ -26594,8 +27020,8 @@ function maskSecret(value) {
26594
27020
 
26595
27021
  // src/runtime/open-agents/index.ts
26596
27022
  init_home();
26597
- import fs33 from "node:fs";
26598
- import path42 from "node:path";
27023
+ import fs35 from "node:fs";
27024
+ import path43 from "node:path";
26599
27025
 
26600
27026
  // src/runtime/open-agents/contract.ts
26601
27027
  var DEFAULT_OPEN_AGENTS_CONFIG = {
@@ -26782,18 +27208,18 @@ var OpenAgentsBackendError = class extends Error {
26782
27208
 
26783
27209
  // src/runtime/open-agents/index.ts
26784
27210
  function resolveConfigPath3() {
26785
- return path42.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
27211
+ return path43.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
26786
27212
  }
26787
27213
  function readOpenAgentsConfig() {
26788
27214
  const configPath = resolveConfigPath3();
26789
- if (!fs33.existsSync(configPath)) {
27215
+ if (!fs35.existsSync(configPath)) {
26790
27216
  return {
26791
27217
  ...DEFAULT_OPEN_AGENTS_CONFIG,
26792
27218
  apiKey: getHarnessCredential("open-agents", "apiKey")
26793
27219
  };
26794
27220
  }
26795
27221
  try {
26796
- const raw = JSON.parse(fs33.readFileSync(configPath, "utf-8"));
27222
+ const raw = JSON.parse(fs35.readFileSync(configPath, "utf-8"));
26797
27223
  const storedApiKey = getHarnessCredential("open-agents", "apiKey");
26798
27224
  return {
26799
27225
  backendType: validateBackendType(raw.backendType),
@@ -26814,13 +27240,13 @@ function readOpenAgentsConfig() {
26814
27240
  }
26815
27241
  function writeOpenAgentsConfig(config) {
26816
27242
  const configPath = resolveConfigPath3();
26817
- fs33.mkdirSync(path42.dirname(configPath), { recursive: true });
27243
+ fs35.mkdirSync(path43.dirname(configPath), { recursive: true });
26818
27244
  const persisted = {
26819
27245
  ...config,
26820
27246
  authMode: validateAuthMode(config.authMode),
26821
27247
  apiKey: void 0
26822
27248
  };
26823
- fs33.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
27249
+ fs35.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
26824
27250
  `, "utf-8");
26825
27251
  setHarnessCredential("open-agents", "apiKey", config.apiKey);
26826
27252
  }
@@ -27381,8 +27807,8 @@ import pc39 from "picocolors";
27381
27807
 
27382
27808
  // src/runtime/qwen-code/index.ts
27383
27809
  init_home();
27384
- import fs34 from "node:fs";
27385
- import path43 from "node:path";
27810
+ import fs36 from "node:fs";
27811
+ import path44 from "node:path";
27386
27812
 
27387
27813
  // src/runtime/qwen-code/contract.ts
27388
27814
  var QWEN_CODE_APPROVAL_MODES = [
@@ -27600,19 +28026,19 @@ function buildSetupGuidance(env) {
27600
28026
 
27601
28027
  // src/runtime/qwen-code/index.ts
27602
28028
  function resolveConfigPath4() {
27603
- return path43.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
28029
+ return path44.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
27604
28030
  }
27605
28031
  function readQwenCodeConfig() {
27606
28032
  const configPath = resolveConfigPath4();
27607
28033
  const storedCredentials = readHarnessCredentials("qwen-code");
27608
- if (!fs34.existsSync(configPath)) {
28034
+ if (!fs36.existsSync(configPath)) {
27609
28035
  return {
27610
28036
  ...DEFAULT_QWEN_CODE_CONFIG,
27611
28037
  env: mergeHarnessEnv(DEFAULT_QWEN_CODE_CONFIG.env, storedCredentials)
27612
28038
  };
27613
28039
  }
27614
28040
  try {
27615
- const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
28041
+ const raw = JSON.parse(fs36.readFileSync(configPath, "utf-8"));
27616
28042
  return {
27617
28043
  binaryPath: typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_QWEN_CODE_CONFIG.binaryPath,
27618
28044
  defaultModel: typeof raw.defaultModel === "string" ? raw.defaultModel : DEFAULT_QWEN_CODE_CONFIG.defaultModel,
@@ -27634,7 +28060,7 @@ function readQwenCodeConfig() {
27634
28060
  }
27635
28061
  function writeQwenCodeConfig(config) {
27636
28062
  const configPath = resolveConfigPath4();
27637
- fs34.mkdirSync(path43.dirname(configPath), { recursive: true });
28063
+ fs36.mkdirSync(path44.dirname(configPath), { recursive: true });
27638
28064
  const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
27639
28065
  const credentialUpdates = {};
27640
28066
  const publicEnv = {};
@@ -27646,7 +28072,7 @@ function writeQwenCodeConfig(config) {
27646
28072
  publicEnv[key] = value;
27647
28073
  }
27648
28074
  setHarnessCredentials("qwen-code", credentialUpdates);
27649
- fs34.writeFileSync(
28075
+ fs36.writeFileSync(
27650
28076
  configPath,
27651
28077
  `${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
27652
28078
  `,
@@ -27979,8 +28405,8 @@ import pc42 from "picocolors";
27979
28405
 
27980
28406
  // src/status/probes.ts
27981
28407
  import { spawnSync as spawnSync3 } from "node:child_process";
27982
- import fs35 from "node:fs";
27983
- import path44 from "node:path";
28408
+ import fs37 from "node:fs";
28409
+ import path45 from "node:path";
27984
28410
  var GITHUB_API = "https://api.github.com";
27985
28411
  var NPM_REGISTRY = "https://registry.npmjs.org";
27986
28412
  function isoNow() {
@@ -28145,7 +28571,7 @@ async function probeKitForksIndex(_timeoutMs) {
28145
28571
  try {
28146
28572
  const { resolveKitForksIndexPath: resolveKitForksIndexPath2 } = await Promise.resolve().then(() => (init_kit_forks_home(), kit_forks_home_exports));
28147
28573
  const p33 = resolveKitForksIndexPath2();
28148
- if (!fs35.existsSync(p33)) {
28574
+ if (!fs37.existsSync(p33)) {
28149
28575
  return {
28150
28576
  componentId: "kit-forks-index",
28151
28577
  level: "operational",
@@ -28153,7 +28579,7 @@ async function probeKitForksIndex(_timeoutMs) {
28153
28579
  lastCheckedAt: isoNow()
28154
28580
  };
28155
28581
  }
28156
- const parsed = JSON.parse(fs35.readFileSync(p33, "utf8"));
28582
+ const parsed = JSON.parse(fs37.readFileSync(p33, "utf8"));
28157
28583
  const count = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
28158
28584
  return {
28159
28585
  componentId: "kit-forks-index",
@@ -28222,10 +28648,10 @@ async function probeNode(_timeoutMs) {
28222
28648
  };
28223
28649
  }
28224
28650
  async function probeReleaseBundleArtifacts(_timeoutMs) {
28225
- const distPath = path44.resolve(process.cwd(), "cli/dist/index.js");
28226
- const installerPath = path44.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
28227
- const distOk = fs35.existsSync(distPath);
28228
- const installerOk = fs35.existsSync(installerPath);
28651
+ const distPath = path45.resolve(process.cwd(), "cli/dist/index.js");
28652
+ const installerPath = path45.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
28653
+ const distOk = fs37.existsSync(distPath);
28654
+ const installerOk = fs37.existsSync(installerPath);
28229
28655
  const ok = distOk && installerOk;
28230
28656
  return {
28231
28657
  componentId: "release-bundle",
@@ -28772,7 +29198,7 @@ import pc44 from "picocolors";
28772
29198
 
28773
29199
  // src/fleet/summary.ts
28774
29200
  init_fork_registry();
28775
- import fs45 from "node:fs";
29201
+ import fs47 from "node:fs";
28776
29202
  init_fork_policy();
28777
29203
  init_fork_trace();
28778
29204
  function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
@@ -28792,7 +29218,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
28792
29218
  "conflict_encountered"
28793
29219
  ]);
28794
29220
  function buildForkSummary(reg) {
28795
- if (!fs45.existsSync(reg.forkPath)) {
29221
+ if (!fs47.existsSync(reg.forkPath)) {
28796
29222
  return {
28797
29223
  forkId: reg.forkId,
28798
29224
  kitId: reg.kitId,
@@ -29253,8 +29679,8 @@ async function fleetApprovals(opts) {
29253
29679
  p30.log.message(
29254
29680
  ` \xB7 ${pc44.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
29255
29681
  );
29256
- for (const path56 of entry.pendingPaths.slice(0, 6)) {
29257
- p30.log.message(` ${pc44.dim("awaits")} ${path56}`);
29682
+ for (const path57 of entry.pendingPaths.slice(0, 6)) {
29683
+ p30.log.message(` ${pc44.dim("awaits")} ${path57}`);
29258
29684
  }
29259
29685
  if (entry.pendingPaths.length > 6) {
29260
29686
  p30.log.message(` ${pc44.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
@@ -29321,14 +29747,14 @@ init_banner();
29321
29747
  init_home();
29322
29748
  function resolveCliVersion() {
29323
29749
  try {
29324
- const moduleDir = path55.dirname(fileURLToPath7(import.meta.url));
29750
+ const moduleDir = path56.dirname(fileURLToPath7(import.meta.url));
29325
29751
  const candidates = [
29326
- path55.resolve(moduleDir, "../package.json"),
29327
- path55.resolve(moduleDir, "../../package.json")
29752
+ path56.resolve(moduleDir, "../package.json"),
29753
+ path56.resolve(moduleDir, "../../package.json")
29328
29754
  ];
29329
29755
  for (const candidate of candidates) {
29330
- if (!fs47.existsSync(candidate)) continue;
29331
- const parsed = JSON.parse(fs47.readFileSync(candidate, "utf8"));
29756
+ if (!fs49.existsSync(candidate)) continue;
29757
+ const parsed = JSON.parse(fs49.readFileSync(candidate, "utf8"));
29332
29758
  if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
29333
29759
  }
29334
29760
  } catch {
@@ -29630,39 +30056,39 @@ User: ${prompt}` : prompt,
29630
30056
  }
29631
30057
  }
29632
30058
  function resolveLocalThreadsDir() {
29633
- return path55.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
30059
+ return path56.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
29634
30060
  }
29635
30061
  function loadOrCreateLocalThread() {
29636
30062
  const dir = resolveLocalThreadsDir();
29637
- fs47.mkdirSync(dir, { recursive: true });
29638
- const activePath = path55.resolve(dir, "active-thread.json");
29639
- if (fs47.existsSync(activePath)) {
30063
+ fs49.mkdirSync(dir, { recursive: true });
30064
+ const activePath = path56.resolve(dir, "active-thread.json");
30065
+ if (fs49.existsSync(activePath)) {
29640
30066
  try {
29641
- const parsed = JSON.parse(fs47.readFileSync(activePath, "utf-8"));
30067
+ const parsed = JSON.parse(fs49.readFileSync(activePath, "utf-8"));
29642
30068
  const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
29643
- const threadFile = path55.resolve(dir, `${id2}.json`);
30069
+ const threadFile = path56.resolve(dir, `${id2}.json`);
29644
30070
  const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
29645
30071
  return { id: id2, filePath: threadFile, messages };
29646
30072
  } catch {
29647
30073
  }
29648
30074
  }
29649
30075
  const id = `thread-${Date.now()}`;
29650
- const filePath = path55.resolve(dir, `${id}.json`);
30076
+ const filePath = path56.resolve(dir, `${id}.json`);
29651
30077
  const thread = { id, filePath, messages: [] };
29652
30078
  saveLocalThread(thread);
29653
30079
  return thread;
29654
30080
  }
29655
30081
  function saveLocalThread(thread) {
29656
30082
  const dir = resolveLocalThreadsDir();
29657
- fs47.mkdirSync(dir, { recursive: true });
29658
- fs47.writeFileSync(
30083
+ fs49.mkdirSync(dir, { recursive: true });
30084
+ fs49.writeFileSync(
29659
30085
  thread.filePath,
29660
30086
  `${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
29661
30087
  `,
29662
30088
  "utf-8"
29663
30089
  );
29664
- const activePath = path55.resolve(dir, "active-thread.json");
29665
- fs47.writeFileSync(
30090
+ const activePath = path56.resolve(dir, "active-thread.json");
30091
+ fs49.writeFileSync(
29666
30092
  activePath,
29667
30093
  `${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
29668
30094
  `,
@@ -30205,12 +30631,12 @@ function isInstallerMode() {
30205
30631
  }
30206
30632
  function listLocalSurfaces() {
30207
30633
  const homeDir = resolvePaperclipHomeDir();
30208
- const instancesDir = path55.resolve(homeDir, "instances");
30209
- if (!fs47.existsSync(instancesDir)) return [];
30210
- return fs47.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
30634
+ const instancesDir = path56.resolve(homeDir, "instances");
30635
+ if (!fs49.existsSync(instancesDir)) return [];
30636
+ return fs49.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
30211
30637
  const instanceId = entry.name;
30212
- const configPath = path55.resolve(instancesDir, instanceId, "config.json");
30213
- if (!fs47.existsSync(configPath)) return null;
30638
+ const configPath = path56.resolve(instancesDir, instanceId, "config.json");
30639
+ if (!fs49.existsSync(configPath)) return null;
30214
30640
  try {
30215
30641
  const config = readConfig(configPath);
30216
30642
  if (!config) return null;