@growthub/cli 0.6.7 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +694 -269
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
4
  var __esm = (fn, res) => function __init() {
@@ -8016,8 +8015,8 @@ var init_onboard = __esm({
8016
8015
 
8017
8016
  // src/client/http.ts
8018
8017
  import { URL as URL2 } from "node:url";
8019
- function buildUrl(apiBase, path56) {
8020
- const normalizedPath = path56.startsWith("/") ? path56 : `/${path56}`;
8018
+ function buildUrl(apiBase, path57) {
8019
+ const normalizedPath = path57.startsWith("/") ? path57 : `/${path57}`;
8021
8020
  const [pathname, query] = normalizedPath.split("?");
8022
8021
  const url = new URL2(apiBase);
8023
8022
  url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
@@ -8079,26 +8078,26 @@ var init_http = __esm({
8079
8078
  this.runId = opts.runId?.trim() || void 0;
8080
8079
  this.userId = opts.userId?.trim() || void 0;
8081
8080
  }
8082
- get(path56, opts) {
8083
- return this.request(path56, { method: "GET" }, opts);
8081
+ get(path57, opts) {
8082
+ return this.request(path57, { method: "GET" }, opts);
8084
8083
  }
8085
- post(path56, body, opts) {
8086
- return this.request(path56, {
8084
+ post(path57, body, opts) {
8085
+ return this.request(path57, {
8087
8086
  method: "POST",
8088
8087
  body: body === void 0 ? void 0 : JSON.stringify(body)
8089
8088
  }, opts);
8090
8089
  }
8091
- patch(path56, body, opts) {
8092
- return this.request(path56, {
8090
+ patch(path57, body, opts) {
8091
+ return this.request(path57, {
8093
8092
  method: "PATCH",
8094
8093
  body: body === void 0 ? void 0 : JSON.stringify(body)
8095
8094
  }, opts);
8096
8095
  }
8097
- delete(path56, opts) {
8098
- return this.request(path56, { method: "DELETE" }, opts);
8096
+ delete(path57, opts) {
8097
+ return this.request(path57, { method: "DELETE" }, opts);
8099
8098
  }
8100
- async request(path56, init, opts) {
8101
- const url = buildUrl(this.apiBase, path56);
8099
+ async request(path57, init, opts) {
8100
+ const url = buildUrl(this.apiBase, path57);
8102
8101
  const headers = {
8103
8102
  accept: "application/json",
8104
8103
  ...toStringRecord(init.headers)
@@ -9623,9 +9622,9 @@ async function fetchHostedIntegrations(session) {
9623
9622
  }
9624
9623
  async function fetchHostedIntegrationCredential(session, providerId) {
9625
9624
  const client = toApiClient2(session);
9626
- const path56 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
9625
+ const path57 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
9627
9626
  try {
9628
- return await client.get(path56, { ignoreNotFound: true });
9627
+ return await client.get(path57, { ignoreNotFound: true });
9629
9628
  } catch (err) {
9630
9629
  if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
9631
9630
  throw new HostedEndpointUnavailableError(err.status, err.message);
@@ -10200,12 +10199,12 @@ var init_github = __esm({
10200
10199
  });
10201
10200
 
10202
10201
  // src/starter/init.ts
10203
- import fs36 from "node:fs";
10204
- import path45 from "node:path";
10202
+ import fs38 from "node:fs";
10203
+ import path46 from "node:path";
10205
10204
  async function initStarterWorkspace(opts) {
10206
10205
  const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
10207
- const absOut = path45.resolve(opts.out);
10208
- 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) {
10209
10208
  throw new Error(`Destination ${absOut} already exists and is not empty.`);
10210
10209
  }
10211
10210
  const info = getBundledKitSourceInfo(kitId);
@@ -10214,7 +10213,7 @@ async function initStarterWorkspace(opts) {
10214
10213
  forkPath: absOut,
10215
10214
  kitId: info.id,
10216
10215
  baseVersion: info.version,
10217
- label: opts.name?.trim() || path45.basename(absOut)
10216
+ label: opts.name?.trim() || path46.basename(absOut)
10218
10217
  });
10219
10218
  const policy = {
10220
10219
  ...makeDefaultKitForkPolicy(),
@@ -10307,8 +10306,8 @@ var init_types2 = __esm({
10307
10306
  });
10308
10307
 
10309
10308
  // src/starter/source-import/github-source.ts
10310
- import fs37 from "node:fs";
10311
- import path46 from "node:path";
10309
+ import fs39 from "node:fs";
10310
+ import path47 from "node:path";
10312
10311
  import { spawnSync as spawnSync4 } from "node:child_process";
10313
10312
  function baseHeaders() {
10314
10313
  return {
@@ -10439,12 +10438,12 @@ function cloneGithubRepo(input) {
10439
10438
  if (!gitAvailable()) {
10440
10439
  throw new Error("`git` is not available on PATH \u2014 cannot clone.");
10441
10440
  }
10442
- if (fs37.existsSync(input.destination)) {
10441
+ if (fs39.existsSync(input.destination)) {
10443
10442
  throw new Error(`Clone destination already exists: ${input.destination}`);
10444
10443
  }
10445
10444
  const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
10446
- const parent = path46.dirname(input.destination);
10447
- fs37.mkdirSync(parent, { recursive: true });
10445
+ const parent = path47.dirname(input.destination);
10446
+ fs39.mkdirSync(parent, { recursive: true });
10448
10447
  const depth = input.depth ?? 1;
10449
10448
  const branch = input.branch ?? input.probe.defaultBranch;
10450
10449
  const args = ["clone"];
@@ -10471,17 +10470,17 @@ function cloneGithubRepo(input) {
10471
10470
  function narrowToSubdirectory(rootDir, subdirectory) {
10472
10471
  const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
10473
10472
  if (!normalizedSub) return;
10474
- const abs = path46.resolve(rootDir, normalizedSub);
10475
- if (!fs37.existsSync(abs) || !fs37.statSync(abs).isDirectory()) {
10473
+ const abs = path47.resolve(rootDir, normalizedSub);
10474
+ if (!fs39.existsSync(abs) || !fs39.statSync(abs).isDirectory()) {
10476
10475
  throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
10477
10476
  }
10478
- const tmp = path46.resolve(
10479
- path46.dirname(rootDir),
10480
- `.${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)}`
10481
10480
  );
10482
- fs37.renameSync(abs, tmp);
10483
- fs37.rmSync(rootDir, { recursive: true, force: true });
10484
- fs37.renameSync(tmp, rootDir);
10481
+ fs39.renameSync(abs, tmp);
10482
+ fs39.rmSync(rootDir, { recursive: true, force: true });
10483
+ fs39.renameSync(tmp, rootDir);
10485
10484
  }
10486
10485
  var GITHUB_API_BASE2;
10487
10486
  var init_github_source = __esm({
@@ -10495,9 +10494,9 @@ var init_github_source = __esm({
10495
10494
  });
10496
10495
 
10497
10496
  // src/starter/source-import/skills-source.ts
10498
- import fs38 from "node:fs";
10499
- import os9 from "node:os";
10500
- import path47 from "node:path";
10497
+ import fs40 from "node:fs";
10498
+ import os10 from "node:os";
10499
+ import path48 from "node:path";
10501
10500
  import { spawnSync as spawnSync5 } from "node:child_process";
10502
10501
  function resolveBase() {
10503
10502
  const raw = process.env.SKILLS_SH_BASE?.trim();
@@ -10763,9 +10762,9 @@ async function probeSkillsSource(input) {
10763
10762
  };
10764
10763
  }
10765
10764
  function assertInsidePayloadRoot(root, candidate) {
10766
- const abs = path47.resolve(candidate);
10767
- const rootAbs = path47.resolve(root);
10768
- 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) {
10769
10768
  throw new Error(`Refusing to write outside payload root: ${candidate}`);
10770
10769
  }
10771
10770
  }
@@ -10781,24 +10780,24 @@ function runGit3(args, cwd) {
10781
10780
  };
10782
10781
  }
10783
10782
  function skillDirectoryMatches(dir, skillSlug) {
10784
- const skillFile = path47.resolve(dir, "SKILL.md");
10785
- 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()) {
10786
10785
  return false;
10787
10786
  }
10788
- if (path47.basename(dir) === skillSlug) {
10787
+ if (path48.basename(dir) === skillSlug) {
10789
10788
  return true;
10790
10789
  }
10791
- const content = fs38.readFileSync(skillFile, "utf8");
10790
+ const content = fs40.readFileSync(skillFile, "utf8");
10792
10791
  const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
10793
10792
  return nameMatch?.[1] === skillSlug;
10794
10793
  }
10795
10794
  function locateSkillDirectory(root, skillSlug) {
10796
10795
  const preferred = [
10797
- path47.resolve(root, "skills", skillSlug),
10798
- path47.resolve(root, skillSlug)
10796
+ path48.resolve(root, "skills", skillSlug),
10797
+ path48.resolve(root, skillSlug)
10799
10798
  ];
10800
10799
  for (const candidate of preferred) {
10801
- if (fs38.existsSync(candidate) && fs38.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
10800
+ if (fs40.existsSync(candidate) && fs40.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
10802
10801
  return candidate;
10803
10802
  }
10804
10803
  }
@@ -10808,12 +10807,12 @@ function locateSkillDirectory(root, skillSlug) {
10808
10807
  if (skillDirectoryMatches(current, skillSlug)) {
10809
10808
  return current;
10810
10809
  }
10811
- for (const entry of fs38.readdirSync(current, { withFileTypes: true })) {
10810
+ for (const entry of fs40.readdirSync(current, { withFileTypes: true })) {
10812
10811
  if (!entry.isDirectory()) continue;
10813
10812
  if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
10814
10813
  continue;
10815
10814
  }
10816
- queue.push(path47.resolve(current, entry.name));
10815
+ queue.push(path48.resolve(current, entry.name));
10817
10816
  }
10818
10817
  }
10819
10818
  return null;
@@ -10823,18 +10822,18 @@ function copySkillTree(sourceDir, destination) {
10823
10822
  const stack = [{ from: sourceDir, to: destination }];
10824
10823
  while (stack.length > 0) {
10825
10824
  const current = stack.pop();
10826
- fs38.mkdirSync(current.to, { recursive: true });
10827
- for (const entry of fs38.readdirSync(current.from, { withFileTypes: true })) {
10828
- const fromPath = path47.resolve(current.from, entry.name);
10829
- 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);
10830
10829
  assertInsidePayloadRoot(destination, toPath);
10831
10830
  if (entry.isDirectory()) {
10832
10831
  stack.push({ from: fromPath, to: toPath });
10833
10832
  continue;
10834
10833
  }
10835
- const data = fs38.readFileSync(fromPath);
10836
- fs38.mkdirSync(path47.dirname(toPath), { recursive: true });
10837
- 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 });
10838
10837
  written += 1;
10839
10838
  }
10840
10839
  }
@@ -10845,7 +10844,7 @@ async function fetchSkillPayload(input) {
10845
10844
  if (!gitAvailable()) {
10846
10845
  throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
10847
10846
  }
10848
- if (fs38.existsSync(destination)) {
10847
+ if (fs40.existsSync(destination)) {
10849
10848
  throw new Error(`Skill payload destination already exists: ${destination}`);
10850
10849
  }
10851
10850
  const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
@@ -10853,11 +10852,11 @@ async function fetchSkillPayload(input) {
10853
10852
  if (!repoSource || !skillSlug) {
10854
10853
  throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
10855
10854
  }
10856
- const cloneRoot = fs38.mkdtempSync(
10857
- path47.join(os9.tmpdir(), "growthub-skills-source-")
10855
+ const cloneRoot = fs40.mkdtempSync(
10856
+ path48.join(os10.tmpdir(), "growthub-skills-source-")
10858
10857
  );
10859
10858
  try {
10860
- const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path47.dirname(cloneRoot));
10859
+ const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path48.dirname(cloneRoot));
10861
10860
  if (!cloneRes.ok) {
10862
10861
  throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
10863
10862
  }
@@ -10870,7 +10869,7 @@ async function fetchSkillPayload(input) {
10870
10869
  const fileCount = copySkillTree(skillDir, destination);
10871
10870
  return { destination, fileCount };
10872
10871
  } finally {
10873
- fs38.rmSync(cloneRoot, { recursive: true, force: true });
10872
+ fs40.rmSync(cloneRoot, { recursive: true, force: true });
10874
10873
  }
10875
10874
  }
10876
10875
  var DEFAULT_BASE, COMMENT_PATTERN;
@@ -10884,13 +10883,13 @@ var init_skills_source = __esm({
10884
10883
  });
10885
10884
 
10886
10885
  // src/starter/source-import/detect.ts
10887
- import fs39 from "node:fs";
10888
- import path48 from "node:path";
10886
+ import fs41 from "node:fs";
10887
+ import path49 from "node:path";
10889
10888
  function safeReadPackageJson(dir) {
10890
- const p33 = path48.resolve(dir, "package.json");
10891
- if (!fs39.existsSync(p33)) return null;
10889
+ const p33 = path49.resolve(dir, "package.json");
10890
+ if (!fs41.existsSync(p33)) return null;
10892
10891
  try {
10893
- return JSON.parse(fs39.readFileSync(p33, "utf8"));
10892
+ return JSON.parse(fs41.readFileSync(p33, "utf8"));
10894
10893
  } catch {
10895
10894
  return null;
10896
10895
  }
@@ -10900,10 +10899,10 @@ function detectPackageManager(dir, pkg) {
10900
10899
  if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
10901
10900
  if (pkg?.packageManager?.startsWith("npm")) return "npm";
10902
10901
  if (pkg?.packageManager?.startsWith("bun")) return "bun";
10903
- if (fs39.existsSync(path48.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
10904
- if (fs39.existsSync(path48.resolve(dir, "yarn.lock"))) return "yarn";
10905
- if (fs39.existsSync(path48.resolve(dir, "bun.lockb"))) return "bun";
10906
- 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";
10907
10906
  return "unknown";
10908
10907
  }
10909
10908
  function collectDeps(pkg) {
@@ -10917,12 +10916,12 @@ function collectDeps(pkg) {
10917
10916
  }
10918
10917
  function looksLikeSkillPayload(rootDir) {
10919
10918
  const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
10920
- return markers.some((name) => fs39.existsSync(path48.resolve(rootDir, name)));
10919
+ return markers.some((name) => fs41.existsSync(path49.resolve(rootDir, name)));
10921
10920
  }
10922
10921
  function detectFramework(rootDir, pkg) {
10923
10922
  if (!pkg) {
10924
10923
  if (looksLikeSkillPayload(rootDir)) return "skill";
10925
- if (fs39.existsSync(path48.resolve(rootDir, "docs"))) return "docs";
10924
+ if (fs41.existsSync(path49.resolve(rootDir, "docs"))) return "docs";
10926
10925
  return "unknown";
10927
10926
  }
10928
10927
  const deps = collectDeps(pkg);
@@ -10931,8 +10930,8 @@ function detectFramework(rootDir, pkg) {
10931
10930
  "vite.config.ts",
10932
10931
  "vite.config.mjs",
10933
10932
  "vite.config.cjs"
10934
- ].some((name) => fs39.existsSync(path48.resolve(rootDir, name)));
10935
- 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"))) {
10936
10935
  return "next";
10937
10936
  }
10938
10937
  if (deps.has("vite") || hasViteConfig) return "vite";
@@ -10958,15 +10957,15 @@ function pickScripts(pkg) {
10958
10957
  return out;
10959
10958
  }
10960
10959
  function listEnvFiles(dir) {
10961
- if (!fs39.existsSync(dir)) return [];
10962
- 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");
10963
10962
  }
10964
10963
  function findAppRoot(rootDir, pkg) {
10965
10964
  if (pkg) return ".";
10966
10965
  const candidates = ["app", "src", "apps", "packages"];
10967
10966
  for (const candidate of candidates) {
10968
- const abs = path48.resolve(rootDir, candidate);
10969
- if (fs39.existsSync(abs) && fs39.statSync(abs).isDirectory()) {
10967
+ const abs = path49.resolve(rootDir, candidate);
10968
+ if (fs41.existsSync(abs) && fs41.statSync(abs).isDirectory()) {
10970
10969
  const child = safeReadPackageJson(abs);
10971
10970
  if (child) return candidate;
10972
10971
  }
@@ -10982,12 +10981,12 @@ function computeConfidence(framework, manager, pkg) {
10982
10981
  return Math.min(1, Number(score.toFixed(2)));
10983
10982
  }
10984
10983
  function detectSourceShape(rootDir) {
10985
- if (!fs39.existsSync(rootDir) || !fs39.statSync(rootDir).isDirectory()) {
10984
+ if (!fs41.existsSync(rootDir) || !fs41.statSync(rootDir).isDirectory()) {
10986
10985
  throw new Error(`Detection target is not a directory: ${rootDir}`);
10987
10986
  }
10988
10987
  const rootPkg = safeReadPackageJson(rootDir);
10989
10988
  const appRootRel = findAppRoot(rootDir, rootPkg);
10990
- const appRootAbs = path48.resolve(rootDir, appRootRel);
10989
+ const appRootAbs = path49.resolve(rootDir, appRootRel);
10991
10990
  const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
10992
10991
  const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
10993
10992
  const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
@@ -11027,18 +11026,18 @@ var init_detect = __esm({
11027
11026
  });
11028
11027
 
11029
11028
  // src/starter/source-import/security.ts
11030
- import fs40 from "node:fs";
11031
- import path49 from "node:path";
11029
+ import fs42 from "node:fs";
11030
+ import path50 from "node:path";
11032
11031
  function isLikelyTextFile(filename) {
11033
- const ext = path49.extname(filename).toLowerCase();
11032
+ const ext = path50.extname(filename).toLowerCase();
11034
11033
  if (!ext) return true;
11035
11034
  return TEXT_EXTENSIONS.has(ext);
11036
11035
  }
11037
11036
  function isSuspiciousBinary(filename) {
11038
- return SUSPICIOUS_BINARY_EXTENSIONS.has(path49.extname(filename).toLowerCase());
11037
+ return SUSPICIOUS_BINARY_EXTENSIONS.has(path50.extname(filename).toLowerCase());
11039
11038
  }
11040
11039
  function isUnexpectedArchive(filename) {
11041
- const ext = path49.extname(filename).toLowerCase();
11040
+ const ext = path50.extname(filename).toLowerCase();
11042
11041
  return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
11043
11042
  }
11044
11043
  function shortExcerpt(line) {
@@ -11093,19 +11092,19 @@ function walkPayload(root, onFile, limits) {
11093
11092
  if (!current) break;
11094
11093
  let entries;
11095
11094
  try {
11096
- entries = fs40.readdirSync(current, { withFileTypes: true });
11095
+ entries = fs42.readdirSync(current, { withFileTypes: true });
11097
11096
  } catch {
11098
11097
  continue;
11099
11098
  }
11100
11099
  for (const entry of entries) {
11101
- const abs = path49.resolve(current, entry.name);
11100
+ const abs = path50.resolve(current, entry.name);
11102
11101
  if (entry.isDirectory()) {
11103
11102
  if (entry.name === ".git" || entry.name === "node_modules") continue;
11104
11103
  stack.push(abs);
11105
11104
  continue;
11106
11105
  }
11107
11106
  if (!entry.isFile()) continue;
11108
- const rel = path49.relative(root, abs);
11107
+ const rel = path50.relative(root, abs);
11109
11108
  onFile(abs, rel);
11110
11109
  visited += 1;
11111
11110
  if (visited >= limits.maxFiles) break;
@@ -11115,7 +11114,7 @@ function walkPayload(root, onFile, limits) {
11115
11114
  }
11116
11115
  function inspectSourcePayload(input) {
11117
11116
  const { payloadRoot } = input;
11118
- if (!fs40.existsSync(payloadRoot) || !fs40.statSync(payloadRoot).isDirectory()) {
11117
+ if (!fs42.existsSync(payloadRoot) || !fs42.statSync(payloadRoot).isDirectory()) {
11119
11118
  throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
11120
11119
  }
11121
11120
  const findings = [];
@@ -11125,7 +11124,7 @@ function inspectSourcePayload(input) {
11125
11124
  (abs, rel) => {
11126
11125
  let size = 0;
11127
11126
  try {
11128
- size = fs40.statSync(abs).size;
11127
+ size = fs42.statSync(abs).size;
11129
11128
  } catch {
11130
11129
  return;
11131
11130
  }
@@ -11134,7 +11133,7 @@ function inspectSourcePayload(input) {
11134
11133
  category: "suspicious-binary",
11135
11134
  severity: "high-risk",
11136
11135
  path: rel,
11137
- 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.`
11138
11137
  });
11139
11138
  return;
11140
11139
  }
@@ -11143,7 +11142,7 @@ function inspectSourcePayload(input) {
11143
11142
  category: "unexpected-archive",
11144
11143
  severity: "caution",
11145
11144
  path: rel,
11146
- 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.`
11147
11146
  });
11148
11147
  return;
11149
11148
  }
@@ -11151,12 +11150,12 @@ function inspectSourcePayload(input) {
11151
11150
  if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
11152
11151
  let buf;
11153
11152
  try {
11154
- const handle = fs40.openSync(abs, "r");
11153
+ const handle = fs42.openSync(abs, "r");
11155
11154
  try {
11156
11155
  buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
11157
- fs40.readSync(handle, buf, 0, buf.length, 0);
11156
+ fs42.readSync(handle, buf, 0, buf.length, 0);
11158
11157
  } finally {
11159
- fs40.closeSync(handle);
11158
+ fs42.closeSync(handle);
11160
11159
  }
11161
11160
  } catch {
11162
11161
  return;
@@ -11365,18 +11364,18 @@ var init_security = __esm({
11365
11364
  });
11366
11365
 
11367
11366
  // src/starter/source-import/plan.ts
11368
- import fs41 from "node:fs";
11369
- import path50 from "node:path";
11367
+ import fs43 from "node:fs";
11368
+ import path51 from "node:path";
11370
11369
  function generateImportId() {
11371
11370
  return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
11372
11371
  }
11373
11372
  function destinationState(absDest) {
11374
- if (!fs41.existsSync(absDest)) return { exists: false, nonEmpty: false };
11375
- const stats = fs41.statSync(absDest);
11373
+ if (!fs43.existsSync(absDest)) return { exists: false, nonEmpty: false };
11374
+ const stats = fs43.statSync(absDest);
11376
11375
  if (!stats.isDirectory()) {
11377
11376
  throw new Error(`Destination is not a directory: ${absDest}`);
11378
11377
  }
11379
- const entries = fs41.readdirSync(absDest);
11378
+ const entries = fs43.readdirSync(absDest);
11380
11379
  return { exists: true, nonEmpty: entries.length > 0 };
11381
11380
  }
11382
11381
  function describeSource(probe) {
@@ -11386,7 +11385,7 @@ function describeSource(probe) {
11386
11385
  return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
11387
11386
  }
11388
11387
  function buildSourceImportPlan(input) {
11389
- const absDest = path50.resolve(input.destination);
11388
+ const absDest = path51.resolve(input.destination);
11390
11389
  const state = destinationState(absDest);
11391
11390
  const payloadPath = "imported";
11392
11391
  const warnings = [...input.probe.warnings];
@@ -11499,8 +11498,8 @@ var init_plan = __esm({
11499
11498
  });
11500
11499
 
11501
11500
  // src/starter/source-import/summarize.ts
11502
- import fs42 from "node:fs";
11503
- import path51 from "node:path";
11501
+ import fs44 from "node:fs";
11502
+ import path52 from "node:path";
11504
11503
  function sourceHeading(manifest) {
11505
11504
  const src = manifest.source;
11506
11505
  if (src.kind === "github-repo") {
@@ -11555,7 +11554,7 @@ function nextStepsSection(manifest) {
11555
11554
  }
11556
11555
  function writeImportSummary(input) {
11557
11556
  const { forkPath, summaryRelativePath, manifest } = input;
11558
- const summaryPath = path51.resolve(forkPath, summaryRelativePath);
11557
+ const summaryPath = path52.resolve(forkPath, summaryRelativePath);
11559
11558
  const body = [
11560
11559
  `# Source Import Summary`,
11561
11560
  ``,
@@ -11585,8 +11584,8 @@ function writeImportSummary(input) {
11585
11584
  `Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
11586
11585
  ``
11587
11586
  ].join("\n");
11588
- fs42.mkdirSync(path51.dirname(summaryPath), { recursive: true });
11589
- fs42.writeFileSync(summaryPath, body, "utf8");
11587
+ fs44.mkdirSync(path52.dirname(summaryPath), { recursive: true });
11588
+ fs44.writeFileSync(summaryPath, body, "utf8");
11590
11589
  return summaryPath;
11591
11590
  }
11592
11591
  var init_summarize = __esm({
@@ -11596,16 +11595,16 @@ var init_summarize = __esm({
11596
11595
  });
11597
11596
 
11598
11597
  // src/starter/source-import/materialize.ts
11599
- import fs43 from "node:fs";
11600
- import os10 from "node:os";
11601
- import path52 from "node:path";
11598
+ import fs45 from "node:fs";
11599
+ import os11 from "node:os";
11600
+ import path53 from "node:path";
11602
11601
  function resolveSourceKind(probe) {
11603
11602
  return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
11604
11603
  }
11605
11604
  function stagingDirFor(forkPath) {
11606
- return path52.join(
11607
- os10.tmpdir(),
11608
- `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)}`
11609
11608
  );
11610
11609
  }
11611
11610
  async function fetchPayload(probe, stagingDir, opts) {
@@ -11637,18 +11636,18 @@ async function fetchPayload(probe, stagingDir, opts) {
11637
11636
  return { payloadRoot: stagingDir };
11638
11637
  }
11639
11638
  function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
11640
- const target = path52.resolve(forkPath, payloadRelativePath);
11641
- if (fs43.existsSync(target)) {
11642
- 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 });
11643
11642
  }
11644
- fs43.mkdirSync(path52.dirname(target), { recursive: true });
11645
- fs43.renameSync(payloadRoot, target);
11643
+ fs45.mkdirSync(path53.dirname(target), { recursive: true });
11644
+ fs45.renameSync(payloadRoot, target);
11646
11645
  return target;
11647
11646
  }
11648
11647
  function writeManifest(forkPath, manifest) {
11649
- const p33 = path52.resolve(forkPath, MANIFEST_RELATIVE_PATH);
11650
- fs43.mkdirSync(path52.dirname(p33), { recursive: true });
11651
- 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");
11652
11651
  return p33;
11653
11652
  }
11654
11653
  function assertConfirmationsSatisfied(plan, confirmations) {
@@ -11688,7 +11687,7 @@ async function materializeImportPlan(input) {
11688
11687
  requireSkillAcknowledgement: sourceKind === "skills-skill"
11689
11688
  });
11690
11689
  if (security.blocked) {
11691
- fs43.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
11690
+ fs45.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
11692
11691
  throw new Error(
11693
11692
  `Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
11694
11693
  );
@@ -11819,26 +11818,26 @@ var init_materialize = __esm({
11819
11818
  });
11820
11819
 
11821
11820
  // src/starter/source-import/agent.ts
11822
- import fs44 from "node:fs";
11823
- import path53 from "node:path";
11821
+ import fs46 from "node:fs";
11822
+ import path54 from "node:path";
11824
11823
  function resolveJobsDir() {
11825
- return path53.resolve(resolveKitForksHomeDir(), "source-import-jobs");
11824
+ return path54.resolve(resolveKitForksHomeDir(), "source-import-jobs");
11826
11825
  }
11827
11826
  function resolveJobPath2(jobId) {
11828
- return path53.resolve(resolveJobsDir(), `${jobId}.json`);
11827
+ return path54.resolve(resolveJobsDir(), `${jobId}.json`);
11829
11828
  }
11830
11829
  function generateJobId2() {
11831
11830
  return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
11832
11831
  }
11833
11832
  function writeJob2(job) {
11834
11833
  const p33 = resolveJobPath2(job.jobId);
11835
- fs44.mkdirSync(path53.dirname(p33), { recursive: true });
11836
- 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");
11837
11836
  }
11838
11837
  function readJobFile(p33) {
11839
- if (!fs44.existsSync(p33)) return null;
11838
+ if (!fs46.existsSync(p33)) return null;
11840
11839
  try {
11841
- return JSON.parse(fs44.readFileSync(p33, "utf8"));
11840
+ return JSON.parse(fs46.readFileSync(p33, "utf8"));
11842
11841
  } catch {
11843
11842
  return null;
11844
11843
  }
@@ -11848,7 +11847,7 @@ function patchJob2(jobId, status, patch = {}) {
11848
11847
  const job = readJobFile(p33);
11849
11848
  if (!job) return null;
11850
11849
  const updated = { ...job, ...patch, status };
11851
- fs44.writeFileSync(p33, JSON.stringify(updated, null, 2) + "\n", "utf8");
11850
+ fs46.writeFileSync(p33, JSON.stringify(updated, null, 2) + "\n", "utf8");
11852
11851
  return updated;
11853
11852
  }
11854
11853
  function getSourceImportJob(jobId) {
@@ -11867,7 +11866,7 @@ async function probeAndPlan(input, destination) {
11867
11866
  }
11868
11867
  async function runSourceImportJob(input) {
11869
11868
  const jobId = generateJobId2();
11870
- const destination = path53.resolve(input.out);
11869
+ const destination = path54.resolve(input.out);
11871
11870
  const sourceKind = input.source.kind;
11872
11871
  const initial = {
11873
11872
  jobId,
@@ -12024,8 +12023,8 @@ __export(source_import_discovery_exports, {
12024
12023
  });
12025
12024
  import * as p31 from "@clack/prompts";
12026
12025
  import pc45 from "picocolors";
12027
- import fs46 from "node:fs";
12028
- import path54 from "node:path";
12026
+ import fs48 from "node:fs";
12027
+ import path55 from "node:path";
12029
12028
  import { pathToFileURL as pathToFileURL4 } from "node:url";
12030
12029
  function slugifyWorkspaceName(input) {
12031
12030
  const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -12053,10 +12052,10 @@ async function promptForInteractiveWorkspacePath(input) {
12053
12052
  });
12054
12053
  if (p31.isCancel(raw) || !raw) return null;
12055
12054
  const trimmed = String(raw).trim();
12056
- const expanded = trimmed.startsWith("~/") ? path54.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
12057
- const resolved = path54.resolve(expanded);
12058
- if (fs46.existsSync(resolved) && fs46.statSync(resolved).isDirectory()) {
12059
- 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);
12060
12059
  p31.note(
12061
12060
  [
12062
12061
  `You selected an existing folder: ${resolved}`,
@@ -12329,8 +12328,8 @@ init_doctor();
12329
12328
  import { Command } from "commander";
12330
12329
  import * as p32 from "@clack/prompts";
12331
12330
  import pc46 from "picocolors";
12332
- import fs47 from "node:fs";
12333
- import path55 from "node:path";
12331
+ import fs49 from "node:fs";
12332
+ import path56 from "node:path";
12334
12333
  import { spawnSync as spawnSync6 } from "node:child_process";
12335
12334
  import { fileURLToPath as fileURLToPath7 } from "node:url";
12336
12335
 
@@ -12991,8 +12990,8 @@ function printItemCompleted(item) {
12991
12990
  const changes = Array.isArray(item.changes) ? item.changes : [];
12992
12991
  const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
12993
12992
  const kind = asString(change.kind, "update");
12994
- const path56 = asString(change.path, "unknown");
12995
- return `${kind} ${path56}`;
12993
+ const path57 = asString(change.path, "unknown");
12994
+ return `${kind} ${path57}`;
12996
12995
  });
12997
12996
  const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
12998
12997
  const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
@@ -15824,8 +15823,8 @@ function registerIssueCommands(program2) {
15824
15823
  if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
15825
15824
  if (opts.projectId) params.set("projectId", opts.projectId);
15826
15825
  const query = params.toString();
15827
- const path56 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
15828
- 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) ?? [];
15829
15828
  const filtered = filterIssueRows(rows, opts.match);
15830
15829
  if (ctx.json) {
15831
15830
  printOutput(filtered, { json: true });
@@ -16431,8 +16430,8 @@ function registerActivityCommands(program2) {
16431
16430
  if (opts.entityType) params.set("entityType", opts.entityType);
16432
16431
  if (opts.entityId) params.set("entityId", opts.entityId);
16433
16432
  const query = params.toString();
16434
- const path56 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
16435
- 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) ?? [];
16436
16435
  if (ctx.json) {
16437
16436
  printOutput(rows, { json: true });
16438
16437
  return;
@@ -17866,12 +17865,13 @@ ${result.lastError}`);
17866
17865
  // src/commands/kit.ts
17867
17866
  init_service();
17868
17867
  init_banner();
17869
- import path33 from "node:path";
17868
+ import path34 from "node:path";
17870
17869
  import { pathToFileURL as pathToFileURL2 } from "node:url";
17871
17870
  import * as p19 from "@clack/prompts";
17872
17871
  import pc30 from "picocolors";
17873
17872
 
17874
17873
  // src/commands/kit-fork.ts
17874
+ import fs27 from "node:fs";
17875
17875
  import * as p18 from "@clack/prompts";
17876
17876
 
17877
17877
  // src/commands/kit-fork-remote.ts
@@ -19220,6 +19220,243 @@ init_fork_registry();
19220
19220
  init_service();
19221
19221
  init_fork_policy();
19222
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
19223
19460
  function hr(width = 72) {
19224
19461
  return pc29.dim("\u2500".repeat(width));
19225
19462
  }
@@ -20368,6 +20605,7 @@ function addForkSubcommands(parentCmd) {
20368
20605
  console.log(hr());
20369
20606
  console.log("");
20370
20607
  });
20608
+ registerAuthoritySubcommands(parentCmd);
20371
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) => {
20372
20610
  const allForks = listKitForkRegistrations();
20373
20611
  const reg = allForks.find((f) => f.forkId === forkId);
@@ -20393,6 +20631,193 @@ function addForkSubcommands(parentCmd) {
20393
20631
  }
20394
20632
  });
20395
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
+ }
20396
20821
 
20397
20822
  // src/commands/kit.ts
20398
20823
  var TYPE_CONFIG = {
@@ -20837,7 +21262,7 @@ Examples:
20837
21262
  $ growthub kit validate ./my-kit
20838
21263
  $ growthub kit validate ~/kits/growthub-open-higgsfield-studio-v1
20839
21264
  `).action((kitPath) => {
20840
- const resolvedPath = path33.resolve(kitPath);
21265
+ const resolvedPath = path34.resolve(kitPath);
20841
21266
  const result = validateKitDirectory(resolvedPath);
20842
21267
  console.log("");
20843
21268
  console.log(pc30.bold("Kit: " + result.kitId) + pc30.dim(" schema v" + result.schemaVersion));
@@ -20882,13 +21307,13 @@ Examples:
20882
21307
  }
20883
21308
 
20884
21309
  // src/commands/template.ts
20885
- import path35 from "node:path";
21310
+ import path36 from "node:path";
20886
21311
  import * as p20 from "@clack/prompts";
20887
21312
  import pc31 from "picocolors";
20888
21313
 
20889
21314
  // src/templates/service.ts
20890
- import fs26 from "node:fs";
20891
- import path34 from "node:path";
21315
+ import fs28 from "node:fs";
21316
+ import path35 from "node:path";
20892
21317
  import { fileURLToPath as fileURLToPath6 } from "node:url";
20893
21318
 
20894
21319
  // src/templates/catalog.ts
@@ -21135,12 +21560,12 @@ var TEMPLATE_CATALOG = [
21135
21560
 
21136
21561
  // src/templates/service.ts
21137
21562
  function resolveSharedTemplatesRoot() {
21138
- const moduleDir = path34.dirname(fileURLToPath6(import.meta.url));
21563
+ const moduleDir = path35.dirname(fileURLToPath6(import.meta.url));
21139
21564
  for (const candidate of [
21140
- path34.resolve(moduleDir, "../../assets/shared-templates"),
21141
- path34.resolve(moduleDir, "../assets/shared-templates")
21565
+ path35.resolve(moduleDir, "../../assets/shared-templates"),
21566
+ path35.resolve(moduleDir, "../assets/shared-templates")
21142
21567
  ]) {
21143
- if (fs26.existsSync(candidate)) return candidate;
21568
+ if (fs28.existsSync(candidate)) return candidate;
21144
21569
  }
21145
21570
  throw new Error("Shared template assets not found at cli/assets/shared-templates/");
21146
21571
  }
@@ -21175,15 +21600,15 @@ function getArtifact(slugOrId) {
21175
21600
  const artifact = resolveSlug(slugOrId);
21176
21601
  if (!artifact) throw new Error(`Unknown template '${slugOrId}'. Run 'growthub template list' to browse.`);
21177
21602
  const root = resolveSharedTemplatesRoot();
21178
- const absolutePath = path34.resolve(root, artifact.path);
21179
- if (!fs26.existsSync(absolutePath)) throw new Error(`Template file missing: ${absolutePath}`);
21180
- 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 };
21181
21606
  }
21182
21607
  function copyArtifact(slugOrId, destDir) {
21183
21608
  const resolved = getArtifact(slugOrId);
21184
- fs26.mkdirSync(destDir, { recursive: true });
21185
- const destPath = path34.resolve(destDir, path34.basename(resolved.absolutePath));
21186
- 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);
21187
21612
  return destPath;
21188
21613
  }
21189
21614
  var GROUP_ORDER = ["ad-formats", "scene-modules/hooks", "scene-modules/body", "scene-modules/cta"];
@@ -21423,7 +21848,7 @@ async function runTemplatePicker(opts) {
21423
21848
  p20.cancel("Cancelled.");
21424
21849
  process.exit(0);
21425
21850
  }
21426
- const destDir = path35.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
21851
+ const destDir = path36.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
21427
21852
  const destPath = copyArtifact(selected.id, destDir);
21428
21853
  p20.outro(pc31.green("Copied \u2192 ") + destPath);
21429
21854
  return "done";
@@ -21497,7 +21922,7 @@ Any agent or kit resolves them by slug.
21497
21922
  return;
21498
21923
  }
21499
21924
  if (opts.out) {
21500
- const destDir = path35.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
21925
+ const destDir = path36.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
21501
21926
  try {
21502
21927
  const dest = copyArtifact(artifact.id, destDir);
21503
21928
  console.log(pc31.green("Copied \u2192 ") + dest);
@@ -22159,10 +22584,10 @@ function createCmsCapabilityRegistryClient() {
22159
22584
  }
22160
22585
 
22161
22586
  // src/runtime/machine-capability-resolver/index.ts
22162
- import os8 from "node:os";
22587
+ import os9 from "node:os";
22163
22588
  function buildMachineContext(profile) {
22164
22589
  return {
22165
- hostname: os8.hostname(),
22590
+ hostname: os9.hostname(),
22166
22591
  machineLabel: profile.local.machineLabel ?? void 0,
22167
22592
  workspaceLabel: profile.local.workspaceLabel ?? void 0,
22168
22593
  instanceId: profile.local.instanceId,
@@ -22581,8 +23006,8 @@ Examples:
22581
23006
  // src/commands/pipeline.ts
22582
23007
  init_session_store();
22583
23008
  init_hosted_client();
22584
- import fs29 from "node:fs";
22585
- import path38 from "node:path";
23009
+ import fs31 from "node:fs";
23010
+ import path39 from "node:path";
22586
23011
  import * as p22 from "@clack/prompts";
22587
23012
  import pc34 from "picocolors";
22588
23013
 
@@ -23092,40 +23517,40 @@ function renderPreSaveReview(input) {
23092
23517
 
23093
23518
  // src/runtime/artifact-contracts/index.ts
23094
23519
  init_home();
23095
- import fs27 from "node:fs";
23096
- import path36 from "node:path";
23520
+ import fs29 from "node:fs";
23521
+ import path37 from "node:path";
23097
23522
  import { randomBytes as randomBytes7 } from "node:crypto";
23098
23523
  function generateArtifactId() {
23099
23524
  return `art_${randomBytes7(8).toString("hex")}`;
23100
23525
  }
23101
23526
  function resolveArtifactsDir() {
23102
- return path36.resolve(resolvePaperclipHomeDir(), "artifacts");
23527
+ return path37.resolve(resolvePaperclipHomeDir(), "artifacts");
23103
23528
  }
23104
23529
  function resolveArtifactManifestPath(artifactId) {
23105
- return path36.resolve(resolveArtifactsDir(), `${artifactId}.json`);
23530
+ return path37.resolve(resolveArtifactsDir(), `${artifactId}.json`);
23106
23531
  }
23107
23532
  function readLocalManifest(artifactId) {
23108
23533
  const filePath = resolveArtifactManifestPath(artifactId);
23109
- if (!fs27.existsSync(filePath)) return null;
23534
+ if (!fs29.existsSync(filePath)) return null;
23110
23535
  try {
23111
- return JSON.parse(fs27.readFileSync(filePath, "utf-8"));
23536
+ return JSON.parse(fs29.readFileSync(filePath, "utf-8"));
23112
23537
  } catch {
23113
23538
  return null;
23114
23539
  }
23115
23540
  }
23116
23541
  function writeLocalManifest(manifest) {
23117
23542
  const dir = resolveArtifactsDir();
23118
- fs27.mkdirSync(dir, { recursive: true });
23543
+ fs29.mkdirSync(dir, { recursive: true });
23119
23544
  const filePath = resolveArtifactManifestPath(manifest.id);
23120
- fs27.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
23545
+ fs29.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
23121
23546
  `, { mode: 384 });
23122
23547
  }
23123
23548
  function listLocalManifests() {
23124
23549
  const dir = resolveArtifactsDir();
23125
- if (!fs27.existsSync(dir)) return [];
23126
- 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) => {
23127
23552
  try {
23128
- const content = fs27.readFileSync(path36.resolve(dir, entry.name), "utf-8");
23553
+ const content = fs29.readFileSync(path37.resolve(dir, entry.name), "utf-8");
23129
23554
  return JSON.parse(content);
23130
23555
  } catch {
23131
23556
  return null;
@@ -23203,8 +23628,8 @@ function createArtifactStore() {
23203
23628
 
23204
23629
  // src/runtime/native-intelligence/index.ts
23205
23630
  init_home();
23206
- import fs28 from "node:fs";
23207
- import path37 from "node:path";
23631
+ import fs30 from "node:fs";
23632
+ import path38 from "node:path";
23208
23633
 
23209
23634
  // src/runtime/native-intelligence/contract.ts
23210
23635
  var DEFAULT_INTELLIGENCE_CONFIG = {
@@ -24314,15 +24739,15 @@ function parseJsonSafe4(text66) {
24314
24739
 
24315
24740
  // src/runtime/native-intelligence/index.ts
24316
24741
  function resolveConfigPath2() {
24317
- return path37.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
24742
+ return path38.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
24318
24743
  }
24319
24744
  function readIntelligenceConfig() {
24320
24745
  const configPath = resolveConfigPath2();
24321
- if (!fs28.existsSync(configPath)) {
24746
+ if (!fs30.existsSync(configPath)) {
24322
24747
  return { ...DEFAULT_INTELLIGENCE_CONFIG };
24323
24748
  }
24324
24749
  try {
24325
- const raw = JSON.parse(fs28.readFileSync(configPath, "utf-8"));
24750
+ const raw = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
24326
24751
  return {
24327
24752
  modelId: validateModelId(raw.modelId),
24328
24753
  backendType: raw.backendType === "hosted" ? "hosted" : "local",
@@ -24339,8 +24764,8 @@ function readIntelligenceConfig() {
24339
24764
  }
24340
24765
  function writeIntelligenceConfig(config) {
24341
24766
  const configPath = resolveConfigPath2();
24342
- fs28.mkdirSync(path37.dirname(configPath), { recursive: true });
24343
- 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)}
24344
24769
  `, "utf-8");
24345
24770
  }
24346
24771
  function validateModelId(id) {
@@ -24696,9 +25121,9 @@ async function runPipelineAssembler(opts) {
24696
25121
  }
24697
25122
  }
24698
25123
  function loadPipelineFromFileOrJson(input) {
24699
- const resolvedPath = path38.resolve(input);
24700
- if (fs29.existsSync(resolvedPath)) {
24701
- 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");
24702
25127
  return deserializePipeline(JSON.parse(content));
24703
25128
  }
24704
25129
  try {
@@ -25243,8 +25668,8 @@ Examples:
25243
25668
  }
25244
25669
 
25245
25670
  // src/commands/workflow.ts
25246
- import fs31 from "node:fs";
25247
- import path40 from "node:path";
25671
+ import fs33 from "node:fs";
25672
+ import path41 from "node:path";
25248
25673
  import * as p23 from "@clack/prompts";
25249
25674
  import pc37 from "picocolors";
25250
25675
  init_session_store();
@@ -25252,15 +25677,15 @@ init_hosted_client();
25252
25677
 
25253
25678
  // src/runtime/workflow-hygiene/labels.ts
25254
25679
  init_home();
25255
- import fs30 from "node:fs";
25256
- import path39 from "node:path";
25680
+ import fs32 from "node:fs";
25681
+ import path40 from "node:path";
25257
25682
  function resolveStorePath() {
25258
- return path39.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
25683
+ return path40.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
25259
25684
  }
25260
25685
  function readStoreFile(filePath) {
25261
- if (!fs30.existsSync(filePath)) return { records: [] };
25686
+ if (!fs32.existsSync(filePath)) return { records: [] };
25262
25687
  try {
25263
- const raw = JSON.parse(fs30.readFileSync(filePath, "utf-8"));
25688
+ const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
25264
25689
  if (!Array.isArray(raw.records)) return { records: [] };
25265
25690
  return raw;
25266
25691
  } catch {
@@ -25268,8 +25693,8 @@ function readStoreFile(filePath) {
25268
25693
  }
25269
25694
  }
25270
25695
  function writeStoreFile(filePath, data) {
25271
- fs30.mkdirSync(path39.dirname(filePath), { recursive: true });
25272
- 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)}
25273
25698
  `, "utf-8");
25274
25699
  }
25275
25700
  function inferDefaultLabel(name, createdAt, versionCount) {
@@ -25370,16 +25795,16 @@ function box5(lines) {
25370
25795
  return [top, ...body, bottom].join("\n");
25371
25796
  }
25372
25797
  function resolveSavedWorkflowsDir() {
25373
- return path40.resolve(resolvePaperclipHomeDir(), "workflows");
25798
+ return path41.resolve(resolvePaperclipHomeDir(), "workflows");
25374
25799
  }
25375
25800
  function resolveDeletedWorkflowIdsPath() {
25376
- return path40.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
25801
+ return path41.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
25377
25802
  }
25378
25803
  function readDeletedWorkflowIds() {
25379
25804
  const filePath = resolveDeletedWorkflowIdsPath();
25380
- if (!fs31.existsSync(filePath)) return /* @__PURE__ */ new Set();
25805
+ if (!fs33.existsSync(filePath)) return /* @__PURE__ */ new Set();
25381
25806
  try {
25382
- const raw = JSON.parse(fs31.readFileSync(filePath, "utf-8"));
25807
+ const raw = JSON.parse(fs33.readFileSync(filePath, "utf-8"));
25383
25808
  if (!Array.isArray(raw?.workflowIds)) return /* @__PURE__ */ new Set();
25384
25809
  return new Set(raw.workflowIds.filter((value) => typeof value === "string"));
25385
25810
  } catch {
@@ -25388,8 +25813,8 @@ function readDeletedWorkflowIds() {
25388
25813
  }
25389
25814
  function writeDeletedWorkflowIds(ids) {
25390
25815
  const filePath = resolveDeletedWorkflowIdsPath();
25391
- fs31.mkdirSync(path40.dirname(filePath), { recursive: true });
25392
- 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)}
25393
25818
  `, "utf-8");
25394
25819
  }
25395
25820
  function markWorkflowDeletedLocally(workflowId) {
@@ -25415,10 +25840,10 @@ function filterLocallyDeletedWorkflows(entries) {
25415
25840
  }
25416
25841
  function listLocalSavedWorkflows() {
25417
25842
  const dir = resolveSavedWorkflowsDir();
25418
- if (!fs31.existsSync(dir)) return [];
25419
- 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) => {
25420
25845
  try {
25421
- 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"));
25422
25847
  const pipeline = raw.pipeline ?? raw;
25423
25848
  return {
25424
25849
  filename: e.name,
@@ -25479,11 +25904,11 @@ async function archiveSavedWorkflow(entry) {
25479
25904
  throw new Error("Local workflow entry is missing filename.");
25480
25905
  }
25481
25906
  const dir = resolveSavedWorkflowsDir();
25482
- const archiveDir = path40.resolve(dir, "archived");
25483
- fs31.mkdirSync(archiveDir, { recursive: true });
25484
- fs31.renameSync(
25485
- path40.resolve(dir, entry.filename),
25486
- 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)
25487
25912
  );
25488
25913
  }
25489
25914
  async function deleteSavedWorkflow(entry) {
@@ -25507,7 +25932,7 @@ async function deleteSavedWorkflow(entry) {
25507
25932
  if (!entry.filename) {
25508
25933
  throw new Error("Local workflow entry is missing filename.");
25509
25934
  }
25510
- fs31.rmSync(path40.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
25935
+ fs33.rmSync(path41.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
25511
25936
  markWorkflowDeletedLocally(entry.workflowId);
25512
25937
  }
25513
25938
  async function loadSavedWorkflowDetail(entry) {
@@ -25526,7 +25951,7 @@ async function loadSavedWorkflowDetail(entry) {
25526
25951
  };
25527
25952
  }
25528
25953
  const dir = resolveSavedWorkflowsDir();
25529
- const content = fs31.readFileSync(path40.resolve(dir, entry.filename), "utf-8");
25954
+ const content = fs33.readFileSync(path41.resolve(dir, entry.filename), "utf-8");
25530
25955
  const raw = JSON.parse(content);
25531
25956
  return {
25532
25957
  pipeline: raw.pipeline ?? raw,
@@ -26510,36 +26935,36 @@ import pc38 from "picocolors";
26510
26935
 
26511
26936
  // src/runtime/agent-harness/auth-store.ts
26512
26937
  init_home();
26513
- import fs32 from "node:fs";
26514
- import path41 from "node:path";
26938
+ import fs34 from "node:fs";
26939
+ import path42 from "node:path";
26515
26940
  function resolveHarnessAuthDir() {
26516
- return path41.resolve(resolvePaperclipHomeDir(), "harness-auth");
26941
+ return path42.resolve(resolvePaperclipHomeDir(), "harness-auth");
26517
26942
  }
26518
26943
  function resolveHarnessAuthFile(harnessId) {
26519
- return path41.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
26944
+ return path42.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
26520
26945
  }
26521
26946
  function normalizeSecret(value) {
26522
26947
  const trimmed = value?.trim();
26523
26948
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
26524
26949
  }
26525
26950
  function ensureSecureDir(dirPath) {
26526
- fs32.mkdirSync(dirPath, { recursive: true });
26951
+ fs34.mkdirSync(dirPath, { recursive: true });
26527
26952
  try {
26528
- fs32.chmodSync(dirPath, 448);
26953
+ fs34.chmodSync(dirPath, 448);
26529
26954
  } catch {
26530
26955
  }
26531
26956
  }
26532
26957
  function ensureSecureFile(filePath) {
26533
26958
  try {
26534
- fs32.chmodSync(filePath, 384);
26959
+ fs34.chmodSync(filePath, 384);
26535
26960
  } catch {
26536
26961
  }
26537
26962
  }
26538
26963
  function readHarnessCredentials(harnessId) {
26539
26964
  const filePath = resolveHarnessAuthFile(harnessId);
26540
- if (!fs32.existsSync(filePath)) return {};
26965
+ if (!fs34.existsSync(filePath)) return {};
26541
26966
  try {
26542
- const parsed = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
26967
+ const parsed = JSON.parse(fs34.readFileSync(filePath, "utf-8"));
26543
26968
  const creds = {};
26544
26969
  for (const [key, value] of Object.entries(parsed)) {
26545
26970
  if (typeof value === "string" && value.trim().length > 0) {
@@ -26566,7 +26991,7 @@ function setHarnessCredential(harnessId, key, value) {
26566
26991
  const dirPath = resolveHarnessAuthDir();
26567
26992
  ensureSecureDir(dirPath);
26568
26993
  const filePath = resolveHarnessAuthFile(harnessId);
26569
- fs32.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
26994
+ fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
26570
26995
  `, "utf-8");
26571
26996
  ensureSecureFile(filePath);
26572
26997
  }
@@ -26583,7 +27008,7 @@ function setHarnessCredentials(harnessId, updates) {
26583
27008
  const dirPath = resolveHarnessAuthDir();
26584
27009
  ensureSecureDir(dirPath);
26585
27010
  const filePath = resolveHarnessAuthFile(harnessId);
26586
- fs32.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
27011
+ fs34.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
26587
27012
  `, "utf-8");
26588
27013
  ensureSecureFile(filePath);
26589
27014
  }
@@ -26595,8 +27020,8 @@ function maskSecret(value) {
26595
27020
 
26596
27021
  // src/runtime/open-agents/index.ts
26597
27022
  init_home();
26598
- import fs33 from "node:fs";
26599
- import path42 from "node:path";
27023
+ import fs35 from "node:fs";
27024
+ import path43 from "node:path";
26600
27025
 
26601
27026
  // src/runtime/open-agents/contract.ts
26602
27027
  var DEFAULT_OPEN_AGENTS_CONFIG = {
@@ -26783,18 +27208,18 @@ var OpenAgentsBackendError = class extends Error {
26783
27208
 
26784
27209
  // src/runtime/open-agents/index.ts
26785
27210
  function resolveConfigPath3() {
26786
- return path42.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
27211
+ return path43.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
26787
27212
  }
26788
27213
  function readOpenAgentsConfig() {
26789
27214
  const configPath = resolveConfigPath3();
26790
- if (!fs33.existsSync(configPath)) {
27215
+ if (!fs35.existsSync(configPath)) {
26791
27216
  return {
26792
27217
  ...DEFAULT_OPEN_AGENTS_CONFIG,
26793
27218
  apiKey: getHarnessCredential("open-agents", "apiKey")
26794
27219
  };
26795
27220
  }
26796
27221
  try {
26797
- const raw = JSON.parse(fs33.readFileSync(configPath, "utf-8"));
27222
+ const raw = JSON.parse(fs35.readFileSync(configPath, "utf-8"));
26798
27223
  const storedApiKey = getHarnessCredential("open-agents", "apiKey");
26799
27224
  return {
26800
27225
  backendType: validateBackendType(raw.backendType),
@@ -26815,13 +27240,13 @@ function readOpenAgentsConfig() {
26815
27240
  }
26816
27241
  function writeOpenAgentsConfig(config) {
26817
27242
  const configPath = resolveConfigPath3();
26818
- fs33.mkdirSync(path42.dirname(configPath), { recursive: true });
27243
+ fs35.mkdirSync(path43.dirname(configPath), { recursive: true });
26819
27244
  const persisted = {
26820
27245
  ...config,
26821
27246
  authMode: validateAuthMode(config.authMode),
26822
27247
  apiKey: void 0
26823
27248
  };
26824
- fs33.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
27249
+ fs35.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
26825
27250
  `, "utf-8");
26826
27251
  setHarnessCredential("open-agents", "apiKey", config.apiKey);
26827
27252
  }
@@ -27382,8 +27807,8 @@ import pc39 from "picocolors";
27382
27807
 
27383
27808
  // src/runtime/qwen-code/index.ts
27384
27809
  init_home();
27385
- import fs34 from "node:fs";
27386
- import path43 from "node:path";
27810
+ import fs36 from "node:fs";
27811
+ import path44 from "node:path";
27387
27812
 
27388
27813
  // src/runtime/qwen-code/contract.ts
27389
27814
  var QWEN_CODE_APPROVAL_MODES = [
@@ -27601,19 +28026,19 @@ function buildSetupGuidance(env) {
27601
28026
 
27602
28027
  // src/runtime/qwen-code/index.ts
27603
28028
  function resolveConfigPath4() {
27604
- return path43.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
28029
+ return path44.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
27605
28030
  }
27606
28031
  function readQwenCodeConfig() {
27607
28032
  const configPath = resolveConfigPath4();
27608
28033
  const storedCredentials = readHarnessCredentials("qwen-code");
27609
- if (!fs34.existsSync(configPath)) {
28034
+ if (!fs36.existsSync(configPath)) {
27610
28035
  return {
27611
28036
  ...DEFAULT_QWEN_CODE_CONFIG,
27612
28037
  env: mergeHarnessEnv(DEFAULT_QWEN_CODE_CONFIG.env, storedCredentials)
27613
28038
  };
27614
28039
  }
27615
28040
  try {
27616
- const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
28041
+ const raw = JSON.parse(fs36.readFileSync(configPath, "utf-8"));
27617
28042
  return {
27618
28043
  binaryPath: typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_QWEN_CODE_CONFIG.binaryPath,
27619
28044
  defaultModel: typeof raw.defaultModel === "string" ? raw.defaultModel : DEFAULT_QWEN_CODE_CONFIG.defaultModel,
@@ -27635,7 +28060,7 @@ function readQwenCodeConfig() {
27635
28060
  }
27636
28061
  function writeQwenCodeConfig(config) {
27637
28062
  const configPath = resolveConfigPath4();
27638
- fs34.mkdirSync(path43.dirname(configPath), { recursive: true });
28063
+ fs36.mkdirSync(path44.dirname(configPath), { recursive: true });
27639
28064
  const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
27640
28065
  const credentialUpdates = {};
27641
28066
  const publicEnv = {};
@@ -27647,7 +28072,7 @@ function writeQwenCodeConfig(config) {
27647
28072
  publicEnv[key] = value;
27648
28073
  }
27649
28074
  setHarnessCredentials("qwen-code", credentialUpdates);
27650
- fs34.writeFileSync(
28075
+ fs36.writeFileSync(
27651
28076
  configPath,
27652
28077
  `${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
27653
28078
  `,
@@ -27980,8 +28405,8 @@ import pc42 from "picocolors";
27980
28405
 
27981
28406
  // src/status/probes.ts
27982
28407
  import { spawnSync as spawnSync3 } from "node:child_process";
27983
- import fs35 from "node:fs";
27984
- import path44 from "node:path";
28408
+ import fs37 from "node:fs";
28409
+ import path45 from "node:path";
27985
28410
  var GITHUB_API = "https://api.github.com";
27986
28411
  var NPM_REGISTRY = "https://registry.npmjs.org";
27987
28412
  function isoNow() {
@@ -28146,7 +28571,7 @@ async function probeKitForksIndex(_timeoutMs) {
28146
28571
  try {
28147
28572
  const { resolveKitForksIndexPath: resolveKitForksIndexPath2 } = await Promise.resolve().then(() => (init_kit_forks_home(), kit_forks_home_exports));
28148
28573
  const p33 = resolveKitForksIndexPath2();
28149
- if (!fs35.existsSync(p33)) {
28574
+ if (!fs37.existsSync(p33)) {
28150
28575
  return {
28151
28576
  componentId: "kit-forks-index",
28152
28577
  level: "operational",
@@ -28154,7 +28579,7 @@ async function probeKitForksIndex(_timeoutMs) {
28154
28579
  lastCheckedAt: isoNow()
28155
28580
  };
28156
28581
  }
28157
- const parsed = JSON.parse(fs35.readFileSync(p33, "utf8"));
28582
+ const parsed = JSON.parse(fs37.readFileSync(p33, "utf8"));
28158
28583
  const count = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
28159
28584
  return {
28160
28585
  componentId: "kit-forks-index",
@@ -28223,10 +28648,10 @@ async function probeNode(_timeoutMs) {
28223
28648
  };
28224
28649
  }
28225
28650
  async function probeReleaseBundleArtifacts(_timeoutMs) {
28226
- const distPath = path44.resolve(process.cwd(), "cli/dist/index.js");
28227
- const installerPath = path44.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
28228
- const distOk = fs35.existsSync(distPath);
28229
- 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);
28230
28655
  const ok = distOk && installerOk;
28231
28656
  return {
28232
28657
  componentId: "release-bundle",
@@ -28773,7 +29198,7 @@ import pc44 from "picocolors";
28773
29198
 
28774
29199
  // src/fleet/summary.ts
28775
29200
  init_fork_registry();
28776
- import fs45 from "node:fs";
29201
+ import fs47 from "node:fs";
28777
29202
  init_fork_policy();
28778
29203
  init_fork_trace();
28779
29204
  function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
@@ -28793,7 +29218,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
28793
29218
  "conflict_encountered"
28794
29219
  ]);
28795
29220
  function buildForkSummary(reg) {
28796
- if (!fs45.existsSync(reg.forkPath)) {
29221
+ if (!fs47.existsSync(reg.forkPath)) {
28797
29222
  return {
28798
29223
  forkId: reg.forkId,
28799
29224
  kitId: reg.kitId,
@@ -29254,8 +29679,8 @@ async function fleetApprovals(opts) {
29254
29679
  p30.log.message(
29255
29680
  ` \xB7 ${pc44.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
29256
29681
  );
29257
- for (const path56 of entry.pendingPaths.slice(0, 6)) {
29258
- 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}`);
29259
29684
  }
29260
29685
  if (entry.pendingPaths.length > 6) {
29261
29686
  p30.log.message(` ${pc44.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
@@ -29322,14 +29747,14 @@ init_banner();
29322
29747
  init_home();
29323
29748
  function resolveCliVersion() {
29324
29749
  try {
29325
- const moduleDir = path55.dirname(fileURLToPath7(import.meta.url));
29750
+ const moduleDir = path56.dirname(fileURLToPath7(import.meta.url));
29326
29751
  const candidates = [
29327
- path55.resolve(moduleDir, "../package.json"),
29328
- path55.resolve(moduleDir, "../../package.json")
29752
+ path56.resolve(moduleDir, "../package.json"),
29753
+ path56.resolve(moduleDir, "../../package.json")
29329
29754
  ];
29330
29755
  for (const candidate of candidates) {
29331
- if (!fs47.existsSync(candidate)) continue;
29332
- const parsed = JSON.parse(fs47.readFileSync(candidate, "utf8"));
29756
+ if (!fs49.existsSync(candidate)) continue;
29757
+ const parsed = JSON.parse(fs49.readFileSync(candidate, "utf8"));
29333
29758
  if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
29334
29759
  }
29335
29760
  } catch {
@@ -29631,39 +30056,39 @@ User: ${prompt}` : prompt,
29631
30056
  }
29632
30057
  }
29633
30058
  function resolveLocalThreadsDir() {
29634
- return path55.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
30059
+ return path56.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
29635
30060
  }
29636
30061
  function loadOrCreateLocalThread() {
29637
30062
  const dir = resolveLocalThreadsDir();
29638
- fs47.mkdirSync(dir, { recursive: true });
29639
- const activePath = path55.resolve(dir, "active-thread.json");
29640
- if (fs47.existsSync(activePath)) {
30063
+ fs49.mkdirSync(dir, { recursive: true });
30064
+ const activePath = path56.resolve(dir, "active-thread.json");
30065
+ if (fs49.existsSync(activePath)) {
29641
30066
  try {
29642
- const parsed = JSON.parse(fs47.readFileSync(activePath, "utf-8"));
30067
+ const parsed = JSON.parse(fs49.readFileSync(activePath, "utf-8"));
29643
30068
  const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
29644
- const threadFile = path55.resolve(dir, `${id2}.json`);
30069
+ const threadFile = path56.resolve(dir, `${id2}.json`);
29645
30070
  const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
29646
30071
  return { id: id2, filePath: threadFile, messages };
29647
30072
  } catch {
29648
30073
  }
29649
30074
  }
29650
30075
  const id = `thread-${Date.now()}`;
29651
- const filePath = path55.resolve(dir, `${id}.json`);
30076
+ const filePath = path56.resolve(dir, `${id}.json`);
29652
30077
  const thread = { id, filePath, messages: [] };
29653
30078
  saveLocalThread(thread);
29654
30079
  return thread;
29655
30080
  }
29656
30081
  function saveLocalThread(thread) {
29657
30082
  const dir = resolveLocalThreadsDir();
29658
- fs47.mkdirSync(dir, { recursive: true });
29659
- fs47.writeFileSync(
30083
+ fs49.mkdirSync(dir, { recursive: true });
30084
+ fs49.writeFileSync(
29660
30085
  thread.filePath,
29661
30086
  `${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
29662
30087
  `,
29663
30088
  "utf-8"
29664
30089
  );
29665
- const activePath = path55.resolve(dir, "active-thread.json");
29666
- fs47.writeFileSync(
30090
+ const activePath = path56.resolve(dir, "active-thread.json");
30091
+ fs49.writeFileSync(
29667
30092
  activePath,
29668
30093
  `${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
29669
30094
  `,
@@ -30206,12 +30631,12 @@ function isInstallerMode() {
30206
30631
  }
30207
30632
  function listLocalSurfaces() {
30208
30633
  const homeDir = resolvePaperclipHomeDir();
30209
- const instancesDir = path55.resolve(homeDir, "instances");
30210
- if (!fs47.existsSync(instancesDir)) return [];
30211
- 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) => {
30212
30637
  const instanceId = entry.name;
30213
- const configPath = path55.resolve(instancesDir, instanceId, "config.json");
30214
- if (!fs47.existsSync(configPath)) return null;
30638
+ const configPath = path56.resolve(instancesDir, instanceId, "config.json");
30639
+ if (!fs49.existsSync(configPath)) return null;
30215
30640
  try {
30216
30641
  const config = readConfig(configPath);
30217
30642
  if (!config) return null;