vibebusiness 1.2.37 → 1.2.39

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 (77) hide show
  1. package/.next/standalone/.env +1 -0
  2. package/.next/standalone/.next/BUILD_ID +1 -1
  3. package/.next/standalone/.next/app-build-manifest.json +13 -13
  4. package/.next/standalone/.next/app-path-routes-manifest.json +1 -1
  5. package/.next/standalone/.next/build-manifest.json +2 -2
  6. package/.next/standalone/.next/prerender-manifest.json +1 -1
  7. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  9. package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  10. package/.next/standalone/.next/server/app/api/analyze/route.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
  12. package/.next/standalone/.next/server/app/api/epics/[id]/ideas/route.js.nft.json +1 -1
  13. package/.next/standalone/.next/server/app/api/epics/[id]/route.js.nft.json +1 -1
  14. package/.next/standalone/.next/server/app/api/epics/route.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/api/goals/[id]/kpis/route.js.nft.json +1 -1
  16. package/.next/standalone/.next/server/app/api/goals/[id]/route.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/api/goals/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/hypotheses/[id]/route.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/api/hypotheses/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/ideas/[id]/comments/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/ideas/[id]/implement/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/ideas/[id]/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/ideas/[id]/transition/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/ideas/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/implementations/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/kpis/refresh/route.js +1 -1
  27. package/.next/standalone/.next/server/app/api/kpis/refresh/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/goals/[id]/page_client-reference-manifest.js +1 -1
  29. package/.next/standalone/.next/server/app/goals/page.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/goals/page_client-reference-manifest.js +1 -1
  31. package/.next/standalone/.next/server/app/hypotheses/[id]/page_client-reference-manifest.js +1 -1
  32. package/.next/standalone/.next/server/app/hypotheses/page.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/hypotheses/page_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/ideas/[id]/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/ideas/[id]/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/landing/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/.next/server/app/landing.html +1 -1
  38. package/.next/standalone/.next/server/app/landing.rsc +1 -1
  39. package/.next/standalone/.next/server/app/page.js +1 -1
  40. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/roadmap/[id]/page.js +1 -1
  43. package/.next/standalone/.next/server/app/roadmap/[id]/page_client-reference-manifest.js +1 -1
  44. package/.next/standalone/.next/server/app/roadmap/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/roadmap/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app/settings.html +1 -1
  50. package/.next/standalone/.next/server/app/settings.rsc +1 -1
  51. package/.next/standalone/.next/server/app-paths-manifest.json +4 -4
  52. package/.next/standalone/.next/server/pages/404.html +1 -1
  53. package/.next/standalone/.next/server/pages/500.html +1 -1
  54. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  55. package/.next/standalone/data/business-context.json +9 -1
  56. package/.next/standalone/data/email-campaigns/sequences/onboarding.md +183 -0
  57. package/.next/standalone/data/email-campaigns/welcome-2026-02-20.md +61 -0
  58. package/.next/standalone/data/goals.json +3 -2
  59. package/.next/standalone/data/heartbeat-sessions.json +798 -0
  60. package/.next/standalone/data/ideas.json +668 -80
  61. package/.next/standalone/data/implementations.json +397 -0
  62. package/.next/standalone/package.json +1 -1
  63. package/.next/static/chunks/app/page-6a52a6950963129a.js +1 -0
  64. package/.next/static/chunks/app/roadmap/[id]/page-3848fd96de497d11.js +1 -0
  65. package/dist/scripts/analyze.js +1 -0
  66. package/dist/scripts/chat.js +1 -0
  67. package/dist/scripts/generate-idea.js +1 -0
  68. package/dist/scripts/heartbeat.js +1620 -168
  69. package/dist/scripts/implement.js +1 -0
  70. package/dist/scripts/init.js +1 -0
  71. package/dist/scripts/scan.js +1 -0
  72. package/package.json +1 -1
  73. package/templates/commands/email-marketing.md +201 -0
  74. package/.next/static/chunks/app/page-999b97bf50993294.js +0 -1
  75. package/.next/static/chunks/app/roadmap/[id]/page-da3d51358c8e2014.js +0 -1
  76. /package/.next/static/{Lbwr50a6hdyjnsxpep4G9 → k40DKimnEdAtBs_zywSQ4}/_buildManifest.js +0 -0
  77. /package/.next/static/{Lbwr50a6hdyjnsxpep4G9 → k40DKimnEdAtBs_zywSQ4}/_ssgManifest.js +0 -0
@@ -2280,9 +2280,9 @@ var require_escape_html = __commonJS({
2280
2280
  });
2281
2281
 
2282
2282
  // scripts/heartbeat.ts
2283
- var fs15 = __toESM(require("fs"));
2284
- var path14 = __toESM(require("path"));
2285
- var import_child_process4 = require("child_process");
2283
+ var fs19 = __toESM(require("fs"));
2284
+ var path18 = __toESM(require("path"));
2285
+ var import_child_process6 = require("child_process");
2286
2286
  var import_util = require("util");
2287
2287
 
2288
2288
  // scripts/lib/api.ts
@@ -2290,16 +2290,16 @@ var API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8001";
2290
2290
  var INTERNAL_API_KEY = process.env.INTERNAL_API_KEY;
