@growthub/cli 0.7.6 → 0.7.7

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 (42) hide show
  1. package/assets/worker-kits/creative-strategist-v1/skills.md +13 -11
  2. package/assets/worker-kits/creative-strategist-v1/templates/brief-template.js +1 -1
  3. package/assets/worker-kits/creative-strategist-v1/workers/creative-strategist/CLAUDE.md +22 -21
  4. package/assets/worker-kits/growthub-ai-website-cloner-v1/.env.example +4 -2
  5. package/assets/worker-kits/growthub-ai-website-cloner-v1/QUICKSTART.md +4 -4
  6. package/assets/worker-kits/growthub-ai-website-cloner-v1/runtime-assumptions.md +1 -1
  7. package/assets/worker-kits/growthub-ai-website-cloner-v1/setup/clone-fork.sh +1 -1
  8. package/assets/worker-kits/growthub-ai-website-cloner-v1/setup/verify-env.mjs +1 -1
  9. package/assets/worker-kits/growthub-ai-website-cloner-v1/validation-checklist.md +1 -1
  10. package/assets/worker-kits/growthub-ai-website-cloner-v1/workers/ai-website-cloner-operator/CLAUDE.md +1 -1
  11. package/assets/worker-kits/growthub-geo-seo-v1/.env.example +5 -3
  12. package/assets/worker-kits/growthub-geo-seo-v1/setup/check-deps.sh +2 -2
  13. package/assets/worker-kits/growthub-geo-seo-v1/setup/clone-fork.sh +1 -1
  14. package/assets/worker-kits/growthub-geo-seo-v1/setup/verify-env.mjs +1 -1
  15. package/assets/worker-kits/growthub-geo-seo-v1/validation-checklist.md +1 -1
  16. package/assets/worker-kits/growthub-geo-seo-v1/workers/geo-seo-operator/CLAUDE.md +1 -1
  17. package/assets/worker-kits/growthub-hyperframes-studio-v1/.env.example +4 -2
  18. package/assets/worker-kits/growthub-hyperframes-studio-v1/docs/hyperframes-discovery-path.md +2 -2
  19. package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/clone-fork.sh +2 -1
  20. package/assets/worker-kits/growthub-hyperframes-studio-v1/setup/verify-env.mjs +3 -2
  21. package/assets/worker-kits/growthub-open-higgsfield-studio-v1/.env.example +5 -0
  22. package/assets/worker-kits/growthub-open-higgsfield-studio-v1/QUICKSTART.md +1 -1
  23. package/assets/worker-kits/growthub-open-higgsfield-studio-v1/runtime-assumptions.md +1 -1
  24. package/assets/worker-kits/growthub-open-montage-studio-v1/.env.example +4 -2
  25. package/assets/worker-kits/growthub-open-montage-studio-v1/QUICKSTART.md +1 -1
  26. package/assets/worker-kits/growthub-open-montage-studio-v1/runtime-assumptions.md +1 -1
  27. package/assets/worker-kits/growthub-open-montage-studio-v1/setup/clone-fork.sh +2 -1
  28. package/assets/worker-kits/growthub-open-montage-studio-v1/setup/verify-env.mjs +5 -2
  29. package/assets/worker-kits/growthub-open-montage-studio-v1/validation-checklist.md +3 -3
  30. package/assets/worker-kits/growthub-open-montage-studio-v1/workers/open-montage-studio-operator/CLAUDE.md +2 -2
  31. package/assets/worker-kits/growthub-postiz-social-v1/.env.example +4 -2
  32. package/assets/worker-kits/growthub-postiz-social-v1/QUICKSTART.md +2 -2
  33. package/assets/worker-kits/growthub-postiz-social-v1/setup/clone-fork.sh +1 -1
  34. package/assets/worker-kits/growthub-postiz-social-v1/setup/verify-env.mjs +2 -1
  35. package/assets/worker-kits/growthub-postiz-social-v1/validation-checklist.md +1 -1
  36. package/assets/worker-kits/growthub-postiz-social-v1/workers/postiz-social-operator/CLAUDE.md +1 -1
  37. package/assets/worker-kits/growthub-twenty-crm-v1/.env.example +4 -2
  38. package/assets/worker-kits/growthub-twenty-crm-v1/QUICKSTART.md +1 -1
  39. package/assets/worker-kits/growthub-twenty-crm-v1/setup/clone-fork.sh +1 -1
  40. package/assets/worker-kits/growthub-twenty-crm-v1/workers/twenty-crm-operator/CLAUDE.md +1 -1
  41. package/dist/index.js +1177 -326
  42. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -8182,8 +8182,8 @@ var init_onboard = __esm({
8182
8182
 
8183
8183
  // src/client/http.ts
8184
8184
  import { URL as URL2 } from "node:url";
8185
- function buildUrl(apiBase, path62) {
8186
- const normalizedPath = path62.startsWith("/") ? path62 : `/${path62}`;
8185
+ function buildUrl(apiBase, path63) {
8186
+ const normalizedPath = path63.startsWith("/") ? path63 : `/${path63}`;
8187
8187
  const [pathname, query] = normalizedPath.split("?");
8188
8188
  const url = new URL2(apiBase);
8189
8189
  url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
@@ -8245,26 +8245,26 @@ var init_http = __esm({
8245
8245
  this.runId = opts.runId?.trim() || void 0;
8246
8246
  this.userId = opts.userId?.trim() || void 0;
8247
8247
  }
8248
- get(path62, opts) {
8249
- return this.request(path62, { method: "GET" }, opts);
8248
+ get(path63, opts) {
8249
+ return this.request(path63, { method: "GET" }, opts);
8250
8250
  }
8251
- post(path62, body, opts) {
8252
- return this.request(path62, {
8251
+ post(path63, body, opts) {
8252
+ return this.request(path63, {
8253
8253
  method: "POST",
8254
8254
  body: body === void 0 ? void 0 : JSON.stringify(body)
8255
8255
  }, opts);
8256
8256
  }
8257
- patch(path62, body, opts) {
8258
- return this.request(path62, {
8257
+ patch(path63, body, opts) {
8258
+ return this.request(path63, {
8259
8259
  method: "PATCH",
8260
8260
  body: body === void 0 ? void 0 : JSON.stringify(body)
8261
8261
  }, opts);
8262
8262
  }
8263
- delete(path62, opts) {
8264
- return this.request(path62, { method: "DELETE" }, opts);
8263
+ delete(path63, opts) {
8264
+ return this.request(path63, { method: "DELETE" }, opts);
8265
8265
  }
8266
- async request(path62, init, opts) {
8267
- const url = buildUrl(this.apiBase, path62);
8266
+ async request(path63, init, opts) {
8267
+ const url = buildUrl(this.apiBase, path63);
8268
8268
  const headers = {
8269
8269
  accept: "application/json",
8270
8270
  ...toStringRecord(init.headers)
@@ -9702,9 +9702,9 @@ async function fetchHostedIntegrations(session) {
9702
9702
  }
9703
9703
  async function fetchHostedIntegrationCredential(session, providerId) {
9704
9704
  const client = toApiClient2(session);
9705
- const path62 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
9705
+ const path63 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
9706
9706
  try {
9707
- return await client.get(path62, { ignoreNotFound: true });
9707
+ return await client.get(path63, { ignoreNotFound: true });
9708
9708
  } catch (err) {
9709
9709
  if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
9710
9710
  throw new HostedEndpointUnavailableError(err.status, err.message);
@@ -10279,12 +10279,12 @@ var init_github = __esm({
10279
10279
  });
10280
10280
 
10281
10281
  // src/starter/init.ts
10282
- import fs42 from "node:fs";
10283
- import path50 from "node:path";
10282
+ import fs43 from "node:fs";
10283
+ import path51 from "node:path";
10284
10284
  async function initStarterWorkspace(opts) {
10285
10285
  const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
10286
- const absOut = path50.resolve(opts.out);
10287
- if (fs42.existsSync(absOut) && fs42.readdirSync(absOut).length > 0) {
10286
+ const absOut = path51.resolve(opts.out);
10287
+ if (fs43.existsSync(absOut) && fs43.readdirSync(absOut).length > 0) {
10288
10288
  throw new Error(`Destination ${absOut} already exists and is not empty.`);
10289
10289
  }
10290
10290
  const info = getBundledKitSourceInfo(kitId);
@@ -10293,7 +10293,7 @@ async function initStarterWorkspace(opts) {
10293
10293
  forkPath: absOut,
10294
10294
  kitId: info.id,
10295
10295
  baseVersion: info.version,
10296
- label: opts.name?.trim() || path50.basename(absOut)
10296
+ label: opts.name?.trim() || path51.basename(absOut)
10297
10297
  });
10298
10298
  const policy = {
10299
10299
  ...makeDefaultKitForkPolicy(),
@@ -10386,8 +10386,8 @@ var init_types2 = __esm({
10386
10386
  });
10387
10387
 
10388
10388
  // src/starter/source-import/github-source.ts
10389
- import fs43 from "node:fs";
10390
- import path51 from "node:path";
10389
+ import fs44 from "node:fs";
10390
+ import path52 from "node:path";
10391
10391
  import { spawnSync as spawnSync5 } from "node:child_process";
10392
10392
  function baseHeaders() {
10393
10393
  return {
@@ -10518,12 +10518,12 @@ function cloneGithubRepo(input) {
10518
10518
  if (!gitAvailable()) {
10519
10519
  throw new Error("`git` is not available on PATH \u2014 cannot clone.");
10520
10520
  }
10521
- if (fs43.existsSync(input.destination)) {
10521
+ if (fs44.existsSync(input.destination)) {
10522
10522
  throw new Error(`Clone destination already exists: ${input.destination}`);
10523
10523
  }
10524
10524
  const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
10525
- const parent = path51.dirname(input.destination);
10526
- fs43.mkdirSync(parent, { recursive: true });
10525
+ const parent = path52.dirname(input.destination);
10526
+ fs44.mkdirSync(parent, { recursive: true });
10527
10527
  const depth = input.depth ?? 1;
10528
10528
  const branch = input.branch ?? input.probe.defaultBranch;
10529
10529
  const args = ["clone"];
@@ -10550,17 +10550,17 @@ function cloneGithubRepo(input) {
10550
10550
  function narrowToSubdirectory(rootDir, subdirectory) {
10551
10551
  const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
10552
10552
  if (!normalizedSub) return;
10553
- const abs = path51.resolve(rootDir, normalizedSub);
10554
- if (!fs43.existsSync(abs) || !fs43.statSync(abs).isDirectory()) {
10553
+ const abs = path52.resolve(rootDir, normalizedSub);
10554
+ if (!fs44.existsSync(abs) || !fs44.statSync(abs).isDirectory()) {
10555
10555
  throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
10556
10556
  }
10557
- const tmp = path51.resolve(
10558
- path51.dirname(rootDir),
10559
- `.${path51.basename(rootDir)}-narrow-${Date.now().toString(36)}`
10557
+ const tmp = path52.resolve(
10558
+ path52.dirname(rootDir),
10559
+ `.${path52.basename(rootDir)}-narrow-${Date.now().toString(36)}`
10560
10560
  );
10561
- fs43.renameSync(abs, tmp);
10562
- fs43.rmSync(rootDir, { recursive: true, force: true });
10563
- fs43.renameSync(tmp, rootDir);
10561
+ fs44.renameSync(abs, tmp);
10562
+ fs44.rmSync(rootDir, { recursive: true, force: true });
10563
+ fs44.renameSync(tmp, rootDir);
10564
10564
  }
10565
10565
  var GITHUB_API_BASE2;
10566
10566
  var init_github_source = __esm({
@@ -10574,9 +10574,9 @@ var init_github_source = __esm({
10574
10574
  });
10575
10575
 
10576
10576
  // src/starter/source-import/skills-source.ts
10577
- import fs44 from "node:fs";
10578
- import os10 from "node:os";
10579
- import path52 from "node:path";
10577
+ import fs45 from "node:fs";
10578
+ import os11 from "node:os";
10579
+ import path53 from "node:path";
10580
10580
  import { spawnSync as spawnSync6 } from "node:child_process";
10581
10581
  function resolveBase() {
10582
10582
  const raw = process.env.SKILLS_SH_BASE?.trim();
@@ -10842,9 +10842,9 @@ async function probeSkillsSource(input) {
10842
10842
  };
10843
10843
  }
10844
10844
  function assertInsidePayloadRoot(root, candidate) {
10845
- const abs = path52.resolve(candidate);
10846
- const rootAbs = path52.resolve(root);
10847
- if (!abs.startsWith(rootAbs + path52.sep) && abs !== rootAbs) {
10845
+ const abs = path53.resolve(candidate);
10846
+ const rootAbs = path53.resolve(root);
10847
+ if (!abs.startsWith(rootAbs + path53.sep) && abs !== rootAbs) {
10848
10848
  throw new Error(`Refusing to write outside payload root: ${candidate}`);
10849
10849
  }
10850
10850
  }
@@ -10860,24 +10860,24 @@ function runGit3(args, cwd) {
10860
10860
  };
10861
10861
  }
10862
10862
  function skillDirectoryMatches(dir, skillSlug) {
10863
- const skillFile = path52.resolve(dir, "SKILL.md");
10864
- if (!fs44.existsSync(skillFile) || !fs44.statSync(skillFile).isFile()) {
10863
+ const skillFile = path53.resolve(dir, "SKILL.md");
10864
+ if (!fs45.existsSync(skillFile) || !fs45.statSync(skillFile).isFile()) {
10865
10865
  return false;
10866
10866
  }
10867
- if (path52.basename(dir) === skillSlug) {
10867
+ if (path53.basename(dir) === skillSlug) {
10868
10868
  return true;
10869
10869
  }
10870
- const content = fs44.readFileSync(skillFile, "utf8");
10870
+ const content = fs45.readFileSync(skillFile, "utf8");
10871
10871
  const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
10872
10872
  return nameMatch?.[1] === skillSlug;
10873
10873
  }
10874
10874
  function locateSkillDirectory(root, skillSlug) {
10875
10875
  const preferred = [
10876
- path52.resolve(root, "skills", skillSlug),
10877
- path52.resolve(root, skillSlug)
10876
+ path53.resolve(root, "skills", skillSlug),
10877
+ path53.resolve(root, skillSlug)
10878
10878
  ];
10879
10879
  for (const candidate of preferred) {
10880
- if (fs44.existsSync(candidate) && fs44.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
10880
+ if (fs45.existsSync(candidate) && fs45.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
10881
10881
  return candidate;
10882
10882
  }
10883
10883
  }
@@ -10887,12 +10887,12 @@ function locateSkillDirectory(root, skillSlug) {
10887
10887
  if (skillDirectoryMatches(current, skillSlug)) {
10888
10888
  return current;
10889
10889
  }
10890
- for (const entry of fs44.readdirSync(current, { withFileTypes: true })) {
10890
+ for (const entry of fs45.readdirSync(current, { withFileTypes: true })) {
10891
10891
  if (!entry.isDirectory()) continue;
10892
10892
  if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
10893
10893
  continue;
10894
10894
  }
10895
- queue.push(path52.resolve(current, entry.name));
10895
+ queue.push(path53.resolve(current, entry.name));
10896
10896
  }
10897
10897
  }
10898
10898
  return null;
@@ -10902,18 +10902,18 @@ function copySkillTree(sourceDir, destination) {
10902
10902
  const stack = [{ from: sourceDir, to: destination }];
10903
10903
  while (stack.length > 0) {
10904
10904
  const current = stack.pop();
10905
- fs44.mkdirSync(current.to, { recursive: true });
10906
- for (const entry of fs44.readdirSync(current.from, { withFileTypes: true })) {
10907
- const fromPath = path52.resolve(current.from, entry.name);
10908
- const toPath = path52.resolve(current.to, entry.name);
10905
+ fs45.mkdirSync(current.to, { recursive: true });
10906
+ for (const entry of fs45.readdirSync(current.from, { withFileTypes: true })) {
10907
+ const fromPath = path53.resolve(current.from, entry.name);
10908
+ const toPath = path53.resolve(current.to, entry.name);
10909
10909
  assertInsidePayloadRoot(destination, toPath);
10910
10910
  if (entry.isDirectory()) {
10911
10911
  stack.push({ from: fromPath, to: toPath });
10912
10912
  continue;
10913
10913
  }
10914
- const data = fs44.readFileSync(fromPath);
10915
- fs44.mkdirSync(path52.dirname(toPath), { recursive: true });
10916
- fs44.writeFileSync(toPath, data, { mode: 420 });
10914
+ const data = fs45.readFileSync(fromPath);
10915
+ fs45.mkdirSync(path53.dirname(toPath), { recursive: true });
10916
+ fs45.writeFileSync(toPath, data, { mode: 420 });
10917
10917
  written += 1;
10918
10918
  }
10919
10919
  }
@@ -10924,7 +10924,7 @@ async function fetchSkillPayload(input) {
10924
10924
  if (!gitAvailable()) {
10925
10925
  throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
10926
10926
  }
10927
- if (fs44.existsSync(destination)) {
10927
+ if (fs45.existsSync(destination)) {
10928
10928
  throw new Error(`Skill payload destination already exists: ${destination}`);
10929
10929
  }
10930
10930
  const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
@@ -10932,11 +10932,11 @@ async function fetchSkillPayload(input) {
10932
10932
  if (!repoSource || !skillSlug) {
10933
10933
  throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
10934
10934
  }
10935
- const cloneRoot = fs44.mkdtempSync(
10936
- path52.join(os10.tmpdir(), "growthub-skills-source-")
10935
+ const cloneRoot = fs45.mkdtempSync(
10936
+ path53.join(os11.tmpdir(), "growthub-skills-source-")
10937
10937
  );
10938
10938
  try {
10939
- const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path52.dirname(cloneRoot));
10939
+ const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path53.dirname(cloneRoot));
10940
10940
  if (!cloneRes.ok) {
10941
10941
  throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
10942
10942
  }
@@ -10949,7 +10949,7 @@ async function fetchSkillPayload(input) {
10949
10949
  const fileCount = copySkillTree(skillDir, destination);
10950
10950
  return { destination, fileCount };
10951
10951
  } finally {
10952
- fs44.rmSync(cloneRoot, { recursive: true, force: true });
10952
+ fs45.rmSync(cloneRoot, { recursive: true, force: true });
10953
10953
  }
10954
10954
  }
10955
10955
  var DEFAULT_BASE, COMMENT_PATTERN;
@@ -10963,13 +10963,13 @@ var init_skills_source = __esm({
10963
10963
  });
10964
10964
 
10965
10965
  // src/starter/source-import/detect.ts
10966
- import fs45 from "node:fs";
10967
- import path53 from "node:path";
10966
+ import fs46 from "node:fs";
10967
+ import path54 from "node:path";
10968
10968
  function safeReadPackageJson(dir) {
10969
- const p35 = path53.resolve(dir, "package.json");
10970
- if (!fs45.existsSync(p35)) return null;
10969
+ const p35 = path54.resolve(dir, "package.json");
10970
+ if (!fs46.existsSync(p35)) return null;
10971
10971
  try {
10972
- return JSON.parse(fs45.readFileSync(p35, "utf8"));
10972
+ return JSON.parse(fs46.readFileSync(p35, "utf8"));
10973
10973
  } catch {
10974
10974
  return null;
10975
10975
  }
@@ -10979,10 +10979,10 @@ function detectPackageManager(dir, pkg) {
10979
10979
  if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
10980
10980
  if (pkg?.packageManager?.startsWith("npm")) return "npm";
10981
10981
  if (pkg?.packageManager?.startsWith("bun")) return "bun";
10982
- if (fs45.existsSync(path53.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
10983
- if (fs45.existsSync(path53.resolve(dir, "yarn.lock"))) return "yarn";
10984
- if (fs45.existsSync(path53.resolve(dir, "bun.lockb"))) return "bun";
10985
- if (fs45.existsSync(path53.resolve(dir, "package-lock.json"))) return "npm";
10982
+ if (fs46.existsSync(path54.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
10983
+ if (fs46.existsSync(path54.resolve(dir, "yarn.lock"))) return "yarn";
10984
+ if (fs46.existsSync(path54.resolve(dir, "bun.lockb"))) return "bun";
10985
+ if (fs46.existsSync(path54.resolve(dir, "package-lock.json"))) return "npm";
10986
10986
  return "unknown";
10987
10987
  }
10988
10988
  function collectDeps(pkg) {
@@ -10996,12 +10996,12 @@ function collectDeps(pkg) {
10996
10996
  }
10997
10997
  function looksLikeSkillPayload(rootDir) {
10998
10998
  const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
10999
- return markers.some((name) => fs45.existsSync(path53.resolve(rootDir, name)));
10999
+ return markers.some((name) => fs46.existsSync(path54.resolve(rootDir, name)));
11000
11000
  }
11001
11001
  function detectFramework(rootDir, pkg) {
11002
11002
  if (!pkg) {
11003
11003
  if (looksLikeSkillPayload(rootDir)) return "skill";
11004
- if (fs45.existsSync(path53.resolve(rootDir, "docs"))) return "docs";
11004
+ if (fs46.existsSync(path54.resolve(rootDir, "docs"))) return "docs";
11005
11005
  return "unknown";
11006
11006
  }
11007
11007
  const deps = collectDeps(pkg);
@@ -11010,8 +11010,8 @@ function detectFramework(rootDir, pkg) {
11010
11010
  "vite.config.ts",
11011
11011
  "vite.config.mjs",
11012
11012
  "vite.config.cjs"
11013
- ].some((name) => fs45.existsSync(path53.resolve(rootDir, name)));
11014
- if (deps.has("next") || fs45.existsSync(path53.resolve(rootDir, "next.config.js")) || fs45.existsSync(path53.resolve(rootDir, "next.config.mjs"))) {
11013
+ ].some((name) => fs46.existsSync(path54.resolve(rootDir, name)));
11014
+ if (deps.has("next") || fs46.existsSync(path54.resolve(rootDir, "next.config.js")) || fs46.existsSync(path54.resolve(rootDir, "next.config.mjs"))) {
11015
11015
  return "next";
11016
11016
  }
11017
11017
  if (deps.has("vite") || hasViteConfig) return "vite";
@@ -11037,15 +11037,15 @@ function pickScripts(pkg) {
11037
11037
  return out;
11038
11038
  }
11039
11039
  function listEnvFiles(dir) {
11040
- if (!fs45.existsSync(dir)) return [];
11041
- return fs45.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
11040
+ if (!fs46.existsSync(dir)) return [];
11041
+ return fs46.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
11042
11042
  }
11043
11043
  function findAppRoot(rootDir, pkg) {
11044
11044
  if (pkg) return ".";
11045
11045
  const candidates = ["app", "src", "apps", "packages"];
11046
11046
  for (const candidate of candidates) {
11047
- const abs = path53.resolve(rootDir, candidate);
11048
- if (fs45.existsSync(abs) && fs45.statSync(abs).isDirectory()) {
11047
+ const abs = path54.resolve(rootDir, candidate);
11048
+ if (fs46.existsSync(abs) && fs46.statSync(abs).isDirectory()) {
11049
11049
  const child = safeReadPackageJson(abs);
11050
11050
  if (child) return candidate;
11051
11051
  }
@@ -11061,12 +11061,12 @@ function computeConfidence(framework, manager, pkg) {
11061
11061
  return Math.min(1, Number(score.toFixed(2)));
11062
11062
  }
11063
11063
  function detectSourceShape(rootDir) {
11064
- if (!fs45.existsSync(rootDir) || !fs45.statSync(rootDir).isDirectory()) {
11064
+ if (!fs46.existsSync(rootDir) || !fs46.statSync(rootDir).isDirectory()) {
11065
11065
  throw new Error(`Detection target is not a directory: ${rootDir}`);
11066
11066
  }
11067
11067
  const rootPkg = safeReadPackageJson(rootDir);
11068
11068
  const appRootRel = findAppRoot(rootDir, rootPkg);
11069
- const appRootAbs = path53.resolve(rootDir, appRootRel);
11069
+ const appRootAbs = path54.resolve(rootDir, appRootRel);
11070
11070
  const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
11071
11071
  const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
11072
11072
  const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
@@ -11106,18 +11106,18 @@ var init_detect = __esm({
11106
11106
  });
11107
11107
 
11108
11108
  // src/starter/source-import/security.ts
11109
- import fs46 from "node:fs";
11110
- import path54 from "node:path";
11109
+ import fs47 from "node:fs";
11110
+ import path55 from "node:path";
11111
11111
  function isLikelyTextFile(filename) {
11112
- const ext = path54.extname(filename).toLowerCase();
11112
+ const ext = path55.extname(filename).toLowerCase();
11113
11113
  if (!ext) return true;
11114
11114
  return TEXT_EXTENSIONS.has(ext);
11115
11115
  }
11116
11116
  function isSuspiciousBinary(filename) {
11117
- return SUSPICIOUS_BINARY_EXTENSIONS.has(path54.extname(filename).toLowerCase());
11117
+ return SUSPICIOUS_BINARY_EXTENSIONS.has(path55.extname(filename).toLowerCase());
11118
11118
  }
11119
11119
  function isUnexpectedArchive(filename) {
11120
- const ext = path54.extname(filename).toLowerCase();
11120
+ const ext = path55.extname(filename).toLowerCase();
11121
11121
  return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
11122
11122
  }
11123
11123
  function shortExcerpt(line) {
@@ -11172,19 +11172,19 @@ function walkPayload(root, onFile, limits) {
11172
11172
  if (!current) break;
11173
11173
  let entries;
11174
11174
  try {
11175
- entries = fs46.readdirSync(current, { withFileTypes: true });
11175
+ entries = fs47.readdirSync(current, { withFileTypes: true });
11176
11176
  } catch {
11177
11177
  continue;
11178
11178
  }
11179
11179
  for (const entry of entries) {
11180
- const abs = path54.resolve(current, entry.name);
11180
+ const abs = path55.resolve(current, entry.name);
11181
11181
  if (entry.isDirectory()) {
11182
11182
  if (entry.name === ".git" || entry.name === "node_modules") continue;
11183
11183
  stack.push(abs);
11184
11184
  continue;
11185
11185
  }
11186
11186
  if (!entry.isFile()) continue;
11187
- const rel = path54.relative(root, abs);
11187
+ const rel = path55.relative(root, abs);
11188
11188
  onFile(abs, rel);
11189
11189
  visited += 1;
11190
11190
  if (visited >= limits.maxFiles) break;
@@ -11194,7 +11194,7 @@ function walkPayload(root, onFile, limits) {
11194
11194
  }
11195
11195
  function inspectSourcePayload(input) {
11196
11196
  const { payloadRoot } = input;
11197
- if (!fs46.existsSync(payloadRoot) || !fs46.statSync(payloadRoot).isDirectory()) {
11197
+ if (!fs47.existsSync(payloadRoot) || !fs47.statSync(payloadRoot).isDirectory()) {
11198
11198
  throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
11199
11199
  }
11200
11200
  const findings = [];
@@ -11204,7 +11204,7 @@ function inspectSourcePayload(input) {
11204
11204
  (abs, rel) => {
11205
11205
  let size = 0;
11206
11206
  try {
11207
- size = fs46.statSync(abs).size;
11207
+ size = fs47.statSync(abs).size;
11208
11208
  } catch {
11209
11209
  return;
11210
11210
  }
@@ -11213,7 +11213,7 @@ function inspectSourcePayload(input) {
11213
11213
  category: "suspicious-binary",
11214
11214
  severity: "high-risk",
11215
11215
  path: rel,
11216
- message: `Payload ships a precompiled binary (${path54.extname(rel)}). Review provenance before use.`
11216
+ message: `Payload ships a precompiled binary (${path55.extname(rel)}). Review provenance before use.`
11217
11217
  });
11218
11218
  return;
11219
11219
  }
@@ -11222,7 +11222,7 @@ function inspectSourcePayload(input) {
11222
11222
  category: "unexpected-archive",
11223
11223
  severity: "caution",
11224
11224
  path: rel,
11225
- message: `Payload ships an archive (${path54.extname(rel)}) \u2014 expand and review contents before use.`
11225
+ message: `Payload ships an archive (${path55.extname(rel)}) \u2014 expand and review contents before use.`
11226
11226
  });
11227
11227
  return;
11228
11228
  }
@@ -11230,12 +11230,12 @@ function inspectSourcePayload(input) {
11230
11230
  if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
11231
11231
  let buf;
11232
11232
  try {
11233
- const handle = fs46.openSync(abs, "r");
11233
+ const handle = fs47.openSync(abs, "r");
11234
11234
  try {
11235
11235
  buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
11236
- fs46.readSync(handle, buf, 0, buf.length, 0);
11236
+ fs47.readSync(handle, buf, 0, buf.length, 0);
11237
11237
  } finally {
11238
- fs46.closeSync(handle);
11238
+ fs47.closeSync(handle);
11239
11239
  }
11240
11240
  } catch {
11241
11241
  return;
@@ -11444,18 +11444,18 @@ var init_security = __esm({
11444
11444
  });
11445
11445
 
11446
11446
  // src/starter/source-import/plan.ts
11447
- import fs47 from "node:fs";
11448
- import path55 from "node:path";
11447
+ import fs48 from "node:fs";
11448
+ import path56 from "node:path";
11449
11449
  function generateImportId() {
11450
11450
  return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
11451
11451
  }
11452
11452
  function destinationState(absDest) {
11453
- if (!fs47.existsSync(absDest)) return { exists: false, nonEmpty: false };
11454
- const stats = fs47.statSync(absDest);
11453
+ if (!fs48.existsSync(absDest)) return { exists: false, nonEmpty: false };
11454
+ const stats = fs48.statSync(absDest);
11455
11455
  if (!stats.isDirectory()) {
11456
11456
  throw new Error(`Destination is not a directory: ${absDest}`);
11457
11457
  }
11458
- const entries = fs47.readdirSync(absDest);
11458
+ const entries = fs48.readdirSync(absDest);
11459
11459
  return { exists: true, nonEmpty: entries.length > 0 };
11460
11460
  }
11461
11461
  function describeSource(probe) {
@@ -11465,7 +11465,7 @@ function describeSource(probe) {
11465
11465
  return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
11466
11466
  }
11467
11467
  function buildSourceImportPlan(input) {
11468
- const absDest = path55.resolve(input.destination);
11468
+ const absDest = path56.resolve(input.destination);
11469
11469
  const state = destinationState(absDest);
11470
11470
  const payloadPath = "imported";
11471
11471
  const warnings = [...input.probe.warnings];
@@ -11578,8 +11578,8 @@ var init_plan = __esm({
11578
11578
  });
11579
11579
 
11580
11580
  // src/starter/source-import/summarize.ts
11581
- import fs48 from "node:fs";
11582
- import path56 from "node:path";
11581
+ import fs49 from "node:fs";
11582
+ import path57 from "node:path";
11583
11583
  function sourceHeading(manifest) {
11584
11584
  const src = manifest.source;
11585
11585
  if (src.kind === "github-repo") {
@@ -11634,7 +11634,7 @@ function nextStepsSection(manifest) {
11634
11634
  }
11635
11635
  function writeImportSummary(input) {
11636
11636
  const { forkPath, summaryRelativePath, manifest } = input;
11637
- const summaryPath = path56.resolve(forkPath, summaryRelativePath);
11637
+ const summaryPath = path57.resolve(forkPath, summaryRelativePath);
11638
11638
  const body = [
11639
11639
  `# Source Import Summary`,
11640
11640
  ``,
@@ -11664,8 +11664,8 @@ function writeImportSummary(input) {
11664
11664
  `Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
11665
11665
  ``
11666
11666
  ].join("\n");
11667
- fs48.mkdirSync(path56.dirname(summaryPath), { recursive: true });
11668
- fs48.writeFileSync(summaryPath, body, "utf8");
11667
+ fs49.mkdirSync(path57.dirname(summaryPath), { recursive: true });
11668
+ fs49.writeFileSync(summaryPath, body, "utf8");
11669
11669
  return summaryPath;
11670
11670
  }
11671
11671
  var init_summarize = __esm({
@@ -11675,16 +11675,16 @@ var init_summarize = __esm({
11675
11675
  });
11676
11676
 
11677
11677
  // src/starter/source-import/materialize.ts
11678
- import fs49 from "node:fs";
11679
- import os11 from "node:os";
11680
- import path57 from "node:path";
11678
+ import fs50 from "node:fs";
11679
+ import os12 from "node:os";
11680
+ import path58 from "node:path";
11681
11681
  function resolveSourceKind(probe) {
11682
11682
  return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
11683
11683
  }
11684
11684
  function stagingDirFor(forkPath) {
11685
- return path57.join(
11686
- os11.tmpdir(),
11687
- `growthub-source-import-${path57.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
11685
+ return path58.join(
11686
+ os12.tmpdir(),
11687
+ `growthub-source-import-${path58.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
11688
11688
  );
11689
11689
  }
11690
11690
  async function fetchPayload(probe, stagingDir, opts) {
@@ -11716,18 +11716,18 @@ async function fetchPayload(probe, stagingDir, opts) {
11716
11716
  return { payloadRoot: stagingDir };
11717
11717
  }
11718
11718
  function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
11719
- const target = path57.resolve(forkPath, payloadRelativePath);
11720
- if (fs49.existsSync(target)) {
11721
- fs49.rmSync(target, { recursive: true, force: true });
11719
+ const target = path58.resolve(forkPath, payloadRelativePath);
11720
+ if (fs50.existsSync(target)) {
11721
+ fs50.rmSync(target, { recursive: true, force: true });
11722
11722
  }
11723
- fs49.mkdirSync(path57.dirname(target), { recursive: true });
11724
- fs49.renameSync(payloadRoot, target);
11723
+ fs50.mkdirSync(path58.dirname(target), { recursive: true });
11724
+ fs50.renameSync(payloadRoot, target);
11725
11725
  return target;
11726
11726
  }
11727
11727
  function writeManifest(forkPath, manifest) {
11728
- const p35 = path57.resolve(forkPath, MANIFEST_RELATIVE_PATH);
11729
- fs49.mkdirSync(path57.dirname(p35), { recursive: true });
11730
- fs49.writeFileSync(p35, JSON.stringify(manifest, null, 2) + "\n", "utf8");
11728
+ const p35 = path58.resolve(forkPath, MANIFEST_RELATIVE_PATH);
11729
+ fs50.mkdirSync(path58.dirname(p35), { recursive: true });
11730
+ fs50.writeFileSync(p35, JSON.stringify(manifest, null, 2) + "\n", "utf8");
11731
11731
  return p35;
11732
11732
  }
11733
11733
  function assertConfirmationsSatisfied(plan, confirmations) {
@@ -11767,7 +11767,7 @@ async function materializeImportPlan(input) {
11767
11767
  requireSkillAcknowledgement: sourceKind === "skills-skill"
11768
11768
  });
11769
11769
  if (security.blocked) {
11770
- fs49.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
11770
+ fs50.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
11771
11771
  throw new Error(
11772
11772
  `Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
11773
11773
  );
@@ -11898,26 +11898,26 @@ var init_materialize = __esm({
11898
11898
  });
11899
11899
 
11900
11900
  // src/starter/source-import/agent.ts
11901
- import fs50 from "node:fs";
11902
- import path58 from "node:path";
11901
+ import fs51 from "node:fs";
11902
+ import path59 from "node:path";
11903
11903
  function resolveJobsDir() {
11904
- return path58.resolve(resolveKitForksHomeDir(), "source-import-jobs");
11904
+ return path59.resolve(resolveKitForksHomeDir(), "source-import-jobs");
11905
11905
  }
11906
11906
  function resolveJobPath2(jobId) {
11907
- return path58.resolve(resolveJobsDir(), `${jobId}.json`);
11907
+ return path59.resolve(resolveJobsDir(), `${jobId}.json`);
11908
11908
  }
11909
11909
  function generateJobId2() {
11910
11910
  return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
11911
11911
  }
11912
11912
  function writeJob2(job) {
11913
11913
  const p35 = resolveJobPath2(job.jobId);
11914
- fs50.mkdirSync(path58.dirname(p35), { recursive: true });
11915
- fs50.writeFileSync(p35, JSON.stringify(job, null, 2) + "\n", "utf8");
11914
+ fs51.mkdirSync(path59.dirname(p35), { recursive: true });
11915
+ fs51.writeFileSync(p35, JSON.stringify(job, null, 2) + "\n", "utf8");
11916
11916
  }
11917
11917
  function readJobFile(p35) {
11918
- if (!fs50.existsSync(p35)) return null;
11918
+ if (!fs51.existsSync(p35)) return null;
11919
11919
  try {
11920
- return JSON.parse(fs50.readFileSync(p35, "utf8"));
11920
+ return JSON.parse(fs51.readFileSync(p35, "utf8"));
11921
11921
  } catch {
11922
11922
  return null;
11923
11923
  }
@@ -11927,7 +11927,7 @@ function patchJob2(jobId, status, patch = {}) {
11927
11927
  const job = readJobFile(p35);
11928
11928
  if (!job) return null;
11929
11929
  const updated = { ...job, ...patch, status };
11930
- fs50.writeFileSync(p35, JSON.stringify(updated, null, 2) + "\n", "utf8");
11930
+ fs51.writeFileSync(p35, JSON.stringify(updated, null, 2) + "\n", "utf8");
11931
11931
  return updated;
11932
11932
  }
11933
11933
  function getSourceImportJob(jobId) {
@@ -11946,7 +11946,7 @@ async function probeAndPlan(input, destination) {
11946
11946
  }
11947
11947
  async function runSourceImportJob(input) {
11948
11948
  const jobId = generateJobId2();
11949
- const destination = path58.resolve(input.out);
11949
+ const destination = path59.resolve(input.out);
11950
11950
  const sourceKind = input.source.kind;
11951
11951
  const initial = {
11952
11952
  jobId,
@@ -12103,8 +12103,8 @@ __export(source_import_discovery_exports, {
12103
12103
  });
12104
12104
  import * as p33 from "@clack/prompts";
12105
12105
  import pc47 from "picocolors";
12106
- import fs53 from "node:fs";
12107
- import path60 from "node:path";
12106
+ import fs54 from "node:fs";
12107
+ import path61 from "node:path";
12108
12108
  import { pathToFileURL as pathToFileURL4 } from "node:url";
12109
12109
  function slugifyWorkspaceName(input) {
12110
12110
  const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -12132,10 +12132,10 @@ async function promptForInteractiveWorkspacePath(input) {
12132
12132
  });
12133
12133
  if (p33.isCancel(raw) || !raw) return null;
12134
12134
  const trimmed = String(raw).trim();
12135
- const expanded = trimmed.startsWith("~/") ? path60.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
12136
- const resolved = path60.resolve(expanded);
12137
- if (fs53.existsSync(resolved) && fs53.statSync(resolved).isDirectory()) {
12138
- const finalPath = path60.join(resolved, suggestedName);
12135
+ const expanded = trimmed.startsWith("~/") ? path61.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
12136
+ const resolved = path61.resolve(expanded);
12137
+ if (fs54.existsSync(resolved) && fs54.statSync(resolved).isDirectory()) {
12138
+ const finalPath = path61.join(resolved, suggestedName);
12139
12139
  p33.note(
12140
12140
  [
12141
12141
  `You selected an existing folder: ${resolved}`,
@@ -12406,8 +12406,8 @@ var init_source_import_discovery = __esm({
12406
12406
  import { Command } from "commander";
12407
12407
  import * as p34 from "@clack/prompts";
12408
12408
  import pc48 from "picocolors";
12409
- import fs54 from "node:fs";
12410
- import path61 from "node:path";
12409
+ import fs55 from "node:fs";
12410
+ import path62 from "node:path";
12411
12411
  import { spawnSync as spawnSync7 } from "node:child_process";
12412
12412
  import { fileURLToPath as fileURLToPath7 } from "node:url";
12413
12413
 
@@ -13208,8 +13208,8 @@ function printItemCompleted(item) {
13208
13208
  const changes = Array.isArray(item.changes) ? item.changes : [];
13209
13209
  const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
13210
13210
  const kind = asString(change.kind, "update");
13211
- const path62 = asString(change.path, "unknown");
13212
- return `${kind} ${path62}`;
13211
+ const path63 = asString(change.path, "unknown");
13212
+ return `${kind} ${path63}`;
13213
13213
  });
13214
13214
  const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
13215
13215
  const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
@@ -16043,8 +16043,8 @@ function registerIssueCommands(program2) {
16043
16043
  if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
16044
16044
  if (opts.projectId) params.set("projectId", opts.projectId);
16045
16045
  const query = params.toString();
16046
- const path62 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
16047
- const rows = await ctx.api.get(path62) ?? [];
16046
+ const path63 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
16047
+ const rows = await ctx.api.get(path63) ?? [];
16048
16048
  const filtered = filterIssueRows(rows, opts.match);
16049
16049
  if (ctx.json) {
16050
16050
  printOutput(filtered, { json: true });
@@ -16650,8 +16650,8 @@ function registerActivityCommands(program2) {
16650
16650
  if (opts.entityType) params.set("entityType", opts.entityType);
16651
16651
  if (opts.entityId) params.set("entityId", opts.entityId);
16652
16652
  const query = params.toString();
16653
- const path62 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
16654
- const rows = await ctx.api.get(path62) ?? [];
16653
+ const path63 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
16654
+ const rows = await ctx.api.get(path63) ?? [];
16655
16655
  if (ctx.json) {
16656
16656
  printOutput(rows, { json: true });
16657
16657
  return;
@@ -22749,6 +22749,388 @@ function createHostedExecutionClient() {
22749
22749
  };
22750
22750
  }
22751
22751
 
22752
+ // src/runtime/cms-manifest-client/index.ts
22753
+ init_store();
22754
+ init_session_store();
22755
+ import {
22756
+ API_CONTRACT_VERSION
22757
+ } from "@growthub/api-contract";
22758
+ var MANIFEST_PATH = "/api/cms/capabilities";
22759
+ var CONTRACT_VERSION_HEADER = "x-growthub-api-contract-version";
22760
+ var DEFAULT_HOSTED_BASE_URL2 = "https://www.growthub.ai";
22761
+ var CLI_SUPPORTED_CONTRACT_VERSION = API_CONTRACT_VERSION;
22762
+ var ManifestClientError = class extends Error {
22763
+ code;
22764
+ constructor(code, message) {
22765
+ super(message);
22766
+ this.code = code;
22767
+ this.name = "ManifestClientError";
22768
+ }
22769
+ };
22770
+ var ManifestUnauthenticatedError = class extends ManifestClientError {
22771
+ constructor(message = "Hosted manifest requires an authenticated session. Run `growthub auth login`.") {
22772
+ super("UNAUTHENTICATED", message);
22773
+ this.name = "ManifestUnauthenticatedError";
22774
+ }
22775
+ };
22776
+ var ManifestEndpointUnavailableError = class extends ManifestClientError {
22777
+ status;
22778
+ constructor(status, message) {
22779
+ super("ENDPOINT_UNAVAILABLE", message);
22780
+ this.status = status;
22781
+ this.name = "ManifestEndpointUnavailableError";
22782
+ }
22783
+ };
22784
+ var ManifestContractMismatchError = class extends ManifestClientError {
22785
+ serverVersion;
22786
+ expectedVersion;
22787
+ constructor(serverVersion, expectedVersion) {
22788
+ super(
22789
+ "CONTRACT_MISMATCH",
22790
+ serverVersion === null ? `Hosted manifest response is missing the \`${CONTRACT_VERSION_HEADER}\` header; expected ${expectedVersion}.` : `Hosted manifest contract version mismatch: expected ${expectedVersion}, server reported ${serverVersion}.`
22791
+ );
22792
+ this.serverVersion = serverVersion;
22793
+ this.expectedVersion = expectedVersion;
22794
+ this.name = "ManifestContractMismatchError";
22795
+ }
22796
+ };
22797
+ var ManifestMalformedError = class extends ManifestClientError {
22798
+ constructor(message) {
22799
+ super("MALFORMED", message);
22800
+ this.name = "ManifestMalformedError";
22801
+ }
22802
+ };
22803
+ function trimSlashes3(value) {
22804
+ return value.replace(/\/+$/, "");
22805
+ }
22806
+ function resolveManifestBaseUrl(opts = {}) {
22807
+ const explicit = opts.explicit?.trim();
22808
+ if (explicit) return { baseUrl: trimSlashes3(explicit), source: "explicit" };
22809
+ const sessionBase = (opts.sessionHostedBaseUrl ?? readSession()?.hostedBaseUrl)?.trim();
22810
+ if (sessionBase) return { baseUrl: trimSlashes3(sessionBase), source: "session" };
22811
+ const envBase = process.env.GROWTHUB_BASE_URL?.trim();
22812
+ if (envBase) return { baseUrl: trimSlashes3(envBase), source: "env" };
22813
+ try {
22814
+ const config = readConfig(opts.configPath);
22815
+ const authNode = config?.auth;
22816
+ const configuredBase = authNode?.growthubBaseUrl?.trim();
22817
+ if (configuredBase) return { baseUrl: trimSlashes3(configuredBase), source: "config.growthubBaseUrl" };
22818
+ const portalBase = authNode?.growthubPortalBaseUrl?.trim();
22819
+ if (portalBase) return { baseUrl: trimSlashes3(portalBase), source: "config.growthubPortalBaseUrl" };
22820
+ } catch {
22821
+ }
22822
+ return { baseUrl: DEFAULT_HOSTED_BASE_URL2, source: "default" };
22823
+ }
22824
+ function isRecord(value) {
22825
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22826
+ }
22827
+ function assertRecord(value, path63) {
22828
+ if (!isRecord(value)) {
22829
+ throw new ManifestMalformedError(`Expected object at \`${path63}\`.`);
22830
+ }
22831
+ return value;
22832
+ }
22833
+ function assertArray(value, path63) {
22834
+ if (!Array.isArray(value)) {
22835
+ throw new ManifestMalformedError(`Expected array at \`${path63}\`.`);
22836
+ }
22837
+ return value;
22838
+ }
22839
+ function assertString(value, path63) {
22840
+ if (typeof value !== "string" || value.length === 0) {
22841
+ throw new ManifestMalformedError(`Expected non-empty string at \`${path63}\`.`);
22842
+ }
22843
+ return value;
22844
+ }
22845
+ function normalizeProvenance(value, path63) {
22846
+ const record = assertRecord(value, path63);
22847
+ const originType = assertString(record.originType, `${path63}.originType`);
22848
+ const allowed = ["hosted", "local-extension", "derived-from-workflow"];
22849
+ if (!allowed.includes(originType)) {
22850
+ throw new ManifestMalformedError(
22851
+ `Unknown provenance originType at \`${path63}.originType\`: ${originType}`
22852
+ );
22853
+ }
22854
+ return {
22855
+ originType,
22856
+ sourceHost: typeof record.sourceHost === "string" ? record.sourceHost : void 0,
22857
+ sourceWorkflowId: typeof record.sourceWorkflowId === "string" ? record.sourceWorkflowId : void 0,
22858
+ sourceManifestId: typeof record.sourceManifestId === "string" ? record.sourceManifestId : void 0,
22859
+ localExtensionPath: typeof record.localExtensionPath === "string" ? record.localExtensionPath : void 0,
22860
+ recordedAt: typeof record.recordedAt === "string" ? record.recordedAt : void 0,
22861
+ note: typeof record.note === "string" ? record.note : void 0
22862
+ };
22863
+ }
22864
+ function normalizeManifestEntry(value, idx) {
22865
+ const record = assertRecord(value, `capabilities[${idx}]`);
22866
+ const slug = assertString(record.slug, `capabilities[${idx}].slug`);
22867
+ const family = assertString(record.family, `capabilities[${idx}].family`);
22868
+ const displayName = assertString(record.displayName, `capabilities[${idx}].displayName`);
22869
+ const executionKind = assertString(record.executionKind, `capabilities[${idx}].executionKind`);
22870
+ const requiredBindings = assertArray(record.requiredBindings, `capabilities[${idx}].requiredBindings`).map((entry, i) => assertString(entry, `capabilities[${idx}].requiredBindings[${i}]`));
22871
+ const outputTypes = assertArray(record.outputTypes, `capabilities[${idx}].outputTypes`).map((entry, i) => assertString(entry, `capabilities[${idx}].outputTypes[${i}]`));
22872
+ const node = assertRecord(record.node, `capabilities[${idx}].node`);
22873
+ const provenance = normalizeProvenance(record.provenance, `capabilities[${idx}].provenance`);
22874
+ return {
22875
+ slug,
22876
+ family,
22877
+ displayName,
22878
+ executionKind,
22879
+ requiredBindings,
22880
+ outputTypes,
22881
+ node,
22882
+ inputSchema: record.inputSchema,
22883
+ outputSchema: record.outputSchema,
22884
+ providerHints: record.providerHints,
22885
+ executionHints: record.executionHints,
22886
+ provenance
22887
+ };
22888
+ }
22889
+ function normalizeEnvelope(raw) {
22890
+ const record = assertRecord(raw, "envelope");
22891
+ if (record.version !== 1) {
22892
+ throw new ManifestMalformedError(
22893
+ `Unsupported envelope version: ${String(record.version)} (expected 1).`
22894
+ );
22895
+ }
22896
+ const host = assertString(record.host, "host");
22897
+ const fetchedAt = assertString(record.fetchedAt, "fetchedAt");
22898
+ const source = assertString(record.source, "source");
22899
+ const allowedSources = ["hosted", "local-extension", "derived"];
22900
+ if (!allowedSources.includes(source)) {
22901
+ throw new ManifestMalformedError(`Unknown envelope source: ${source}`);
22902
+ }
22903
+ const capabilities = assertArray(record.capabilities, "capabilities").map((entry, idx) => normalizeManifestEntry(entry, idx));
22904
+ const provenance = record.provenance !== void 0 ? normalizeProvenance(record.provenance, "provenance") : void 0;
22905
+ return {
22906
+ version: 1,
22907
+ host,
22908
+ fetchedAt,
22909
+ source,
22910
+ capabilities,
22911
+ provenance
22912
+ };
22913
+ }
22914
+ function parseContractVersionHeader(raw) {
22915
+ if (raw === null) return null;
22916
+ const trimmed = raw.trim();
22917
+ if (!trimmed) return null;
22918
+ const parsed = Number(trimmed);
22919
+ return Number.isFinite(parsed) ? parsed : null;
22920
+ }
22921
+ async function fetchCapabilityManifest(opts = {}) {
22922
+ const resolvedBaseUrl = resolveManifestBaseUrl(opts);
22923
+ const url = new URL(MANIFEST_PATH, `${resolvedBaseUrl.baseUrl}/`).toString();
22924
+ const headers = {
22925
+ accept: "application/json"
22926
+ };
22927
+ if (!opts.anonymous) {
22928
+ const session = readSession();
22929
+ if (session && isSessionExpired(session)) {
22930
+ throw new ManifestUnauthenticatedError(
22931
+ "Hosted session expired. Run `growthub auth login` to re-authenticate."
22932
+ );
22933
+ }
22934
+ if (session) {
22935
+ headers.authorization = `Bearer ${session.accessToken}`;
22936
+ if (session.userId) headers["x-user-id"] = session.userId;
22937
+ }
22938
+ }
22939
+ const controller = opts.timeoutMs && opts.timeoutMs > 0 ? new AbortController() : null;
22940
+ const timer = controller ? setTimeout(() => controller.abort(), opts.timeoutMs) : null;
22941
+ let response;
22942
+ try {
22943
+ response = await fetch(url, {
22944
+ method: "GET",
22945
+ headers,
22946
+ signal: controller?.signal
22947
+ });
22948
+ } catch (err) {
22949
+ throw new ManifestEndpointUnavailableError(
22950
+ void 0,
22951
+ `Hosted manifest endpoint unreachable (${url}): ${err instanceof Error ? err.message : String(err)}`
22952
+ );
22953
+ } finally {
22954
+ if (timer) clearTimeout(timer);
22955
+ }
22956
+ if (response.status === 401 || response.status === 403) {
22957
+ throw new ManifestUnauthenticatedError(
22958
+ `Hosted manifest endpoint returned ${response.status}. Run \`growthub auth login\` to authenticate.`
22959
+ );
22960
+ }
22961
+ if (response.status === 404 || response.status === 501) {
22962
+ throw new ManifestEndpointUnavailableError(
22963
+ response.status,
22964
+ `Hosted manifest endpoint not available (status ${response.status}). The hosted app may not yet expose /api/cms/capabilities.`
22965
+ );
22966
+ }
22967
+ if (!response.ok) {
22968
+ throw new ManifestEndpointUnavailableError(
22969
+ response.status,
22970
+ `Hosted manifest endpoint failed (status ${response.status}).`
22971
+ );
22972
+ }
22973
+ const rawHeader = response.headers.get(CONTRACT_VERSION_HEADER);
22974
+ const serverVersion = parseContractVersionHeader(rawHeader);
22975
+ if (rawHeader === null) {
22976
+ throw new ManifestContractMismatchError(null, CLI_SUPPORTED_CONTRACT_VERSION);
22977
+ }
22978
+ if (serverVersion !== CLI_SUPPORTED_CONTRACT_VERSION) {
22979
+ throw new ManifestContractMismatchError(rawHeader, CLI_SUPPORTED_CONTRACT_VERSION);
22980
+ }
22981
+ let bodyText;
22982
+ try {
22983
+ bodyText = await response.text();
22984
+ } catch (err) {
22985
+ throw new ManifestMalformedError(
22986
+ `Failed to read manifest response body: ${err instanceof Error ? err.message : String(err)}`
22987
+ );
22988
+ }
22989
+ let parsed;
22990
+ try {
22991
+ parsed = JSON.parse(bodyText);
22992
+ } catch (err) {
22993
+ throw new ManifestMalformedError(
22994
+ `Failed to parse manifest JSON: ${err instanceof Error ? err.message : String(err)}`
22995
+ );
22996
+ }
22997
+ const envelope = normalizeEnvelope(parsed);
22998
+ return {
22999
+ envelope,
23000
+ resolvedBaseUrl,
23001
+ serverContractVersion: rawHeader
23002
+ };
23003
+ }
23004
+
23005
+ // src/runtime/cms-manifest-cache/index.ts
23006
+ init_home();
23007
+ import fs30 from "node:fs";
23008
+ import path38 from "node:path";
23009
+ import os9 from "node:os";
23010
+ function resolveManifestCacheDir() {
23011
+ return path38.resolve(resolvePaperclipHomeDir(), "manifests");
23012
+ }
23013
+ function resolveManifestCachePath() {
23014
+ return path38.resolve(resolveManifestCacheDir(), "capabilities.json");
23015
+ }
23016
+ function parseJsonSafe(text69) {
23017
+ try {
23018
+ return JSON.parse(text69);
23019
+ } catch {
23020
+ return null;
23021
+ }
23022
+ }
23023
+ function isValidEnvelopeShape(value) {
23024
+ if (typeof value !== "object" || value === null) return false;
23025
+ const record = value;
23026
+ return record.version === 1 && typeof record.host === "string" && typeof record.fetchedAt === "string" && typeof record.source === "string" && Array.isArray(record.capabilities);
23027
+ }
23028
+ function readManifestCache() {
23029
+ const filePath = resolveManifestCachePath();
23030
+ if (!fs30.existsSync(filePath)) return null;
23031
+ let text69;
23032
+ try {
23033
+ text69 = fs30.readFileSync(filePath, "utf-8");
23034
+ } catch {
23035
+ return null;
23036
+ }
23037
+ const parsed = parseJsonSafe(text69);
23038
+ if (!isValidEnvelopeShape(parsed)) return null;
23039
+ return parsed;
23040
+ }
23041
+ function writeManifestCache(envelope) {
23042
+ const dir = resolveManifestCacheDir();
23043
+ fs30.mkdirSync(dir, { recursive: true });
23044
+ const filePath = resolveManifestCachePath();
23045
+ const tmpPath = path38.join(dir, `.capabilities.${process.pid}.${Date.now()}.tmp`);
23046
+ const body = `${JSON.stringify(envelope, null, 2)}
23047
+ `;
23048
+ fs30.writeFileSync(tmpPath, body, { mode: 420 });
23049
+ try {
23050
+ fs30.renameSync(tmpPath, filePath);
23051
+ } catch (err) {
23052
+ try {
23053
+ fs30.copyFileSync(tmpPath, filePath);
23054
+ fs30.unlinkSync(tmpPath);
23055
+ } catch {
23056
+ throw err;
23057
+ }
23058
+ }
23059
+ return filePath;
23060
+ }
23061
+ function describeManifestCachePath() {
23062
+ const filePath = resolveManifestCachePath();
23063
+ const home = os9.homedir();
23064
+ if (filePath.startsWith(`${home}/`)) {
23065
+ return `~${filePath.slice(home.length)}`;
23066
+ }
23067
+ return filePath;
23068
+ }
23069
+
23070
+ // src/runtime/cms-manifest-projection/index.ts
23071
+ function coerceFamily(value) {
23072
+ const known = CAPABILITY_FAMILIES;
23073
+ if (known.includes(value)) {
23074
+ return value;
23075
+ }
23076
+ return "ops";
23077
+ }
23078
+ function buildManifestMetadata(baseMetadata, entry) {
23079
+ const merged = {
23080
+ ...baseMetadata ?? {}
23081
+ };
23082
+ if (entry.inputSchema) merged.inputSchema = entry.inputSchema;
23083
+ if (entry.outputSchema) merged.outputSchema = entry.outputSchema;
23084
+ if (entry.providerHints) merged.providerHints = entry.providerHints;
23085
+ if (entry.executionHints) merged.executionHints = entry.executionHints;
23086
+ merged.__provenance = entry.provenance;
23087
+ return merged;
23088
+ }
23089
+ function projectEntry(entry) {
23090
+ const node = entry.node;
23091
+ const family = coerceFamily(entry.family);
23092
+ return {
23093
+ slug: entry.slug,
23094
+ displayName: node?.displayName ?? entry.displayName,
23095
+ icon: typeof node?.icon === "string" ? node.icon : "",
23096
+ family,
23097
+ category: typeof node?.category === "string" ? node.category : "automation",
23098
+ nodeType: node?.nodeType ?? "tool_execution",
23099
+ executionKind: entry.executionKind,
23100
+ executionBinding: node?.executionBinding ?? {
23101
+ type: "mcp_tool_call",
23102
+ strategy: "direct"
23103
+ },
23104
+ executionTokens: node?.executionTokens ?? {
23105
+ tool_name: entry.slug,
23106
+ input_template: {},
23107
+ output_mapping: {}
23108
+ },
23109
+ requiredBindings: entry.requiredBindings,
23110
+ outputTypes: entry.outputTypes,
23111
+ enabled: typeof node?.enabled === "boolean" ? node.enabled : true,
23112
+ experimental: typeof node?.experimental === "boolean" ? node.experimental : false,
23113
+ visibility: node?.visibility ?? "authenticated",
23114
+ description: typeof node?.description === "string" ? node.description : void 0,
23115
+ manifestMetadata: buildManifestMetadata(node?.manifestMetadata, entry)
23116
+ };
23117
+ }
23118
+ function projectManifestEnvelope(envelope) {
23119
+ const seen = /* @__PURE__ */ new Set();
23120
+ const nodes = [];
23121
+ for (const entry of envelope.capabilities) {
23122
+ if (seen.has(entry.slug)) continue;
23123
+ seen.add(entry.slug);
23124
+ nodes.push(projectEntry(entry));
23125
+ }
23126
+ return {
23127
+ nodes,
23128
+ host: envelope.host,
23129
+ fetchedAt: envelope.fetchedAt,
23130
+ source: envelope.source
23131
+ };
23132
+ }
23133
+
22752
23134
  // src/runtime/cms-capability-registry/index.ts
22753
23135
  init_session_store();
22754
23136
  init_hosted_client();
@@ -22867,29 +23249,80 @@ function matchesQuery(node, query) {
22867
23249
  }
22868
23250
  return true;
22869
23251
  }
23252
+ async function resolveFromManifest() {
23253
+ try {
23254
+ const { envelope } = await fetchCapabilityManifest();
23255
+ writeManifestCache(envelope);
23256
+ const projected = projectManifestEnvelope(envelope);
23257
+ return {
23258
+ kind: "manifest",
23259
+ nodes: projected.nodes,
23260
+ fetchedAt: projected.fetchedAt,
23261
+ stale: false
23262
+ };
23263
+ } catch (err) {
23264
+ if (err instanceof ManifestContractMismatchError || err instanceof ManifestMalformedError) {
23265
+ const cached = readManifestCache();
23266
+ if (cached) {
23267
+ const projected = projectManifestEnvelope(cached);
23268
+ return {
23269
+ kind: "manifest",
23270
+ nodes: projected.nodes,
23271
+ fetchedAt: projected.fetchedAt,
23272
+ stale: true,
23273
+ stalenessReason: err instanceof ManifestContractMismatchError ? `Contract version mismatch (${err.message}); using cached manifest from ${projected.fetchedAt}.` : `Hosted manifest malformed (${err.message}); using cached manifest from ${projected.fetchedAt}.`
23274
+ };
23275
+ }
23276
+ throw err;
23277
+ }
23278
+ if (err instanceof ManifestEndpointUnavailableError || err instanceof ManifestUnauthenticatedError) {
23279
+ const cached = readManifestCache();
23280
+ if (cached) {
23281
+ const projected = projectManifestEnvelope(cached);
23282
+ return {
23283
+ kind: "manifest",
23284
+ nodes: projected.nodes,
23285
+ fetchedAt: projected.fetchedAt,
23286
+ stale: true,
23287
+ stalenessReason: `Hosted manifest unavailable (${err.message}); using cached manifest from ${projected.fetchedAt}.`
23288
+ };
23289
+ }
23290
+ return resolveFromLegacy();
23291
+ }
23292
+ throw err;
23293
+ }
23294
+ }
23295
+ async function resolveFromLegacy() {
23296
+ const executionClient = createHostedExecutionClient();
23297
+ let hostedRecords = await executionClient.getHostedCapabilities();
23298
+ if (hostedRecords.length === 0) {
23299
+ hostedRecords = await deriveCapabilitiesFromHostedWorkflows();
23300
+ }
23301
+ if (hostedRecords.length === 0) {
23302
+ throw new Error(
23303
+ "Hosted capability registry returned zero nodes, cache is empty, and derivation produced nothing."
23304
+ );
23305
+ }
23306
+ return {
23307
+ kind: "legacy",
23308
+ nodes: hostedRecords.map(toCapabilityNode),
23309
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
23310
+ };
23311
+ }
22870
23312
  function createCmsCapabilityRegistryClient() {
22871
23313
  return {
22872
23314
  async listCapabilities(query) {
22873
- const executionClient = createHostedExecutionClient();
22874
- let hostedRecords = await executionClient.getHostedCapabilities();
22875
- if (hostedRecords.length === 0) {
22876
- hostedRecords = await deriveCapabilitiesFromHostedWorkflows();
22877
- }
22878
- if (hostedRecords.length === 0) {
22879
- throw new Error("Hosted capability registry returned zero nodes. No local fallback is enabled.");
22880
- }
22881
- const nodes = hostedRecords.map(toCapabilityNode);
23315
+ const outcome = await resolveFromManifest();
23316
+ const nodes = outcome.nodes;
22882
23317
  const enabledCount = nodes.filter((n) => n.enabled).length;
22883
23318
  const filtered = query ? nodes.filter((n) => matchesQuery(n, query)) : nodes;
22884
- return {
22885
- nodes: filtered,
22886
- meta: {
22887
- total: nodes.length,
22888
- enabledCount,
22889
- fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
22890
- source: "hosted"
22891
- }
23319
+ const meta = {
23320
+ total: nodes.length,
23321
+ enabledCount,
23322
+ fetchedAt: outcome.fetchedAt,
23323
+ source: "hosted"
22892
23324
  };
23325
+ return { nodes: filtered, meta };
22893
23326
  },
22894
23327
  async getCapability(slug) {
22895
23328
  const { nodes } = await this.listCapabilities({ slug, enabledOnly: false });
@@ -22899,10 +23332,10 @@ function createCmsCapabilityRegistryClient() {
22899
23332
  }
22900
23333
 
22901
23334
  // src/runtime/machine-capability-resolver/index.ts
22902
- import os9 from "node:os";
23335
+ import os10 from "node:os";
22903
23336
  function buildMachineContext(profile) {
22904
23337
  return {
22905
- hostname: os9.hostname(),
23338
+ hostname: os10.hostname(),
22906
23339
  machineLabel: profile.local.machineLabel ?? void 0,
22907
23340
  workspaceLabel: profile.local.workspaceLabel ?? void 0,
22908
23341
  instanceId: profile.local.instanceId,
@@ -22987,6 +23420,186 @@ function createMachineCapabilityResolver() {
22987
23420
  };
22988
23421
  }
22989
23422
 
23423
+ // src/runtime/cms-manifest-diff/index.ts
23424
+ function stableStringify(value) {
23425
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
23426
+ if (Array.isArray(value)) {
23427
+ return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
23428
+ }
23429
+ const record = value;
23430
+ const keys = Object.keys(record).sort();
23431
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
23432
+ }
23433
+ function arraysEqual(a, b) {
23434
+ if (a.length !== b.length) return false;
23435
+ const aSorted = [...a].sort();
23436
+ const bSorted = [...b].sort();
23437
+ for (let i = 0; i < aSorted.length; i++) {
23438
+ if (aSorted[i] !== bSorted[i]) return false;
23439
+ }
23440
+ return true;
23441
+ }
23442
+ function diffInputSchema(prev, next) {
23443
+ const prevFields = prev.inputSchema?.fields ?? null;
23444
+ const nextFields = next.inputSchema?.fields ?? null;
23445
+ if (prevFields === null && nextFields === null) return null;
23446
+ if (prevFields === null && nextFields !== null) {
23447
+ return `input schema introduced (${nextFields.length} field${nextFields.length === 1 ? "" : "s"})`;
23448
+ }
23449
+ if (prevFields !== null && nextFields === null) {
23450
+ return `input schema removed (was ${prevFields.length} field${prevFields.length === 1 ? "" : "s"})`;
23451
+ }
23452
+ const prevKeys = new Set((prevFields ?? []).map((f) => f.key));
23453
+ const nextKeys = new Set((nextFields ?? []).map((f) => f.key));
23454
+ const added = [];
23455
+ for (const key of nextKeys) if (!prevKeys.has(key)) added.push(key);
23456
+ const removed = [];
23457
+ for (const key of prevKeys) if (!nextKeys.has(key)) removed.push(key);
23458
+ const changed = [];
23459
+ for (const field of nextFields ?? []) {
23460
+ const prior = (prevFields ?? []).find((f) => f.key === field.key);
23461
+ if (!prior) continue;
23462
+ if (stableStringify(prior) !== stableStringify(field)) changed.push(field.key);
23463
+ }
23464
+ if (added.length === 0 && removed.length === 0 && changed.length === 0) return null;
23465
+ const parts = [];
23466
+ if (added.length > 0) parts.push(`+${added.join(", +")}`);
23467
+ if (removed.length > 0) parts.push(`-${removed.join(", -")}`);
23468
+ if (changed.length > 0) parts.push(`~${changed.join(", ~")}`);
23469
+ return `input schema fields ${parts.join(" ")}`;
23470
+ }
23471
+ function diffOutputSchema(prev, next) {
23472
+ const prevFields = prev.outputSchema?.outputs ?? null;
23473
+ const nextFields = next.outputSchema?.outputs ?? null;
23474
+ if (prevFields === null && nextFields === null) return null;
23475
+ if (stableStringify(prevFields) === stableStringify(nextFields)) return null;
23476
+ const prevKeys = new Set((prevFields ?? []).map((f) => f.key));
23477
+ const nextKeys = new Set((nextFields ?? []).map((f) => f.key));
23478
+ const added = [];
23479
+ for (const key of nextKeys) if (!prevKeys.has(key)) added.push(key);
23480
+ const removed = [];
23481
+ for (const key of prevKeys) if (!nextKeys.has(key)) removed.push(key);
23482
+ const parts = [];
23483
+ if (added.length > 0) parts.push(`+${added.join(", +")}`);
23484
+ if (removed.length > 0) parts.push(`-${removed.join(", -")}`);
23485
+ if (parts.length === 0) parts.push("reshuffled");
23486
+ return `output schema ${parts.join(" ")}`;
23487
+ }
23488
+ function diffExecutionTokens(prev, next) {
23489
+ const prevTokens = prev.node?.executionTokens;
23490
+ const nextTokens = next.node?.executionTokens;
23491
+ if (!prevTokens && !nextTokens) return null;
23492
+ const diffs = [];
23493
+ if (prevTokens?.tool_name !== nextTokens?.tool_name) {
23494
+ diffs.push(`tool_name ${prevTokens?.tool_name ?? "\u2205"} \u2192 ${nextTokens?.tool_name ?? "\u2205"}`);
23495
+ }
23496
+ if (stableStringify(prevTokens?.input_template ?? {}) !== stableStringify(nextTokens?.input_template ?? {})) {
23497
+ diffs.push("input_template changed");
23498
+ }
23499
+ if (stableStringify(prevTokens?.output_mapping ?? {}) !== stableStringify(nextTokens?.output_mapping ?? {})) {
23500
+ diffs.push("output_mapping changed");
23501
+ }
23502
+ if (diffs.length === 0) return null;
23503
+ return `execution tokens: ${diffs.join("; ")}`;
23504
+ }
23505
+ function diffProvenance(prev, next) {
23506
+ const prevOrigin = prev.provenance?.originType;
23507
+ const nextOrigin = next.provenance?.originType;
23508
+ if (prevOrigin === nextOrigin) return null;
23509
+ return `provenance.originType ${prevOrigin ?? "\u2205"} \u2192 ${nextOrigin ?? "\u2205"}`;
23510
+ }
23511
+ function diffManifestEnvelopes(prev, next, opts = {}) {
23512
+ const comparedAt = opts.comparedAt ?? (/* @__PURE__ */ new Date()).toISOString();
23513
+ const markers = [];
23514
+ const changeDetails = {};
23515
+ const prevBySlug = /* @__PURE__ */ new Map();
23516
+ if (prev) {
23517
+ for (const entry of prev.capabilities) prevBySlug.set(entry.slug, entry);
23518
+ }
23519
+ const nextBySlug = /* @__PURE__ */ new Map();
23520
+ for (const entry of next.capabilities) nextBySlug.set(entry.slug, entry);
23521
+ const added = [];
23522
+ const removed = [];
23523
+ const changed = [];
23524
+ for (const [slug] of nextBySlug) {
23525
+ if (!prevBySlug.has(slug)) {
23526
+ added.push(slug);
23527
+ markers.push({ slug, change: "added" });
23528
+ }
23529
+ }
23530
+ for (const [slug] of prevBySlug) {
23531
+ if (!nextBySlug.has(slug)) {
23532
+ removed.push(slug);
23533
+ markers.push({ slug, change: "removed" });
23534
+ }
23535
+ }
23536
+ for (const [slug, nextEntry] of nextBySlug) {
23537
+ const prevEntry = prevBySlug.get(slug);
23538
+ if (!prevEntry) continue;
23539
+ const slugChanges = [];
23540
+ if (prevEntry.family !== nextEntry.family) {
23541
+ const description = `family ${prevEntry.family} \u2192 ${nextEntry.family}`;
23542
+ markers.push({ slug, change: "schema", description });
23543
+ slugChanges.push(description);
23544
+ }
23545
+ if (prevEntry.executionKind !== nextEntry.executionKind) {
23546
+ const description = `executionKind ${prevEntry.executionKind} \u2192 ${nextEntry.executionKind}`;
23547
+ markers.push({ slug, change: "executionKind", description });
23548
+ slugChanges.push(description);
23549
+ }
23550
+ if (!arraysEqual(prevEntry.requiredBindings, nextEntry.requiredBindings)) {
23551
+ const description = `requiredBindings [${prevEntry.requiredBindings.join(", ")}] \u2192 [${nextEntry.requiredBindings.join(", ")}]`;
23552
+ markers.push({ slug, change: "requiredBindings", description });
23553
+ slugChanges.push(description);
23554
+ }
23555
+ if (!arraysEqual(prevEntry.outputTypes, nextEntry.outputTypes)) {
23556
+ const description = `outputTypes [${prevEntry.outputTypes.join(", ")}] \u2192 [${nextEntry.outputTypes.join(", ")}]`;
23557
+ markers.push({ slug, change: "outputTypes", description });
23558
+ slugChanges.push(description);
23559
+ }
23560
+ const prevEnabled = prevEntry.node?.enabled;
23561
+ const nextEnabled = nextEntry.node?.enabled;
23562
+ if (prevEnabled !== nextEnabled) {
23563
+ const description = `enabled ${String(prevEnabled)} \u2192 ${String(nextEnabled)}`;
23564
+ markers.push({ slug, change: "enabled", description });
23565
+ slugChanges.push(description);
23566
+ }
23567
+ const tokensDiff = diffExecutionTokens(prevEntry, nextEntry);
23568
+ if (tokensDiff) {
23569
+ markers.push({ slug, change: "schema", description: tokensDiff });
23570
+ slugChanges.push(tokensDiff);
23571
+ }
23572
+ const inputSchemaDiff = diffInputSchema(prevEntry, nextEntry);
23573
+ if (inputSchemaDiff) {
23574
+ markers.push({ slug, change: "schema", description: inputSchemaDiff });
23575
+ slugChanges.push(inputSchemaDiff);
23576
+ }
23577
+ const outputSchemaDiff = diffOutputSchema(prevEntry, nextEntry);
23578
+ if (outputSchemaDiff) {
23579
+ markers.push({ slug, change: "schema", description: outputSchemaDiff });
23580
+ slugChanges.push(outputSchemaDiff);
23581
+ }
23582
+ const provenanceDiff = diffProvenance(prevEntry, nextEntry);
23583
+ if (provenanceDiff) {
23584
+ markers.push({ slug, change: "schema", description: provenanceDiff });
23585
+ slugChanges.push(provenanceDiff);
23586
+ }
23587
+ if (slugChanges.length > 0) {
23588
+ changed.push(slug);
23589
+ changeDetails[slug] = slugChanges;
23590
+ }
23591
+ }
23592
+ const report = { comparedAt, markers };
23593
+ return {
23594
+ added: added.sort(),
23595
+ removed: removed.sort(),
23596
+ changed: changed.sort(),
23597
+ changeDetails,
23598
+ markers,
23599
+ report
23600
+ };
23601
+ }
23602
+
22990
23603
  // src/auth/workflow-access.ts
22991
23604
  function getWorkflowAccess() {
22992
23605
  const profile = computeEffectiveProfile();
@@ -23218,6 +23831,7 @@ Examples:
23218
23831
  $ growthub capability list --json # machine-readable output
23219
23832
  $ growthub capability inspect video-gen # inspect a specific capability
23220
23833
  $ growthub capability resolve # resolve machine bindings for all
23834
+ $ growthub capability refresh # sync hosted CMS manifest and diff
23221
23835
  `);
23222
23836
  cap.action(async () => {
23223
23837
  await runCapabilityPicker({});
@@ -23316,13 +23930,206 @@ Examples:
23316
23930
  process.exitCode = 1;
23317
23931
  }
23318
23932
  });
23933
+ cap.command("refresh").description("Sync the canonical hosted CMS capability manifest and diff against the local cache").option("--base-url <url>", "Hosted Growthub base URL (defaults to session / env / config / https://www.growthub.ai)").option("--json", "Output raw JSON for scripting").option("--include-experimental", "Include experimental capabilities in the summary rendering").option("--include-disabled", "Include disabled capabilities in the summary rendering").action(async (opts) => {
23934
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
23935
+ const cachePath = resolveManifestCachePath();
23936
+ const describedCachePath = describeManifestCachePath();
23937
+ let fetchResult;
23938
+ try {
23939
+ fetchResult = await fetchCapabilityManifest({ explicit: opts.baseUrl });
23940
+ } catch (err) {
23941
+ const prior2 = readManifestCache();
23942
+ if (err instanceof ManifestContractMismatchError) {
23943
+ if (prior2) {
23944
+ if (opts.json) {
23945
+ console.log(JSON.stringify({
23946
+ status: "contract_mismatch_using_cache",
23947
+ error: { code: err.code, message: err.message },
23948
+ cache: {
23949
+ host: prior2.host,
23950
+ fetchedAt: prior2.fetchedAt,
23951
+ total: prior2.capabilities.length,
23952
+ path: cachePath
23953
+ }
23954
+ }, null, 2));
23955
+ } else {
23956
+ console.error(pc32.yellow(`\u26A0 Contract version mismatch: ${err.message}`));
23957
+ console.error(pc32.dim(` Using cached manifest from ${prior2.fetchedAt} (${prior2.capabilities.length} capabilities).`));
23958
+ console.error(pc32.dim(` Cache: ${describedCachePath}`));
23959
+ console.error(pc32.dim(" Hosted manifest was NOT overwritten. Run `growthub upgrade` or re-login if the contract version is supposed to match."));
23960
+ }
23961
+ process.exitCode = 0;
23962
+ return;
23963
+ }
23964
+ reportRefreshError(err, opts.json, describedCachePath);
23965
+ process.exitCode = 1;
23966
+ return;
23967
+ }
23968
+ if (err instanceof ManifestMalformedError) {
23969
+ if (prior2) {
23970
+ if (opts.json) {
23971
+ console.log(JSON.stringify({
23972
+ status: "malformed_using_cache",
23973
+ error: { code: err.code, message: err.message },
23974
+ cache: {
23975
+ host: prior2.host,
23976
+ fetchedAt: prior2.fetchedAt,
23977
+ total: prior2.capabilities.length,
23978
+ path: cachePath
23979
+ }
23980
+ }, null, 2));
23981
+ } else {
23982
+ console.error(pc32.yellow(`\u26A0 Hosted manifest was malformed: ${err.message}`));
23983
+ console.error(pc32.dim(` Using cached manifest from ${prior2.fetchedAt} (${prior2.capabilities.length} capabilities).`));
23984
+ console.error(pc32.dim(` Cache: ${describedCachePath}`));
23985
+ }
23986
+ process.exitCode = 0;
23987
+ return;
23988
+ }
23989
+ reportRefreshError(err, opts.json, describedCachePath);
23990
+ process.exitCode = 1;
23991
+ return;
23992
+ }
23993
+ if (err instanceof ManifestEndpointUnavailableError || err instanceof ManifestUnauthenticatedError) {
23994
+ reportRefreshError(err, opts.json, describedCachePath);
23995
+ process.exitCode = 1;
23996
+ return;
23997
+ }
23998
+ reportRefreshError(err, opts.json, describedCachePath);
23999
+ process.exitCode = 1;
24000
+ return;
24001
+ }
24002
+ const { envelope, resolvedBaseUrl, serverContractVersion } = fetchResult;
24003
+ const prior = readManifestCache();
24004
+ const diffSummary = diffManifestEnvelopes(prior, envelope, { comparedAt: startedAt });
24005
+ try {
24006
+ writeManifestCache(envelope);
24007
+ } catch (writeErr) {
24008
+ const message = writeErr instanceof Error ? writeErr.message : String(writeErr);
24009
+ if (opts.json) {
24010
+ console.log(JSON.stringify({
24011
+ status: "fetched_but_cache_write_failed",
24012
+ error: message,
24013
+ host: envelope.host,
24014
+ fetchedAt: envelope.fetchedAt,
24015
+ total: envelope.capabilities.length,
24016
+ cachePath
24017
+ }, null, 2));
24018
+ } else {
24019
+ console.error(pc32.yellow(`\u26A0 Manifest fetched but cache write failed: ${message}`));
24020
+ console.error(pc32.dim(` Cache path: ${describedCachePath}`));
24021
+ }
24022
+ process.exitCode = 1;
24023
+ return;
24024
+ }
24025
+ if (opts.json) {
24026
+ console.log(JSON.stringify({
24027
+ status: "ok",
24028
+ host: envelope.host,
24029
+ resolvedBaseUrl: resolvedBaseUrl.baseUrl,
24030
+ resolvedBaseUrlSource: resolvedBaseUrl.source,
24031
+ fetchedAt: envelope.fetchedAt,
24032
+ contractVersion: serverContractVersion,
24033
+ total: envelope.capabilities.length,
24034
+ added: diffSummary.added,
24035
+ removed: diffSummary.removed,
24036
+ changed: diffSummary.changed,
24037
+ changeDetails: diffSummary.changeDetails,
24038
+ drift: diffSummary.report,
24039
+ cachePath
24040
+ }, null, 2));
24041
+ return;
24042
+ }
24043
+ printRefreshSummary({
24044
+ envelope,
24045
+ diff: diffSummary,
24046
+ resolvedBaseUrl: resolvedBaseUrl.baseUrl,
24047
+ resolvedBaseUrlSource: resolvedBaseUrl.source,
24048
+ cachePath: describedCachePath,
24049
+ serverContractVersion,
24050
+ includeExperimental: opts.includeExperimental === true,
24051
+ includeDisabled: opts.includeDisabled === true
24052
+ });
24053
+ });
24054
+ }
24055
+ function reportRefreshError(err, json, describedCachePath) {
24056
+ if (json) {
24057
+ const payload = err instanceof ManifestClientError ? { status: "error", code: err.code, message: err.message } : { status: "error", code: "UNKNOWN", message: err instanceof Error ? err.message : String(err) };
24058
+ console.log(JSON.stringify({ ...payload, cachePath: describedCachePath }, null, 2));
24059
+ return;
24060
+ }
24061
+ const message = err instanceof Error ? err.message : String(err);
24062
+ if (err instanceof ManifestUnauthenticatedError) {
24063
+ console.error(pc32.red("\u2717 Not authenticated: ") + message);
24064
+ } else if (err instanceof ManifestEndpointUnavailableError) {
24065
+ console.error(pc32.red("\u2717 Hosted manifest endpoint unavailable: ") + message);
24066
+ } else if (err instanceof ManifestContractMismatchError) {
24067
+ console.error(pc32.red("\u2717 Contract version mismatch: ") + message);
24068
+ console.error(pc32.dim(" No local cache is present to fall back on."));
24069
+ } else if (err instanceof ManifestMalformedError) {
24070
+ console.error(pc32.red("\u2717 Hosted manifest malformed: ") + message);
24071
+ console.error(pc32.dim(" No local cache is present to fall back on."));
24072
+ } else {
24073
+ console.error(pc32.red("\u2717 Failed to refresh manifest: ") + message);
24074
+ }
24075
+ console.error(pc32.dim(` Cache path: ${describedCachePath}`));
24076
+ }
24077
+ function printRefreshSummary(args) {
24078
+ const { envelope, diff, resolvedBaseUrl, resolvedBaseUrlSource, cachePath, serverContractVersion } = args;
24079
+ const { added, removed, changed, changeDetails } = diff;
24080
+ console.log("");
24081
+ console.log(pc32.bold("CMS Capability Manifest \u2014 Refresh"));
24082
+ console.log(hr4());
24083
+ console.log(` ${pc32.dim("Host:")} ${envelope.host}`);
24084
+ console.log(` ${pc32.dim("Resolved base:")} ${resolvedBaseUrl} ${pc32.dim(`(${resolvedBaseUrlSource})`)}`);
24085
+ console.log(` ${pc32.dim("Fetched at:")} ${envelope.fetchedAt}`);
24086
+ if (serverContractVersion !== null) {
24087
+ console.log(` ${pc32.dim("Contract version:")} ${serverContractVersion}`);
24088
+ }
24089
+ console.log(` ${pc32.dim("Source:")} ${envelope.source}`);
24090
+ console.log(` ${pc32.dim("Total:")} ${envelope.capabilities.length}`);
24091
+ console.log(` ${pc32.dim("Added:")} ${added.length > 0 ? pc32.green(String(added.length)) : pc32.dim("0")}`);
24092
+ console.log(` ${pc32.dim("Removed:")} ${removed.length > 0 ? pc32.red(String(removed.length)) : pc32.dim("0")}`);
24093
+ console.log(` ${pc32.dim("Changed:")} ${changed.length > 0 ? pc32.yellow(String(changed.length)) : pc32.dim("0")}`);
24094
+ console.log(` ${pc32.dim("Cache:")} ${cachePath}`);
24095
+ console.log(hr4());
24096
+ if (added.length > 0) {
24097
+ console.log(pc32.green("\n + Added"));
24098
+ for (const slug of added) {
24099
+ const entry = envelope.capabilities.find((c) => c.slug === slug);
24100
+ const label = entry?.displayName ? pc32.dim(` ${entry.displayName}`) : "";
24101
+ console.log(` ${pc32.bold(slug)}${label}`);
24102
+ }
24103
+ }
24104
+ if (removed.length > 0) {
24105
+ console.log(pc32.red("\n \u2212 Removed"));
24106
+ for (const slug of removed) {
24107
+ console.log(` ${pc32.bold(slug)}`);
24108
+ }
24109
+ }
24110
+ if (changed.length > 0) {
24111
+ console.log(pc32.yellow("\n ~ Changed"));
24112
+ for (const slug of changed) {
24113
+ console.log(` ${pc32.bold(slug)}`);
24114
+ for (const detail of changeDetails[slug] ?? []) {
24115
+ console.log(` ${pc32.dim("\xB7")} ${pc32.dim(detail)}`);
24116
+ }
24117
+ }
24118
+ }
24119
+ if (added.length === 0 && removed.length === 0 && changed.length === 0) {
24120
+ console.log(pc32.dim("\n No drift detected since the last cached manifest."));
24121
+ }
24122
+ console.log("");
24123
+ console.log(pc32.dim(" growthub capability list # browse refreshed registry"));
24124
+ console.log(pc32.dim(" growthub capability inspect <slug>"));
24125
+ console.log("");
23319
24126
  }
23320
24127
 
23321
24128
  // src/commands/pipeline.ts
23322
24129
  init_session_store();
23323
24130
  init_hosted_client();
23324
- import fs33 from "node:fs";
23325
- import path41 from "node:path";
24131
+ import fs34 from "node:fs";
24132
+ import path42 from "node:path";
23326
24133
  import * as p22 from "@clack/prompts";
23327
24134
  import pc34 from "picocolors";
23328
24135
 
@@ -23517,10 +24324,53 @@ function outputTypeFromSchema(value) {
23517
24324
  function humanizeFieldKey(key) {
23518
24325
  return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/^\w/, (c) => c.toUpperCase());
23519
24326
  }
24327
+ function nodeInputFieldTypeToContractType(field) {
24328
+ switch (field.fieldType) {
24329
+ case "text":
24330
+ case "long-text":
24331
+ case "select":
24332
+ case "url":
24333
+ case "file":
24334
+ case "url-or-file":
24335
+ return "string";
24336
+ case "number":
24337
+ return "number";
24338
+ case "boolean":
24339
+ return "boolean";
24340
+ case "array":
24341
+ return "array";
24342
+ case "json":
24343
+ return "object";
24344
+ default:
24345
+ return "unknown";
24346
+ }
24347
+ }
24348
+ function projectHostedInputField(field) {
24349
+ return {
24350
+ key: field.key,
24351
+ label: field.label?.trim() ? field.label : humanizeFieldKey(field.key),
24352
+ type: nodeInputFieldTypeToContractType(field),
24353
+ required: field.required === true,
24354
+ defaultValue: field.defaultValue
24355
+ };
24356
+ }
24357
+ function extractHostedInputSchema(node) {
24358
+ const schema = node.manifestMetadata?.inputSchema;
24359
+ if (!schema || typeof schema !== "object") return null;
24360
+ const fields = schema.fields;
24361
+ if (!Array.isArray(fields)) return null;
24362
+ return schema;
24363
+ }
23520
24364
  function introspectNodeContract(node) {
23521
24365
  const inputTemplate = node.executionTokens.input_template ?? {};
23522
24366
  const outputMapping = node.executionTokens.output_mapping ?? {};
23523
- const inputs = Object.entries(inputTemplate).map(([key, value]) => {
24367
+ const hostedSchema = extractHostedInputSchema(node);
24368
+ const hostedFields = hostedSchema?.fields ?? [];
24369
+ const hostedByKey = new Map(
24370
+ hostedFields.map((field) => [field.key, field])
24371
+ );
24372
+ const hostedContracts = hostedFields.map(projectHostedInputField);
24373
+ const heuristicContracts = Object.entries(inputTemplate).filter(([key]) => !hostedByKey.has(key)).map(([key, value]) => {
23524
24374
  const required = value === "" || value === null || value === void 0;
23525
24375
  return {
23526
24376
  key,
@@ -23530,6 +24380,7 @@ function introspectNodeContract(node) {
23530
24380
  defaultValue: value
23531
24381
  };
23532
24382
  });
24383
+ const inputs = [...hostedContracts, ...heuristicContracts];
23533
24384
  const outputs = Object.entries(outputMapping).map(([key, value]) => ({
23534
24385
  key,
23535
24386
  type: outputTypeFromSchema(value),
@@ -23832,40 +24683,40 @@ function renderPreSaveReview(input) {
23832
24683
 
23833
24684
  // src/runtime/artifact-contracts/index.ts
23834
24685
  init_home();
23835
- import fs30 from "node:fs";
23836
- import path38 from "node:path";
24686
+ import fs31 from "node:fs";
24687
+ import path39 from "node:path";
23837
24688
  import { randomBytes as randomBytes7 } from "node:crypto";
23838
24689
  function generateArtifactId() {
23839
24690
  return `art_${randomBytes7(8).toString("hex")}`;
23840
24691
  }
23841
24692
  function resolveArtifactsDir() {
23842
- return path38.resolve(resolvePaperclipHomeDir(), "artifacts");
24693
+ return path39.resolve(resolvePaperclipHomeDir(), "artifacts");
23843
24694
  }
23844
24695
  function resolveArtifactManifestPath(artifactId) {
23845
- return path38.resolve(resolveArtifactsDir(), `${artifactId}.json`);
24696
+ return path39.resolve(resolveArtifactsDir(), `${artifactId}.json`);
23846
24697
  }
23847
24698
  function readLocalManifest(artifactId) {
23848
24699
  const filePath = resolveArtifactManifestPath(artifactId);
23849
- if (!fs30.existsSync(filePath)) return null;
24700
+ if (!fs31.existsSync(filePath)) return null;
23850
24701
  try {
23851
- return JSON.parse(fs30.readFileSync(filePath, "utf-8"));
24702
+ return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
23852
24703
  } catch {
23853
24704
  return null;
23854
24705
  }
23855
24706
  }
23856
24707
  function writeLocalManifest(manifest) {
23857
24708
  const dir = resolveArtifactsDir();
23858
- fs30.mkdirSync(dir, { recursive: true });
24709
+ fs31.mkdirSync(dir, { recursive: true });
23859
24710
  const filePath = resolveArtifactManifestPath(manifest.id);
23860
- fs30.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
24711
+ fs31.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}
23861
24712
  `, { mode: 384 });
23862
24713
  }
23863
24714
  function listLocalManifests() {
23864
24715
  const dir = resolveArtifactsDir();
23865
- if (!fs30.existsSync(dir)) return [];
23866
- return fs30.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => {
24716
+ if (!fs31.existsSync(dir)) return [];
24717
+ return fs31.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => {
23867
24718
  try {
23868
- const content = fs30.readFileSync(path38.resolve(dir, entry.name), "utf-8");
24719
+ const content = fs31.readFileSync(path39.resolve(dir, entry.name), "utf-8");
23869
24720
  return JSON.parse(content);
23870
24721
  } catch {
23871
24722
  return null;
@@ -23943,8 +24794,8 @@ function createArtifactStore() {
23943
24794
 
23944
24795
  // src/runtime/native-intelligence/index.ts
23945
24796
  init_home();
23946
- import fs32 from "node:fs";
23947
- import path40 from "node:path";
24797
+ import fs33 from "node:fs";
24798
+ import path41 from "node:path";
23948
24799
 
23949
24800
  // src/runtime/native-intelligence/contract.ts
23950
24801
  var DEFAULT_INTELLIGENCE_CONFIG = {
@@ -24337,7 +25188,7 @@ async function summarizeExecution2(input, backend) {
24337
25188
  maxTokens: 2048,
24338
25189
  responseFormat: "json"
24339
25190
  });
24340
- const parsed = parseJsonSafe(completion.text);
25191
+ const parsed = parseJsonSafe2(completion.text);
24341
25192
  if (parsed) {
24342
25193
  return validateSummaryResult(parsed);
24343
25194
  }
@@ -24455,7 +25306,7 @@ function buildSummarizerPrompt(input) {
24455
25306
  }
24456
25307
  return sections.join("\n");
24457
25308
  }
24458
- function parseJsonSafe(text69) {
25309
+ function parseJsonSafe2(text69) {
24459
25310
  try {
24460
25311
  const trimmed = text69.trim();
24461
25312
  const jsonStart = trimmed.indexOf("{");
@@ -24524,7 +25375,7 @@ async function intelligentNormalizeBindings(input, backend) {
24524
25375
  maxTokens: 2048,
24525
25376
  responseFormat: "json"
24526
25377
  });
24527
- const parsed = parseJsonSafe2(completion.text);
25378
+ const parsed = parseJsonSafe3(completion.text);
24528
25379
  if (parsed) {
24529
25380
  return toNormalizationResult(parsed, input);
24530
25381
  }
@@ -24709,7 +25560,7 @@ function validateAction(action) {
24709
25560
  }
24710
25561
  return "kept";
24711
25562
  }
24712
- function parseJsonSafe2(text69) {
25563
+ function parseJsonSafe3(text69) {
24713
25564
  try {
24714
25565
  const trimmed = text69.trim();
24715
25566
  const jsonStart = trimmed.indexOf("{");
@@ -24775,7 +25626,7 @@ async function recommendWorkflow(input, backend) {
24775
25626
  maxTokens: 2048,
24776
25627
  responseFormat: "json"
24777
25628
  });
24778
- const parsed = parseJsonSafe3(completion.text);
25629
+ const parsed = parseJsonSafe4(completion.text);
24779
25630
  if (parsed) {
24780
25631
  return validateRecommendationResult(parsed, input);
24781
25632
  }
@@ -24952,7 +25803,7 @@ function validateStrategy(strategy) {
24952
25803
  }
24953
25804
  return "synthesize-new";
24954
25805
  }
24955
- function parseJsonSafe3(text69) {
25806
+ function parseJsonSafe4(text69) {
24956
25807
  try {
24957
25808
  const trimmed = text69.trim();
24958
25809
  const jsonStart = trimmed.indexOf("{");
@@ -25011,7 +25862,7 @@ async function planWorkflow(input, backend) {
25011
25862
  maxTokens: 3072,
25012
25863
  responseFormat: "json"
25013
25864
  });
25014
- const parsed = parseJsonSafe4(completion.text);
25865
+ const parsed = parseJsonSafe5(completion.text);
25015
25866
  if (parsed) {
25016
25867
  return validatePlanningResult(parsed, input);
25017
25868
  }
@@ -25200,7 +26051,7 @@ function validatePlanningResult(raw, input) {
25200
26051
  warnings
25201
26052
  };
25202
26053
  }
25203
- function parseJsonSafe4(text69) {
26054
+ function parseJsonSafe5(text69) {
25204
26055
  try {
25205
26056
  const trimmed = text69.trim();
25206
26057
  const jsonStart = trimmed.indexOf("{");
@@ -25215,8 +26066,8 @@ function parseJsonSafe4(text69) {
25215
26066
  }
25216
26067
 
25217
26068
  // src/runtime/native-intelligence/marketing-context-builder.ts
25218
- import fs31 from "node:fs";
25219
- import path39 from "node:path";
26069
+ import fs32 from "node:fs";
26070
+ import path40 from "node:path";
25220
26071
  var CONTEXT_BUILDER_SYSTEM_PROMPT = `You are a marketing strategist drafting a product-marketing-context document.
25221
26072
 
25222
26073
  You receive project artifacts (README content, package.json metadata, any landing page content) and produce a structured product-marketing-context.md with 12 sections.
@@ -25235,17 +26086,17 @@ var MAX_ARTIFACT_LENGTH = 8e3;
25235
26086
  function scanProjectArtifacts(projectDir, existingContext) {
25236
26087
  const artifacts = { otherFiles: [] };
25237
26088
  for (const name of ["README.md", "readme.md", "Readme.md", "README"]) {
25238
- const readmePath = path39.join(projectDir, name);
25239
- if (fs31.existsSync(readmePath)) {
25240
- const content = fs31.readFileSync(readmePath, "utf-8");
26089
+ const readmePath = path40.join(projectDir, name);
26090
+ if (fs32.existsSync(readmePath)) {
26091
+ const content = fs32.readFileSync(readmePath, "utf-8");
25241
26092
  artifacts.readme = content.slice(0, MAX_ARTIFACT_LENGTH);
25242
26093
  break;
25243
26094
  }
25244
26095
  }
25245
- const pkgPath = path39.join(projectDir, "package.json");
25246
- if (fs31.existsSync(pkgPath)) {
26096
+ const pkgPath = path40.join(projectDir, "package.json");
26097
+ if (fs32.existsSync(pkgPath)) {
25247
26098
  try {
25248
- artifacts.packageJson = JSON.parse(fs31.readFileSync(pkgPath, "utf-8"));
26099
+ artifacts.packageJson = JSON.parse(fs32.readFileSync(pkgPath, "utf-8"));
25249
26100
  } catch {
25250
26101
  }
25251
26102
  }
@@ -25257,15 +26108,15 @@ function scanProjectArtifacts(projectDir, existingContext) {
25257
26108
  ".claude/product-marketing-context.md",
25258
26109
  "brands/_template/product-marketing-context.md"
25259
26110
  ]) {
25260
- const ctxPath = path39.join(projectDir, candidate);
25261
- if (fs31.existsSync(ctxPath)) {
25262
- artifacts.existingContext = fs31.readFileSync(ctxPath, "utf-8");
26111
+ const ctxPath = path40.join(projectDir, candidate);
26112
+ if (fs32.existsSync(ctxPath)) {
26113
+ artifacts.existingContext = fs32.readFileSync(ctxPath, "utf-8");
25263
26114
  break;
25264
26115
  }
25265
26116
  }
25266
26117
  }
25267
26118
  for (const name of ["CONTRIBUTING.md", "AGENTS.md", "landing-page.md", "about.md"]) {
25268
- if (fs31.existsSync(path39.join(projectDir, name))) {
26119
+ if (fs32.existsSync(path40.join(projectDir, name))) {
25269
26120
  artifacts.otherFiles.push(name);
25270
26121
  }
25271
26122
  }
@@ -25506,15 +26357,15 @@ function cleanMarkdownResponse(text69) {
25506
26357
 
25507
26358
  // src/runtime/native-intelligence/index.ts
25508
26359
  function resolveConfigPath2() {
25509
- return path40.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
26360
+ return path41.resolve(resolvePaperclipHomeDir(), "native-intelligence", "config.json");
25510
26361
  }
25511
26362
  function readIntelligenceConfig() {
25512
26363
  const configPath = resolveConfigPath2();
25513
- if (!fs32.existsSync(configPath)) {
26364
+ if (!fs33.existsSync(configPath)) {
25514
26365
  return { ...DEFAULT_INTELLIGENCE_CONFIG };
25515
26366
  }
25516
26367
  try {
25517
- const raw = JSON.parse(fs32.readFileSync(configPath, "utf-8"));
26368
+ const raw = JSON.parse(fs33.readFileSync(configPath, "utf-8"));
25518
26369
  return {
25519
26370
  modelId: validateModelId(raw.modelId),
25520
26371
  backendType: raw.backendType === "hosted" ? "hosted" : "local",
@@ -25531,8 +26382,8 @@ function readIntelligenceConfig() {
25531
26382
  }
25532
26383
  function writeIntelligenceConfig(config) {
25533
26384
  const configPath = resolveConfigPath2();
25534
- fs32.mkdirSync(path40.dirname(configPath), { recursive: true });
25535
- fs32.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
26385
+ fs33.mkdirSync(path41.dirname(configPath), { recursive: true });
26386
+ fs33.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
25536
26387
  `, "utf-8");
25537
26388
  }
25538
26389
  function validateModelId(id) {
@@ -25888,9 +26739,9 @@ async function runPipelineAssembler(opts) {
25888
26739
  }
25889
26740
  }
25890
26741
  function loadPipelineFromFileOrJson(input) {
25891
- const resolvedPath = path41.resolve(input);
25892
- if (fs33.existsSync(resolvedPath)) {
25893
- const content = fs33.readFileSync(resolvedPath, "utf-8");
26742
+ const resolvedPath = path42.resolve(input);
26743
+ if (fs34.existsSync(resolvedPath)) {
26744
+ const content = fs34.readFileSync(resolvedPath, "utf-8");
25894
26745
  return deserializePipeline(JSON.parse(content));
25895
26746
  }
25896
26747
  try {
@@ -26435,8 +27286,8 @@ Examples:
26435
27286
  }
26436
27287
 
26437
27288
  // src/commands/workflow.ts
26438
- import fs35 from "node:fs";
26439
- import path43 from "node:path";
27289
+ import fs36 from "node:fs";
27290
+ import path44 from "node:path";
26440
27291
  import * as p23 from "@clack/prompts";
26441
27292
  import pc37 from "picocolors";
26442
27293
  init_session_store();
@@ -26444,15 +27295,15 @@ init_hosted_client();
26444
27295
 
26445
27296
  // src/runtime/workflow-hygiene/labels.ts
26446
27297
  init_home();
26447
- import fs34 from "node:fs";
26448
- import path42 from "node:path";
27298
+ import fs35 from "node:fs";
27299
+ import path43 from "node:path";
26449
27300
  function resolveStorePath() {
26450
- return path42.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
27301
+ return path43.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "labels.json");
26451
27302
  }
26452
27303
  function readStoreFile(filePath) {
26453
- if (!fs34.existsSync(filePath)) return { records: [] };
27304
+ if (!fs35.existsSync(filePath)) return { records: [] };
26454
27305
  try {
26455
- const raw = JSON.parse(fs34.readFileSync(filePath, "utf-8"));
27306
+ const raw = JSON.parse(fs35.readFileSync(filePath, "utf-8"));
26456
27307
  if (!Array.isArray(raw.records)) return { records: [] };
26457
27308
  return raw;
26458
27309
  } catch {
@@ -26460,8 +27311,8 @@ function readStoreFile(filePath) {
26460
27311
  }
26461
27312
  }
26462
27313
  function writeStoreFile(filePath, data) {
26463
- fs34.mkdirSync(path42.dirname(filePath), { recursive: true });
26464
- fs34.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
27314
+ fs35.mkdirSync(path43.dirname(filePath), { recursive: true });
27315
+ fs35.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
26465
27316
  `, "utf-8");
26466
27317
  }
26467
27318
  function inferDefaultLabel(name, createdAt, versionCount) {
@@ -26562,16 +27413,16 @@ function box5(lines) {
26562
27413
  return [top, ...body, bottom].join("\n");
26563
27414
  }
26564
27415
  function resolveSavedWorkflowsDir() {
26565
- return path43.resolve(resolvePaperclipHomeDir(), "workflows");
27416
+ return path44.resolve(resolvePaperclipHomeDir(), "workflows");
26566
27417
  }
26567
27418
  function resolveDeletedWorkflowIdsPath() {
26568
- return path43.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
27419
+ return path44.resolve(resolvePaperclipHomeDir(), "workflow-hygiene", "deleted-workflows.json");
26569
27420
  }
26570
27421
  function readDeletedWorkflowIds() {
26571
27422
  const filePath = resolveDeletedWorkflowIdsPath();
26572
- if (!fs35.existsSync(filePath)) return /* @__PURE__ */ new Set();
27423
+ if (!fs36.existsSync(filePath)) return /* @__PURE__ */ new Set();
26573
27424
  try {
26574
- const raw = JSON.parse(fs35.readFileSync(filePath, "utf-8"));
27425
+ const raw = JSON.parse(fs36.readFileSync(filePath, "utf-8"));
26575
27426
  if (!Array.isArray(raw?.workflowIds)) return /* @__PURE__ */ new Set();
26576
27427
  return new Set(raw.workflowIds.filter((value) => typeof value === "string"));
26577
27428
  } catch {
@@ -26580,8 +27431,8 @@ function readDeletedWorkflowIds() {
26580
27431
  }
26581
27432
  function writeDeletedWorkflowIds(ids) {
26582
27433
  const filePath = resolveDeletedWorkflowIdsPath();
26583
- fs35.mkdirSync(path43.dirname(filePath), { recursive: true });
26584
- fs35.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
27434
+ fs36.mkdirSync(path44.dirname(filePath), { recursive: true });
27435
+ fs36.writeFileSync(filePath, `${JSON.stringify({ workflowIds: [...ids] }, null, 2)}
26585
27436
  `, "utf-8");
26586
27437
  }
26587
27438
  function markWorkflowDeletedLocally(workflowId) {
@@ -26607,10 +27458,10 @@ function filterLocallyDeletedWorkflows(entries) {
26607
27458
  }
26608
27459
  function listLocalSavedWorkflows() {
26609
27460
  const dir = resolveSavedWorkflowsDir();
26610
- if (!fs35.existsSync(dir)) return [];
26611
- const entries = fs35.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
27461
+ if (!fs36.existsSync(dir)) return [];
27462
+ const entries = fs36.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
26612
27463
  try {
26613
- const raw = JSON.parse(fs35.readFileSync(path43.resolve(dir, e.name), "utf-8"));
27464
+ const raw = JSON.parse(fs36.readFileSync(path44.resolve(dir, e.name), "utf-8"));
26614
27465
  const pipeline = raw.pipeline ?? raw;
26615
27466
  return {
26616
27467
  filename: e.name,
@@ -26671,11 +27522,11 @@ async function archiveSavedWorkflow(entry) {
26671
27522
  throw new Error("Local workflow entry is missing filename.");
26672
27523
  }
26673
27524
  const dir = resolveSavedWorkflowsDir();
26674
- const archiveDir = path43.resolve(dir, "archived");
26675
- fs35.mkdirSync(archiveDir, { recursive: true });
26676
- fs35.renameSync(
26677
- path43.resolve(dir, entry.filename),
26678
- path43.resolve(archiveDir, entry.filename)
27525
+ const archiveDir = path44.resolve(dir, "archived");
27526
+ fs36.mkdirSync(archiveDir, { recursive: true });
27527
+ fs36.renameSync(
27528
+ path44.resolve(dir, entry.filename),
27529
+ path44.resolve(archiveDir, entry.filename)
26679
27530
  );
26680
27531
  }
26681
27532
  async function deleteSavedWorkflow(entry) {
@@ -26699,7 +27550,7 @@ async function deleteSavedWorkflow(entry) {
26699
27550
  if (!entry.filename) {
26700
27551
  throw new Error("Local workflow entry is missing filename.");
26701
27552
  }
26702
- fs35.rmSync(path43.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
27553
+ fs36.rmSync(path44.resolve(resolveSavedWorkflowsDir(), entry.filename), { force: true });
26703
27554
  markWorkflowDeletedLocally(entry.workflowId);
26704
27555
  }
26705
27556
  async function loadSavedWorkflowDetail(entry) {
@@ -26718,7 +27569,7 @@ async function loadSavedWorkflowDetail(entry) {
26718
27569
  };
26719
27570
  }
26720
27571
  const dir = resolveSavedWorkflowsDir();
26721
- const content = fs35.readFileSync(path43.resolve(dir, entry.filename), "utf-8");
27572
+ const content = fs36.readFileSync(path44.resolve(dir, entry.filename), "utf-8");
26722
27573
  const raw = JSON.parse(content);
26723
27574
  return {
26724
27575
  pipeline: raw.pipeline ?? raw,
@@ -27702,36 +28553,36 @@ import pc38 from "picocolors";
27702
28553
 
27703
28554
  // src/runtime/agent-harness/auth-store.ts
27704
28555
  init_home();
27705
- import fs36 from "node:fs";
27706
- import path44 from "node:path";
28556
+ import fs37 from "node:fs";
28557
+ import path45 from "node:path";
27707
28558
  function resolveHarnessAuthDir() {
27708
- return path44.resolve(resolvePaperclipHomeDir(), "harness-auth");
28559
+ return path45.resolve(resolvePaperclipHomeDir(), "harness-auth");
27709
28560
  }
27710
28561
  function resolveHarnessAuthFile(harnessId) {
27711
- return path44.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
28562
+ return path45.resolve(resolveHarnessAuthDir(), `${harnessId}.json`);
27712
28563
  }
27713
28564
  function normalizeSecret(value) {
27714
28565
  const trimmed = value?.trim();
27715
28566
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
27716
28567
  }
27717
28568
  function ensureSecureDir(dirPath) {
27718
- fs36.mkdirSync(dirPath, { recursive: true });
28569
+ fs37.mkdirSync(dirPath, { recursive: true });
27719
28570
  try {
27720
- fs36.chmodSync(dirPath, 448);
28571
+ fs37.chmodSync(dirPath, 448);
27721
28572
  } catch {
27722
28573
  }
27723
28574
  }
27724
28575
  function ensureSecureFile(filePath) {
27725
28576
  try {
27726
- fs36.chmodSync(filePath, 384);
28577
+ fs37.chmodSync(filePath, 384);
27727
28578
  } catch {
27728
28579
  }
27729
28580
  }
27730
28581
  function readHarnessCredentials(harnessId) {
27731
28582
  const filePath = resolveHarnessAuthFile(harnessId);
27732
- if (!fs36.existsSync(filePath)) return {};
28583
+ if (!fs37.existsSync(filePath)) return {};
27733
28584
  try {
27734
- const parsed = JSON.parse(fs36.readFileSync(filePath, "utf-8"));
28585
+ const parsed = JSON.parse(fs37.readFileSync(filePath, "utf-8"));
27735
28586
  const creds = {};
27736
28587
  for (const [key, value] of Object.entries(parsed)) {
27737
28588
  if (typeof value === "string" && value.trim().length > 0) {
@@ -27758,7 +28609,7 @@ function setHarnessCredential(harnessId, key, value) {
27758
28609
  const dirPath = resolveHarnessAuthDir();
27759
28610
  ensureSecureDir(dirPath);
27760
28611
  const filePath = resolveHarnessAuthFile(harnessId);
27761
- fs36.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
28612
+ fs37.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
27762
28613
  `, "utf-8");
27763
28614
  ensureSecureFile(filePath);
27764
28615
  }
@@ -27775,7 +28626,7 @@ function setHarnessCredentials(harnessId, updates) {
27775
28626
  const dirPath = resolveHarnessAuthDir();
27776
28627
  ensureSecureDir(dirPath);
27777
28628
  const filePath = resolveHarnessAuthFile(harnessId);
27778
- fs36.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
28629
+ fs37.writeFileSync(filePath, `${JSON.stringify(creds, null, 2)}
27779
28630
  `, "utf-8");
27780
28631
  ensureSecureFile(filePath);
27781
28632
  }
@@ -27787,8 +28638,8 @@ function maskSecret(value) {
27787
28638
 
27788
28639
  // src/runtime/open-agents/index.ts
27789
28640
  init_home();
27790
- import fs37 from "node:fs";
27791
- import path45 from "node:path";
28641
+ import fs38 from "node:fs";
28642
+ import path46 from "node:path";
27792
28643
 
27793
28644
  // src/runtime/open-agents/contract.ts
27794
28645
  var DEFAULT_OPEN_AGENTS_CONFIG = {
@@ -27975,18 +28826,18 @@ var OpenAgentsBackendError = class extends Error {
27975
28826
 
27976
28827
  // src/runtime/open-agents/index.ts
27977
28828
  function resolveConfigPath3() {
27978
- return path45.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
28829
+ return path46.resolve(resolvePaperclipHomeDir(), "open-agents", "config.json");
27979
28830
  }
27980
28831
  function readOpenAgentsConfig() {
27981
28832
  const configPath = resolveConfigPath3();
27982
- if (!fs37.existsSync(configPath)) {
28833
+ if (!fs38.existsSync(configPath)) {
27983
28834
  return {
27984
28835
  ...DEFAULT_OPEN_AGENTS_CONFIG,
27985
28836
  apiKey: getHarnessCredential("open-agents", "apiKey")
27986
28837
  };
27987
28838
  }
27988
28839
  try {
27989
- const raw = JSON.parse(fs37.readFileSync(configPath, "utf-8"));
28840
+ const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
27990
28841
  const storedApiKey = getHarnessCredential("open-agents", "apiKey");
27991
28842
  return {
27992
28843
  backendType: validateBackendType(raw.backendType),
@@ -28007,13 +28858,13 @@ function readOpenAgentsConfig() {
28007
28858
  }
28008
28859
  function writeOpenAgentsConfig(config) {
28009
28860
  const configPath = resolveConfigPath3();
28010
- fs37.mkdirSync(path45.dirname(configPath), { recursive: true });
28861
+ fs38.mkdirSync(path46.dirname(configPath), { recursive: true });
28011
28862
  const persisted = {
28012
28863
  ...config,
28013
28864
  authMode: validateAuthMode(config.authMode),
28014
28865
  apiKey: void 0
28015
28866
  };
28016
- fs37.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
28867
+ fs38.writeFileSync(configPath, `${JSON.stringify(persisted, null, 2)}
28017
28868
  `, "utf-8");
28018
28869
  setHarnessCredential("open-agents", "apiKey", config.apiKey);
28019
28870
  }
@@ -28574,8 +29425,8 @@ import pc39 from "picocolors";
28574
29425
 
28575
29426
  // src/runtime/qwen-code/index.ts
28576
29427
  init_home();
28577
- import fs38 from "node:fs";
28578
- import path46 from "node:path";
29428
+ import fs39 from "node:fs";
29429
+ import path47 from "node:path";
28579
29430
 
28580
29431
  // src/runtime/qwen-code/contract.ts
28581
29432
  var QWEN_CODE_APPROVAL_MODES = [
@@ -28793,19 +29644,19 @@ function buildSetupGuidance(env) {
28793
29644
 
28794
29645
  // src/runtime/qwen-code/index.ts
28795
29646
  function resolveConfigPath4() {
28796
- return path46.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
29647
+ return path47.resolve(resolvePaperclipHomeDir(), "qwen-code", "config.json");
28797
29648
  }
28798
29649
  function readQwenCodeConfig() {
28799
29650
  const configPath = resolveConfigPath4();
28800
29651
  const storedCredentials = readHarnessCredentials("qwen-code");
28801
- if (!fs38.existsSync(configPath)) {
29652
+ if (!fs39.existsSync(configPath)) {
28802
29653
  return {
28803
29654
  ...DEFAULT_QWEN_CODE_CONFIG,
28804
29655
  env: mergeHarnessEnv(DEFAULT_QWEN_CODE_CONFIG.env, storedCredentials)
28805
29656
  };
28806
29657
  }
28807
29658
  try {
28808
- const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
29659
+ const raw = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
28809
29660
  return {
28810
29661
  binaryPath: typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_QWEN_CODE_CONFIG.binaryPath,
28811
29662
  defaultModel: typeof raw.defaultModel === "string" ? raw.defaultModel : DEFAULT_QWEN_CODE_CONFIG.defaultModel,
@@ -28827,7 +29678,7 @@ function readQwenCodeConfig() {
28827
29678
  }
28828
29679
  function writeQwenCodeConfig(config) {
28829
29680
  const configPath = resolveConfigPath4();
28830
- fs38.mkdirSync(path46.dirname(configPath), { recursive: true });
29681
+ fs39.mkdirSync(path47.dirname(configPath), { recursive: true });
28831
29682
  const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
28832
29683
  const credentialUpdates = {};
28833
29684
  const publicEnv = {};
@@ -28839,7 +29690,7 @@ function writeQwenCodeConfig(config) {
28839
29690
  publicEnv[key] = value;
28840
29691
  }
28841
29692
  setHarnessCredentials("qwen-code", credentialUpdates);
28842
- fs38.writeFileSync(
29693
+ fs39.writeFileSync(
28843
29694
  configPath,
28844
29695
  `${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
28845
29696
  `,
@@ -29089,32 +29940,32 @@ import pc41 from "picocolors";
29089
29940
 
29090
29941
  // src/runtime/t3code/index.ts
29091
29942
  init_home();
29092
- import fs40 from "node:fs";
29093
- import path48 from "node:path";
29943
+ import fs41 from "node:fs";
29944
+ import path49 from "node:path";
29094
29945
 
29095
29946
  // src/runtime/agent-harness/harness-profile.ts
29096
29947
  init_home();
29097
- import fs39 from "node:fs";
29098
- import path47 from "node:path";
29948
+ import fs40 from "node:fs";
29949
+ import path48 from "node:path";
29099
29950
  import * as p26 from "@clack/prompts";
29100
29951
  import pc40 from "picocolors";
29101
29952
  function resolveProfileDir(harnessId) {
29102
- return path47.resolve(resolvePaperclipHomeDir(), harnessId);
29953
+ return path48.resolve(resolvePaperclipHomeDir(), harnessId);
29103
29954
  }
29104
29955
  function resolveProfilePath(harnessId) {
29105
- return path47.resolve(resolveProfileDir(harnessId), "growthub-profile.json");
29956
+ return path48.resolve(resolveProfileDir(harnessId), "growthub-profile.json");
29106
29957
  }
29107
29958
  function ensureSecureFile2(filePath) {
29108
29959
  try {
29109
- fs39.chmodSync(filePath, 384);
29960
+ fs40.chmodSync(filePath, 384);
29110
29961
  } catch {
29111
29962
  }
29112
29963
  }
29113
29964
  function readHarnessProfile(harnessId) {
29114
29965
  const filePath = resolveProfilePath(harnessId);
29115
- if (!fs39.existsSync(filePath)) return null;
29966
+ if (!fs40.existsSync(filePath)) return null;
29116
29967
  try {
29117
- const raw = JSON.parse(fs39.readFileSync(filePath, "utf-8"));
29968
+ const raw = JSON.parse(fs40.readFileSync(filePath, "utf-8"));
29118
29969
  if (typeof raw.workspaceId !== "string" || typeof raw.machineLabel !== "string") return null;
29119
29970
  return {
29120
29971
  profileVersion: 1,
@@ -29132,14 +29983,14 @@ function readHarnessProfile(harnessId) {
29132
29983
  function writeHarnessProfile(harnessId, profile) {
29133
29984
  const dirPath = resolveProfileDir(harnessId);
29134
29985
  const filePath = resolveProfilePath(harnessId);
29135
- fs39.mkdirSync(dirPath, { recursive: true });
29136
- fs39.writeFileSync(filePath, `${JSON.stringify(profile, null, 2)}
29986
+ fs40.mkdirSync(dirPath, { recursive: true });
29987
+ fs40.writeFileSync(filePath, `${JSON.stringify(profile, null, 2)}
29137
29988
  `, "utf-8");
29138
29989
  ensureSecureFile2(filePath);
29139
29990
  }
29140
29991
  function clearHarnessProfile(harnessId) {
29141
29992
  const filePath = resolveProfilePath(harnessId);
29142
- if (fs39.existsSync(filePath)) fs39.rmSync(filePath);
29993
+ if (fs40.existsSync(filePath)) fs40.rmSync(filePath);
29143
29994
  }
29144
29995
  function buildProfileStatusLines(harnessId, harnessLabel, profile) {
29145
29996
  if (!profile) {
@@ -29420,19 +30271,19 @@ function writeT3GrowthubProfile(profile) {
29420
30271
  writeHarnessProfile(T3_HARNESS_ID, profile);
29421
30272
  }
29422
30273
  function resolveConfigPath5() {
29423
- return path48.resolve(resolvePaperclipHomeDir(), "t3code", "config.json");
30274
+ return path49.resolve(resolvePaperclipHomeDir(), "t3code", "config.json");
29424
30275
  }
29425
30276
  function readT3CodeConfig() {
29426
30277
  const configPath = resolveConfigPath5();
29427
30278
  const storedCredentials = readHarnessCredentials(T3_HARNESS_ID);
29428
- if (!fs40.existsSync(configPath)) {
30279
+ if (!fs41.existsSync(configPath)) {
29429
30280
  return {
29430
30281
  ...DEFAULT_T3_CODE_CONFIG,
29431
30282
  env: mergeHarnessEnv2(DEFAULT_T3_CODE_CONFIG.env, storedCredentials)
29432
30283
  };
29433
30284
  }
29434
30285
  try {
29435
- const raw = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
30286
+ const raw = JSON.parse(fs41.readFileSync(configPath, "utf-8"));
29436
30287
  const profile = readHarnessProfile(T3_HARNESS_ID);
29437
30288
  const resolvedBinaryPath = profile?.forkBinaryPath ?? (typeof raw.binaryPath === "string" ? raw.binaryPath : DEFAULT_T3_CODE_CONFIG.binaryPath);
29438
30289
  return {
@@ -29455,7 +30306,7 @@ function readT3CodeConfig() {
29455
30306
  }
29456
30307
  function writeT3CodeConfig(config) {
29457
30308
  const configPath = resolveConfigPath5();
29458
- fs40.mkdirSync(path48.dirname(configPath), { recursive: true });
30309
+ fs41.mkdirSync(path49.dirname(configPath), { recursive: true });
29459
30310
  const rawEnv = typeof config.env === "object" && config.env !== null ? config.env : {};
29460
30311
  const credentialUpdates = {};
29461
30312
  const publicEnv = {};
@@ -29467,7 +30318,7 @@ function writeT3CodeConfig(config) {
29467
30318
  }
29468
30319
  }
29469
30320
  setHarnessCredentials(T3_HARNESS_ID, credentialUpdates);
29470
- fs40.writeFileSync(
30321
+ fs41.writeFileSync(
29471
30322
  configPath,
29472
30323
  `${JSON.stringify({ ...config, env: publicEnv }, null, 2)}
29473
30324
  `,
@@ -29849,8 +30700,8 @@ import pc44 from "picocolors";
29849
30700
 
29850
30701
  // src/status/probes.ts
29851
30702
  import { spawnSync as spawnSync4 } from "node:child_process";
29852
- import fs41 from "node:fs";
29853
- import path49 from "node:path";
30703
+ import fs42 from "node:fs";
30704
+ import path50 from "node:path";
29854
30705
  var GITHUB_API = "https://api.github.com";
29855
30706
  var NPM_REGISTRY = "https://registry.npmjs.org";
29856
30707
  function isoNow() {
@@ -30015,7 +30866,7 @@ async function probeKitForksIndex(_timeoutMs) {
30015
30866
  try {
30016
30867
  const { resolveKitForksIndexPath: resolveKitForksIndexPath2 } = await Promise.resolve().then(() => (init_kit_forks_home(), kit_forks_home_exports));
30017
30868
  const p35 = resolveKitForksIndexPath2();
30018
- if (!fs41.existsSync(p35)) {
30869
+ if (!fs42.existsSync(p35)) {
30019
30870
  return {
30020
30871
  componentId: "kit-forks-index",
30021
30872
  level: "operational",
@@ -30023,7 +30874,7 @@ async function probeKitForksIndex(_timeoutMs) {
30023
30874
  lastCheckedAt: isoNow()
30024
30875
  };
30025
30876
  }
30026
- const parsed = JSON.parse(fs41.readFileSync(p35, "utf8"));
30877
+ const parsed = JSON.parse(fs42.readFileSync(p35, "utf8"));
30027
30878
  const count = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
30028
30879
  return {
30029
30880
  componentId: "kit-forks-index",
@@ -30092,10 +30943,10 @@ async function probeNode(_timeoutMs) {
30092
30943
  };
30093
30944
  }
30094
30945
  async function probeReleaseBundleArtifacts(_timeoutMs) {
30095
- const distPath = path49.resolve(process.cwd(), "cli/dist/index.js");
30096
- const installerPath = path49.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
30097
- const distOk = fs41.existsSync(distPath);
30098
- const installerOk = fs41.existsSync(installerPath);
30946
+ const distPath = path50.resolve(process.cwd(), "cli/dist/index.js");
30947
+ const installerPath = path50.resolve(process.cwd(), "packages/create-growthub-local/bin/create-growthub-local.mjs");
30948
+ const distOk = fs42.existsSync(distPath);
30949
+ const installerOk = fs42.existsSync(installerPath);
30099
30950
  const ok = distOk && installerOk;
30100
30951
  return {
30101
30952
  componentId: "release-bundle",
@@ -30654,7 +31505,7 @@ import pc46 from "picocolors";
30654
31505
 
30655
31506
  // src/fleet/summary.ts
30656
31507
  init_fork_registry();
30657
- import fs51 from "node:fs";
31508
+ import fs52 from "node:fs";
30658
31509
  init_fork_policy();
30659
31510
  init_fork_trace();
30660
31511
  function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
@@ -30674,7 +31525,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
30674
31525
  "conflict_encountered"
30675
31526
  ]);
30676
31527
  function buildForkSummary(reg) {
30677
- if (!fs51.existsSync(reg.forkPath)) {
31528
+ if (!fs52.existsSync(reg.forkPath)) {
30678
31529
  return {
30679
31530
  forkId: reg.forkId,
30680
31531
  kitId: reg.kitId,
@@ -31135,8 +31986,8 @@ async function fleetApprovals(opts) {
31135
31986
  p32.log.message(
31136
31987
  ` \xB7 ${pc46.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
31137
31988
  );
31138
- for (const path62 of entry.pendingPaths.slice(0, 6)) {
31139
- p32.log.message(` ${pc46.dim("awaits")} ${path62}`);
31989
+ for (const path63 of entry.pendingPaths.slice(0, 6)) {
31990
+ p32.log.message(` ${pc46.dim("awaits")} ${path63}`);
31140
31991
  }
31141
31992
  if (entry.pendingPaths.length > 6) {
31142
31993
  p32.log.message(` ${pc46.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
@@ -31222,21 +32073,21 @@ var DEFAULT_MEMORY_PROVIDER_CONFIG = {
31222
32073
 
31223
32074
  // src/runtime/memory/store.ts
31224
32075
  init_home();
31225
- import fs52 from "node:fs";
31226
- import path59 from "node:path";
32076
+ import fs53 from "node:fs";
32077
+ import path60 from "node:path";
31227
32078
  function toProjectSlug(project) {
31228
32079
  return project.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "default";
31229
32080
  }
31230
32081
  function resolveProjectPath(project) {
31231
- return path59.resolve(resolveMemoryProjectsDir(), `${toProjectSlug(project)}.json`);
32082
+ return path60.resolve(resolveMemoryProjectsDir(), `${toProjectSlug(project)}.json`);
31232
32083
  }
31233
32084
  function loadMemoryDatabase(project) {
31234
32085
  const filePath = resolveProjectPath(project);
31235
- if (!fs52.existsSync(filePath)) {
32086
+ if (!fs53.existsSync(filePath)) {
31236
32087
  return { version: 1, project, observations: [], summaries: [], nextObservationId: 1, nextSummaryId: 1 };
31237
32088
  }
31238
32089
  try {
31239
- const raw = JSON.parse(fs52.readFileSync(filePath, "utf-8"));
32090
+ const raw = JSON.parse(fs53.readFileSync(filePath, "utf-8"));
31240
32091
  return {
31241
32092
  version: 1,
31242
32093
  project,
@@ -31251,9 +32102,9 @@ function loadMemoryDatabase(project) {
31251
32102
  }
31252
32103
  function saveMemoryDatabase(db) {
31253
32104
  const dir = resolveMemoryProjectsDir();
31254
- fs52.mkdirSync(dir, { recursive: true });
32105
+ fs53.mkdirSync(dir, { recursive: true });
31255
32106
  const filePath = resolveProjectPath(db.project);
31256
- fs52.writeFileSync(filePath, `${JSON.stringify(db, null, 2)}
32107
+ fs53.writeFileSync(filePath, `${JSON.stringify(db, null, 2)}
31257
32108
  `, "utf-8");
31258
32109
  }
31259
32110
  function addObservation(project, input) {
@@ -31347,8 +32198,8 @@ function incrementRelevanceCount(project, observationId) {
31347
32198
  }
31348
32199
  function listMemoryProjects() {
31349
32200
  const dir = resolveMemoryProjectsDir();
31350
- if (!fs52.existsSync(dir)) return [];
31351
- return fs52.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, "")).sort();
32201
+ if (!fs53.existsSync(dir)) return [];
32202
+ return fs53.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, "")).sort();
31352
32203
  }
31353
32204
  function getMemoryStats(project) {
31354
32205
  const db = loadMemoryDatabase(project);
@@ -31360,15 +32211,15 @@ function getMemoryStats(project) {
31360
32211
  };
31361
32212
  }
31362
32213
  function resolveProviderConfigPath() {
31363
- return path59.resolve(resolveMemoryDir(), "provider-config.json");
32214
+ return path60.resolve(resolveMemoryDir(), "provider-config.json");
31364
32215
  }
31365
32216
  function readProviderConfig() {
31366
32217
  const filePath = resolveProviderConfigPath();
31367
- if (!fs52.existsSync(filePath)) {
32218
+ if (!fs53.existsSync(filePath)) {
31368
32219
  return { ...DEFAULT_MEMORY_PROVIDER_CONFIG };
31369
32220
  }
31370
32221
  try {
31371
- const raw = JSON.parse(fs52.readFileSync(filePath, "utf-8"));
32222
+ const raw = JSON.parse(fs53.readFileSync(filePath, "utf-8"));
31372
32223
  return {
31373
32224
  provider: validateProvider(raw.provider),
31374
32225
  apiKey: typeof raw.apiKey === "string" ? raw.apiKey : void 0,
@@ -31381,9 +32232,9 @@ function readProviderConfig() {
31381
32232
  }
31382
32233
  function writeProviderConfig(config) {
31383
32234
  const dir = resolveMemoryDir();
31384
- fs52.mkdirSync(dir, { recursive: true });
32235
+ fs53.mkdirSync(dir, { recursive: true });
31385
32236
  const filePath = resolveProviderConfigPath();
31386
- fs52.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}
32237
+ fs53.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}
31387
32238
  `, { mode: 384 });
31388
32239
  }
31389
32240
  function validateProvider(value) {
@@ -31762,14 +32613,14 @@ async function syncMemoriesToHosted(project, options) {
31762
32613
  init_llm();
31763
32614
  function resolveCliVersion() {
31764
32615
  try {
31765
- const moduleDir = path61.dirname(fileURLToPath7(import.meta.url));
32616
+ const moduleDir = path62.dirname(fileURLToPath7(import.meta.url));
31766
32617
  const candidates = [
31767
- path61.resolve(moduleDir, "../package.json"),
31768
- path61.resolve(moduleDir, "../../package.json")
32618
+ path62.resolve(moduleDir, "../package.json"),
32619
+ path62.resolve(moduleDir, "../../package.json")
31769
32620
  ];
31770
32621
  for (const candidate of candidates) {
31771
- if (!fs54.existsSync(candidate)) continue;
31772
- const parsed = JSON.parse(fs54.readFileSync(candidate, "utf8"));
32622
+ if (!fs55.existsSync(candidate)) continue;
32623
+ const parsed = JSON.parse(fs55.readFileSync(candidate, "utf8"));
31773
32624
  if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
31774
32625
  }
31775
32626
  } catch {
@@ -31996,7 +32847,7 @@ async function runMarketingContextBuilder(baseUrl, model) {
31996
32847
  });
31997
32848
  if (p34.isCancel(projectDir)) return;
31998
32849
  const dir = String(projectDir).trim() || process.cwd();
31999
- if (!fs54.existsSync(dir)) {
32850
+ if (!fs55.existsSync(dir)) {
32000
32851
  p34.note(`Directory not found: ${dir}`, "Marketing Context Builder");
32001
32852
  return;
32002
32853
  }
@@ -32032,10 +32883,10 @@ async function runMarketingContextBuilder(baseUrl, model) {
32032
32883
  p34.note("Draft was not saved. You can copy it from the output above.", "Marketing Context Builder");
32033
32884
  return;
32034
32885
  }
32035
- const outDir = path61.resolve(dir, ".agents");
32036
- fs54.mkdirSync(outDir, { recursive: true });
32037
- const outPath = path61.resolve(outDir, "product-marketing-context.md");
32038
- fs54.writeFileSync(outPath, result.contextMarkdown, "utf-8");
32886
+ const outDir = path62.resolve(dir, ".agents");
32887
+ fs55.mkdirSync(outDir, { recursive: true });
32888
+ const outPath = path62.resolve(outDir, "product-marketing-context.md");
32889
+ fs55.writeFileSync(outPath, result.contextMarkdown, "utf-8");
32039
32890
  p34.note(`Saved to: ${outPath}
32040
32891
 
32041
32892
  Review the file and replace [NEEDS INPUT] placeholders with real data.`, "Marketing Context Builder");
@@ -32244,39 +33095,39 @@ function captureSessionSummary(project, sessionId, messages) {
32244
33095
  }
32245
33096
  }
32246
33097
  function resolveLocalThreadsDir() {
32247
- return path61.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
33098
+ return path62.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
32248
33099
  }
32249
33100
  function loadOrCreateLocalThread() {
32250
33101
  const dir = resolveLocalThreadsDir();
32251
- fs54.mkdirSync(dir, { recursive: true });
32252
- const activePath = path61.resolve(dir, "active-thread.json");
32253
- if (fs54.existsSync(activePath)) {
33102
+ fs55.mkdirSync(dir, { recursive: true });
33103
+ const activePath = path62.resolve(dir, "active-thread.json");
33104
+ if (fs55.existsSync(activePath)) {
32254
33105
  try {
32255
- const parsed = JSON.parse(fs54.readFileSync(activePath, "utf-8"));
33106
+ const parsed = JSON.parse(fs55.readFileSync(activePath, "utf-8"));
32256
33107
  const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
32257
- const threadFile = path61.resolve(dir, `${id2}.json`);
33108
+ const threadFile = path62.resolve(dir, `${id2}.json`);
32258
33109
  const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
32259
33110
  return { id: id2, filePath: threadFile, messages };
32260
33111
  } catch {
32261
33112
  }
32262
33113
  }
32263
33114
  const id = `thread-${Date.now()}`;
32264
- const filePath = path61.resolve(dir, `${id}.json`);
33115
+ const filePath = path62.resolve(dir, `${id}.json`);
32265
33116
  const thread = { id, filePath, messages: [] };
32266
33117
  saveLocalThread(thread);
32267
33118
  return thread;
32268
33119
  }
32269
33120
  function saveLocalThread(thread) {
32270
33121
  const dir = resolveLocalThreadsDir();
32271
- fs54.mkdirSync(dir, { recursive: true });
32272
- fs54.writeFileSync(
33122
+ fs55.mkdirSync(dir, { recursive: true });
33123
+ fs55.writeFileSync(
32273
33124
  thread.filePath,
32274
33125
  `${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
32275
33126
  `,
32276
33127
  "utf-8"
32277
33128
  );
32278
- const activePath = path61.resolve(dir, "active-thread.json");
32279
- fs54.writeFileSync(
33129
+ const activePath = path62.resolve(dir, "active-thread.json");
33130
+ fs55.writeFileSync(
32280
33131
  activePath,
32281
33132
  `${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
32282
33133
  `,
@@ -32422,7 +33273,7 @@ async function collectBindingsFromContract(contract, promptSeed) {
32422
33273
  return bindings;
32423
33274
  }
32424
33275
  function resolveCurrentProject() {
32425
- return path61.basename(process.cwd());
33276
+ return path62.basename(process.cwd());
32426
33277
  }
32427
33278
  async function runMemoryKnowledgeHub() {
32428
33279
  const project = resolveCurrentProject();
@@ -33002,12 +33853,12 @@ function isInstallerMode() {
33002
33853
  }
33003
33854
  function listLocalSurfaces() {
33004
33855
  const homeDir = resolvePaperclipHomeDir();
33005
- const instancesDir = path61.resolve(homeDir, "instances");
33006
- if (!fs54.existsSync(instancesDir)) return [];
33007
- return fs54.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
33856
+ const instancesDir = path62.resolve(homeDir, "instances");
33857
+ if (!fs55.existsSync(instancesDir)) return [];
33858
+ return fs55.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
33008
33859
  const instanceId = entry.name;
33009
- const configPath = path61.resolve(instancesDir, instanceId, "config.json");
33010
- if (!fs54.existsSync(configPath)) return null;
33860
+ const configPath = path62.resolve(instancesDir, instanceId, "config.json");
33861
+ if (!fs55.existsSync(configPath)) return null;
33011
33862
  try {
33012
33863
  const config = readConfig(configPath);
33013
33864
  if (!config) return null;