2291
2291
  async function apiRequest(endpoint, options = {}) {
2292
2292
  const url = `${API_URL}${endpoint}`;
2293
- const headers = {
2293
+ const headers2 = {
2294
2294
  "Content-Type": "application/json",
2295
2295
  ...options.headers
2296
2296
  };
2297
2297
  if (INTERNAL_API_KEY) {
2298
- headers["X-Internal-API-Key"] = INTERNAL_API_KEY;
2298
+ headers2["X-Internal-API-Key"] = INTERNAL_API_KEY;
2299
2299
  }
2300
2300
  const response = await fetch(url, {
2301
2301
  ...options,
2302
- headers
2302
+ headers: headers2
2303
2303
  });
2304
2304
  if (!response.ok) {
2305
2305
  const errorText = await response.text();
@@ -2511,6 +2511,7 @@ var ROADMAP_FILE = path.join(DATA_DIR, "roadmap.json");
2511
2511
  var COMPETITORS_FILE = path.join(DATA_DIR, "competitors.json");
2512
2512
  var POSITIONING_FILE = path.join(DATA_DIR, "positioning.json");
2513
2513
  var PAGES_FILE = path.join(DATA_DIR, "pages.json");
2514
+ var PAYMENTS_FILE = path.join(DATA_DIR, "payments.json");
2514
2515
  var TEMPLATES_DIR = path.join(__dirname, "..", "..", "templates");
2515
2516
  var COMMAND_TEMPLATES_DIR = path.join(TEMPLATES_DIR, "commands");
2516
2517
 
@@ -5658,8 +5659,8 @@ function argument(predicate, message) {
5658
5659
  }
5659
5660
  }
5660
5661
  var check = { fail, argument, assert: argument };
5661
- function getPathDefinition(glyph, path15) {
5662
- var _path = path15 || new Path();
5662
+ function getPathDefinition(glyph, path19) {
5663
+ var _path = path19 || new Path();
5663
5664
  return {
5664
5665
  configurable: true,
5665
5666
  get: function() {
@@ -5882,9 +5883,9 @@ function ttfGlyphLoader(font, index, parseGlyph2, data, position, buildPath2) {
5882
5883
  var glyph = new Glyph({ index, font });
5883
5884
  glyph.path = function() {
5884
5885
  parseGlyph2(glyph, data, position);
5885
- var path15 = buildPath2(font.glyphs, glyph);
5886
- path15.unitsPerEm = font.unitsPerEm;
5887
- return path15;
5886
+ var path19 = buildPath2(font.glyphs, glyph);
5887
+ path19.unitsPerEm = font.unitsPerEm;
5888
+ return path19;
5888
5889
  };
5889
5890
  defineDependentProperty(glyph, "xMin", "_xMin");
5890
5891
  defineDependentProperty(glyph, "xMax", "_xMax");
@@ -5897,9 +5898,9 @@ function cffGlyphLoader(font, index, parseCFFCharstring2, charstring) {
5897
5898
  return function() {
5898
5899
  var glyph = new Glyph({ index, font });
5899
5900
  glyph.path = function() {
5900
- var path15 = parseCFFCharstring2(font, glyph, charstring);
5901
- path15.unitsPerEm = font.unitsPerEm;
5902
- return path15;
5901
+ var path19 = parseCFFCharstring2(font, glyph, charstring);
5902
+ path19.unitsPerEm = font.unitsPerEm;
5903
+ return path19;
5903
5904
  };
5904
5905
  return glyph;
5905
5906
  };
@@ -20706,12 +20707,1433 @@ async function executeMarketingVisualTask(taskId, _description) {
20706
20707
  }
20707
20708
  }
20708
20709
 
20709
- // scripts/lib/run.ts
20710
+ // scripts/skills/email-marketing.ts
20711
+ var import_child_process3 = require("child_process");
20712
+ var fs7 = __toESM(require("fs"));
20710
20713
  var path5 = __toESM(require("path"));
20714
+
20715
+ // src/lib/loops.ts
20716
+ var LOOPS_BASE_URL = "https://app.loops.so/api/v1";
20717
+ function getApiKey() {
20718
+ const key = process.env.LOOPS_API_KEY;
20719
+ if (!key) throw new Error("LOOPS_API_KEY environment variable is not set");
20720
+ return key;
20721
+ }
20722
+ function headers() {
20723
+ return {
20724
+ Authorization: `Bearer ${getApiKey()}`,
20725
+ "Content-Type": "application/json"
20726
+ };
20727
+ }
20728
+ async function verifyApiKey() {
20729
+ const res = await fetch(`${LOOPS_BASE_URL}/api-key`, {
20730
+ method: "GET",
20731
+ headers: headers()
20732
+ });
20733
+ if (!res.ok) return { success: false };
20734
+ const data = await res.json();
20735
+ return { success: true, teamName: data.teamName };
20736
+ }
20737
+ async function listContacts(options) {
20738
+ const params = new URLSearchParams();
20739
+ if (options?.limit) params.set("limit", String(options.limit));
20740
+ const res = await fetch(
20741
+ `${LOOPS_BASE_URL}/contacts?${params.toString()}`,
20742
+ { method: "GET", headers: headers() }
20743
+ );
20744
+ if (!res.ok) {
20745
+ throw new Error(`Loops API error: ${res.status} ${await res.text()}`);
20746
+ }
20747
+ const contacts = await res.json();
20748
+ return { contacts, count: Array.isArray(contacts) ? contacts.length : 0 };
20749
+ }
20750
+ async function getMailingLists() {
20751
+ const res = await fetch(`${LOOPS_BASE_URL}/lists`, {
20752
+ method: "GET",
20753
+ headers: headers()
20754
+ });
20755
+ if (!res.ok) {
20756
+ throw new Error(`Loops API error: ${res.status} ${await res.text()}`);
20757
+ }
20758
+ return res.json();
20759
+ }
20760
+
20761
+ // scripts/skills/email-marketing.ts
20762
+ var EMAIL_PREFIXES = [
20763
+ "email-setup",
20764
+ "email-campaign-",
20765
+ "email-sequence-",
20766
+ "email-blast-",
20767
+ "email-health"
20768
+ ];
20769
+ function isEmailMarketingTask(taskId) {
20770
+ return EMAIL_PREFIXES.some((prefix) => taskId.startsWith(prefix));
20771
+ }
20772
+ function parseTaskToCommand2(taskId, description) {
20773
+ if (taskId === "email-setup") {
20774
+ return { command: "email-marketing", args: "setup" };
20775
+ }
20776
+ if (taskId.startsWith("email-campaign-")) {
20777
+ const type = taskId.replace("email-campaign-", "");
20778
+ return { command: "email-marketing", args: `campaign ${type}` };
20779
+ }
20780
+ if (taskId.startsWith("email-sequence-")) {
20781
+ const name = taskId.replace("email-sequence-", "");
20782
+ return { command: "email-marketing", args: `sequence ${name}` };
20783
+ }
20784
+ if (taskId.startsWith("email-blast-")) {
20785
+ const slug = taskId.replace("email-blast-", "");
20786
+ return { command: "email-marketing", args: `blast ${slug}` };
20787
+ }
20788
+ if (taskId === "email-health") {
20789
+ return null;
20790
+ }
20791
+ return null;
20792
+ }
20793
+ async function executeHealthCheck() {
20794
+ const lines = ["## Email Marketing Health Check\n"];
20795
+ try {
20796
+ const keyResult = await verifyApiKey();
20797
+ if (!keyResult.success) {
20798
+ return {
20799
+ success: false,
20800
+ output: "Loops API key is invalid or not set. Run email-setup to configure."
20801
+ };
20802
+ }
20803
+ lines.push(`- API Key: valid (team: ${keyResult.teamName || "unknown"})`);
20804
+ const contactResult = await listContacts({ limit: 1 });
20805
+ lines.push(`- Total Contacts: ${contactResult.count}`);
20806
+ const lists = await getMailingLists();
20807
+ lines.push(`- Mailing Lists: ${lists.length}`);
20808
+ for (const list of lists) {
20809
+ lines.push(` - ${list.name} (${list.id})`);
20810
+ }
20811
+ lines.push("\nLoops integration is healthy.");
20812
+ return { success: true, output: lines.join("\n") };
20813
+ } catch (error) {
20814
+ const errMsg = error instanceof Error ? error.message : String(error);
20815
+ return {
20816
+ success: false,
20817
+ output: `Health check failed: ${errMsg}`
20818
+ };
20819
+ }
20820
+ }
20821
+ async function executeEmailMarketingTask(taskId, description, businessContext) {
20822
+ if (taskId === "email-health") {
20823
+ return executeHealthCheck();
20824
+ }
20825
+ const parsed = parseTaskToCommand2(taskId, description);
20826
+ if (!parsed) {
20827
+ return {
20828
+ success: false,
20829
+ output: `Unknown email marketing task: ${taskId}`
20830
+ };
20831
+ }
20832
+ const { command, args: args2 } = parsed;
20833
+ const commandFile = path5.join(PROJECT_DIR, ".claude", "commands", `${command}.md`);
20834
+ if (!fs7.existsSync(commandFile)) {
20835
+ return {
20836
+ success: false,
20837
+ output: `Slash command template not found: ${commandFile}. Run 'init' to scaffold commands.`
20838
+ };
20839
+ }
20840
+ const template = fs7.readFileSync(commandFile, "utf-8");
20841
+ const prompt = args2 ? `${template}
20842
+
20843
+ ---
20844
+
20845
+ $ARGUMENTS: ${args2}
20846
+
20847
+ Execute this command now.` : `${template}
20848
+
20849
+ ---
20850
+
20851
+ Execute this command now with no arguments (use defaults).`;
20852
+ console.log(`[EmailMarketing] Executing: /${command} ${args2} (task: ${taskId})`);
20853
+ try {
20854
+ const result = (0, import_child_process3.spawnSync)("claude", [
20855
+ "--print",
20856
+ "--dangerously-skip-permissions",
20857
+ "--allowedTools",
20858
+ "Read Write Edit Bash Glob Grep WebSearch WebFetch"
20859
+ ], {
20860
+ cwd: PROJECT_DIR,
20861
+ input: prompt,
20862
+ encoding: "utf-8",
20863
+ timeout: 6e5,
20864
+ // 10 minute timeout
20865
+ stdio: ["pipe", "pipe", "pipe"],
20866
+ env: process.env
20867
+ });
20868
+ if (result.status !== 0) {
20869
+ const errorOutput = result.stderr || result.stdout || "Unknown error";
20870
+ return {
20871
+ success: false,
20872
+ output: `Claude Code exited with code ${result.status}: ${errorOutput.slice(-1e3)}`
20873
+ };
20874
+ }
20875
+ return {
20876
+ success: true,
20877
+ output: (result.stdout || "").slice(-2e3)
20878
+ // Last 2000 chars
20879
+ };
20880
+ } catch (error) {
20881
+ const errMsg = error instanceof Error ? error.message : String(error);
20882
+ return {
20883
+ success: false,
20884
+ output: `Email marketing task failed: ${errMsg.slice(-1e3)}`
20885
+ };
20886
+ }
20887
+ }
20888
+
20889
+ // scripts/skills/payments.ts
20890
+ var import_child_process4 = require("child_process");
20891
+ var fs10 = __toESM(require("fs"));
20892
+ var path8 = __toESM(require("path"));
20893
+
20894
+ // scripts/lib/payments/framework-detect.ts
20895
+ var fs8 = __toESM(require("fs"));
20896
+ var path6 = __toESM(require("path"));
20897
+ function detectFramework(projectPath) {
20898
+ const result = {
20899
+ framework: "other",
20900
+ typescript: false,
20901
+ appRouter: false,
20902
+ srcDir: false
20903
+ };
20904
+ result.typescript = fs8.existsSync(path6.join(projectPath, "tsconfig.json"));
20905
+ result.srcDir = fs8.existsSync(path6.join(projectPath, "src"));
20906
+ const pkgPath = path6.join(projectPath, "package.json");
20907
+ if (!fs8.existsSync(pkgPath)) {
20908
+ return result;
20909
+ }
20910
+ let pkg;
20911
+ try {
20912
+ pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
20913
+ } catch {
20914
+ return result;
20915
+ }
20916
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
20917
+ if (allDeps["next"]) {
20918
+ result.framework = "nextjs";
20919
+ const appDirs = [
20920
+ path6.join(projectPath, "app"),
20921
+ path6.join(projectPath, "src", "app")
20922
+ ];
20923
+ result.appRouter = appDirs.some((dir) => fs8.existsSync(dir));
20924
+ } else if (allDeps["@remix-run/node"] || allDeps["@remix-run/react"]) {
20925
+ result.framework = "remix";
20926
+ } else if (allDeps["fastify"]) {
20927
+ result.framework = "fastify";
20928
+ } else if (allDeps["express"]) {
20929
+ result.framework = "express";
20930
+ }
20931
+ return result;
20932
+ }
20933
+ function getFileExtension(info) {
20934
+ return info.typescript ? ".ts" : ".js";
20935
+ }
20936
+
20937
+ // scripts/lib/payments/stripe-api.ts
20938
+ function loadStripeModule(projectPath) {
20939
+ try {
20940
+ const stripePath = require.resolve("stripe", { paths: [projectPath] });
20941
+ return require(stripePath);
20942
+ } catch {
20943
+ return null;
20944
+ }
20945
+ }
20946
+ function createStripeClient(secretKey, projectPath) {
20947
+ const Stripe = loadStripeModule(projectPath);
20948
+ if (!Stripe) {
20949
+ throw new Error(
20950
+ "stripe package not found. Install it with: npm install stripe"
20951
+ );
20952
+ }
20953
+ const StripeConstructor = Stripe.default || Stripe;
20954
+ return new StripeConstructor(secretKey);
20955
+ }
20956
+ async function validateApiKey(stripe) {
20957
+ try {
20958
+ const account = await stripe.accounts.retrieve();
20959
+ return {
20960
+ valid: true,
20961
+ accountName: account.business_profile?.name || account.email || "Unknown"
20962
+ };
20963
+ } catch (error) {
20964
+ return {
20965
+ valid: false,
20966
+ error: error.message || "Invalid API key"
20967
+ };
20968
+ }
20969
+ }
20970
+ async function findExistingProducts(stripe, name) {
20971
+ try {
20972
+ const products = await stripe.products.search({
20973
+ query: `name~"${name}"`
20974
+ });
20975
+ return products.data.map((p) => ({
20976
+ id: p.id,
20977
+ name: p.name,
20978
+ description: p.description
20979
+ }));
20980
+ } catch {
20981
+ try {
20982
+ const products = await stripe.products.list({ limit: 100 });
20983
+ return products.data.filter((p) => p.name.toLowerCase().includes(name.toLowerCase())).map((p) => ({
20984
+ id: p.id,
20985
+ name: p.name,
20986
+ description: p.description
20987
+ }));
20988
+ } catch {
20989
+ return [];
20990
+ }
20991
+ }
20992
+ }
20993
+ async function createProduct(stripe, input) {
20994
+ const product = await stripe.products.create({
20995
+ name: input.name,
20996
+ ...input.description && { description: input.description }
20997
+ });
20998
+ return {
20999
+ id: product.id,
21000
+ name: product.name,
21001
+ description: product.description
21002
+ };
21003
+ }
21004
+ async function createPrice(stripe, input) {
21005
+ const params = {
21006
+ product: input.productId,
21007
+ unit_amount: input.unitAmount,
21008
+ currency: input.currency
21009
+ };
21010
+ if (input.interval) {
21011
+ params.recurring = { interval: input.interval };
21012
+ }
21013
+ const price = await stripe.prices.create(params);
21014
+ return {
21015
+ id: price.id,
21016
+ productId: input.productId,
21017
+ unitAmount: input.unitAmount,
21018
+ currency: input.currency,
21019
+ interval: input.interval || null
21020
+ };
21021
+ }
21022
+
21023
+ // scripts/lib/payments/code-templates.ts
21024
+ var DEFAULT_WEBHOOK_EVENTS = [
21025
+ "checkout.session.completed",
21026
+ "customer.subscription.created",
21027
+ "customer.subscription.updated",
21028
+ "customer.subscription.deleted",
21029
+ "invoice.paid",
21030
+ "invoice.payment_failed"
21031
+ ];
21032
+ function getTemplatesForFramework(info, config = {}) {
21033
+ switch (info.framework) {
21034
+ case "nextjs":
21035
+ return info.appRouter ? getNextjsAppRouterTemplates(info, config) : getNextjsPagesTemplates(info, config);
21036
+ case "express":
21037
+ return getExpressTemplates(info, config);
21038
+ case "fastify":
21039
+ return getFastifyTemplates(info, config);
21040
+ case "remix":
21041
+ return getRemixTemplates(info, config);
21042
+ default:
21043
+ return getGenericTemplates(info, config);
21044
+ }
21045
+ }
21046
+ function getNextjsAppRouterTemplates(info, config) {
21047
+ const ext = getFileExtension(info);
21048
+ const prefix = info.srcDir ? "src/" : "";
21049
+ const successUrl = config.successUrl || "/success";
21050
+ const cancelUrl = config.cancelUrl || "/pricing";
21051
+ const events = config.webhookEvents || DEFAULT_WEBHOOK_EVENTS;
21052
+ const ts2 = info.typescript;
21053
+ return [
21054
+ {
21055
+ path: `${prefix}app/api/checkout/route${ext}`,
21056
+ description: "Stripe Checkout session creation endpoint",
21057
+ content: `import { NextResponse } from 'next/server';
21058
+ import Stripe from 'stripe';
21059
+
21060
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21061
+
21062
+ export async function POST(request${ts2 ? ": Request" : ""}) {
21063
+ try {
21064
+ const body = await request.json();
21065
+ const { priceId, customerId } = body;
21066
+
21067
+ if (!priceId) {
21068
+ return NextResponse.json({ error: 'priceId is required' }, { status: 400 });
21069
+ }
21070
+
21071
+ const session = await stripe.checkout.sessions.create({
21072
+ mode: 'subscription',
21073
+ payment_method_types: ['card'],
21074
+ line_items: [{ price: priceId, quantity: 1 }],
21075
+ success_url: \`\${process.env.NEXT_PUBLIC_APP_URL}${successUrl}?session_id={CHECKOUT_SESSION_ID}\`,
21076
+ cancel_url: \`\${process.env.NEXT_PUBLIC_APP_URL}${cancelUrl}\`,
21077
+ ...(customerId && { customer: customerId }),
21078
+ });
21079
+
21080
+ return NextResponse.json({ url: session.url });
21081
+ } catch (error${ts2 ? ": unknown" : ""}) {
21082
+ const message = error instanceof Error ? error.message : 'Unknown error';
21083
+ return NextResponse.json({ error: message }, { status: 500 });
21084
+ }
21085
+ }
21086
+ `
21087
+ },
21088
+ {
21089
+ path: `${prefix}app/api/webhook/route${ext}`,
21090
+ description: "Stripe webhook handler with signature verification",
21091
+ content: `import { NextResponse } from 'next/server';
21092
+ import Stripe from 'stripe';
21093
+
21094
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21095
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
21096
+
21097
+ export async function POST(request${ts2 ? ": Request" : ""}) {
21098
+ const body = await request.text();
21099
+ const signature = request.headers.get('stripe-signature')!;
21100
+
21101
+ let event${ts2 ? ": Stripe.Event" : ""};
21102
+ try {
21103
+ event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
21104
+ } catch (error${ts2 ? ": unknown" : ""}) {
21105
+ const message = error instanceof Error ? error.message : 'Unknown error';
21106
+ console.error('Webhook signature verification failed:', message);
21107
+ return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
21108
+ }
21109
+
21110
+ switch (event.type) {
21111
+ ${events.map((e) => ` case '${e}':
21112
+ console.log('${e}:', event.data.object);
21113
+ // TODO: Handle ${e}
21114
+ break;`).join("\n")}
21115
+ default:
21116
+ console.log('Unhandled event type:', event.type);
21117
+ }
21118
+
21119
+ return NextResponse.json({ received: true });
21120
+ }
21121
+ `
21122
+ },
21123
+ {
21124
+ path: `${prefix}app/api/portal/route${ext}`,
21125
+ description: "Stripe Customer Portal redirect endpoint",
21126
+ content: `import { NextResponse } from 'next/server';
21127
+ import Stripe from 'stripe';
21128
+
21129
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21130
+
21131
+ export async function POST(request${ts2 ? ": Request" : ""}) {
21132
+ try {
21133
+ const body = await request.json();
21134
+ const { customerId } = body;
21135
+
21136
+ if (!customerId) {
21137
+ return NextResponse.json({ error: 'customerId is required' }, { status: 400 });
21138
+ }
21139
+
21140
+ const session = await stripe.billingPortal.sessions.create({
21141
+ customer: customerId,
21142
+ return_url: \`\${process.env.NEXT_PUBLIC_APP_URL}/account\`,
21143
+ });
21144
+
21145
+ return NextResponse.json({ url: session.url });
21146
+ } catch (error${ts2 ? ": unknown" : ""}) {
21147
+ const message = error instanceof Error ? error.message : 'Unknown error';
21148
+ return NextResponse.json({ error: message }, { status: 500 });
21149
+ }
21150
+ }
21151
+ `
21152
+ },
21153
+ {
21154
+ path: `${prefix}components/CheckoutButton${ext}x`,
21155
+ description: "React checkout button component",
21156
+ content: `'use client';
21157
+
21158
+ import { useState } from 'react';
21159
+
21160
+ ${ts2 ? `interface CheckoutButtonProps {
21161
+ priceId: string;
21162
+ label?: string;
21163
+ }
21164
+
21165
+ ` : ""}export default function CheckoutButton(${ts2 ? '{ priceId, label = "Subscribe" }: CheckoutButtonProps' : '{ priceId, label = "Subscribe" }'}) {
21166
+ const [loading, setLoading] = useState(false);
21167
+
21168
+ async function handleCheckout() {
21169
+ setLoading(true);
21170
+ try {
21171
+ const response = await fetch('/api/checkout', {
21172
+ method: 'POST',
21173
+ headers: { 'Content-Type': 'application/json' },
21174
+ body: JSON.stringify({ priceId }),
21175
+ });
21176
+ const data = await response.json();
21177
+ if (data.url) {
21178
+ window.location.href = data.url;
21179
+ }
21180
+ } catch (error) {
21181
+ console.error('Checkout error:', error);
21182
+ } finally {
21183
+ setLoading(false);
21184
+ }
21185
+ }
21186
+
21187
+ return (
21188
+ <button onClick={handleCheckout} disabled={loading}>
21189
+ {loading ? 'Redirecting...' : label}
21190
+ </button>
21191
+ );
21192
+ }
21193
+ `
21194
+ }
21195
+ ];
21196
+ }
21197
+ function getNextjsPagesTemplates(info, config) {
21198
+ const ext = getFileExtension(info);
21199
+ const successUrl = config.successUrl || "/success";
21200
+ const cancelUrl = config.cancelUrl || "/pricing";
21201
+ const events = config.webhookEvents || DEFAULT_WEBHOOK_EVENTS;
21202
+ const ts2 = info.typescript;
21203
+ return [
21204
+ {
21205
+ path: `pages/api/checkout${ext}`,
21206
+ description: "Stripe Checkout session creation endpoint",
21207
+ content: `import type { NextApiRequest, NextApiResponse } from 'next';
21208
+ import Stripe from 'stripe';
21209
+
21210
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21211
+
21212
+ export default async function handler(req${ts2 ? ": NextApiRequest" : ""}, res${ts2 ? ": NextApiResponse" : ""}) {
21213
+ if (req.method !== 'POST') {
21214
+ return res.status(405).json({ error: 'Method not allowed' });
21215
+ }
21216
+
21217
+ try {
21218
+ const { priceId, customerId } = req.body;
21219
+
21220
+ if (!priceId) {
21221
+ return res.status(400).json({ error: 'priceId is required' });
21222
+ }
21223
+
21224
+ const session = await stripe.checkout.sessions.create({
21225
+ mode: 'subscription',
21226
+ payment_method_types: ['card'],
21227
+ line_items: [{ price: priceId, quantity: 1 }],
21228
+ success_url: \`\${process.env.NEXT_PUBLIC_APP_URL}${successUrl}?session_id={CHECKOUT_SESSION_ID}\`,
21229
+ cancel_url: \`\${process.env.NEXT_PUBLIC_APP_URL}${cancelUrl}\`,
21230
+ ...(customerId && { customer: customerId }),
21231
+ });
21232
+
21233
+ res.json({ url: session.url });
21234
+ } catch (error${ts2 ? ": unknown" : ""}) {
21235
+ const message = error instanceof Error ? error.message : 'Unknown error';
21236
+ res.status(500).json({ error: message });
21237
+ }
21238
+ }
21239
+ `
21240
+ },
21241
+ {
21242
+ path: `pages/api/webhook${ext}`,
21243
+ description: "Stripe webhook handler with signature verification",
21244
+ content: `import type { NextApiRequest, NextApiResponse } from 'next';
21245
+ import Stripe from 'stripe';
21246
+
21247
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21248
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
21249
+
21250
+ // Disable body parsing \u2014 Stripe needs raw body for signature verification
21251
+ export const config = { api: { bodyParser: false } };
21252
+
21253
+ async function getRawBody(req${ts2 ? ": NextApiRequest" : ""}): Promise<string> {
21254
+ return new Promise((resolve, reject) => {
21255
+ const chunks${ts2 ? ": Buffer[]" : ""} = [];
21256
+ req.on('data', (chunk${ts2 ? ": Buffer" : ""}) => chunks.push(chunk));
21257
+ req.on('end', () => resolve(Buffer.concat(chunks).toString()));
21258
+ req.on('error', reject);
21259
+ });
21260
+ }
21261
+
21262
+ export default async function handler(req${ts2 ? ": NextApiRequest" : ""}, res${ts2 ? ": NextApiResponse" : ""}) {
21263
+ if (req.method !== 'POST') {
21264
+ return res.status(405).json({ error: 'Method not allowed' });
21265
+ }
21266
+
21267
+ const body = await getRawBody(req);
21268
+ const signature = req.headers['stripe-signature'] as string;
21269
+
21270
+ let event${ts2 ? ": Stripe.Event" : ""};
21271
+ try {
21272
+ event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
21273
+ } catch (error${ts2 ? ": unknown" : ""}) {
21274
+ const message = error instanceof Error ? error.message : 'Unknown error';
21275
+ console.error('Webhook signature verification failed:', message);
21276
+ return res.status(400).json({ error: 'Invalid signature' });
21277
+ }
21278
+
21279
+ switch (event.type) {
21280
+ ${events.map((e) => ` case '${e}':
21281
+ console.log('${e}:', event.data.object);
21282
+ // TODO: Handle ${e}
21283
+ break;`).join("\n")}
21284
+ default:
21285
+ console.log('Unhandled event type:', event.type);
21286
+ }
21287
+
21288
+ res.json({ received: true });
21289
+ }
21290
+ `
21291
+ },
21292
+ {
21293
+ path: `pages/api/portal${ext}`,
21294
+ description: "Stripe Customer Portal redirect endpoint",
21295
+ content: `import type { NextApiRequest, NextApiResponse } from 'next';
21296
+ import Stripe from 'stripe';
21297
+
21298
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21299
+
21300
+ export default async function handler(req${ts2 ? ": NextApiRequest" : ""}, res${ts2 ? ": NextApiResponse" : ""}) {
21301
+ if (req.method !== 'POST') {
21302
+ return res.status(405).json({ error: 'Method not allowed' });
21303
+ }
21304
+
21305
+ try {
21306
+ const { customerId } = req.body;
21307
+
21308
+ if (!customerId) {
21309
+ return res.status(400).json({ error: 'customerId is required' });
21310
+ }
21311
+
21312
+ const session = await stripe.billingPortal.sessions.create({
21313
+ customer: customerId,
21314
+ return_url: \`\${process.env.NEXT_PUBLIC_APP_URL}/account\`,
21315
+ });
21316
+
21317
+ res.json({ url: session.url });
21318
+ } catch (error${ts2 ? ": unknown" : ""}) {
21319
+ const message = error instanceof Error ? error.message : 'Unknown error';
21320
+ res.status(500).json({ error: message });
21321
+ }
21322
+ }
21323
+ `
21324
+ }
21325
+ ];
21326
+ }
21327
+ function getExpressTemplates(info, config) {
21328
+ const ext = getFileExtension(info);
21329
+ const successUrl = config.successUrl || "/success";
21330
+ const cancelUrl = config.cancelUrl || "/pricing";
21331
+ const events = config.webhookEvents || DEFAULT_WEBHOOK_EVENTS;
21332
+ const ts2 = info.typescript;
21333
+ return [
21334
+ {
21335
+ path: `routes/checkout${ext}`,
21336
+ description: "Stripe Checkout session creation route",
21337
+ content: `import { Router } from 'express';
21338
+ ${ts2 ? "import type { Request, Response } from 'express';\n" : ""}import Stripe from 'stripe';
21339
+
21340
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21341
+ const router = Router();
21342
+
21343
+ router.post('/checkout', async (req${ts2 ? ": Request" : ""}, res${ts2 ? ": Response" : ""}) => {
21344
+ try {
21345
+ const { priceId, customerId } = req.body;
21346
+
21347
+ if (!priceId) {
21348
+ return res.status(400).json({ error: 'priceId is required' });
21349
+ }
21350
+
21351
+ const session = await stripe.checkout.sessions.create({
21352
+ mode: 'subscription',
21353
+ payment_method_types: ['card'],
21354
+ line_items: [{ price: priceId, quantity: 1 }],
21355
+ success_url: \`\${process.env.APP_URL}${successUrl}?session_id={CHECKOUT_SESSION_ID}\`,
21356
+ cancel_url: \`\${process.env.APP_URL}${cancelUrl}\`,
21357
+ ...(customerId && { customer: customerId }),
21358
+ });
21359
+
21360
+ res.json({ url: session.url });
21361
+ } catch (error${ts2 ? ": unknown" : ""}) {
21362
+ const message = error instanceof Error ? error.message : 'Unknown error';
21363
+ res.status(500).json({ error: message });
21364
+ }
21365
+ });
21366
+
21367
+ export default router;
21368
+ `
21369
+ },
21370
+ {
21371
+ path: `routes/webhook${ext}`,
21372
+ description: "Stripe webhook handler with signature verification",
21373
+ content: `import { Router } from 'express';
21374
+ ${ts2 ? "import type { Request, Response } from 'express';\n" : ""}import Stripe from 'stripe';
21375
+
21376
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21377
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
21378
+ const router = Router();
21379
+
21380
+ // IMPORTANT: This route needs raw body parsing.
21381
+ // Add to your main app: app.use('/webhook', express.raw({ type: 'application/json' }))
21382
+ router.post('/webhook', (req${ts2 ? ": Request" : ""}, res${ts2 ? ": Response" : ""}) => {
21383
+ const signature = req.headers['stripe-signature'] as string;
21384
+
21385
+ let event${ts2 ? ": Stripe.Event" : ""};
21386
+ try {
21387
+ event = stripe.webhooks.constructEvent(req.body, signature, webhookSecret);
21388
+ } catch (error${ts2 ? ": unknown" : ""}) {
21389
+ const message = error instanceof Error ? error.message : 'Unknown error';
21390
+ console.error('Webhook signature verification failed:', message);
21391
+ return res.status(400).json({ error: 'Invalid signature' });
21392
+ }
21393
+
21394
+ switch (event.type) {
21395
+ ${events.map((e) => ` case '${e}':
21396
+ console.log('${e}:', event.data.object);
21397
+ // TODO: Handle ${e}
21398
+ break;`).join("\n")}
21399
+ default:
21400
+ console.log('Unhandled event type:', event.type);
21401
+ }
21402
+
21403
+ res.json({ received: true });
21404
+ });
21405
+
21406
+ export default router;
21407
+ `
21408
+ },
21409
+ {
21410
+ path: `routes/portal${ext}`,
21411
+ description: "Stripe Customer Portal redirect route",
21412
+ content: `import { Router } from 'express';
21413
+ ${ts2 ? "import type { Request, Response } from 'express';\n" : ""}import Stripe from 'stripe';
21414
+
21415
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21416
+ const router = Router();
21417
+
21418
+ router.post('/portal', async (req${ts2 ? ": Request" : ""}, res${ts2 ? ": Response" : ""}) => {
21419
+ try {
21420
+ const { customerId } = req.body;
21421
+
21422
+ if (!customerId) {
21423
+ return res.status(400).json({ error: 'customerId is required' });
21424
+ }
21425
+
21426
+ const session = await stripe.billingPortal.sessions.create({
21427
+ customer: customerId,
21428
+ return_url: \`\${process.env.APP_URL}/account\`,
21429
+ });
21430
+
21431
+ res.json({ url: session.url });
21432
+ } catch (error${ts2 ? ": unknown" : ""}) {
21433
+ const message = error instanceof Error ? error.message : 'Unknown error';
21434
+ res.status(500).json({ error: message });
21435
+ }
21436
+ });
21437
+
21438
+ export default router;
21439
+ `
21440
+ }
21441
+ ];
21442
+ }
21443
+ function getFastifyTemplates(info, config) {
21444
+ const ext = getFileExtension(info);
21445
+ const successUrl = config.successUrl || "/success";
21446
+ const cancelUrl = config.cancelUrl || "/pricing";
21447
+ const events = config.webhookEvents || DEFAULT_WEBHOOK_EVENTS;
21448
+ const ts2 = info.typescript;
21449
+ return [
21450
+ {
21451
+ path: `routes/checkout${ext}`,
21452
+ description: "Stripe Checkout session creation route",
21453
+ content: `import Stripe from 'stripe';
21454
+ ${ts2 ? "import type { FastifyInstance } from 'fastify';\n" : ""}
21455
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21456
+
21457
+ export default async function checkoutRoutes(fastify${ts2 ? ": FastifyInstance" : ""}) {
21458
+ fastify.post('/checkout', async (request, reply) => {
21459
+ const { priceId, customerId } = request.body as any;
21460
+
21461
+ if (!priceId) {
21462
+ return reply.status(400).send({ error: 'priceId is required' });
21463
+ }
21464
+
21465
+ const session = await stripe.checkout.sessions.create({
21466
+ mode: 'subscription',
21467
+ payment_method_types: ['card'],
21468
+ line_items: [{ price: priceId, quantity: 1 }],
21469
+ success_url: \`\${process.env.APP_URL}${successUrl}?session_id={CHECKOUT_SESSION_ID}\`,
21470
+ cancel_url: \`\${process.env.APP_URL}${cancelUrl}\`,
21471
+ ...(customerId && { customer: customerId }),
21472
+ });
21473
+
21474
+ return { url: session.url };
21475
+ });
21476
+ }
21477
+ `
21478
+ },
21479
+ {
21480
+ path: `routes/webhook${ext}`,
21481
+ description: "Stripe webhook handler with signature verification",
21482
+ content: `import Stripe from 'stripe';
21483
+ ${ts2 ? "import type { FastifyInstance } from 'fastify';\n" : ""}
21484
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21485
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
21486
+
21487
+ export default async function webhookRoutes(fastify${ts2 ? ": FastifyInstance" : ""}) {
21488
+ // Register raw body parser for webhook route
21489
+ fastify.addContentTypeParser('application/json', { parseAs: 'string' }, (req, body, done) => {
21490
+ done(null, body);
21491
+ });
21492
+
21493
+ fastify.post('/webhook', async (request, reply) => {
21494
+ const signature = request.headers['stripe-signature'] as string;
21495
+
21496
+ let event${ts2 ? ": Stripe.Event" : ""};
21497
+ try {
21498
+ event = stripe.webhooks.constructEvent(request.body as string, signature, webhookSecret);
21499
+ } catch (error${ts2 ? ": unknown" : ""}) {
21500
+ const message = error instanceof Error ? error.message : 'Unknown error';
21501
+ fastify.log.error('Webhook signature verification failed:', message);
21502
+ return reply.status(400).send({ error: 'Invalid signature' });
21503
+ }
21504
+
21505
+ switch (event.type) {
21506
+ ${events.map((e) => ` case '${e}':
21507
+ fastify.log.info('${e}:', event.data.object);
21508
+ // TODO: Handle ${e}
21509
+ break;`).join("\n")}
21510
+ default:
21511
+ fastify.log.info('Unhandled event type:', event.type);
21512
+ }
21513
+
21514
+ return { received: true };
21515
+ });
21516
+ }
21517
+ `
21518
+ },
21519
+ {
21520
+ path: `routes/portal${ext}`,
21521
+ description: "Stripe Customer Portal redirect route",
21522
+ content: `import Stripe from 'stripe';
21523
+ ${ts2 ? "import type { FastifyInstance } from 'fastify';\n" : ""}
21524
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21525
+
21526
+ export default async function portalRoutes(fastify${ts2 ? ": FastifyInstance" : ""}) {
21527
+ fastify.post('/portal', async (request, reply) => {
21528
+ const { customerId } = request.body as any;
21529
+
21530
+ if (!customerId) {
21531
+ return reply.status(400).send({ error: 'customerId is required' });
21532
+ }
21533
+
21534
+ const session = await stripe.billingPortal.sessions.create({
21535
+ customer: customerId,
21536
+ return_url: \`\${process.env.APP_URL}/account\`,
21537
+ });
21538
+
21539
+ return { url: session.url };
21540
+ });
21541
+ }
21542
+ `
21543
+ }
21544
+ ];
21545
+ }
21546
+ function getRemixTemplates(info, config) {
21547
+ const ext = getFileExtension(info);
21548
+ const successUrl = config.successUrl || "/success";
21549
+ const cancelUrl = config.cancelUrl || "/pricing";
21550
+ const events = config.webhookEvents || DEFAULT_WEBHOOK_EVENTS;
21551
+ const ts2 = info.typescript;
21552
+ return [
21553
+ {
21554
+ path: `app/routes/api.checkout${ext}`,
21555
+ description: "Stripe Checkout session creation action",
21556
+ content: `import { json } from '@remix-run/node';
21557
+ ${ts2 ? "import type { ActionFunctionArgs } from '@remix-run/node';\n" : ""}import Stripe from 'stripe';
21558
+
21559
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21560
+
21561
+ export async function action({ request }${ts2 ? ": ActionFunctionArgs" : ""}) {
21562
+ const body = await request.json();
21563
+ const { priceId, customerId } = body;
21564
+
21565
+ if (!priceId) {
21566
+ return json({ error: 'priceId is required' }, { status: 400 });
21567
+ }
21568
+
21569
+ const session = await stripe.checkout.sessions.create({
21570
+ mode: 'subscription',
21571
+ payment_method_types: ['card'],
21572
+ line_items: [{ price: priceId, quantity: 1 }],
21573
+ success_url: \`\${process.env.APP_URL}${successUrl}?session_id={CHECKOUT_SESSION_ID}\`,
21574
+ cancel_url: \`\${process.env.APP_URL}${cancelUrl}\`,
21575
+ ...(customerId && { customer: customerId }),
21576
+ });
21577
+
21578
+ return json({ url: session.url });
21579
+ }
21580
+ `
21581
+ },
21582
+ {
21583
+ path: `app/routes/api.webhook${ext}`,
21584
+ description: "Stripe webhook handler with signature verification",
21585
+ content: `import { json } from '@remix-run/node';
21586
+ ${ts2 ? "import type { ActionFunctionArgs } from '@remix-run/node';\n" : ""}import Stripe from 'stripe';
21587
+
21588
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21589
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
21590
+
21591
+ export async function action({ request }${ts2 ? ": ActionFunctionArgs" : ""}) {
21592
+ const body = await request.text();
21593
+ const signature = request.headers.get('stripe-signature')!;
21594
+
21595
+ let event${ts2 ? ": Stripe.Event" : ""};
21596
+ try {
21597
+ event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
21598
+ } catch (error${ts2 ? ": unknown" : ""}) {
21599
+ const message = error instanceof Error ? error.message : 'Unknown error';
21600
+ console.error('Webhook signature verification failed:', message);
21601
+ return json({ error: 'Invalid signature' }, { status: 400 });
21602
+ }
21603
+
21604
+ switch (event.type) {
21605
+ ${events.map((e) => ` case '${e}':
21606
+ console.log('${e}:', event.data.object);
21607
+ // TODO: Handle ${e}
21608
+ break;`).join("\n")}
21609
+ default:
21610
+ console.log('Unhandled event type:', event.type);
21611
+ }
21612
+
21613
+ return json({ received: true });
21614
+ }
21615
+ `
21616
+ },
21617
+ {
21618
+ path: `app/routes/api.portal${ext}`,
21619
+ description: "Stripe Customer Portal redirect action",
21620
+ content: `import { json } from '@remix-run/node';
21621
+ ${ts2 ? "import type { ActionFunctionArgs } from '@remix-run/node';\n" : ""}import Stripe from 'stripe';
21622
+
21623
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21624
+
21625
+ export async function action({ request }${ts2 ? ": ActionFunctionArgs" : ""}) {
21626
+ const body = await request.json();
21627
+ const { customerId } = body;
21628
+
21629
+ if (!customerId) {
21630
+ return json({ error: 'customerId is required' }, { status: 400 });
21631
+ }
21632
+
21633
+ const session = await stripe.billingPortal.sessions.create({
21634
+ customer: customerId,
21635
+ return_url: \`\${process.env.APP_URL}/account\`,
21636
+ });
21637
+
21638
+ return json({ url: session.url });
21639
+ }
21640
+ `
21641
+ }
21642
+ ];
21643
+ }
21644
+ function getGenericTemplates(info, config) {
21645
+ const ext = getFileExtension(info);
21646
+ const successUrl = config.successUrl || "/success";
21647
+ const cancelUrl = config.cancelUrl || "/pricing";
21648
+ const events = config.webhookEvents || DEFAULT_WEBHOOK_EVENTS;
21649
+ const ts2 = info.typescript;
21650
+ return [
21651
+ {
21652
+ path: `stripe/checkout${ext}`,
21653
+ description: "Stripe Checkout session creation function",
21654
+ content: `import Stripe from 'stripe';
21655
+
21656
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21657
+
21658
+ export async function createCheckoutSession(priceId${ts2 ? ": string" : ""}, customerId${ts2 ? "?: string" : ""}) {
21659
+ const session = await stripe.checkout.sessions.create({
21660
+ mode: 'subscription',
21661
+ payment_method_types: ['card'],
21662
+ line_items: [{ price: priceId, quantity: 1 }],
21663
+ success_url: \`\${process.env.APP_URL}${successUrl}?session_id={CHECKOUT_SESSION_ID}\`,
21664
+ cancel_url: \`\${process.env.APP_URL}${cancelUrl}\`,
21665
+ ...(customerId && { customer: customerId }),
21666
+ });
21667
+
21668
+ return session;
21669
+ }
21670
+ `
21671
+ },
21672
+ {
21673
+ path: `stripe/webhook${ext}`,
21674
+ description: "Stripe webhook event handler",
21675
+ content: `import Stripe from 'stripe';
21676
+
21677
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21678
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
21679
+
21680
+ export function handleWebhookEvent(body${ts2 ? ": string" : ""}, signature${ts2 ? ": string" : ""}) {
21681
+ const event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
21682
+
21683
+ switch (event.type) {
21684
+ ${events.map((e) => ` case '${e}':
21685
+ console.log('${e}:', event.data.object);
21686
+ // TODO: Handle ${e}
21687
+ break;`).join("\n")}
21688
+ default:
21689
+ console.log('Unhandled event type:', event.type);
21690
+ }
21691
+
21692
+ return event;
21693
+ }
21694
+ `
21695
+ },
21696
+ {
21697
+ path: `stripe/portal${ext}`,
21698
+ description: "Stripe Customer Portal session creation function",
21699
+ content: `import Stripe from 'stripe';
21700
+
21701
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
21702
+
21703
+ export async function createPortalSession(customerId${ts2 ? ": string" : ""}) {
21704
+ const session = await stripe.billingPortal.sessions.create({
21705
+ customer: customerId,
21706
+ return_url: \`\${process.env.APP_URL}/account\`,
21707
+ });
21708
+
21709
+ return session;
21710
+ }
21711
+ `
21712
+ }
21713
+ ];
21714
+ }
21715
+
21716
+ // scripts/lib/payments/env-manager.ts
21717
+ var fs9 = __toESM(require("fs"));
21718
+ var path7 = __toESM(require("path"));
21719
+ function readEnvFile(projectPath) {
21720
+ const envPath = path7.join(projectPath, ".env");
21721
+ if (!fs9.existsSync(envPath)) {
21722
+ return {};
21723
+ }
21724
+ const content = fs9.readFileSync(envPath, "utf-8");
21725
+ const vars = {};
21726
+ for (const line of content.split("\n")) {
21727
+ const trimmed = line.trim();
21728
+ if (!trimmed || trimmed.startsWith("#")) continue;
21729
+ const eqIndex = trimmed.indexOf("=");
21730
+ if (eqIndex === -1) continue;
21731
+ const key = trimmed.slice(0, eqIndex).trim();
21732
+ let value = trimmed.slice(eqIndex + 1).trim();
21733
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
21734
+ value = value.slice(1, -1);
21735
+ }
21736
+ vars[key] = value;
21737
+ }
21738
+ return vars;
21739
+ }
21740
+ function writeEnvVars(projectPath, vars) {
21741
+ const envPath = path7.join(projectPath, ".env");
21742
+ const existing = readEnvFile(projectPath);
21743
+ const added = [];
21744
+ const lines = [];
21745
+ for (const [key, value] of Object.entries(vars)) {
21746
+ if (existing[key]) continue;
21747
+ lines.push(`${key}=${value}`);
21748
+ added.push(key);
21749
+ }
21750
+ if (lines.length === 0) return added;
21751
+ const prefix = fs9.existsSync(envPath) ? "\n" : "";
21752
+ const content = prefix + lines.join("\n") + "\n";
21753
+ fs9.appendFileSync(envPath, content);
21754
+ return added;
21755
+ }
21756
+ function ensureGitignore(projectPath) {
21757
+ const gitignorePath = path7.join(projectPath, ".gitignore");
21758
+ let content = "";
21759
+ let modified = false;
21760
+ if (fs9.existsSync(gitignorePath)) {
21761
+ content = fs9.readFileSync(gitignorePath, "utf-8");
21762
+ }
21763
+ const lines = content.split("\n").map((l2) => l2.trim());
21764
+ if (!lines.includes(".env")) {
21765
+ const suffix = content.endsWith("\n") || content === "" ? "" : "\n";
21766
+ fs9.appendFileSync(gitignorePath, `${suffix}.env
21767
+ `);
21768
+ modified = true;
21769
+ }
21770
+ return modified;
21771
+ }
21772
+
21773
+ // scripts/skills/payments.ts
21774
+ var EMPTY_STATE = {
21775
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
21776
+ integration_status: {
21777
+ sdk_installed: false,
21778
+ api_key_valid: false,
21779
+ products_created: false,
21780
+ checkout_endpoint: false,
21781
+ webhook_endpoint: false,
21782
+ portal_endpoint: false
21783
+ },
21784
+ framework: null,
21785
+ products: [],
21786
+ generated_files: []
21787
+ };
21788
+ var PAYMENTS_PREFIXES = [
21789
+ "payments-setup-stripe",
21790
+ "payments-add-checkout",
21791
+ "payments-add-webhook",
21792
+ "payments-add-portal",
21793
+ "payments-check-status"
21794
+ ];
21795
+ function isPaymentsTask(taskId) {
21796
+ return PAYMENTS_PREFIXES.some((prefix) => taskId.startsWith(prefix));
21797
+ }
21798
+ async function executePaymentsTask(taskId, description, _businessContext) {
21799
+ try {
21800
+ switch (taskId) {
21801
+ case "payments-setup-stripe":
21802
+ return await executeFullSetup();
21803
+ case "payments-add-checkout":
21804
+ return await executeAddEndpoint("checkout");
21805
+ case "payments-add-webhook":
21806
+ return await executeAddEndpoint("webhook");
21807
+ case "payments-add-portal":
21808
+ return await executeAddEndpoint("portal");
21809
+ case "payments-check-status":
21810
+ return await executeCheckStatus();
21811
+ default:
21812
+ return { success: false, output: `Unknown payments task: ${taskId}` };
21813
+ }
21814
+ } catch (error) {
21815
+ const errMsg = error instanceof Error ? error.message : String(error);
21816
+ return { success: false, output: `Payments task failed: ${errMsg}` };
21817
+ }
21818
+ }
21819
+ function readPaymentsFreshness() {
21820
+ const result = {
21821
+ configured: false,
21822
+ completeness: 0,
21823
+ missing: []
21824
+ };
21825
+ if (!fs10.existsSync(PAYMENTS_FILE)) {
21826
+ result.missing = ["sdk", "api_key", "products", "checkout", "webhook", "portal"];
21827
+ return result;
21828
+ }
21829
+ try {
21830
+ const state = JSON.parse(fs10.readFileSync(PAYMENTS_FILE, "utf-8"));
21831
+ const status = state.integration_status;
21832
+ const fields = Object.entries(status);
21833
+ const completed = fields.filter(([, v2]) => v2).length;
21834
+ result.configured = completed > 0;
21835
+ result.completeness = Math.round(completed / fields.length * 100);
21836
+ if (!status.sdk_installed) result.missing.push("sdk");
21837
+ if (!status.api_key_valid) result.missing.push("api_key");
21838
+ if (!status.products_created) result.missing.push("products");
21839
+ if (!status.checkout_endpoint) result.missing.push("checkout");
21840
+ if (!status.webhook_endpoint) result.missing.push("webhook");
21841
+ if (!status.portal_endpoint) result.missing.push("portal");
21842
+ } catch {
21843
+ result.missing = ["sdk", "api_key", "products", "checkout", "webhook", "portal"];
21844
+ }
21845
+ return result;
21846
+ }
21847
+ function loadState() {
21848
+ if (!fs10.existsSync(PAYMENTS_FILE)) {
21849
+ return { ...EMPTY_STATE };
21850
+ }
21851
+ try {
21852
+ return JSON.parse(fs10.readFileSync(PAYMENTS_FILE, "utf-8"));
21853
+ } catch {
21854
+ return { ...EMPTY_STATE };
21855
+ }
21856
+ }
21857
+ function saveState(state) {
21858
+ state.last_updated = (/* @__PURE__ */ new Date()).toISOString();
21859
+ const dir = path8.dirname(PAYMENTS_FILE);
21860
+ if (!fs10.existsSync(dir)) {
21861
+ fs10.mkdirSync(dir, { recursive: true });
21862
+ }
21863
+ fs10.writeFileSync(PAYMENTS_FILE, JSON.stringify(state, null, 2) + "\n");
21864
+ }
21865
+ function getUserProjectPath() {
21866
+ const configPath = path8.join(PROJECT_DIR, "data", "config.json");
21867
+ if (fs10.existsSync(configPath)) {
21868
+ try {
21869
+ const config = JSON.parse(fs10.readFileSync(configPath, "utf-8"));
21870
+ if (config.repos?.[0]?.path) {
21871
+ const repoPath = config.repos[0].path;
21872
+ if (fs10.existsSync(repoPath)) {
21873
+ return repoPath;
21874
+ }
21875
+ }
21876
+ } catch {
21877
+ }
21878
+ }
21879
+ return PROJECT_DIR;
21880
+ }
21881
+ function isStripeInstalled(projectPath) {
21882
+ try {
21883
+ require.resolve("stripe", { paths: [projectPath] });
21884
+ return true;
21885
+ } catch {
21886
+ return false;
21887
+ }
21888
+ }
21889
+ function installStripe(projectPath) {
21890
+ try {
21891
+ (0, import_child_process4.execSync)("npm install stripe", {
21892
+ cwd: projectPath,
21893
+ encoding: "utf-8",
21894
+ timeout: 6e4,
21895
+ stdio: "pipe"
21896
+ });
21897
+ return true;
21898
+ } catch {
21899
+ return false;
21900
+ }
21901
+ }
21902
+ function writeGeneratedFile(projectPath, file) {
21903
+ const fullPath = path8.join(projectPath, file.path);
21904
+ if (fs10.existsSync(fullPath)) {
21905
+ return false;
21906
+ }
21907
+ const dir = path8.dirname(fullPath);
21908
+ if (!fs10.existsSync(dir)) {
21909
+ fs10.mkdirSync(dir, { recursive: true });
21910
+ }
21911
+ fs10.writeFileSync(fullPath, file.content);
21912
+ return true;
21913
+ }
21914
+ async function executeFullSetup() {
21915
+ const lines = ["## Stripe Integration Setup\n"];
21916
+ const state = loadState();
21917
+ const projectPath = getUserProjectPath();
21918
+ lines.push("### Step 1: Framework Detection");
21919
+ const frameworkInfo = detectFramework(projectPath);
21920
+ state.framework = frameworkInfo;
21921
+ lines.push(`- Framework: ${frameworkInfo.framework}`);
21922
+ lines.push(`- TypeScript: ${frameworkInfo.typescript}`);
21923
+ lines.push(`- App Router: ${frameworkInfo.appRouter}`);
21924
+ lines.push(`- Src directory: ${frameworkInfo.srcDir}`);
21925
+ lines.push("");
21926
+ lines.push("### Step 2: Stripe SDK");
21927
+ if (isStripeInstalled(projectPath)) {
21928
+ lines.push("- Stripe SDK: already installed");
21929
+ state.integration_status.sdk_installed = true;
21930
+ } else {
21931
+ lines.push("- Installing stripe...");
21932
+ if (installStripe(projectPath)) {
21933
+ lines.push("- Stripe SDK: installed successfully");
21934
+ state.integration_status.sdk_installed = true;
21935
+ } else {
21936
+ lines.push("- Failed to install stripe. Run manually: npm install stripe");
21937
+ saveState(state);
21938
+ return { success: false, output: lines.join("\n") };
21939
+ }
21940
+ }
21941
+ lines.push("");
21942
+ lines.push("### Step 3: API Key Validation");
21943
+ const envVars = readEnvFile(projectPath);
21944
+ const secretKey = envVars["STRIPE_SECRET_KEY"] || process.env.STRIPE_SECRET_KEY;
21945
+ if (!secretKey) {
21946
+ lines.push("- STRIPE_SECRET_KEY not found in .env or environment.");
21947
+ lines.push("- Add it to your .env file: STRIPE_SECRET_KEY=sk_test_...");
21948
+ lines.push("- Get your key from: https://dashboard.stripe.com/apikeys");
21949
+ state.integration_status.api_key_valid = false;
21950
+ saveState(state);
21951
+ return { success: false, output: lines.join("\n") };
21952
+ }
21953
+ const stripe = createStripeClient(secretKey, projectPath);
21954
+ const validation = await validateApiKey(stripe);
21955
+ if (!validation.valid) {
21956
+ lines.push(`- API key invalid: ${validation.error}`);
21957
+ lines.push("- Check your STRIPE_SECRET_KEY and try again.");
21958
+ state.integration_status.api_key_valid = false;
21959
+ saveState(state);
21960
+ return { success: false, output: lines.join("\n") };
21961
+ }
21962
+ lines.push(`- API key valid (account: ${validation.accountName})`);
21963
+ state.integration_status.api_key_valid = true;
21964
+ lines.push("");
21965
+ lines.push("### Step 4: Products");
21966
+ const existing = await findExistingProducts(stripe, "Pro");
21967
+ if (existing.length > 0) {
21968
+ lines.push(`- Found ${existing.length} existing product(s): ${existing.map((p) => p.name).join(", ")}`);
21969
+ state.integration_status.products_created = true;
21970
+ state.products = existing.map((p) => ({
21971
+ name: p.name,
21972
+ stripe_product_id: p.id,
21973
+ stripe_price_id: null,
21974
+ amount_cents: null,
21975
+ currency: "usd",
21976
+ interval: null
21977
+ }));
21978
+ } else {
21979
+ lines.push("- No existing products found. Creating default product...");
21980
+ try {
21981
+ const product = await createProduct(stripe, {
21982
+ name: "Pro Plan",
21983
+ description: "Pro subscription plan"
21984
+ });
21985
+ const price = await createPrice(stripe, {
21986
+ productId: product.id,
21987
+ unitAmount: 2900,
21988
+ currency: "usd",
21989
+ interval: "month"
21990
+ });
21991
+ lines.push(`- Created product: ${product.name} (${product.id})`);
21992
+ lines.push(`- Created price: $29/mo (${price.id})`);
21993
+ state.integration_status.products_created = true;
21994
+ state.products = [{
21995
+ name: product.name,
21996
+ stripe_product_id: product.id,
21997
+ stripe_price_id: price.id,
21998
+ amount_cents: 2900,
21999
+ currency: "usd",
22000
+ interval: "month"
22001
+ }];
22002
+ } catch (error) {
22003
+ const msg = error instanceof Error ? error.message : String(error);
22004
+ lines.push(`- Failed to create products: ${msg}`);
22005
+ }
22006
+ }
22007
+ lines.push("");
22008
+ lines.push("### Step 5: Code Generation");
22009
+ const templates = getTemplatesForFramework(frameworkInfo);
22010
+ const written = [];
22011
+ const skipped = [];
22012
+ for (const template of templates) {
22013
+ if (writeGeneratedFile(projectPath, template)) {
22014
+ written.push(template.path);
22015
+ lines.push(`- Created: ${template.path} (${template.description})`);
22016
+ } else {
22017
+ skipped.push(template.path);
22018
+ lines.push(`- Skipped: ${template.path} (already exists)`);
22019
+ }
22020
+ }
22021
+ state.generated_files = Array.from(/* @__PURE__ */ new Set([...state.generated_files, ...written]));
22022
+ const allPaths = templates.map((t) => t.path);
22023
+ const checkoutFiles = allPaths.filter((p) => p.includes("checkout"));
22024
+ const webhookFiles = allPaths.filter((p) => p.includes("webhook"));
22025
+ const portalFiles = allPaths.filter((p) => p.includes("portal"));
22026
+ state.integration_status.checkout_endpoint = checkoutFiles.some((f) => written.includes(f) || skipped.includes(f));
22027
+ state.integration_status.webhook_endpoint = webhookFiles.some((f) => written.includes(f) || skipped.includes(f));
22028
+ state.integration_status.portal_endpoint = portalFiles.some((f) => written.includes(f) || skipped.includes(f));
22029
+ lines.push("");
22030
+ lines.push("### Step 6: Environment Variables");
22031
+ const addedVars = writeEnvVars(projectPath, {
22032
+ STRIPE_SECRET_KEY: secretKey,
22033
+ STRIPE_WEBHOOK_SECRET: "whsec_... # Replace with your webhook secret from Stripe Dashboard"
22034
+ });
22035
+ if (addedVars.length > 0) {
22036
+ lines.push(`- Added to .env: ${addedVars.join(", ")}`);
22037
+ } else {
22038
+ lines.push("- All required env vars already present");
22039
+ }
22040
+ const gitignoreModified = ensureGitignore(projectPath);
22041
+ if (gitignoreModified) {
22042
+ lines.push("- Added .env to .gitignore");
22043
+ }
22044
+ lines.push("");
22045
+ saveState(state);
22046
+ const status = state.integration_status;
22047
+ const completed = Object.values(status).filter(Boolean).length;
22048
+ const total = Object.values(status).length;
22049
+ lines.push(`### Summary: ${completed}/${total} steps complete (${Math.round(completed / total * 100)}%)`);
22050
+ if (completed < total) {
22051
+ const missing = Object.entries(status).filter(([, v2]) => !v2).map(([k]) => k);
22052
+ lines.push(`Missing: ${missing.join(", ")}`);
22053
+ }
22054
+ return { success: true, output: lines.join("\n") };
22055
+ }
22056
+ async function executeAddEndpoint(type) {
22057
+ const lines = [`## Add Stripe ${type} endpoint
22058
+ `];
22059
+ const state = loadState();
22060
+ const projectPath = getUserProjectPath();
22061
+ const frameworkInfo = state.framework || detectFramework(projectPath);
22062
+ state.framework = frameworkInfo;
22063
+ const templates = getTemplatesForFramework(frameworkInfo);
22064
+ const matching = templates.filter((t) => t.path.includes(type));
22065
+ if (matching.length === 0) {
22066
+ return { success: false, output: `No ${type} template found for framework: ${frameworkInfo.framework}` };
22067
+ }
22068
+ for (const template of matching) {
22069
+ if (writeGeneratedFile(projectPath, template)) {
22070
+ lines.push(`Created: ${template.path} \u2014 ${template.description}`);
22071
+ state.generated_files = Array.from(/* @__PURE__ */ new Set([...state.generated_files, template.path]));
22072
+ } else {
22073
+ lines.push(`Skipped: ${template.path} \u2014 file already exists`);
22074
+ }
22075
+ }
22076
+ const statusKey = `${type}_endpoint`;
22077
+ state.integration_status[statusKey] = true;
22078
+ saveState(state);
22079
+ return { success: true, output: lines.join("\n") };
22080
+ }
22081
+ async function executeCheckStatus() {
22082
+ const lines = ["## Stripe Integration Status\n"];
22083
+ const state = loadState();
22084
+ const projectPath = getUserProjectPath();
22085
+ const sdkInstalled = isStripeInstalled(projectPath);
22086
+ state.integration_status.sdk_installed = sdkInstalled;
22087
+ lines.push(`- SDK installed: ${sdkInstalled ? "yes" : "no"}`);
22088
+ const envVars = readEnvFile(projectPath);
22089
+ const secretKey = envVars["STRIPE_SECRET_KEY"] || process.env.STRIPE_SECRET_KEY;
22090
+ if (secretKey && sdkInstalled) {
22091
+ try {
22092
+ const stripe = createStripeClient(secretKey, projectPath);
22093
+ const validation = await validateApiKey(stripe);
22094
+ state.integration_status.api_key_valid = validation.valid;
22095
+ lines.push(`- API key: ${validation.valid ? `valid (${validation.accountName})` : `invalid (${validation.error})`}`);
22096
+ if (validation.valid) {
22097
+ const products = await findExistingProducts(stripe, "");
22098
+ state.integration_status.products_created = products.length > 0;
22099
+ lines.push(`- Products: ${products.length} found`);
22100
+ }
22101
+ } catch (error) {
22102
+ const msg = error instanceof Error ? error.message : String(error);
22103
+ lines.push(`- API key check failed: ${msg}`);
22104
+ }
22105
+ } else {
22106
+ state.integration_status.api_key_valid = false;
22107
+ lines.push(`- API key: ${secretKey ? "present but SDK not installed" : "not found"}`);
22108
+ }
22109
+ const hasWebhookSecret = !!(envVars["STRIPE_WEBHOOK_SECRET"] || process.env.STRIPE_WEBHOOK_SECRET);
22110
+ lines.push(`- Webhook secret: ${hasWebhookSecret ? "configured" : "not set"}`);
22111
+ const framework = state.framework || detectFramework(projectPath);
22112
+ const templates = getTemplatesForFramework(framework);
22113
+ for (const template of templates) {
22114
+ const exists = fs10.existsSync(path8.join(projectPath, template.path));
22115
+ const type = template.path.includes("checkout") ? "checkout" : template.path.includes("webhook") ? "webhook" : template.path.includes("portal") ? "portal" : null;
22116
+ if (type) {
22117
+ const statusKey = `${type}_endpoint`;
22118
+ state.integration_status[statusKey] = exists;
22119
+ }
22120
+ lines.push(`- ${template.path}: ${exists ? "exists" : "missing"}`);
22121
+ }
22122
+ lines.push("");
22123
+ const status = state.integration_status;
22124
+ const completed = Object.values(status).filter(Boolean).length;
22125
+ const total = Object.values(status).length;
22126
+ lines.push(`**Completeness: ${completed}/${total} (${Math.round(completed / total * 100)}%)**`);
22127
+ saveState(state);
22128
+ return { success: true, output: lines.join("\n") };
22129
+ }
22130
+
22131
+ // scripts/lib/run.ts
22132
+ var path9 = __toESM(require("path"));
20711
22133
  function resolveScript(baseDir, scriptName) {
20712
- const isCompiled = path5.extname(__filename) === ".js";
22134
+ const isCompiled = path9.extname(__filename) === ".js";
20713
22135
  const resolvedName = isCompiled ? scriptName.replace(/\.ts$/, ".js") : scriptName;
20714
- const scriptPath = path5.join(baseDir, resolvedName);
22136
+ const scriptPath = path9.join(baseDir, resolvedName);
20715
22137
  return isCompiled ? { command: "node", args: [scriptPath] } : { command: "npx", args: ["tsx", scriptPath] };
20716
22138
  }
20717
22139
 
@@ -20765,8 +22187,8 @@ function groupTasksByConflicts(tasks) {
20765
22187
  }
20766
22188
 
20767
22189
  // scripts/lib/json-lock.ts
20768
- var fs7 = __toESM(require("fs"));
20769
- var path6 = __toESM(require("path"));
22190
+ var fs11 = __toESM(require("fs"));
22191
+ var path10 = __toESM(require("path"));
20770
22192
  var DEFAULT_MAX_WAIT_MS = 1e4;
20771
22193
  var BASE_BACKOFF_MS = 50;
20772
22194
  var MAX_BACKOFF_MS = 150;
@@ -20774,10 +22196,10 @@ function withFileLock(lockPath, fn, maxWaitMs = DEFAULT_MAX_WAIT_MS) {
20774
22196
  const startTime = Date.now();
20775
22197
  while (true) {
20776
22198
  try {
20777
- fs7.mkdirSync(lockPath, { recursive: false });
22199
+ fs11.mkdirSync(lockPath, { recursive: false });
20778
22200
  try {
20779
- fs7.writeFileSync(
20780
- path6.join(lockPath, "owner"),
22201
+ fs11.writeFileSync(
22202
+ path10.join(lockPath, "owner"),
20781
22203
  JSON.stringify({ pid: process.pid, acquired: Date.now() })
20782
22204
  );
20783
22205
  } catch {
@@ -20806,16 +22228,16 @@ function withFileLock(lockPath, fn, maxWaitMs = DEFAULT_MAX_WAIT_MS) {
20806
22228
  }
20807
22229
  function atomicWriteFileSync(filePath, content) {
20808
22230
  const tmpPath = `${filePath}.${process.pid}.tmp`;
20809
- fs7.writeFileSync(tmpPath, content, "utf-8");
20810
- fs7.renameSync(tmpPath, filePath);
22231
+ fs11.writeFileSync(tmpPath, content, "utf-8");
22232
+ fs11.renameSync(tmpPath, filePath);
20811
22233
  }
20812
22234
  function releaseLock(lockPath) {
20813
22235
  try {
20814
- const ownerFile = path6.join(lockPath, "owner");
20815
- if (fs7.existsSync(ownerFile)) {
20816
- fs7.unlinkSync(ownerFile);
22236
+ const ownerFile = path10.join(lockPath, "owner");
22237
+ if (fs11.existsSync(ownerFile)) {
22238
+ fs11.unlinkSync(ownerFile);
20817
22239
  }
20818
- fs7.rmdirSync(lockPath);
22240
+ fs11.rmdirSync(lockPath);
20819
22241
  } catch {
20820
22242
  }
20821
22243
  }
@@ -20826,57 +22248,57 @@ function sleepSync(ms2) {
20826
22248
  }
20827
22249
 
20828
22250
  // scripts/lib/worktree.ts
20829
- var fs9 = __toESM(require("fs"));
20830
- var path8 = __toESM(require("path"));
22251
+ var fs13 = __toESM(require("fs"));
22252
+ var path12 = __toESM(require("path"));
20831
22253
 
20832
22254
  // scripts/lib/git-utils.ts
20833
- var import_child_process3 = require("child_process");
20834
- var fs8 = __toESM(require("fs"));
20835
- var path7 = __toESM(require("path"));
22255
+ var import_child_process5 = require("child_process");
22256
+ var fs12 = __toESM(require("fs"));
22257
+ var path11 = __toESM(require("path"));
20836
22258
  function cleanGitState(workspacePath) {
20837
- if (!fs8.existsSync(workspacePath)) return;
20838
- const gitDir = path7.join(workspacePath, ".git");
20839
- if (!fs8.existsSync(gitDir)) return;
22259
+ if (!fs12.existsSync(workspacePath)) return;
22260
+ const gitDir = path11.join(workspacePath, ".git");
22261
+ if (!fs12.existsSync(gitDir)) return;
20840
22262
  let actualGitDir = gitDir;
20841
22263
  try {
20842
- const stat = fs8.statSync(gitDir);
22264
+ const stat = fs12.statSync(gitDir);
20843
22265
  if (stat.isFile()) {
20844
- const content = fs8.readFileSync(gitDir, "utf-8").trim();
22266
+ const content = fs12.readFileSync(gitDir, "utf-8").trim();
20845
22267
  const match = content.match(/^gitdir:\s+(.+)$/);
20846
22268
  if (match) actualGitDir = match[1];
20847
22269
  }
20848
22270
  } catch {
20849
22271
  return;
20850
22272
  }
20851
- const lockFile = path7.join(actualGitDir, "index.lock");
20852
- if (fs8.existsSync(lockFile)) {
22273
+ const lockFile = path11.join(actualGitDir, "index.lock");
22274
+ if (fs12.existsSync(lockFile)) {
20853
22275
  try {
20854
- const result = (0, import_child_process3.execSync)(
22276
+ const result = (0, import_child_process5.execSync)(
20855
22277
  `lsof "${lockFile}" 2>/dev/null || true`,
20856
22278
  { encoding: "utf-8", timeout: 5e3 }
20857
22279
  ).trim();
20858
22280
  if (!result) {
20859
- fs8.unlinkSync(lockFile);
22281
+ fs12.unlinkSync(lockFile);
20860
22282
  console.log(`[pre-flight] Removed stale .git/index.lock in ${workspacePath}`);
20861
22283
  }
20862
22284
  } catch {
20863
22285
  try {
20864
- fs8.unlinkSync(lockFile);
22286
+ fs12.unlinkSync(lockFile);
20865
22287
  console.log(`[pre-flight] Removed stale .git/index.lock in ${workspacePath}`);
20866
22288
  } catch {
20867
22289
  }
20868
22290
  }
20869
22291
  }
20870
22292
  const staleOps = [
20871
- { marker: path7.join(actualGitDir, "MERGE_HEAD"), abort: "git merge --abort" },
20872
- { marker: path7.join(actualGitDir, "rebase-merge"), abort: "git rebase --abort" },
20873
- { marker: path7.join(actualGitDir, "rebase-apply"), abort: "git rebase --abort" },
20874
- { marker: path7.join(actualGitDir, "CHERRY_PICK_HEAD"), abort: "git cherry-pick --abort" }
22293
+ { marker: path11.join(actualGitDir, "MERGE_HEAD"), abort: "git merge --abort" },
22294
+ { marker: path11.join(actualGitDir, "rebase-merge"), abort: "git rebase --abort" },
22295
+ { marker: path11.join(actualGitDir, "rebase-apply"), abort: "git rebase --abort" },
22296
+ { marker: path11.join(actualGitDir, "CHERRY_PICK_HEAD"), abort: "git cherry-pick --abort" }
20875
22297
  ];
20876
22298
  for (const { marker, abort } of staleOps) {
20877
- if (fs8.existsSync(marker)) {
22299
+ if (fs12.existsSync(marker)) {
20878
22300
  try {
20879
- (0, import_child_process3.execSync)(abort, { cwd: workspacePath, encoding: "utf-8", timeout: 1e4, stdio: "pipe" });
22301
+ (0, import_child_process5.execSync)(abort, { cwd: workspacePath, encoding: "utf-8", timeout: 1e4, stdio: "pipe" });
20880
22302
  console.log(`[pre-flight] Aborted stale operation: ${abort} in ${workspacePath}`);
20881
22303
  } catch {
20882
22304
  }
@@ -20885,7 +22307,7 @@ function cleanGitState(workspacePath) {
20885
22307
  }
20886
22308
  function exec2(cmd, cwd, timeoutMs) {
20887
22309
  try {
20888
- return (0, import_child_process3.execSync)(cmd, {
22310
+ return (0, import_child_process5.execSync)(cmd, {
20889
22311
  cwd,
20890
22312
  encoding: "utf-8",
20891
22313
  stdio: ["pipe", "pipe", "pipe"],
@@ -20901,8 +22323,8 @@ ${execError.stderr || execError.message}`);
20901
22323
  // scripts/lib/worktree.ts
20902
22324
  function getWorktreePaths(workspaceDir, repoName) {
20903
22325
  return {
20904
- baseClonePath: path8.join(workspaceDir, repoName),
20905
- worktreeContainerDir: path8.join(workspaceDir, `${repoName}-worktrees`)
22326
+ baseClonePath: path12.join(workspaceDir, repoName),
22327
+ worktreeContainerDir: path12.join(workspaceDir, `${repoName}-worktrees`)
20906
22328
  };
20907
22329
  }
20908
22330
  function syncBaseClone(baseClonePath, defaultBranch) {
@@ -20921,16 +22343,16 @@ function ensureIdeaBranch(baseClonePath, branchName, defaultBranch) {
20921
22343
  }
20922
22344
  }
20923
22345
  function createWorktree(baseClonePath, worktreeContainerDir, ideaBranch, groupIndex) {
20924
- if (!fs9.existsSync(worktreeContainerDir)) {
20925
- fs9.mkdirSync(worktreeContainerDir, { recursive: true });
22346
+ if (!fs13.existsSync(worktreeContainerDir)) {
22347
+ fs13.mkdirSync(worktreeContainerDir, { recursive: true });
20926
22348
  }
20927
22349
  const tempBranch = `wt/${ideaBranch}-g${groupIndex}`;
20928
- const worktreePath = path8.join(worktreeContainerDir, `${ideaBranch}-g${groupIndex}`);
20929
- if (fs9.existsSync(worktreePath)) {
22350
+ const worktreePath = path12.join(worktreeContainerDir, `${ideaBranch}-g${groupIndex}`);
22351
+ if (fs13.existsSync(worktreePath)) {
20930
22352
  try {
20931
22353
  exec2(`git worktree remove --force "${worktreePath}"`, baseClonePath, 1e4);
20932
22354
  } catch {
20933
- fs9.rmSync(worktreePath, { recursive: true, force: true });
22355
+ fs13.rmSync(worktreePath, { recursive: true, force: true });
20934
22356
  }
20935
22357
  }
20936
22358
  try {
@@ -20964,7 +22386,7 @@ function removeWorktree(baseClonePath, worktreePath, tempBranch) {
20964
22386
  exec2(`git worktree remove --force "${worktreePath}"`, baseClonePath, 1e4);
20965
22387
  } catch {
20966
22388
  try {
20967
- fs9.rmSync(worktreePath, { recursive: true, force: true });
22389
+ fs13.rmSync(worktreePath, { recursive: true, force: true });
20968
22390
  } catch {
20969
22391
  }
20970
22392
  }
@@ -20974,12 +22396,12 @@ function removeWorktree(baseClonePath, worktreePath, tempBranch) {
20974
22396
  }
20975
22397
  }
20976
22398
  function cleanupIdeaWorktrees(baseClonePath, worktreeContainerDir, ideaBranch) {
20977
- if (fs9.existsSync(worktreeContainerDir)) {
22399
+ if (fs13.existsSync(worktreeContainerDir)) {
20978
22400
  try {
20979
- const entries = fs9.readdirSync(worktreeContainerDir);
22401
+ const entries = fs13.readdirSync(worktreeContainerDir);
20980
22402
  for (const entry of entries) {
20981
22403
  if (entry.startsWith(`${ideaBranch}-g`)) {
20982
- const wtPath = path8.join(worktreeContainerDir, entry);
22404
+ const wtPath = path12.join(worktreeContainerDir, entry);
20983
22405
  const groupMatch = entry.match(/-g(\d+)$/);
20984
22406
  const tempBranch = `wt/${ideaBranch}-g${groupMatch?.[1] ?? "0"}`;
20985
22407
  removeWorktree(baseClonePath, wtPath, tempBranch);
@@ -20993,9 +22415,9 @@ function cleanupIdeaWorktrees(baseClonePath, worktreeContainerDir, ideaBranch) {
20993
22415
  } catch {
20994
22416
  }
20995
22417
  try {
20996
- const remaining = fs9.readdirSync(worktreeContainerDir);
22418
+ const remaining = fs13.readdirSync(worktreeContainerDir);
20997
22419
  if (remaining.length === 0) {
20998
- fs9.rmdirSync(worktreeContainerDir);
22420
+ fs13.rmdirSync(worktreeContainerDir);
20999
22421
  }
21000
22422
  } catch {
21001
22423
  }
@@ -21005,24 +22427,24 @@ function getTempBranchName(ideaBranch, groupIndex) {
21005
22427
  }
21006
22428
 
21007
22429
  // scripts/lib/telemetry.ts
21008
- var fs10 = __toESM(require("fs"));
21009
- var path9 = __toESM(require("path"));
21010
- var CONFIG_DIR = path9.join(
22430
+ var fs14 = __toESM(require("fs"));
22431
+ var path13 = __toESM(require("path"));
22432
+ var CONFIG_DIR = path13.join(
21011
22433
  process.env.HOME || process.env.USERPROFILE || "~",
21012
22434
  ".vibebusiness"
21013
22435
  );
21014
- var TELEMETRY_CONFIG_FILE = path9.join(CONFIG_DIR, "telemetry.json");
21015
- var EVENTS_FILE = path9.join(CONFIG_DIR, "events.jsonl");
22436
+ var TELEMETRY_CONFIG_FILE = path13.join(CONFIG_DIR, "telemetry.json");
22437
+ var EVENTS_FILE = path13.join(CONFIG_DIR, "events.jsonl");
21016
22438
  function getVersion() {
21017
22439
  try {
21018
22440
  let dir = __dirname;
21019
22441
  for (let i = 0; i < 4; i++) {
21020
- const pkgPath = path9.join(dir, "package.json");
21021
- if (fs10.existsSync(pkgPath)) {
21022
- const pkg = JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
22442
+ const pkgPath = path13.join(dir, "package.json");
22443
+ if (fs14.existsSync(pkgPath)) {
22444
+ const pkg = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
21023
22445
  return pkg.version || "0.0.0";
21024
22446
  }
21025
- dir = path9.dirname(dir);
22447
+ dir = path13.dirname(dir);
21026
22448
  }
21027
22449
  } catch {
21028
22450
  }
@@ -21030,8 +22452,8 @@ function getVersion() {
21030
22452
  }
21031
22453
  function getTelemetryConfig() {
21032
22454
  try {
21033
- if (!fs10.existsSync(TELEMETRY_CONFIG_FILE)) return null;
21034
- return JSON.parse(fs10.readFileSync(TELEMETRY_CONFIG_FILE, "utf-8"));
22455
+ if (!fs14.existsSync(TELEMETRY_CONFIG_FILE)) return null;
22456
+ return JSON.parse(fs14.readFileSync(TELEMETRY_CONFIG_FILE, "utf-8"));
21035
22457
  } catch {
21036
22458
  return null;
21037
22459
  }
@@ -21052,8 +22474,8 @@ function trackEvent(event, properties = {}) {
21052
22474
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
21053
22475
  version: getVersion()
21054
22476
  };
21055
- fs10.mkdirSync(CONFIG_DIR, { recursive: true });
21056
- fs10.appendFileSync(EVENTS_FILE, JSON.stringify(entry) + "\n");
22477
+ fs14.mkdirSync(CONFIG_DIR, { recursive: true });
22478
+ fs14.appendFileSync(EVENTS_FILE, JSON.stringify(entry) + "\n");
21057
22479
  } catch {
21058
22480
  }
21059
22481
  }
@@ -21333,22 +22755,22 @@ async function suggestEpics(opts) {
21333
22755
  }
21334
22756
 
21335
22757
  // scripts/lib/scaffold.ts
21336
- var fs12 = __toESM(require("fs"));
21337
- var path11 = __toESM(require("path"));
22758
+ var fs16 = __toESM(require("fs"));
22759
+ var path15 = __toESM(require("path"));
21338
22760
  function scaffoldSlashCommands(rootDir) {
21339
- let templatesDir = path11.join(__dirname, "..", "..", "templates", "commands");
21340
- if (!fs12.existsSync(templatesDir)) {
21341
- templatesDir = path11.join(__dirname, "..", "..", "..", "templates", "commands");
21342
- }
21343
- if (!fs12.existsSync(templatesDir)) return;
21344
- const targetDir = path11.join(rootDir, ".claude", "commands");
21345
- fs12.mkdirSync(targetDir, { recursive: true });
21346
- const templates = fs12.readdirSync(templatesDir).filter((f) => f.endsWith(".md"));
22761
+ let templatesDir = path15.join(__dirname, "..", "..", "templates", "commands");
22762
+ if (!fs16.existsSync(templatesDir)) {
22763
+ templatesDir = path15.join(__dirname, "..", "..", "..", "templates", "commands");
22764
+ }
22765
+ if (!fs16.existsSync(templatesDir)) return;
22766
+ const targetDir = path15.join(rootDir, ".claude", "commands");
22767
+ fs16.mkdirSync(targetDir, { recursive: true });
22768
+ const templates = fs16.readdirSync(templatesDir).filter((f) => f.endsWith(".md"));
21347
22769
  let copied = 0;
21348
22770
  for (const file of templates) {
21349
- const src = path11.join(templatesDir, file);
21350
- const dest = path11.join(targetDir, file);
21351
- fs12.copyFileSync(src, dest);
22771
+ const src = path15.join(templatesDir, file);
22772
+ const dest = path15.join(targetDir, file);
22773
+ fs16.copyFileSync(src, dest);
21352
22774
  copied++;
21353
22775
  }
21354
22776
  if (copied > 0) {
@@ -21357,23 +22779,23 @@ function scaffoldSlashCommands(rootDir) {
21357
22779
  }
21358
22780
 
21359
22781
  // scripts/lib/vibe-credits.ts
21360
- var fs13 = __toESM(require("fs"));
21361
- var path12 = __toESM(require("path"));
22782
+ var fs17 = __toESM(require("fs"));
22783
+ var path16 = __toESM(require("path"));
21362
22784
  var WORKER_URL = "https://vibe-credits.luis-e13.workers.dev";
21363
- var CONFIG_DIR2 = path12.join(process.env.HOME || "", ".vibebusiness");
21364
- var BUFFER_FILE = path12.join(CONFIG_DIR2, "vibe-buffer.json");
22785
+ var CONFIG_DIR2 = path16.join(process.env.HOME || "", ".vibebusiness");
22786
+ var BUFFER_FILE = path16.join(CONFIG_DIR2, "vibe-buffer.json");
21365
22787
  var MAX_BUFFER = 5;
21366
22788
  var REQUEST_TIMEOUT_MS = 5e3;
21367
22789
  function getClientVersion() {
21368
22790
  try {
21369
22791
  let dir = __dirname;
21370
22792
  for (let i = 0; i < 4; i++) {
21371
- const pkgPath = path12.join(dir, "package.json");
21372
- if (fs13.existsSync(pkgPath)) {
21373
- const pkg = JSON.parse(fs13.readFileSync(pkgPath, "utf-8"));
22793
+ const pkgPath = path16.join(dir, "package.json");
22794
+ if (fs17.existsSync(pkgPath)) {
22795
+ const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf-8"));
21374
22796
  return pkg.version || "0.0.0";
21375
22797
  }
21376
- dir = path12.dirname(dir);
22798
+ dir = path16.dirname(dir);
21377
22799
  }
21378
22800
  } catch {
21379
22801
  }
@@ -21382,18 +22804,18 @@ function getClientVersion() {
21382
22804
  var CLIENT_VERSION = getClientVersion();
21383
22805
  function loadBuffer() {
21384
22806
  try {
21385
- if (fs13.existsSync(BUFFER_FILE)) {
21386
- return JSON.parse(fs13.readFileSync(BUFFER_FILE, "utf-8"));
22807
+ if (fs17.existsSync(BUFFER_FILE)) {
22808
+ return JSON.parse(fs17.readFileSync(BUFFER_FILE, "utf-8"));
21387
22809
  }
21388
22810
  } catch {
21389
22811
  }
21390
22812
  return { vibes: 0, last_synced: "" };
21391
22813
  }
21392
22814
  function saveBuffer(buffer) {
21393
- if (!fs13.existsSync(CONFIG_DIR2)) {
21394
- fs13.mkdirSync(CONFIG_DIR2, { recursive: true });
22815
+ if (!fs17.existsSync(CONFIG_DIR2)) {
22816
+ fs17.mkdirSync(CONFIG_DIR2, { recursive: true });
21395
22817
  }
21396
- fs13.writeFileSync(BUFFER_FILE, JSON.stringify(buffer, null, 2));
22818
+ fs17.writeFileSync(BUFFER_FILE, JSON.stringify(buffer, null, 2));
21397
22819
  }
21398
22820
  function syncBuffer(vibesRemaining) {
21399
22821
  const buffered = Math.min(vibesRemaining, MAX_BUFFER);
@@ -21467,14 +22889,14 @@ async function consumeVibe(instanceId) {
21467
22889
  }
21468
22890
 
21469
22891
  // scripts/lib/license.ts
21470
- var fs14 = __toESM(require("fs"));
21471
- var path13 = __toESM(require("path"));
21472
- var CONFIG_DIR3 = path13.join(process.env.HOME || "", ".vibebusiness");
21473
- var LICENSE_FILE = path13.join(CONFIG_DIR3, "license.json");
22892
+ var fs18 = __toESM(require("fs"));
22893
+ var path17 = __toESM(require("path"));
22894
+ var CONFIG_DIR3 = path17.join(process.env.HOME || "", ".vibebusiness");
22895
+ var LICENSE_FILE = path17.join(CONFIG_DIR3, "license.json");
21474
22896
  function loadStoredLicense() {
21475
22897
  try {
21476
- if (fs14.existsSync(LICENSE_FILE)) {
21477
- return JSON.parse(fs14.readFileSync(LICENSE_FILE, "utf-8"));
22898
+ if (fs18.existsSync(LICENSE_FILE)) {
22899
+ return JSON.parse(fs18.readFileSync(LICENSE_FILE, "utf-8"));
21478
22900
  }
21479
22901
  } catch {
21480
22902
  }
@@ -21494,10 +22916,10 @@ function getInstanceId() {
21494
22916
 
21495
22917
  // scripts/heartbeat.ts
21496
22918
  var ROOT_DIR = PROJECT_DIR;
21497
- var execAsync = (0, import_util.promisify)(import_child_process4.exec);
22919
+ var execAsync = (0, import_util.promisify)(import_child_process6.exec);
21498
22920
  function spawnAsync(cmd, args2, options) {
21499
22921
  return new Promise((resolve, reject) => {
21500
- const child = (0, import_child_process4.spawn)(cmd, args2, {
22922
+ const child = (0, import_child_process6.spawn)(cmd, args2, {
21501
22923
  cwd: options.cwd,
21502
22924
  stdio: ["pipe", "pipe", "pipe"]
21503
22925
  });
@@ -21601,7 +23023,7 @@ function sleep(ms2) {
21601
23023
  }
21602
23024
  function loadJson(filePath, defaultValue) {
21603
23025
  try {
21604
- const content = fs15.readFileSync(filePath, "utf-8");
23026
+ const content = fs19.readFileSync(filePath, "utf-8");
21605
23027
  return JSON.parse(content);
21606
23028
  } catch {
21607
23029
  return defaultValue;
@@ -21660,7 +23082,7 @@ function isMetaTaskFalseCompletion(output) {
21660
23082
  }
21661
23083
  function reclassifyAsHumanDependent(taskId) {
21662
23084
  try {
21663
- let content = fs15.readFileSync(TODO_FILE, "utf-8");
23085
+ let content = fs19.readFileSync(TODO_FILE, "utf-8");
21664
23086
  const taskPattern = new RegExp(`^- \\[[ x]\\] \`${taskId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\`.*$`, "m");
21665
23087
  const match = content.match(taskPattern);
21666
23088
  if (!match) return;
@@ -21673,7 +23095,7 @@ function reclassifyAsHumanDependent(taskId) {
21673
23095
  const insertIndex = content.indexOf("\n", blockedIndex) + 1;
21674
23096
  content = content.slice(0, insertIndex) + "\n" + uncheckedLine + content.slice(insertIndex);
21675
23097
  }
21676
- fs15.writeFileSync(TODO_FILE, content);
23098
+ fs19.writeFileSync(TODO_FILE, content);
21677
23099
  log(`Reclassified task ${taskId} as human-dependent (moved to Blocked)`);
21678
23100
  } catch (error) {
21679
23101
  log(`Failed to reclassify task: ${error instanceof Error ? error.message : "Unknown"}`);
@@ -21681,7 +23103,7 @@ function reclassifyAsHumanDependent(taskId) {
21681
23103
  }
21682
23104
  function parseTodoFile() {
21683
23105
  try {
21684
- const content = fs15.readFileSync(TODO_FILE, "utf-8");
23106
+ const content = fs19.readFileSync(TODO_FILE, "utf-8");
21685
23107
  const tasks = [];
21686
23108
  let currentSection = "high_priority";
21687
23109
  const lines = content.split("\n");
@@ -21712,7 +23134,7 @@ function parseTodoFile() {
21712
23134
  return [];
21713
23135
  }
21714
23136
  }
21715
- function loadState() {
23137
+ function loadState2() {
21716
23138
  const ideasData = loadJson(IDEAS_FILE, { ideas: [] });
21717
23139
  const goalsData = loadJson(GOALS_FILE, { goals: [] });
21718
23140
  const sessionsData = loadJson(SESSIONS_FILE, { sessions: [] });
@@ -21845,10 +23267,10 @@ function captureGitDiffInfo(workspacePath) {
21845
23267
  has_uncommitted_changes: false
21846
23268
  };
21847
23269
  try {
21848
- const status = (0, import_child_process4.execSync)("git status --porcelain", { cwd: workspacePath, encoding: "utf-8" });
23270
+ const status = (0, import_child_process6.execSync)("git status --porcelain", { cwd: workspacePath, encoding: "utf-8" });
21849
23271
  result.has_uncommitted_changes = status.trim().length > 0;
21850
23272
  try {
21851
- const diffStat = (0, import_child_process4.execSync)("git diff --stat HEAD~1 HEAD 2>/dev/null || git diff --stat --cached 2>/dev/null || git diff --stat 2>/dev/null", {
23273
+ const diffStat = (0, import_child_process6.execSync)("git diff --stat HEAD~1 HEAD 2>/dev/null || git diff --stat --cached 2>/dev/null || git diff --stat 2>/dev/null", {
21852
23274
  cwd: workspacePath,
21853
23275
  encoding: "utf-8",
21854
23276
  shell: "/bin/bash"
@@ -22139,10 +23561,10 @@ function updateGoalsWithKPIs(kpis) {
22139
23561
  log("Updated goals.json with live KPIs");
22140
23562
  }
22141
23563
  function loadCodebaseSnapshot() {
22142
- const snapshotPath = path14.join(DATA_DIR, "codebase-snapshot.json");
23564
+ const snapshotPath = path18.join(DATA_DIR, "codebase-snapshot.json");
22143
23565
  try {
22144
- if (fs15.existsSync(snapshotPath)) {
22145
- return JSON.parse(fs15.readFileSync(snapshotPath, "utf-8"));
23566
+ if (fs19.existsSync(snapshotPath)) {
23567
+ return JSON.parse(fs19.readFileSync(snapshotPath, "utf-8"));
22146
23568
  }
22147
23569
  } catch {
22148
23570
  }
@@ -22296,7 +23718,8 @@ function buildContextForClaude(state, alerts, businessContext) {
22296
23718
  date: s.completed_at || s.created_at,
22297
23719
  ideas_generated: s.ideas_generated.length
22298
23720
  })),
22299
- business_intelligence: readBusinessIntelFreshness()
23721
+ business_intelligence: readBusinessIntelFreshness(),
23722
+ payments: readPaymentsFreshness()
22300
23723
  };
22301
23724
  }
22302
23725
  function buildBusinessConstraints(businessContext) {
@@ -22460,6 +23883,23 @@ When to recommend BI tasks:
22460
23883
  5. If business_intelligence.pages.livePagesWithoutMetrics > 0 \u2192 suggest "measure-page-{id}"
22461
23884
  6. These form a natural chain: research competitors \u2192 develop positioning \u2192 generate copy \u2192 build pages \u2192 measure results
22462
23885
 
23886
+ PAYMENTS TASKS:
23887
+ The "payments" field in CURRENT STATE shows Stripe integration completeness.
23888
+ These tasks run directly (no Claude Code delegation) for fast execution.
23889
+
23890
+ Task prefixes:
23891
+ - "payments-setup-stripe" \u2192 Full wizard: detect framework \u2192 install SDK \u2192 validate key \u2192 create products \u2192 generate code \u2192 update .env
23892
+ - "payments-add-checkout" \u2192 Generate only checkout session endpoint
23893
+ - "payments-add-webhook" \u2192 Generate only webhook handler
23894
+ - "payments-add-portal" \u2192 Generate only customer portal redirect
23895
+ - "payments-check-status" \u2192 Audit: SDK installed? Keys present? Products exist? Webhook configured?
23896
+
23897
+ When to recommend payments tasks:
23898
+ 1. If payments.completeness == 0 AND monetization goals exist \u2192 suggest "payments-setup-stripe" (high priority)
23899
+ 2. If payments.missing includes specific endpoints \u2192 suggest "payments-add-{type}" for each missing endpoint
23900
+ 3. If payments.completeness > 0 AND payments.completeness < 100 \u2192 suggest "payments-check-status" to audit
23901
+ 4. Payments setup is a prerequisite for any monetization/subscription features
23902
+
22463
23903
  - Use codebase_context (if present) to understand what services/integrations actually exist before proposing solutions
22464
23904
 
22465
23905
  ${buildBusinessConstraints(businessContext)}
@@ -22557,7 +23997,7 @@ Focus on actionable, specific recommendations. Be concise.`;
22557
23997
  },
22558
23998
  required: ["analysis", "next_action"]
22559
23999
  });
22560
- const claude = (0, import_child_process4.spawn)("claude", [
24000
+ const claude = (0, import_child_process6.spawn)("claude", [
22561
24001
  "--print",
22562
24002
  "--output-format",
22563
24003
  "json",
@@ -22637,9 +24077,9 @@ Focus on actionable, specific recommendations. Be concise.`;
22637
24077
  }
22638
24078
  function addTasksToTodo(tasks) {
22639
24079
  if (tasks.length === 0) return;
22640
- if (!fs15.existsSync(TODO_FILE)) return;
24080
+ if (!fs19.existsSync(TODO_FILE)) return;
22641
24081
  try {
22642
- let content = fs15.readFileSync(TODO_FILE, "utf-8");
24082
+ let content = fs19.readFileSync(TODO_FILE, "utf-8");
22643
24083
  if (!content.includes("## High Priority (Do Now)")) {
22644
24084
  log('TODO.md missing "## High Priority (Do Now)" section \u2014 creating it');
22645
24085
  const scheduledIdx = content.indexOf("## Scheduled");
@@ -22738,15 +24178,15 @@ function addTasksToTodo(tasks) {
22738
24178
  }
22739
24179
  log(`Added TODO: ${task.id} \u2014 ${task.description}`);
22740
24180
  }
22741
- fs15.writeFileSync(TODO_FILE, content);
24181
+ fs19.writeFileSync(TODO_FILE, content);
22742
24182
  } catch (error) {
22743
24183
  log(`Failed to add tasks to TODO.md: ${error instanceof Error ? error.message : "Unknown error"}`);
22744
24184
  }
22745
24185
  }
22746
24186
  function archiveCompletedTodos() {
22747
24187
  try {
22748
- if (!fs15.existsSync(TODO_FILE)) return;
22749
- let content = fs15.readFileSync(TODO_FILE, "utf-8");
24188
+ if (!fs19.existsSync(TODO_FILE)) return;
24189
+ let content = fs19.readFileSync(TODO_FILE, "utf-8");
22750
24190
  const lines = content.split("\n");
22751
24191
  const completedLines = [];
22752
24192
  const newLines = [];
@@ -22772,7 +24212,7 @@ function archiveCompletedTodos() {
22772
24212
  const archiveBlock = completedLines.join("\n") + "\n";
22773
24213
  content = content.slice(0, insertIndex) + archiveBlock + content.slice(insertIndex);
22774
24214
  }
22775
- fs15.writeFileSync(TODO_FILE, content);
24215
+ fs19.writeFileSync(TODO_FILE, content);
22776
24216
  log(`Archived ${completedLines.length} completed TODO(s) to Completed This Week`);
22777
24217
  } catch (error) {
22778
24218
  log(`Failed to archive completed TODOs: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -22780,8 +24220,8 @@ function archiveCompletedTodos() {
22780
24220
  }
22781
24221
  function pruneStaleScheduledTodos(state) {
22782
24222
  try {
22783
- if (!fs15.existsSync(TODO_FILE)) return;
22784
- const content = fs15.readFileSync(TODO_FILE, "utf-8");
24223
+ if (!fs19.existsSync(TODO_FILE)) return;
24224
+ const content = fs19.readFileSync(TODO_FILE, "utf-8");
22785
24225
  const lines = content.split("\n");
22786
24226
  const prunedIds = [];
22787
24227
  const terminalIdeaIds = new Set(
@@ -22808,7 +24248,7 @@ function pruneStaleScheduledTodos(state) {
22808
24248
  return true;
22809
24249
  });
22810
24250
  if (prunedIds.length === 0) return;
22811
- fs15.writeFileSync(TODO_FILE, newLines.join("\n"));
24251
+ fs19.writeFileSync(TODO_FILE, newLines.join("\n"));
22812
24252
  log(`Pruned ${prunedIds.length} stale TODO(s): ${prunedIds.join(", ")}`);
22813
24253
  } catch (error) {
22814
24254
  log(`Failed to prune stale TODOs: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -22816,8 +24256,8 @@ function pruneStaleScheduledTodos(state) {
22816
24256
  }
22817
24257
  function deduplicateActiveTodos() {
22818
24258
  try {
22819
- if (!fs15.existsSync(TODO_FILE)) return;
22820
- const content = fs15.readFileSync(TODO_FILE, "utf-8");
24259
+ if (!fs19.existsSync(TODO_FILE)) return;
24260
+ const content = fs19.readFileSync(TODO_FILE, "utf-8");
22821
24261
  const lines = content.split("\n");
22822
24262
  const removedIds = [];
22823
24263
  let currentSection = "";
@@ -22854,7 +24294,7 @@ function deduplicateActiveTodos() {
22854
24294
  if (linesToRemove.size === 0) return;
22855
24295
  const newLines = lines.filter((_, i) => !linesToRemove.has(i));
22856
24296
  const cleaned = newLines.join("\n").replace(/\n{3,}/g, "\n\n");
22857
- fs15.writeFileSync(TODO_FILE, cleaned);
24297
+ fs19.writeFileSync(TODO_FILE, cleaned);
22858
24298
  log(`Deduplicated ${removedIds.length} TODO(s): ${removedIds.join(", ")}`);
22859
24299
  } catch (error) {
22860
24300
  log(`Failed to deduplicate TODOs: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -22862,14 +24302,14 @@ function deduplicateActiveTodos() {
22862
24302
  }
22863
24303
  function appendToMemory(learnings) {
22864
24304
  if (learnings.length === 0) return;
22865
- if (!fs15.existsSync(MEMORY_FILE)) return;
24305
+ if (!fs19.existsSync(MEMORY_FILE)) return;
22866
24306
  const MAX_LEARNINGS_PER_HEARTBEAT = 2;
22867
24307
  let filtered = learnings.slice(0, MAX_LEARNINGS_PER_HEARTBEAT);
22868
24308
  if (learnings.length > MAX_LEARNINGS_PER_HEARTBEAT) {
22869
24309
  log(`Capped learnings from ${learnings.length} to ${MAX_LEARNINGS_PER_HEARTBEAT}`);
22870
24310
  }
22871
24311
  try {
22872
- let content = fs15.readFileSync(MEMORY_FILE, "utf-8");
24312
+ let content = fs19.readFileSync(MEMORY_FILE, "utf-8");
22873
24313
  const existingLines = content.split("\n").filter((l2) => l2.match(/^- \*\*/));
22874
24314
  filtered = filtered.filter((learning) => {
22875
24315
  const words = learning.toLowerCase().split(/\s+/).filter((w2) => w2.length > 4);
@@ -22910,7 +24350,7 @@ function appendToMemory(learnings) {
22910
24350
  const subsectionEnd = subsectionEndMatch ? subsectionIndex + 22 + (subsectionEndMatch.index || 0) : insertPoint;
22911
24351
  content = content.slice(0, subsectionEnd) + newEntries + "\n" + content.slice(subsectionEnd);
22912
24352
  }
22913
- fs15.writeFileSync(MEMORY_FILE, content);
24353
+ fs19.writeFileSync(MEMORY_FILE, content);
22914
24354
  log(`Added ${filtered.length} learning(s) to MEMORY.md`);
22915
24355
  }
22916
24356
  } catch (error) {
@@ -23027,7 +24467,7 @@ function detectTargetRepo(idea, config) {
23027
24467
  const filesAnalyzed = idea.source.files_analyzed || [];
23028
24468
  for (const file of filesAnalyzed) {
23029
24469
  for (const repo of config.repos) {
23030
- if (file.includes(repo.name) || file.includes(path14.basename(repo.path))) {
24470
+ if (file.includes(repo.name) || file.includes(path18.basename(repo.path))) {
23031
24471
  return repo;
23032
24472
  }
23033
24473
  }
@@ -23225,7 +24665,7 @@ Respond with JSON only (no markdown code blocks, just raw JSON):
23225
24665
  },
23226
24666
  required: ["sub_tasks"]
23227
24667
  });
23228
- const claude = (0, import_child_process4.spawn)("claude", [
24668
+ const claude = (0, import_child_process6.spawn)("claude", [
23229
24669
  "--print",
23230
24670
  "--output-format",
23231
24671
  "json",
@@ -23355,17 +24795,17 @@ function runTestsForRepo(workspacePath, testCommands, sourceRepoPath) {
23355
24795
  const allOutput = [];
23356
24796
  const env = { ...process.env };
23357
24797
  if (sourceRepoPath) {
23358
- const venvBin = path14.join(sourceRepoPath, ".venv", "bin");
23359
- if (fs15.existsSync(venvBin)) {
24798
+ const venvBin = path18.join(sourceRepoPath, ".venv", "bin");
24799
+ if (fs19.existsSync(venvBin)) {
23360
24800
  env.PATH = `${venvBin}:${env.PATH}`;
23361
- env.VIRTUAL_ENV = path14.join(sourceRepoPath, ".venv");
24801
+ env.VIRTUAL_ENV = path18.join(sourceRepoPath, ".venv");
23362
24802
  log(`Using venv from ${sourceRepoPath}/.venv`);
23363
24803
  }
23364
24804
  }
23365
24805
  for (const cmd of testCommands) {
23366
24806
  log(`Running test: ${cmd}`);
23367
24807
  try {
23368
- const result = (0, import_child_process4.execSync)(cmd, {
24808
+ const result = (0, import_child_process6.execSync)(cmd, {
23369
24809
  cwd: workspacePath,
23370
24810
  encoding: "utf-8",
23371
24811
  env,
@@ -23411,7 +24851,7 @@ async function executeSingleSubTask(subTask, ideaId, idea, ideasData, autonomy,
23411
24851
  });
23412
24852
  const scopeBase64 = Buffer.from(scopePayload).toString("base64");
23413
24853
  const timeoutMs = autonomy.max_sub_task_timeout_ms || 3e5;
23414
- const workspacePath = options?.worktreePath || path14.join(config.workspace_dir, targetRepo.name);
24854
+ const workspacePath = options?.worktreePath || path18.join(config.workspace_dir, targetRepo.name);
23415
24855
  const executionStartTime = Date.now();
23416
24856
  const updateSubTaskWithDetails = (status, updateOpts = {}) => {
23417
24857
  const endTime = Date.now();
@@ -23489,7 +24929,7 @@ async function executeSingleSubTask(subTask, ideaId, idea, ideasData, autonomy,
23489
24929
  }
23490
24930
  let commitHash = null;
23491
24931
  try {
23492
- commitHash = (0, import_child_process4.execSync)("git rev-parse HEAD", { cwd: workspacePath, encoding: "utf-8" }).trim();
24932
+ commitHash = (0, import_child_process6.execSync)("git rev-parse HEAD", { cwd: workspacePath, encoding: "utf-8" }).trim();
23493
24933
  } catch {
23494
24934
  }
23495
24935
  updateSubTaskWithDetails("completed", {
@@ -23533,7 +24973,7 @@ async function analyzeAndRetrySubTask(subTask, ideaId, maxRetries) {
23533
24973
  "Be specific about what went wrong and how to fix it. Include any workarounds needed."
23534
24974
  ].filter(Boolean).join("\n");
23535
24975
  try {
23536
- const spawnResult = (0, import_child_process4.spawnSync)("claude", ["--print", "--model", "sonnet", "-p", errorContext], {
24976
+ const spawnResult = (0, import_child_process6.spawnSync)("claude", ["--print", "--model", "sonnet", "-p", errorContext], {
23537
24977
  cwd: ROOT_DIR,
23538
24978
  encoding: "utf-8",
23539
24979
  timeout: 6e4,
@@ -23630,7 +25070,7 @@ async function executeAutonomousImplementation(ideaId) {
23630
25070
  }
23631
25071
  try {
23632
25072
  const { command, args: args2 } = resolveScript(__dirname, "implement.ts");
23633
- (0, import_child_process4.execSync)(
25073
+ (0, import_child_process6.execSync)(
23634
25074
  [command, ...args2, `--idea=${ideaId}`, `--repo=${targetRepo2.name}`, "--create-pr-only"].join(" "),
23635
25075
  { cwd: ROOT_DIR, encoding: "utf-8", stdio: "inherit", timeout: 12e4 }
23636
25076
  );
@@ -23638,16 +25078,16 @@ async function executeAutonomousImplementation(ideaId) {
23638
25078
  if (autonomy.auto_merge) {
23639
25079
  const branchName2 = idea.implementation.branch_name;
23640
25080
  const defaultBranch2 = targetRepo2.default_branch || "main";
23641
- const workspacePath = path14.join(config.workspace_dir, targetRepo2.name);
25081
+ const workspacePath = path18.join(config.workspace_dir, targetRepo2.name);
23642
25082
  if (branchName2) {
23643
25083
  try {
23644
25084
  log(`Auto-merging branch ${branchName2} into ${defaultBranch2}...`);
23645
- (0, import_child_process4.execSync)(`git checkout ${defaultBranch2}`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
23646
- (0, import_child_process4.execSync)(`git merge ${branchName2} --squash --no-edit`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
23647
- (0, import_child_process4.execSync)(`git commit -m "feat: ${idea.title} (auto-merged from ${branchName2})"`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
25085
+ (0, import_child_process6.execSync)(`git checkout ${defaultBranch2}`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
25086
+ (0, import_child_process6.execSync)(`git merge ${branchName2} --squash --no-edit`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
25087
+ (0, import_child_process6.execSync)(`git commit -m "feat: ${idea.title} (auto-merged from ${branchName2})"`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
23648
25088
  log(`Successfully merged ${branchName2} into ${defaultBranch2}`);
23649
25089
  try {
23650
- (0, import_child_process4.execSync)(`git branch -d ${branchName2}`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
25090
+ (0, import_child_process6.execSync)(`git branch -d ${branchName2}`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
23651
25091
  log(`Deleted branch ${branchName2}`);
23652
25092
  } catch {
23653
25093
  log(`Could not delete branch ${branchName2} (may need force delete)`);
@@ -23672,8 +25112,8 @@ async function executeAutonomousImplementation(ideaId) {
23672
25112
  } catch (mergeError) {
23673
25113
  log(`Auto-merge failed (likely conflicts): ${mergeError instanceof Error ? mergeError.message : "Unknown"}`);
23674
25114
  try {
23675
- (0, import_child_process4.execSync)(`git merge --abort`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
23676
- (0, import_child_process4.execSync)(`git checkout ${branchName2}`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
25115
+ (0, import_child_process6.execSync)(`git merge --abort`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
25116
+ (0, import_child_process6.execSync)(`git checkout ${branchName2}`, { cwd: workspacePath, encoding: "utf-8", stdio: "pipe" });
23677
25117
  } catch {
23678
25118
  }
23679
25119
  log(`Left idea ${ideaId} in testing stage for manual merge`);
@@ -23867,7 +25307,7 @@ ${allowedFiles.map((f) => `- ${f}`).join("\n")}
23867
25307
  7. Be direct \u2014 read the file, make the edit, done
23868
25308
  `;
23869
25309
  return new Promise((resolve) => {
23870
- const claude = (0, import_child_process4.spawn)("claude", [
25310
+ const claude = (0, import_child_process6.spawn)("claude", [
23871
25311
  "--print",
23872
25312
  "--dangerously-skip-permissions",
23873
25313
  "--model",
@@ -23951,7 +25391,7 @@ ${kpiLines}`;
23951
25391
  const contextBase64 = Buffer.from(contextPayload).toString("base64");
23952
25392
  return new Promise((resolve) => {
23953
25393
  const { command: analyzeCmd, args: analyzeArgs } = resolveScript(__dirname, "analyze.ts");
23954
- const childProcess = (0, import_child_process4.spawn)(analyzeCmd, [
25394
+ const childProcess = (0, import_child_process6.spawn)(analyzeCmd, [
23955
25395
  ...analyzeArgs,
23956
25396
  `--type=research`,
23957
25397
  `--topic=${topic}`,
@@ -24115,7 +25555,7 @@ async function executeGoalGapResearch(goalId) {
24115
25555
  return new Promise((resolve) => {
24116
25556
  const TIMEOUT_MS = 9e5;
24117
25557
  const startTime = Date.now();
24118
- const claude = (0, import_child_process4.spawn)("claude", [
25558
+ const claude = (0, import_child_process6.spawn)("claude", [
24119
25559
  "--print",
24120
25560
  "--dangerously-skip-permissions"
24121
25561
  ], {
@@ -24359,7 +25799,7 @@ async function executeShippedEvaluation(ideaId) {
24359
25799
  const prompt = buildEvaluationPrompt(idea, goal, liveKPIs, daysShipped);
24360
25800
  const TIMEOUT_MS = 12e4;
24361
25801
  const result = await new Promise((resolve) => {
24362
- const claude = (0, import_child_process4.spawn)("claude", ["--print"], {
25802
+ const claude = (0, import_child_process6.spawn)("claude", ["--print"], {
24363
25803
  cwd: ROOT_DIR,
24364
25804
  env: process.env,
24365
25805
  stdio: ["pipe", "pipe", "pipe"]
@@ -24757,7 +26197,7 @@ async function executeSingleResearchTask(task) {
24757
26197
  task.status = "running";
24758
26198
  task.started_at = (/* @__PURE__ */ new Date()).toISOString();
24759
26199
  return new Promise((resolve) => {
24760
- const claude = (0, import_child_process4.spawn)("claude", ["--print", "--dangerously-skip-permissions"], {
26200
+ const claude = (0, import_child_process6.spawn)("claude", ["--print", "--dangerously-skip-permissions"], {
24761
26201
  cwd: ROOT_DIR,
24762
26202
  env: process.env,
24763
26203
  stdio: ["pipe", "pipe", "pipe"]
@@ -24868,7 +26308,7 @@ Respond with JSON only:
24868
26308
  "recommendation_reasoning": "Why this recommendation"
24869
26309
  }`;
24870
26310
  return new Promise((resolve) => {
24871
- const claude = (0, import_child_process4.spawn)("claude", ["--print"], {
26311
+ const claude = (0, import_child_process6.spawn)("claude", ["--print"], {
24872
26312
  cwd: ROOT_DIR,
24873
26313
  env: process.env,
24874
26314
  stdio: ["pipe", "pipe", "pipe"]
@@ -25052,6 +26492,18 @@ async function executeTask(task, config, businessContext) {
25052
26492
  trackEvent("heartbeat_task", { taskType: taskTypePrefix, success: result.success });
25053
26493
  return result.success;
25054
26494
  }
26495
+ if (isEmailMarketingTask(taskId)) {
26496
+ const result = await executeEmailMarketingTask(taskId, taskDesc, businessContext);
26497
+ log(`Email marketing task: ${result.success ? "SUCCESS" : "FAILED"} \u2014 ${result.output.slice(0, 200)}`);
26498
+ trackEvent("heartbeat_task", { taskType: taskTypePrefix, success: result.success });
26499
+ return result.success;
26500
+ }
26501
+ if (isPaymentsTask(taskId)) {
26502
+ const result = await executePaymentsTask(taskId, taskDesc, businessContext);
26503
+ log(`Payments task: ${result.success ? "SUCCESS" : "FAILED"} \u2014 ${result.output.slice(0, 200)}`);
26504
+ trackEvent("heartbeat_task", { taskType: taskTypePrefix, success: result.success });
26505
+ return result.success;
26506
+ }
25055
26507
  if (taskId === "suggest-epics") {
25056
26508
  return await executeSuggestEpics();
25057
26509
  }
@@ -25081,7 +26533,7 @@ async function runAnalysis(type) {
25081
26533
  return new Promise((resolve, reject) => {
25082
26534
  log(`Running ${type} analysis...`);
25083
26535
  const { command: runCmd, args: runArgs } = resolveScript(__dirname, "analyze.ts");
25084
- const childProcess = (0, import_child_process4.spawn)(runCmd, [...runArgs, `--type=${type}`], {
26536
+ const childProcess = (0, import_child_process6.spawn)(runCmd, [...runArgs, `--type=${type}`], {
25085
26537
  cwd: ROOT_DIR,
25086
26538
  stdio: "inherit"
25087
26539
  });
@@ -25100,7 +26552,7 @@ async function runAnalysis(type) {
25100
26552
  }
25101
26553
  function updateTodoFile(taskId, completed) {
25102
26554
  try {
25103
- let content = fs15.readFileSync(TODO_FILE, "utf-8");
26555
+ let content = fs19.readFileSync(TODO_FILE, "utf-8");
25104
26556
  const uncheckedPattern = new RegExp(`- \\[ \\] \`${taskId}\``, "g");
25105
26557
  const checkedPattern = new RegExp(`- \\[x\\] \`${taskId}\``, "g");
25106
26558
  if (completed) {
@@ -25108,7 +26560,7 @@ function updateTodoFile(taskId, completed) {
25108
26560
  } else {
25109
26561
  content = content.replace(checkedPattern, `- [ ] \`${taskId}\``);
25110
26562
  }
25111
- fs15.writeFileSync(TODO_FILE, content);
26563
+ fs19.writeFileSync(TODO_FILE, content);
25112
26564
  log(`Updated TODO.md: ${taskId} marked as ${completed ? "completed" : "pending"}`);
25113
26565
  } catch (error) {
25114
26566
  log(`Failed to update TODO.md: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -25212,8 +26664,8 @@ async function runSingleHeartbeat(beatNumber = 1) {
25212
26664
  if (vibeResult.warning) log(vibeResult.warning);
25213
26665
  log(`Vibe consumed (${vibeResult.vibes_remaining} remaining${vibeResult.plan ? `, plan: ${vibeResult.plan}` : ""}${vibeResult.offline ? ", offline" : ""})`);
25214
26666
  }
25215
- const commandsDir = path14.join(PROJECT_DIR, ".claude", "commands");
25216
- if (!fs15.existsSync(commandsDir)) {
26667
+ const commandsDir = path18.join(PROJECT_DIR, ".claude", "commands");
26668
+ if (!fs19.existsSync(commandsDir)) {
25217
26669
  log("Slash commands missing \u2014 auto-scaffolding...");
25218
26670
  scaffoldSlashCommands(PROJECT_DIR);
25219
26671
  }
@@ -25223,7 +26675,7 @@ async function runSingleHeartbeat(beatNumber = 1) {
25223
26675
  if (SKIP_REASONING) {
25224
26676
  log("Skipping Claude reasoning step");
25225
26677
  }
25226
- const state = loadState();
26678
+ const state = loadState2();
25227
26679
  const businessContext = loadBusinessContext();
25228
26680
  const heartbeatConfig = loadConfig();
25229
26681
  log(`Loaded state: ${state.ideas.length} ideas, ${state.goals.length} goals, ${state.sessions.length} sessions`);
@@ -25390,7 +26842,7 @@ async function runSingleHeartbeat(beatNumber = 1) {
25390
26842
  }
25391
26843
  if (!DRY_RUN) {
25392
26844
  const statusContent = generateStatusContent(state, alerts, taskToDo, reasoning, systemHealth);
25393
- fs15.writeFileSync(STATUS_FILE, statusContent);
26845
+ fs19.writeFileSync(STATUS_FILE, statusContent);
25394
26846
  log("Updated STATUS.md");
25395
26847
  } else {
25396
26848
  log("[DRY RUN] Would update STATUS.md");
@@ -25505,7 +26957,7 @@ ${"=".repeat(60)}
25505
26957
  `);
25506
26958
  }
25507
26959
  function saveSessionToJson(summary) {
25508
- const sessionsFile = path14.join(DATA_DIR, "heartbeat-sessions.json");
26960
+ const sessionsFile = path18.join(DATA_DIR, "heartbeat-sessions.json");
25509
26961
  const sessionsData = loadJson(sessionsFile, { sessions: [] });
25510
26962
  sessionsData.sessions.push(summary);
25511
26963
  if (sessionsData.sessions.length > 50) {