vibebusiness 1.2.77 → 1.2.80
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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-build-manifest.json +24 -24
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -1
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/api/analyze/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/epics/[id]/ideas/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/epics/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/epics/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/goals/[id]/kpis/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/goals/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/goals/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/hypotheses/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/hypotheses/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[id]/card/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[id]/comments/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[id]/implement/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[id]/transition/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/implementations/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/kpis/refresh/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/[id]/publish/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/goals/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/goals/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/goals/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/hypotheses/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/hypotheses/page.js +1 -1
- package/.next/standalone/.next/server/app/hypotheses/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/hypotheses/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/ideas/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/ideas/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/page.js +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/roadmap/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/roadmap/investors/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/roadmap/investors/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/roadmap/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/roadmap/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/roadmap/public/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/roadmap/public/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +1 -1
- package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/social.html +1 -1
- package/.next/standalone/.next/server/app/social.rsc +1 -1
- package/.next/standalone/.next/server/app/updates/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/updates/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/updates/new/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/updates/new.html +1 -1
- package/.next/standalone/.next/server/app/updates/new.rsc +1 -1
- package/.next/standalone/.next/server/app/updates/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/updates/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +20 -20
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/data/business-context.json +1 -1
- package/.next/standalone/data/ideas.json +306 -26
- package/.next/standalone/data/implementations.json +147 -0
- package/.next/standalone/data/marketing-plans/plan-1771885743819-2d9m2nt.json +115 -0
- package/.next/standalone/data/marketing-plans/plan-1771886455031-mwdob3k.json +115 -0
- package/.next/standalone/data/reports/visuals/idea-first-tweet-card.png +0 -0
- package/.next/standalone/data/reports/visuals/idea-lp-hero-specificity-card.png +0 -0
- package/.next/standalone/data/reports/visuals/idea-lp-social-proof-card.png +0 -0
- package/.next/standalone/data/videos/ad-solo-founder-burnout.mp4 +0 -0
- package/.next/standalone/data/videos/staging/ad-solo-founder-burnout.json +54 -0
- package/.next/standalone/node_modules/.cache/webpack/remotion-production-4.0.428/48519e014fdef8bfe40a0d447e90c96c/0.pack +0 -0
- package/.next/standalone/node_modules/.cache/webpack/remotion-production-4.0.428/48519e014fdef8bfe40a0d447e90c96c/index.pack +0 -0
- package/.next/standalone/package.json +1 -1
- package/dist/scripts/heartbeat.js +648 -462
- package/package.json +1 -1
- package/scripts/lib/video/compositions/AdVideo.tsx +7 -3
- package/scripts/lib/video/compositions/BackgroundMusic.tsx +41 -0
- package/scripts/lib/video/compositions/Root.tsx +17 -6
- package/scripts/lib/video/compositions/ShipVideo.tsx +6 -2
- /package/.next/static/{_6NFRXGMGhuMnoqW7NVno → 9C8sbF668J83TlKDjSvQm}/_buildManifest.js +0 -0
- /package/.next/static/{_6NFRXGMGhuMnoqW7NVno → 9C8sbF668J83TlKDjSvQm}/_ssgManifest.js +0 -0
|
@@ -105,8 +105,8 @@ var require_package = __commonJS({
|
|
|
105
105
|
var require_main = __commonJS({
|
|
106
106
|
"node_modules/dotenv/lib/main.js"(exports2, module2) {
|
|
107
107
|
"use strict";
|
|
108
|
-
var
|
|
109
|
-
var
|
|
108
|
+
var fs35 = require("fs");
|
|
109
|
+
var path30 = require("path");
|
|
110
110
|
var os3 = require("os");
|
|
111
111
|
var crypto5 = require("crypto");
|
|
112
112
|
var packageJson = require_package();
|
|
@@ -244,7 +244,7 @@ var require_main = __commonJS({
|
|
|
244
244
|
if (options && options.path && options.path.length > 0) {
|
|
245
245
|
if (Array.isArray(options.path)) {
|
|
246
246
|
for (const filepath of options.path) {
|
|
247
|
-
if (
|
|
247
|
+
if (fs35.existsSync(filepath)) {
|
|
248
248
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
249
249
|
}
|
|
250
250
|
}
|
|
@@ -252,15 +252,15 @@ var require_main = __commonJS({
|
|
|
252
252
|
possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
253
253
|
}
|
|
254
254
|
} else {
|
|
255
|
-
possibleVaultPath =
|
|
255
|
+
possibleVaultPath = path30.resolve(process.cwd(), ".env.vault");
|
|
256
256
|
}
|
|
257
|
-
if (
|
|
257
|
+
if (fs35.existsSync(possibleVaultPath)) {
|
|
258
258
|
return possibleVaultPath;
|
|
259
259
|
}
|
|
260
260
|
return null;
|
|
261
261
|
}
|
|
262
262
|
function _resolveHome(envPath) {
|
|
263
|
-
return envPath[0] === "~" ?
|
|
263
|
+
return envPath[0] === "~" ? path30.join(os3.homedir(), envPath.slice(1)) : envPath;
|
|
264
264
|
}
|
|
265
265
|
function _configVault(options) {
|
|
266
266
|
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
@@ -277,7 +277,7 @@ var require_main = __commonJS({
|
|
|
277
277
|
return { parsed };
|
|
278
278
|
}
|
|
279
279
|
function configDotenv(options) {
|
|
280
|
-
const dotenvPath =
|
|
280
|
+
const dotenvPath = path30.resolve(process.cwd(), ".env");
|
|
281
281
|
let encoding = "utf8";
|
|
282
282
|
let processEnv = process.env;
|
|
283
283
|
if (options && options.processEnv != null) {
|
|
@@ -305,13 +305,13 @@ var require_main = __commonJS({
|
|
|
305
305
|
}
|
|
306
306
|
let lastError;
|
|
307
307
|
const parsedAll = {};
|
|
308
|
-
for (const
|
|
308
|
+
for (const path31 of optionPaths) {
|
|
309
309
|
try {
|
|
310
|
-
const parsed = DotenvModule.parse(
|
|
310
|
+
const parsed = DotenvModule.parse(fs35.readFileSync(path31, { encoding }));
|
|
311
311
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
312
312
|
} catch (e) {
|
|
313
313
|
if (debug) {
|
|
314
|
-
_debug(`Failed to load ${
|
|
314
|
+
_debug(`Failed to load ${path31} ${e.message}`);
|
|
315
315
|
}
|
|
316
316
|
lastError = e;
|
|
317
317
|
}
|
|
@@ -324,7 +324,7 @@ var require_main = __commonJS({
|
|
|
324
324
|
const shortPaths = [];
|
|
325
325
|
for (const filePath of optionPaths) {
|
|
326
326
|
try {
|
|
327
|
-
const relative =
|
|
327
|
+
const relative = path30.relative(process.cwd(), filePath);
|
|
328
328
|
shortPaths.push(relative);
|
|
329
329
|
} catch (e) {
|
|
330
330
|
if (debug) {
|
|
@@ -476,6 +476,36 @@ var require_cli_options = __commonJS({
|
|
|
476
476
|
}
|
|
477
477
|
});
|
|
478
478
|
|
|
479
|
+
// src/lib/prompts.ts
|
|
480
|
+
function buildBaseContext(businessConfig) {
|
|
481
|
+
if (!businessConfig) {
|
|
482
|
+
return `You are an AI Product Manager.
|
|
483
|
+
|
|
484
|
+
Your role is to analyze the codebase and generate actionable improvement ideas.`;
|
|
485
|
+
}
|
|
486
|
+
const { product } = businessConfig;
|
|
487
|
+
let architectureSection = "";
|
|
488
|
+
if (product.architecture && product.architecture.length > 0) {
|
|
489
|
+
const components = product.architecture.map((c2, i) => {
|
|
490
|
+
const techLine = c2.tech_stack.length > 0 ? `
|
|
491
|
+
Tech: ${c2.tech_stack.join(", ")}` : "";
|
|
492
|
+
return `${i + 1}. **${c2.name} (${c2.repo_name})**: ${c2.description}${techLine}`;
|
|
493
|
+
}).join("\n\n");
|
|
494
|
+
architectureSection = `
|
|
495
|
+
|
|
496
|
+
The product consists of:
|
|
497
|
+
${components}`;
|
|
498
|
+
}
|
|
499
|
+
return `You are an AI Product Manager for ${product.name}, ${product.summary}.${architectureSection}
|
|
500
|
+
|
|
501
|
+
Your role is to analyze the codebase and generate actionable improvement ideas.`;
|
|
502
|
+
}
|
|
503
|
+
var init_prompts = __esm({
|
|
504
|
+
"src/lib/prompts.ts"() {
|
|
505
|
+
"use strict";
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
479
509
|
// scripts/lib/paths.ts
|
|
480
510
|
function findProjectRoot() {
|
|
481
511
|
const cwd = process.cwd();
|
|
@@ -9428,6 +9458,407 @@ var init_esm = __esm({
|
|
|
9428
9458
|
}
|
|
9429
9459
|
});
|
|
9430
9460
|
|
|
9461
|
+
// scripts/lib/ai-provider.ts
|
|
9462
|
+
function loadCredentials2() {
|
|
9463
|
+
try {
|
|
9464
|
+
if (fs16.existsSync(CREDENTIALS_FILE)) {
|
|
9465
|
+
return JSON.parse(fs16.readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
9466
|
+
}
|
|
9467
|
+
} catch {
|
|
9468
|
+
}
|
|
9469
|
+
return {};
|
|
9470
|
+
}
|
|
9471
|
+
function detectClaudeCLI(customPath) {
|
|
9472
|
+
const claudeBin = customPath || "claude";
|
|
9473
|
+
try {
|
|
9474
|
+
const version = (0, import_child_process6.execSync)(`${claudeBin} --version 2>/dev/null`, {
|
|
9475
|
+
timeout: 1e4,
|
|
9476
|
+
encoding: "utf-8"
|
|
9477
|
+
}).trim();
|
|
9478
|
+
return { found: true, path: claudeBin, version };
|
|
9479
|
+
} catch {
|
|
9480
|
+
const commonPaths = [
|
|
9481
|
+
"/usr/local/bin/claude",
|
|
9482
|
+
path11.join(process.env.HOME || "", ".claude", "bin", "claude"),
|
|
9483
|
+
path11.join(process.env.HOME || "", ".local", "bin", "claude")
|
|
9484
|
+
];
|
|
9485
|
+
for (const p of commonPaths) {
|
|
9486
|
+
try {
|
|
9487
|
+
if (fs16.existsSync(p)) {
|
|
9488
|
+
const version = (0, import_child_process6.execSync)(`${p} --version 2>/dev/null`, {
|
|
9489
|
+
timeout: 1e4,
|
|
9490
|
+
encoding: "utf-8"
|
|
9491
|
+
}).trim();
|
|
9492
|
+
return { found: true, path: p, version };
|
|
9493
|
+
}
|
|
9494
|
+
} catch {
|
|
9495
|
+
continue;
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
return { found: false, path: claudeBin };
|
|
9499
|
+
}
|
|
9500
|
+
}
|
|
9501
|
+
function detectBYOKKeys() {
|
|
9502
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
9503
|
+
return { provider: "anthropic-api", key: process.env.ANTHROPIC_API_KEY, source: "env" };
|
|
9504
|
+
}
|
|
9505
|
+
if (process.env.OPENAI_API_KEY) {
|
|
9506
|
+
return { provider: "openai-api", key: process.env.OPENAI_API_KEY, source: "env" };
|
|
9507
|
+
}
|
|
9508
|
+
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY) {
|
|
9509
|
+
return { provider: "google-api", key: process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY, source: "env" };
|
|
9510
|
+
}
|
|
9511
|
+
const creds = loadCredentials2();
|
|
9512
|
+
if (creds.ANTHROPIC_API_KEY) {
|
|
9513
|
+
process.env.ANTHROPIC_API_KEY = creds.ANTHROPIC_API_KEY;
|
|
9514
|
+
return { provider: "anthropic-api", key: creds.ANTHROPIC_API_KEY, source: "credentials" };
|
|
9515
|
+
}
|
|
9516
|
+
if (creds.OPENAI_API_KEY) {
|
|
9517
|
+
process.env.OPENAI_API_KEY = creds.OPENAI_API_KEY;
|
|
9518
|
+
return { provider: "openai-api", key: creds.OPENAI_API_KEY, source: "credentials" };
|
|
9519
|
+
}
|
|
9520
|
+
if (creds.GOOGLE_API_KEY || creds.GEMINI_API_KEY) {
|
|
9521
|
+
const key = creds.GOOGLE_API_KEY || creds.GEMINI_API_KEY;
|
|
9522
|
+
process.env.GOOGLE_API_KEY = key;
|
|
9523
|
+
return { provider: "google-api", key, source: "credentials" };
|
|
9524
|
+
}
|
|
9525
|
+
return null;
|
|
9526
|
+
}
|
|
9527
|
+
function detectProvider(config) {
|
|
9528
|
+
const cli = detectClaudeCLI(config?.claudePath);
|
|
9529
|
+
if (cli.found) {
|
|
9530
|
+
return {
|
|
9531
|
+
provider: "claude-cli",
|
|
9532
|
+
version: cli.version,
|
|
9533
|
+
message: `Found Claude Code CLI${cli.version ? ` (${cli.version})` : ""}. Using your Claude subscription for AI reasoning.`
|
|
9534
|
+
};
|
|
9535
|
+
}
|
|
9536
|
+
const byok = detectBYOKKeys();
|
|
9537
|
+
if (byok) {
|
|
9538
|
+
const providerNames = {
|
|
9539
|
+
"claude-cli": "Claude CLI",
|
|
9540
|
+
"anthropic-api": "Anthropic API",
|
|
9541
|
+
"openai-api": "OpenAI API",
|
|
9542
|
+
"google-api": "Google AI API"
|
|
9543
|
+
};
|
|
9544
|
+
return {
|
|
9545
|
+
provider: byok.provider,
|
|
9546
|
+
message: `Using ${providerNames[byok.provider]} key for AI reasoning.`
|
|
9547
|
+
};
|
|
9548
|
+
}
|
|
9549
|
+
return {
|
|
9550
|
+
provider: "claude-cli",
|
|
9551
|
+
// default, will fail at invocation
|
|
9552
|
+
message: "No AI provider found. Install Claude Code CLI or set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY)."
|
|
9553
|
+
};
|
|
9554
|
+
}
|
|
9555
|
+
function loadProviderConfig() {
|
|
9556
|
+
try {
|
|
9557
|
+
if (fs16.existsSync(PROVIDER_CONFIG_FILE)) {
|
|
9558
|
+
return JSON.parse(fs16.readFileSync(PROVIDER_CONFIG_FILE, "utf-8"));
|
|
9559
|
+
}
|
|
9560
|
+
} catch {
|
|
9561
|
+
}
|
|
9562
|
+
return null;
|
|
9563
|
+
}
|
|
9564
|
+
function invokeClaudeCLI(options, claudePath) {
|
|
9565
|
+
const bin = claudePath || "claude";
|
|
9566
|
+
const args2 = ["--print"];
|
|
9567
|
+
if (options.model) {
|
|
9568
|
+
args2.push("--model", options.model);
|
|
9569
|
+
}
|
|
9570
|
+
if (options.jsonSchema) {
|
|
9571
|
+
args2.push("--output-format", "json", "--json-schema", JSON.stringify(options.jsonSchema));
|
|
9572
|
+
}
|
|
9573
|
+
if (options.claudeFlags) {
|
|
9574
|
+
args2.push(...options.claudeFlags);
|
|
9575
|
+
}
|
|
9576
|
+
const useStdin = options.useStdin !== false && options.prompt.length > 1e4;
|
|
9577
|
+
if (!useStdin) {
|
|
9578
|
+
args2.push("-p", options.prompt);
|
|
9579
|
+
}
|
|
9580
|
+
const startTime = Date.now();
|
|
9581
|
+
const timeoutMs = options.timeoutMs || 3e5;
|
|
9582
|
+
return new Promise((resolve3) => {
|
|
9583
|
+
const child = (0, import_child_process6.spawn)(bin, args2, {
|
|
9584
|
+
cwd: options.cwd || process.cwd(),
|
|
9585
|
+
env: process.env,
|
|
9586
|
+
stdio: useStdin ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
|
|
9587
|
+
});
|
|
9588
|
+
let stdout = "";
|
|
9589
|
+
let stderr = "";
|
|
9590
|
+
child.stdout?.on("data", (data) => {
|
|
9591
|
+
stdout += data.toString();
|
|
9592
|
+
});
|
|
9593
|
+
child.stderr?.on("data", (data) => {
|
|
9594
|
+
stderr += data.toString();
|
|
9595
|
+
});
|
|
9596
|
+
if (useStdin && child.stdin) {
|
|
9597
|
+
child.stdin.write(options.prompt);
|
|
9598
|
+
child.stdin.end();
|
|
9599
|
+
}
|
|
9600
|
+
const timeout = setTimeout(() => {
|
|
9601
|
+
child.kill("SIGTERM");
|
|
9602
|
+
resolve3({
|
|
9603
|
+
output: stdout,
|
|
9604
|
+
provider: "claude-cli",
|
|
9605
|
+
durationMs: Date.now() - startTime,
|
|
9606
|
+
error: `Timeout after ${timeoutMs}ms`
|
|
9607
|
+
});
|
|
9608
|
+
}, timeoutMs);
|
|
9609
|
+
child.on("close", (code) => {
|
|
9610
|
+
clearTimeout(timeout);
|
|
9611
|
+
resolve3({
|
|
9612
|
+
output: stdout.trim(),
|
|
9613
|
+
provider: "claude-cli",
|
|
9614
|
+
durationMs: Date.now() - startTime,
|
|
9615
|
+
error: code !== 0 ? `Claude CLI exited with code ${code}: ${stderr}` : void 0
|
|
9616
|
+
});
|
|
9617
|
+
});
|
|
9618
|
+
child.on("error", (err2) => {
|
|
9619
|
+
clearTimeout(timeout);
|
|
9620
|
+
resolve3({
|
|
9621
|
+
output: "",
|
|
9622
|
+
provider: "claude-cli",
|
|
9623
|
+
durationMs: Date.now() - startTime,
|
|
9624
|
+
error: `Failed to spawn Claude CLI: ${err2.message}`
|
|
9625
|
+
});
|
|
9626
|
+
});
|
|
9627
|
+
});
|
|
9628
|
+
}
|
|
9629
|
+
async function invokeAnthropicAPI(options) {
|
|
9630
|
+
const startTime = Date.now();
|
|
9631
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
9632
|
+
if (!apiKey) {
|
|
9633
|
+
return {
|
|
9634
|
+
output: "",
|
|
9635
|
+
provider: "anthropic-api",
|
|
9636
|
+
durationMs: 0,
|
|
9637
|
+
error: "ANTHROPIC_API_KEY not set"
|
|
9638
|
+
};
|
|
9639
|
+
}
|
|
9640
|
+
try {
|
|
9641
|
+
const model = options.model || "claude-sonnet-4-6";
|
|
9642
|
+
let prompt = options.prompt;
|
|
9643
|
+
if (options.jsonSchema) {
|
|
9644
|
+
prompt += `
|
|
9645
|
+
|
|
9646
|
+
IMPORTANT: Respond with valid JSON matching this schema:
|
|
9647
|
+
${JSON.stringify(options.jsonSchema, null, 2)}
|
|
9648
|
+
|
|
9649
|
+
Output ONLY the JSON object, no markdown code blocks or other text.`;
|
|
9650
|
+
} else if (options.expectJson) {
|
|
9651
|
+
prompt += "\n\nIMPORTANT: Respond with valid JSON only. No markdown code blocks or other text.";
|
|
9652
|
+
}
|
|
9653
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
9654
|
+
method: "POST",
|
|
9655
|
+
headers: {
|
|
9656
|
+
"Content-Type": "application/json",
|
|
9657
|
+
"x-api-key": apiKey,
|
|
9658
|
+
"anthropic-version": "2023-06-01"
|
|
9659
|
+
},
|
|
9660
|
+
body: JSON.stringify({
|
|
9661
|
+
model,
|
|
9662
|
+
max_tokens: 16384,
|
|
9663
|
+
messages: [{ role: "user", content: prompt }]
|
|
9664
|
+
}),
|
|
9665
|
+
signal: AbortSignal.timeout(options.timeoutMs || 3e5)
|
|
9666
|
+
});
|
|
9667
|
+
if (!response.ok) {
|
|
9668
|
+
const errorText = await response.text();
|
|
9669
|
+
return {
|
|
9670
|
+
output: "",
|
|
9671
|
+
provider: "anthropic-api",
|
|
9672
|
+
durationMs: Date.now() - startTime,
|
|
9673
|
+
error: `Anthropic API error ${response.status}: ${errorText}`
|
|
9674
|
+
};
|
|
9675
|
+
}
|
|
9676
|
+
const data = await response.json();
|
|
9677
|
+
const text = data.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
9678
|
+
return {
|
|
9679
|
+
output: text,
|
|
9680
|
+
provider: "anthropic-api",
|
|
9681
|
+
durationMs: Date.now() - startTime
|
|
9682
|
+
};
|
|
9683
|
+
} catch (err2) {
|
|
9684
|
+
return {
|
|
9685
|
+
output: "",
|
|
9686
|
+
provider: "anthropic-api",
|
|
9687
|
+
durationMs: Date.now() - startTime,
|
|
9688
|
+
error: `Anthropic API call failed: ${err2.message}`
|
|
9689
|
+
};
|
|
9690
|
+
}
|
|
9691
|
+
}
|
|
9692
|
+
async function invokeAI(options) {
|
|
9693
|
+
const savedConfig = loadProviderConfig();
|
|
9694
|
+
const detection = detectProvider(savedConfig || void 0);
|
|
9695
|
+
switch (detection.provider) {
|
|
9696
|
+
case "claude-cli":
|
|
9697
|
+
return invokeClaudeCLI(options, savedConfig?.claudePath);
|
|
9698
|
+
case "anthropic-api":
|
|
9699
|
+
return invokeAnthropicAPI(options);
|
|
9700
|
+
case "openai-api":
|
|
9701
|
+
case "google-api":
|
|
9702
|
+
return {
|
|
9703
|
+
output: "",
|
|
9704
|
+
provider: detection.provider,
|
|
9705
|
+
durationMs: 0,
|
|
9706
|
+
error: `${detection.provider} provider not yet implemented. Use Claude CLI or set ANTHROPIC_API_KEY.`
|
|
9707
|
+
};
|
|
9708
|
+
default:
|
|
9709
|
+
return {
|
|
9710
|
+
output: "",
|
|
9711
|
+
provider: "claude-cli",
|
|
9712
|
+
durationMs: 0,
|
|
9713
|
+
error: detection.message
|
|
9714
|
+
};
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
9717
|
+
function requireClaudeCLI(feature) {
|
|
9718
|
+
const cli = detectClaudeCLI();
|
|
9719
|
+
if (cli.found) {
|
|
9720
|
+
return cli.path;
|
|
9721
|
+
}
|
|
9722
|
+
const featureLabel = feature ? ` (${feature})` : "";
|
|
9723
|
+
const byok = detectBYOKKeys();
|
|
9724
|
+
if (byok) {
|
|
9725
|
+
throw new Error(
|
|
9726
|
+
`This feature${featureLabel} requires Claude Code CLI for file and tool access.
|
|
9727
|
+
Your API key is detected and works for reasoning tasks (heartbeat, idea evaluation),
|
|
9728
|
+
but codebase analysis and implementation need the full CLI.
|
|
9729
|
+
Install it with: npm install -g @anthropic-ai/claude-code`
|
|
9730
|
+
);
|
|
9731
|
+
}
|
|
9732
|
+
throw new Error(
|
|
9733
|
+
`No AI provider found${featureLabel}.
|
|
9734
|
+
Install Claude Code CLI: npm install -g @anthropic-ai/claude-code
|
|
9735
|
+
Or set an API key: export ANTHROPIC_API_KEY=sk-ant-...`
|
|
9736
|
+
);
|
|
9737
|
+
}
|
|
9738
|
+
var import_child_process6, fs16, path11, CREDENTIALS_DIR, CREDENTIALS_FILE, CONFIG_DIR, PROVIDER_CONFIG_FILE;
|
|
9739
|
+
var init_ai_provider = __esm({
|
|
9740
|
+
"scripts/lib/ai-provider.ts"() {
|
|
9741
|
+
"use strict";
|
|
9742
|
+
import_child_process6 = require("child_process");
|
|
9743
|
+
fs16 = __toESM(require("fs"));
|
|
9744
|
+
path11 = __toESM(require("path"));
|
|
9745
|
+
CREDENTIALS_DIR = path11.join(process.env.HOME || "", ".vibebusiness");
|
|
9746
|
+
CREDENTIALS_FILE = path11.join(CREDENTIALS_DIR, "credentials.json");
|
|
9747
|
+
CONFIG_DIR = path11.join(process.env.HOME || "", ".ai-analyst");
|
|
9748
|
+
PROVIDER_CONFIG_FILE = path11.join(CONFIG_DIR, "provider.json");
|
|
9749
|
+
}
|
|
9750
|
+
});
|
|
9751
|
+
|
|
9752
|
+
// scripts/skills/marketing-strategy.ts
|
|
9753
|
+
function detectMarketingTriggers(goals, previousGoals) {
|
|
9754
|
+
const triggers = [];
|
|
9755
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9756
|
+
goals.forEach((goal) => {
|
|
9757
|
+
if (goal.status === "achieved") {
|
|
9758
|
+
return;
|
|
9759
|
+
}
|
|
9760
|
+
goal.kpis.forEach((kpi) => {
|
|
9761
|
+
const stagnation = detectKPIStagnation(kpi, STAGNATION_WINDOW_DAYS);
|
|
9762
|
+
if (stagnation && goal.status === "behind") {
|
|
9763
|
+
triggers.push({
|
|
9764
|
+
id: `trigger-stagnation-${goal.id}-${kpi.id}`,
|
|
9765
|
+
type: "kpi_stagnation",
|
|
9766
|
+
severity: "urgent",
|
|
9767
|
+
description: `KPI "${kpi.name}" has been stagnant at ${kpi.current_value} for ${stagnation.days} days while goal "${goal.title}" is behind schedule`,
|
|
9768
|
+
affected_goal_ids: [goal.id],
|
|
9769
|
+
suggested_action: `Launch marketing campaign to move ${kpi.name} from ${kpi.current_value} toward target of ${kpi.target_value}`,
|
|
9770
|
+
detected_at: now
|
|
9771
|
+
});
|
|
9772
|
+
}
|
|
9773
|
+
});
|
|
9774
|
+
if (previousGoals) {
|
|
9775
|
+
const previousGoal = previousGoals.find((g2) => g2.id === goal.id);
|
|
9776
|
+
if (previousGoal && previousGoal.status === "on_track" && goal.status === "behind") {
|
|
9777
|
+
triggers.push({
|
|
9778
|
+
id: `trigger-regression-${goal.id}`,
|
|
9779
|
+
type: "goal_regression",
|
|
9780
|
+
severity: "high",
|
|
9781
|
+
description: `Goal "${goal.title}" regressed from "on_track" to "behind" schedule`,
|
|
9782
|
+
affected_goal_ids: [goal.id],
|
|
9783
|
+
suggested_action: `Analyze why goal regressed and launch recovery campaign`,
|
|
9784
|
+
detected_at: now
|
|
9785
|
+
});
|
|
9786
|
+
}
|
|
9787
|
+
}
|
|
9788
|
+
});
|
|
9789
|
+
return triggers;
|
|
9790
|
+
}
|
|
9791
|
+
function detectKPIStagnation(kpi, windowDays) {
|
|
9792
|
+
if (kpi.history.length < 2) {
|
|
9793
|
+
return null;
|
|
9794
|
+
}
|
|
9795
|
+
const sortedHistory = [...kpi.history].sort(
|
|
9796
|
+
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
9797
|
+
);
|
|
9798
|
+
const latestValue = sortedHistory[0].value;
|
|
9799
|
+
const latestDate = new Date(sortedHistory[0].date);
|
|
9800
|
+
const windowStart = /* @__PURE__ */ new Date();
|
|
9801
|
+
windowStart.setDate(windowStart.getDate() - windowDays);
|
|
9802
|
+
let isStagnant = true;
|
|
9803
|
+
let lastChangeDate = sortedHistory[0].date;
|
|
9804
|
+
for (let i = 0; i < sortedHistory.length; i++) {
|
|
9805
|
+
const entry = sortedHistory[i];
|
|
9806
|
+
const entryDate = new Date(entry.date);
|
|
9807
|
+
if (entryDate < windowStart) {
|
|
9808
|
+
break;
|
|
9809
|
+
}
|
|
9810
|
+
if (entry.value !== latestValue) {
|
|
9811
|
+
isStagnant = false;
|
|
9812
|
+
lastChangeDate = sortedHistory[i - 1]?.date || entry.date;
|
|
9813
|
+
break;
|
|
9814
|
+
}
|
|
9815
|
+
}
|
|
9816
|
+
if (isStagnant && latestDate > windowStart) {
|
|
9817
|
+
const daysDiff = Math.floor(
|
|
9818
|
+
(Date.now() - new Date(lastChangeDate).getTime()) / (1e3 * 60 * 60 * 24)
|
|
9819
|
+
);
|
|
9820
|
+
return {
|
|
9821
|
+
days: daysDiff,
|
|
9822
|
+
last_change_date: lastChangeDate
|
|
9823
|
+
};
|
|
9824
|
+
}
|
|
9825
|
+
return null;
|
|
9826
|
+
}
|
|
9827
|
+
function generateIdeasFromPlan(plan) {
|
|
9828
|
+
const ideas = [];
|
|
9829
|
+
plan.campaigns.forEach((campaign) => {
|
|
9830
|
+
const idea = {
|
|
9831
|
+
title: campaign.name,
|
|
9832
|
+
summary: `Execute ${campaign.name} campaign for ${plan.title}`,
|
|
9833
|
+
category: "growth",
|
|
9834
|
+
priority: campaign.channel === plan.channels[0]?.name ? "high" : "medium",
|
|
9835
|
+
effort: campaign.command_sequence.length > 3 ? "m" : "s",
|
|
9836
|
+
impact: "l",
|
|
9837
|
+
// Marketing campaigns typically have large impact
|
|
9838
|
+
context: `Marketing campaign from plan ${plan.id}. Timeline: ${campaign.timeline.start} to ${campaign.timeline.end}. Budget: $${campaign.budget}.`,
|
|
9839
|
+
rationale: `This campaign targets ${campaign.target_value} ${campaign.success_metric} via ${campaign.channel}. Part of ${plan.strategy}.`,
|
|
9840
|
+
implementation_plan: `**Commands to Execute:**
|
|
9841
|
+
${campaign.command_sequence.map((cmd) => `- ${cmd.command} ${cmd.args || ""} (${cmd.frequency})`).join("\n")}
|
|
9842
|
+
|
|
9843
|
+
**Success Metric:** ${campaign.success_metric} = ${campaign.target_value}`,
|
|
9844
|
+
success_metrics: [`${campaign.success_metric}: ${campaign.target_value}`, `Campaign ROI: ${campaign.budget > 0 ? "positive" : "N/A"}`],
|
|
9845
|
+
goal_id: plan.goal_id,
|
|
9846
|
+
tags: ["marketing", "campaign", campaign.channel, plan.strategy]
|
|
9847
|
+
};
|
|
9848
|
+
ideas.push(idea);
|
|
9849
|
+
});
|
|
9850
|
+
return ideas;
|
|
9851
|
+
}
|
|
9852
|
+
var STAGNATION_WINDOW_DAYS;
|
|
9853
|
+
var init_marketing_strategy = __esm({
|
|
9854
|
+
"scripts/skills/marketing-strategy.ts"() {
|
|
9855
|
+
"use strict";
|
|
9856
|
+
init_ai_provider();
|
|
9857
|
+
init_prompts();
|
|
9858
|
+
STAGNATION_WINDOW_DAYS = 14;
|
|
9859
|
+
}
|
|
9860
|
+
});
|
|
9861
|
+
|
|
9431
9862
|
// scripts/lib/screenshot.ts
|
|
9432
9863
|
var screenshot_exports = {};
|
|
9433
9864
|
__export(screenshot_exports, {
|
|
@@ -9477,6 +9908,97 @@ var init_screenshot = __esm({
|
|
|
9477
9908
|
}
|
|
9478
9909
|
});
|
|
9479
9910
|
|
|
9911
|
+
// scripts/skills/marketing-integration.ts
|
|
9912
|
+
var marketing_integration_exports = {};
|
|
9913
|
+
__export(marketing_integration_exports, {
|
|
9914
|
+
addMarketingIdeasToDashboard: () => addMarketingIdeasToDashboard,
|
|
9915
|
+
integrateMarketingPlanToUI: () => integrateMarketingPlanToUI
|
|
9916
|
+
});
|
|
9917
|
+
async function addMarketingIdeasToDashboard(plan, dataDir) {
|
|
9918
|
+
const ideasPath = path28.join(dataDir, "ideas.json");
|
|
9919
|
+
let ideasData = { ideas: [] };
|
|
9920
|
+
if (fs33.existsSync(ideasPath)) {
|
|
9921
|
+
ideasData = JSON.parse(fs33.readFileSync(ideasPath, "utf-8"));
|
|
9922
|
+
}
|
|
9923
|
+
const newIdeasPartial = generateIdeasFromPlan(plan);
|
|
9924
|
+
let added = 0;
|
|
9925
|
+
let skipped = 0;
|
|
9926
|
+
for (const partialIdea of newIdeasPartial) {
|
|
9927
|
+
const exists = ideasData.ideas.some((idea) => idea.title === partialIdea.title);
|
|
9928
|
+
if (exists) {
|
|
9929
|
+
skipped++;
|
|
9930
|
+
continue;
|
|
9931
|
+
}
|
|
9932
|
+
const newIdea = {
|
|
9933
|
+
id: generateIdeaId(),
|
|
9934
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9935
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9936
|
+
title: partialIdea.title,
|
|
9937
|
+
summary: partialIdea.summary,
|
|
9938
|
+
category: partialIdea.category,
|
|
9939
|
+
priority: partialIdea.priority,
|
|
9940
|
+
effort: partialIdea.effort,
|
|
9941
|
+
impact: partialIdea.impact,
|
|
9942
|
+
context: partialIdea.context,
|
|
9943
|
+
rationale: partialIdea.rationale,
|
|
9944
|
+
implementation_plan: partialIdea.implementation_plan,
|
|
9945
|
+
success_metrics: partialIdea.success_metrics,
|
|
9946
|
+
stage: "inbox",
|
|
9947
|
+
// Start in inbox
|
|
9948
|
+
source: {
|
|
9949
|
+
type: "manual",
|
|
9950
|
+
session_id: null
|
|
9951
|
+
},
|
|
9952
|
+
implementation: {
|
|
9953
|
+
branch_name: null,
|
|
9954
|
+
pr_url: null,
|
|
9955
|
+
pr_number: null,
|
|
9956
|
+
commits: [],
|
|
9957
|
+
started_at: null,
|
|
9958
|
+
completed_at: null,
|
|
9959
|
+
sub_tasks: []
|
|
9960
|
+
},
|
|
9961
|
+
comments: [],
|
|
9962
|
+
tags: partialIdea.tags || [],
|
|
9963
|
+
related_ideas: [],
|
|
9964
|
+
goal_id: partialIdea.goal_id || null,
|
|
9965
|
+
expected_impact: null,
|
|
9966
|
+
hypothesis_id: null,
|
|
9967
|
+
epic_id: null
|
|
9968
|
+
};
|
|
9969
|
+
ideasData.ideas.push(newIdea);
|
|
9970
|
+
added++;
|
|
9971
|
+
}
|
|
9972
|
+
fs33.writeFileSync(ideasPath, JSON.stringify(ideasData, null, 2));
|
|
9973
|
+
return { added, skipped };
|
|
9974
|
+
}
|
|
9975
|
+
function generateIdeaId() {
|
|
9976
|
+
const timestamp = Date.now().toString(36);
|
|
9977
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
9978
|
+
return `idea-${timestamp}-${random}`;
|
|
9979
|
+
}
|
|
9980
|
+
async function integrateMarketingPlanToUI(planId, dataDir) {
|
|
9981
|
+
const planPath = path28.join(dataDir, "marketing-plans", `${planId}.json`);
|
|
9982
|
+
if (!fs33.existsSync(planPath)) {
|
|
9983
|
+
throw new Error(`Plan ${planId} not found`);
|
|
9984
|
+
}
|
|
9985
|
+
const plan = JSON.parse(fs33.readFileSync(planPath, "utf-8"));
|
|
9986
|
+
const result = await addMarketingIdeasToDashboard(plan, dataDir);
|
|
9987
|
+
console.log(`\u2713 Added ${result.added} marketing ideas to dashboard`);
|
|
9988
|
+
if (result.skipped > 0) {
|
|
9989
|
+
console.log(` (Skipped ${result.skipped} duplicate ideas)`);
|
|
9990
|
+
}
|
|
9991
|
+
}
|
|
9992
|
+
var fs33, path28;
|
|
9993
|
+
var init_marketing_integration = __esm({
|
|
9994
|
+
"scripts/skills/marketing-integration.ts"() {
|
|
9995
|
+
"use strict";
|
|
9996
|
+
fs33 = __toESM(require("fs"));
|
|
9997
|
+
path28 = __toESM(require("path"));
|
|
9998
|
+
init_marketing_strategy();
|
|
9999
|
+
}
|
|
10000
|
+
});
|
|
10001
|
+
|
|
9480
10002
|
// node_modules/dotenv/config.js
|
|
9481
10003
|
(function() {
|
|
9482
10004
|
require_main().config(
|
|
@@ -9489,8 +10011,8 @@ var init_screenshot = __esm({
|
|
|
9489
10011
|
})();
|
|
9490
10012
|
|
|
9491
10013
|
// scripts/heartbeat.ts
|
|
9492
|
-
var
|
|
9493
|
-
var
|
|
10014
|
+
var fs34 = __toESM(require("fs"));
|
|
10015
|
+
var path29 = __toESM(require("path"));
|
|
9494
10016
|
var import_child_process11 = require("child_process");
|
|
9495
10017
|
var import_util = require("util");
|
|
9496
10018
|
|
|
@@ -9857,30 +10379,8 @@ async function fetchAllKPIs(configs, startDate, endDate) {
|
|
|
9857
10379
|
};
|
|
9858
10380
|
}
|
|
9859
10381
|
|
|
9860
|
-
//
|
|
9861
|
-
|
|
9862
|
-
if (!businessConfig) {
|
|
9863
|
-
return `You are an AI Product Manager.
|
|
9864
|
-
|
|
9865
|
-
Your role is to analyze the codebase and generate actionable improvement ideas.`;
|
|
9866
|
-
}
|
|
9867
|
-
const { product } = businessConfig;
|
|
9868
|
-
let architectureSection = "";
|
|
9869
|
-
if (product.architecture && product.architecture.length > 0) {
|
|
9870
|
-
const components = product.architecture.map((c2, i) => {
|
|
9871
|
-
const techLine = c2.tech_stack.length > 0 ? `
|
|
9872
|
-
Tech: ${c2.tech_stack.join(", ")}` : "";
|
|
9873
|
-
return `${i + 1}. **${c2.name} (${c2.repo_name})**: ${c2.description}${techLine}`;
|
|
9874
|
-
}).join("\n\n");
|
|
9875
|
-
architectureSection = `
|
|
9876
|
-
|
|
9877
|
-
The product consists of:
|
|
9878
|
-
${components}`;
|
|
9879
|
-
}
|
|
9880
|
-
return `You are an AI Product Manager for ${product.name}, ${product.summary}.${architectureSection}
|
|
9881
|
-
|
|
9882
|
-
Your role is to analyze the codebase and generate actionable improvement ideas.`;
|
|
9883
|
-
}
|
|
10382
|
+
// scripts/heartbeat.ts
|
|
10383
|
+
init_prompts();
|
|
9884
10384
|
|
|
9885
10385
|
// scripts/skills/content-marketing.ts
|
|
9886
10386
|
var import_child_process = require("child_process");
|
|
@@ -13134,8 +13634,8 @@ function argument(predicate, message) {
|
|
|
13134
13634
|
}
|
|
13135
13635
|
}
|
|
13136
13636
|
var check = { fail, argument, assert: argument };
|
|
13137
|
-
function getPathDefinition(glyph,
|
|
13138
|
-
var _path =
|
|
13637
|
+
function getPathDefinition(glyph, path30) {
|
|
13638
|
+
var _path = path30 || new Path();
|
|
13139
13639
|
return {
|
|
13140
13640
|
configurable: true,
|
|
13141
13641
|
get: function() {
|
|
@@ -13358,9 +13858,9 @@ function ttfGlyphLoader(font, index, parseGlyph2, data, position, buildPath2) {
|
|
|
13358
13858
|
var glyph = new Glyph({ index, font });
|
|
13359
13859
|
glyph.path = function() {
|
|
13360
13860
|
parseGlyph2(glyph, data, position);
|
|
13361
|
-
var
|
|
13362
|
-
|
|
13363
|
-
return
|
|
13861
|
+
var path30 = buildPath2(font.glyphs, glyph);
|
|
13862
|
+
path30.unitsPerEm = font.unitsPerEm;
|
|
13863
|
+
return path30;
|
|
13364
13864
|
};
|
|
13365
13865
|
defineDependentProperty(glyph, "xMin", "_xMin");
|
|
13366
13866
|
defineDependentProperty(glyph, "xMax", "_xMax");
|
|
@@ -13373,9 +13873,9 @@ function cffGlyphLoader(font, index, parseCFFCharstring2, charstring) {
|
|
|
13373
13873
|
return function() {
|
|
13374
13874
|
var glyph = new Glyph({ index, font });
|
|
13375
13875
|
glyph.path = function() {
|
|
13376
|
-
var
|
|
13377
|
-
|
|
13378
|
-
return
|
|
13876
|
+
var path30 = parseCFFCharstring2(font, glyph, charstring);
|
|
13877
|
+
path30.unitsPerEm = font.unitsPerEm;
|
|
13878
|
+
return path30;
|
|
13379
13879
|
};
|
|
13380
13880
|
return glyph;
|
|
13381
13881
|
};
|
|
@@ -30494,8 +30994,8 @@ async function postTweet(client, text) {
|
|
|
30494
30994
|
};
|
|
30495
30995
|
}
|
|
30496
30996
|
async function uploadMedia(client, filePath) {
|
|
30497
|
-
const
|
|
30498
|
-
if (!
|
|
30997
|
+
const fs35 = require("fs");
|
|
30998
|
+
if (!fs35.existsSync(filePath)) {
|
|
30499
30999
|
throw new Error(`Media file not found: ${filePath}`);
|
|
30500
31000
|
}
|
|
30501
31001
|
const mediaId = await client.v1.uploadMedia(filePath);
|
|
@@ -30746,293 +31246,7 @@ function generateProductUpdateTweet(positioning) {
|
|
|
30746
31246
|
// scripts/lib/social/ai-content-generator.ts
|
|
30747
31247
|
var fs17 = __toESM(require("fs"));
|
|
30748
31248
|
init_paths();
|
|
30749
|
-
|
|
30750
|
-
// scripts/lib/ai-provider.ts
|
|
30751
|
-
var import_child_process6 = require("child_process");
|
|
30752
|
-
var fs16 = __toESM(require("fs"));
|
|
30753
|
-
var path11 = __toESM(require("path"));
|
|
30754
|
-
var CREDENTIALS_DIR = path11.join(process.env.HOME || "", ".vibebusiness");
|
|
30755
|
-
var CREDENTIALS_FILE = path11.join(CREDENTIALS_DIR, "credentials.json");
|
|
30756
|
-
function loadCredentials2() {
|
|
30757
|
-
try {
|
|
30758
|
-
if (fs16.existsSync(CREDENTIALS_FILE)) {
|
|
30759
|
-
return JSON.parse(fs16.readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
30760
|
-
}
|
|
30761
|
-
} catch {
|
|
30762
|
-
}
|
|
30763
|
-
return {};
|
|
30764
|
-
}
|
|
30765
|
-
function detectClaudeCLI(customPath) {
|
|
30766
|
-
const claudeBin = customPath || "claude";
|
|
30767
|
-
try {
|
|
30768
|
-
const version = (0, import_child_process6.execSync)(`${claudeBin} --version 2>/dev/null`, {
|
|
30769
|
-
timeout: 1e4,
|
|
30770
|
-
encoding: "utf-8"
|
|
30771
|
-
}).trim();
|
|
30772
|
-
return { found: true, path: claudeBin, version };
|
|
30773
|
-
} catch {
|
|
30774
|
-
const commonPaths = [
|
|
30775
|
-
"/usr/local/bin/claude",
|
|
30776
|
-
path11.join(process.env.HOME || "", ".claude", "bin", "claude"),
|
|
30777
|
-
path11.join(process.env.HOME || "", ".local", "bin", "claude")
|
|
30778
|
-
];
|
|
30779
|
-
for (const p of commonPaths) {
|
|
30780
|
-
try {
|
|
30781
|
-
if (fs16.existsSync(p)) {
|
|
30782
|
-
const version = (0, import_child_process6.execSync)(`${p} --version 2>/dev/null`, {
|
|
30783
|
-
timeout: 1e4,
|
|
30784
|
-
encoding: "utf-8"
|
|
30785
|
-
}).trim();
|
|
30786
|
-
return { found: true, path: p, version };
|
|
30787
|
-
}
|
|
30788
|
-
} catch {
|
|
30789
|
-
continue;
|
|
30790
|
-
}
|
|
30791
|
-
}
|
|
30792
|
-
return { found: false, path: claudeBin };
|
|
30793
|
-
}
|
|
30794
|
-
}
|
|
30795
|
-
function detectBYOKKeys() {
|
|
30796
|
-
if (process.env.ANTHROPIC_API_KEY) {
|
|
30797
|
-
return { provider: "anthropic-api", key: process.env.ANTHROPIC_API_KEY, source: "env" };
|
|
30798
|
-
}
|
|
30799
|
-
if (process.env.OPENAI_API_KEY) {
|
|
30800
|
-
return { provider: "openai-api", key: process.env.OPENAI_API_KEY, source: "env" };
|
|
30801
|
-
}
|
|
30802
|
-
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY) {
|
|
30803
|
-
return { provider: "google-api", key: process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY, source: "env" };
|
|
30804
|
-
}
|
|
30805
|
-
const creds = loadCredentials2();
|
|
30806
|
-
if (creds.ANTHROPIC_API_KEY) {
|
|
30807
|
-
process.env.ANTHROPIC_API_KEY = creds.ANTHROPIC_API_KEY;
|
|
30808
|
-
return { provider: "anthropic-api", key: creds.ANTHROPIC_API_KEY, source: "credentials" };
|
|
30809
|
-
}
|
|
30810
|
-
if (creds.OPENAI_API_KEY) {
|
|
30811
|
-
process.env.OPENAI_API_KEY = creds.OPENAI_API_KEY;
|
|
30812
|
-
return { provider: "openai-api", key: creds.OPENAI_API_KEY, source: "credentials" };
|
|
30813
|
-
}
|
|
30814
|
-
if (creds.GOOGLE_API_KEY || creds.GEMINI_API_KEY) {
|
|
30815
|
-
const key = creds.GOOGLE_API_KEY || creds.GEMINI_API_KEY;
|
|
30816
|
-
process.env.GOOGLE_API_KEY = key;
|
|
30817
|
-
return { provider: "google-api", key, source: "credentials" };
|
|
30818
|
-
}
|
|
30819
|
-
return null;
|
|
30820
|
-
}
|
|
30821
|
-
function detectProvider(config) {
|
|
30822
|
-
const cli = detectClaudeCLI(config?.claudePath);
|
|
30823
|
-
if (cli.found) {
|
|
30824
|
-
return {
|
|
30825
|
-
provider: "claude-cli",
|
|
30826
|
-
version: cli.version,
|
|
30827
|
-
message: `Found Claude Code CLI${cli.version ? ` (${cli.version})` : ""}. Using your Claude subscription for AI reasoning.`
|
|
30828
|
-
};
|
|
30829
|
-
}
|
|
30830
|
-
const byok = detectBYOKKeys();
|
|
30831
|
-
if (byok) {
|
|
30832
|
-
const providerNames = {
|
|
30833
|
-
"claude-cli": "Claude CLI",
|
|
30834
|
-
"anthropic-api": "Anthropic API",
|
|
30835
|
-
"openai-api": "OpenAI API",
|
|
30836
|
-
"google-api": "Google AI API"
|
|
30837
|
-
};
|
|
30838
|
-
return {
|
|
30839
|
-
provider: byok.provider,
|
|
30840
|
-
message: `Using ${providerNames[byok.provider]} key for AI reasoning.`
|
|
30841
|
-
};
|
|
30842
|
-
}
|
|
30843
|
-
return {
|
|
30844
|
-
provider: "claude-cli",
|
|
30845
|
-
// default, will fail at invocation
|
|
30846
|
-
message: "No AI provider found. Install Claude Code CLI or set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY)."
|
|
30847
|
-
};
|
|
30848
|
-
}
|
|
30849
|
-
var CONFIG_DIR = path11.join(process.env.HOME || "", ".ai-analyst");
|
|
30850
|
-
var PROVIDER_CONFIG_FILE = path11.join(CONFIG_DIR, "provider.json");
|
|
30851
|
-
function loadProviderConfig() {
|
|
30852
|
-
try {
|
|
30853
|
-
if (fs16.existsSync(PROVIDER_CONFIG_FILE)) {
|
|
30854
|
-
return JSON.parse(fs16.readFileSync(PROVIDER_CONFIG_FILE, "utf-8"));
|
|
30855
|
-
}
|
|
30856
|
-
} catch {
|
|
30857
|
-
}
|
|
30858
|
-
return null;
|
|
30859
|
-
}
|
|
30860
|
-
function invokeClaudeCLI(options, claudePath) {
|
|
30861
|
-
const bin = claudePath || "claude";
|
|
30862
|
-
const args2 = ["--print"];
|
|
30863
|
-
if (options.model) {
|
|
30864
|
-
args2.push("--model", options.model);
|
|
30865
|
-
}
|
|
30866
|
-
if (options.jsonSchema) {
|
|
30867
|
-
args2.push("--output-format", "json", "--json-schema", JSON.stringify(options.jsonSchema));
|
|
30868
|
-
}
|
|
30869
|
-
if (options.claudeFlags) {
|
|
30870
|
-
args2.push(...options.claudeFlags);
|
|
30871
|
-
}
|
|
30872
|
-
const useStdin = options.useStdin !== false && options.prompt.length > 1e4;
|
|
30873
|
-
if (!useStdin) {
|
|
30874
|
-
args2.push("-p", options.prompt);
|
|
30875
|
-
}
|
|
30876
|
-
const startTime = Date.now();
|
|
30877
|
-
const timeoutMs = options.timeoutMs || 3e5;
|
|
30878
|
-
return new Promise((resolve3) => {
|
|
30879
|
-
const child = (0, import_child_process6.spawn)(bin, args2, {
|
|
30880
|
-
cwd: options.cwd || process.cwd(),
|
|
30881
|
-
env: process.env,
|
|
30882
|
-
stdio: useStdin ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
|
|
30883
|
-
});
|
|
30884
|
-
let stdout = "";
|
|
30885
|
-
let stderr = "";
|
|
30886
|
-
child.stdout?.on("data", (data) => {
|
|
30887
|
-
stdout += data.toString();
|
|
30888
|
-
});
|
|
30889
|
-
child.stderr?.on("data", (data) => {
|
|
30890
|
-
stderr += data.toString();
|
|
30891
|
-
});
|
|
30892
|
-
if (useStdin && child.stdin) {
|
|
30893
|
-
child.stdin.write(options.prompt);
|
|
30894
|
-
child.stdin.end();
|
|
30895
|
-
}
|
|
30896
|
-
const timeout = setTimeout(() => {
|
|
30897
|
-
child.kill("SIGTERM");
|
|
30898
|
-
resolve3({
|
|
30899
|
-
output: stdout,
|
|
30900
|
-
provider: "claude-cli",
|
|
30901
|
-
durationMs: Date.now() - startTime,
|
|
30902
|
-
error: `Timeout after ${timeoutMs}ms`
|
|
30903
|
-
});
|
|
30904
|
-
}, timeoutMs);
|
|
30905
|
-
child.on("close", (code) => {
|
|
30906
|
-
clearTimeout(timeout);
|
|
30907
|
-
resolve3({
|
|
30908
|
-
output: stdout.trim(),
|
|
30909
|
-
provider: "claude-cli",
|
|
30910
|
-
durationMs: Date.now() - startTime,
|
|
30911
|
-
error: code !== 0 ? `Claude CLI exited with code ${code}: ${stderr}` : void 0
|
|
30912
|
-
});
|
|
30913
|
-
});
|
|
30914
|
-
child.on("error", (err2) => {
|
|
30915
|
-
clearTimeout(timeout);
|
|
30916
|
-
resolve3({
|
|
30917
|
-
output: "",
|
|
30918
|
-
provider: "claude-cli",
|
|
30919
|
-
durationMs: Date.now() - startTime,
|
|
30920
|
-
error: `Failed to spawn Claude CLI: ${err2.message}`
|
|
30921
|
-
});
|
|
30922
|
-
});
|
|
30923
|
-
});
|
|
30924
|
-
}
|
|
30925
|
-
async function invokeAnthropicAPI(options) {
|
|
30926
|
-
const startTime = Date.now();
|
|
30927
|
-
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
30928
|
-
if (!apiKey) {
|
|
30929
|
-
return {
|
|
30930
|
-
output: "",
|
|
30931
|
-
provider: "anthropic-api",
|
|
30932
|
-
durationMs: 0,
|
|
30933
|
-
error: "ANTHROPIC_API_KEY not set"
|
|
30934
|
-
};
|
|
30935
|
-
}
|
|
30936
|
-
try {
|
|
30937
|
-
const model = options.model || "claude-sonnet-4-6";
|
|
30938
|
-
let prompt = options.prompt;
|
|
30939
|
-
if (options.jsonSchema) {
|
|
30940
|
-
prompt += `
|
|
30941
|
-
|
|
30942
|
-
IMPORTANT: Respond with valid JSON matching this schema:
|
|
30943
|
-
${JSON.stringify(options.jsonSchema, null, 2)}
|
|
30944
|
-
|
|
30945
|
-
Output ONLY the JSON object, no markdown code blocks or other text.`;
|
|
30946
|
-
} else if (options.expectJson) {
|
|
30947
|
-
prompt += "\n\nIMPORTANT: Respond with valid JSON only. No markdown code blocks or other text.";
|
|
30948
|
-
}
|
|
30949
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
30950
|
-
method: "POST",
|
|
30951
|
-
headers: {
|
|
30952
|
-
"Content-Type": "application/json",
|
|
30953
|
-
"x-api-key": apiKey,
|
|
30954
|
-
"anthropic-version": "2023-06-01"
|
|
30955
|
-
},
|
|
30956
|
-
body: JSON.stringify({
|
|
30957
|
-
model,
|
|
30958
|
-
max_tokens: 16384,
|
|
30959
|
-
messages: [{ role: "user", content: prompt }]
|
|
30960
|
-
}),
|
|
30961
|
-
signal: AbortSignal.timeout(options.timeoutMs || 3e5)
|
|
30962
|
-
});
|
|
30963
|
-
if (!response.ok) {
|
|
30964
|
-
const errorText = await response.text();
|
|
30965
|
-
return {
|
|
30966
|
-
output: "",
|
|
30967
|
-
provider: "anthropic-api",
|
|
30968
|
-
durationMs: Date.now() - startTime,
|
|
30969
|
-
error: `Anthropic API error ${response.status}: ${errorText}`
|
|
30970
|
-
};
|
|
30971
|
-
}
|
|
30972
|
-
const data = await response.json();
|
|
30973
|
-
const text = data.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
30974
|
-
return {
|
|
30975
|
-
output: text,
|
|
30976
|
-
provider: "anthropic-api",
|
|
30977
|
-
durationMs: Date.now() - startTime
|
|
30978
|
-
};
|
|
30979
|
-
} catch (err2) {
|
|
30980
|
-
return {
|
|
30981
|
-
output: "",
|
|
30982
|
-
provider: "anthropic-api",
|
|
30983
|
-
durationMs: Date.now() - startTime,
|
|
30984
|
-
error: `Anthropic API call failed: ${err2.message}`
|
|
30985
|
-
};
|
|
30986
|
-
}
|
|
30987
|
-
}
|
|
30988
|
-
async function invokeAI(options) {
|
|
30989
|
-
const savedConfig = loadProviderConfig();
|
|
30990
|
-
const detection = detectProvider(savedConfig || void 0);
|
|
30991
|
-
switch (detection.provider) {
|
|
30992
|
-
case "claude-cli":
|
|
30993
|
-
return invokeClaudeCLI(options, savedConfig?.claudePath);
|
|
30994
|
-
case "anthropic-api":
|
|
30995
|
-
return invokeAnthropicAPI(options);
|
|
30996
|
-
case "openai-api":
|
|
30997
|
-
case "google-api":
|
|
30998
|
-
return {
|
|
30999
|
-
output: "",
|
|
31000
|
-
provider: detection.provider,
|
|
31001
|
-
durationMs: 0,
|
|
31002
|
-
error: `${detection.provider} provider not yet implemented. Use Claude CLI or set ANTHROPIC_API_KEY.`
|
|
31003
|
-
};
|
|
31004
|
-
default:
|
|
31005
|
-
return {
|
|
31006
|
-
output: "",
|
|
31007
|
-
provider: "claude-cli",
|
|
31008
|
-
durationMs: 0,
|
|
31009
|
-
error: detection.message
|
|
31010
|
-
};
|
|
31011
|
-
}
|
|
31012
|
-
}
|
|
31013
|
-
function requireClaudeCLI(feature) {
|
|
31014
|
-
const cli = detectClaudeCLI();
|
|
31015
|
-
if (cli.found) {
|
|
31016
|
-
return cli.path;
|
|
31017
|
-
}
|
|
31018
|
-
const featureLabel = feature ? ` (${feature})` : "";
|
|
31019
|
-
const byok = detectBYOKKeys();
|
|
31020
|
-
if (byok) {
|
|
31021
|
-
throw new Error(
|
|
31022
|
-
`This feature${featureLabel} requires Claude Code CLI for file and tool access.
|
|
31023
|
-
Your API key is detected and works for reasoning tasks (heartbeat, idea evaluation),
|
|
31024
|
-
but codebase analysis and implementation need the full CLI.
|
|
31025
|
-
Install it with: npm install -g @anthropic-ai/claude-code`
|
|
31026
|
-
);
|
|
31027
|
-
}
|
|
31028
|
-
throw new Error(
|
|
31029
|
-
`No AI provider found${featureLabel}.
|
|
31030
|
-
Install Claude Code CLI: npm install -g @anthropic-ai/claude-code
|
|
31031
|
-
Or set an API key: export ANTHROPIC_API_KEY=sk-ant-...`
|
|
31032
|
-
);
|
|
31033
|
-
}
|
|
31034
|
-
|
|
31035
|
-
// scripts/lib/social/ai-content-generator.ts
|
|
31249
|
+
init_ai_provider();
|
|
31036
31250
|
function readJsonSafe2(filePath, fallback) {
|
|
31037
31251
|
try {
|
|
31038
31252
|
if (!fs17.existsSync(filePath)) return fallback;
|
|
@@ -33028,10 +33242,15 @@ async function renderCarousel(carousel, format, outputPath, _depsOverride) {
|
|
|
33028
33242
|
console.log(
|
|
33029
33243
|
"[Renderer] Bundling Remotion compositions (first render \u2014 may take ~1-2 min)..."
|
|
33030
33244
|
);
|
|
33031
|
-
|
|
33245
|
+
const publicDir = path14.resolve(path14.dirname(entryPoint), "..", "public");
|
|
33246
|
+
bundleCache = await bundle({ entryPoint, publicDir });
|
|
33032
33247
|
console.log("[Renderer] Bundle complete.");
|
|
33033
33248
|
}
|
|
33034
|
-
const inputProps = {
|
|
33249
|
+
const inputProps = {
|
|
33250
|
+
slides: carousel.slides,
|
|
33251
|
+
format,
|
|
33252
|
+
...carousel.audio ? { audio: carousel.audio } : {}
|
|
33253
|
+
};
|
|
33035
33254
|
const composition = await selectComposition({
|
|
33036
33255
|
serveUrl: bundleCache,
|
|
33037
33256
|
id: compositionId,
|
|
@@ -33307,6 +33526,12 @@ Objection handlers: ${JSON.stringify(copyBank?.objection_handlers ?? [], null, 2
|
|
|
33307
33526
|
## Framework
|
|
33308
33527
|
Use PAS (Problem \u2192 Agitate \u2192 Solution) or AIDA. Start with a hook that names the pain, build tension, present the solution, close with a high-contrast CTA. Each slide displays for ~3 seconds \u2014 text must be short and punchy.
|
|
33309
33528
|
|
|
33529
|
+
## Audio
|
|
33530
|
+
Pick one background music track that fits the angle's emotional tone:
|
|
33531
|
+
- "corporate-upbeat.mp3" \u2014 energetic, confident, high-momentum (good for burnout relief, 10x moment)
|
|
33532
|
+
- "modern-minimal.mp3" \u2014 clean, calm, professional (good for BYOK, fragmented-tools, trust angles)
|
|
33533
|
+
- "tech-innovation.mp3" \u2014 forward-looking, tech-focused (good for cursor-users, autonomous AI, product angles)
|
|
33534
|
+
|
|
33310
33535
|
## Output format
|
|
33311
33536
|
Return ONLY valid JSON matching this exact schema (no markdown, no explanation):
|
|
33312
33537
|
{
|
|
@@ -33316,6 +33541,10 @@ Return ONLY valid JSON matching this exact schema (no markdown, no explanation):
|
|
|
33316
33541
|
"slides": [
|
|
33317
33542
|
{ "type": "<slide_type>", ...slide_props }
|
|
33318
33543
|
],
|
|
33544
|
+
"audio": {
|
|
33545
|
+
"src": "<track filename>",
|
|
33546
|
+
"volume": 0.3
|
|
33547
|
+
},
|
|
33319
33548
|
"theme": {
|
|
33320
33549
|
"background_style": "gradient",
|
|
33321
33550
|
"primary_color": "#6366f1",
|
|
@@ -33350,6 +33579,7 @@ Return ONLY valid JSON matching this exact schema (no markdown, no explanation):
|
|
|
33350
33579
|
{ type: "statistic", title: "Results", value: "10x", label: "faster decisions" },
|
|
33351
33580
|
{ type: "cta", title: "Try VibeBusiness", subtitle: "Free to start", cta_text: "Get Started" }
|
|
33352
33581
|
],
|
|
33582
|
+
audio: { src: "corporate-upbeat.mp3", volume: 0.3 },
|
|
33353
33583
|
theme: { background_style: "gradient", primary_color: "#6366f1", text_color: "#ffffff" }
|
|
33354
33584
|
};
|
|
33355
33585
|
}
|
|
@@ -33740,82 +33970,9 @@ function readCampaignFreshness() {
|
|
|
33740
33970
|
};
|
|
33741
33971
|
}
|
|
33742
33972
|
|
|
33743
|
-
// scripts/
|
|
33744
|
-
|
|
33745
|
-
|
|
33746
|
-
const triggers = [];
|
|
33747
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
33748
|
-
goals.forEach((goal) => {
|
|
33749
|
-
if (goal.status === "achieved") {
|
|
33750
|
-
return;
|
|
33751
|
-
}
|
|
33752
|
-
goal.kpis.forEach((kpi) => {
|
|
33753
|
-
const stagnation = detectKPIStagnation(kpi, STAGNATION_WINDOW_DAYS);
|
|
33754
|
-
if (stagnation && goal.status === "behind") {
|
|
33755
|
-
triggers.push({
|
|
33756
|
-
id: `trigger-stagnation-${goal.id}-${kpi.id}`,
|
|
33757
|
-
type: "kpi_stagnation",
|
|
33758
|
-
severity: "urgent",
|
|
33759
|
-
description: `KPI "${kpi.name}" has been stagnant at ${kpi.current_value} for ${stagnation.days} days while goal "${goal.title}" is behind schedule`,
|
|
33760
|
-
affected_goal_ids: [goal.id],
|
|
33761
|
-
suggested_action: `Launch marketing campaign to move ${kpi.name} from ${kpi.current_value} toward target of ${kpi.target_value}`,
|
|
33762
|
-
detected_at: now
|
|
33763
|
-
});
|
|
33764
|
-
}
|
|
33765
|
-
});
|
|
33766
|
-
if (previousGoals) {
|
|
33767
|
-
const previousGoal = previousGoals.find((g2) => g2.id === goal.id);
|
|
33768
|
-
if (previousGoal && previousGoal.status === "on_track" && goal.status === "behind") {
|
|
33769
|
-
triggers.push({
|
|
33770
|
-
id: `trigger-regression-${goal.id}`,
|
|
33771
|
-
type: "goal_regression",
|
|
33772
|
-
severity: "high",
|
|
33773
|
-
description: `Goal "${goal.title}" regressed from "on_track" to "behind" schedule`,
|
|
33774
|
-
affected_goal_ids: [goal.id],
|
|
33775
|
-
suggested_action: `Analyze why goal regressed and launch recovery campaign`,
|
|
33776
|
-
detected_at: now
|
|
33777
|
-
});
|
|
33778
|
-
}
|
|
33779
|
-
}
|
|
33780
|
-
});
|
|
33781
|
-
return triggers;
|
|
33782
|
-
}
|
|
33783
|
-
function detectKPIStagnation(kpi, windowDays) {
|
|
33784
|
-
if (kpi.history.length < 2) {
|
|
33785
|
-
return null;
|
|
33786
|
-
}
|
|
33787
|
-
const sortedHistory = [...kpi.history].sort(
|
|
33788
|
-
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
33789
|
-
);
|
|
33790
|
-
const latestValue = sortedHistory[0].value;
|
|
33791
|
-
const latestDate = new Date(sortedHistory[0].date);
|
|
33792
|
-
const windowStart = /* @__PURE__ */ new Date();
|
|
33793
|
-
windowStart.setDate(windowStart.getDate() - windowDays);
|
|
33794
|
-
let isStagnant = true;
|
|
33795
|
-
let lastChangeDate = sortedHistory[0].date;
|
|
33796
|
-
for (let i = 0; i < sortedHistory.length; i++) {
|
|
33797
|
-
const entry = sortedHistory[i];
|
|
33798
|
-
const entryDate = new Date(entry.date);
|
|
33799
|
-
if (entryDate < windowStart) {
|
|
33800
|
-
break;
|
|
33801
|
-
}
|
|
33802
|
-
if (entry.value !== latestValue) {
|
|
33803
|
-
isStagnant = false;
|
|
33804
|
-
lastChangeDate = sortedHistory[i - 1]?.date || entry.date;
|
|
33805
|
-
break;
|
|
33806
|
-
}
|
|
33807
|
-
}
|
|
33808
|
-
if (isStagnant && latestDate > windowStart) {
|
|
33809
|
-
const daysDiff = Math.floor(
|
|
33810
|
-
(Date.now() - new Date(lastChangeDate).getTime()) / (1e3 * 60 * 60 * 24)
|
|
33811
|
-
);
|
|
33812
|
-
return {
|
|
33813
|
-
days: daysDiff,
|
|
33814
|
-
last_change_date: lastChangeDate
|
|
33815
|
-
};
|
|
33816
|
-
}
|
|
33817
|
-
return null;
|
|
33818
|
-
}
|
|
33973
|
+
// scripts/heartbeat.ts
|
|
33974
|
+
init_marketing_strategy();
|
|
33975
|
+
init_ai_provider();
|
|
33819
33976
|
|
|
33820
33977
|
// scripts/lib/run.ts
|
|
33821
33978
|
var path18 = __toESM(require("path"));
|
|
@@ -34714,7 +34871,7 @@ function sleep(ms2) {
|
|
|
34714
34871
|
}
|
|
34715
34872
|
function loadJson(filePath, defaultValue) {
|
|
34716
34873
|
try {
|
|
34717
|
-
const content =
|
|
34874
|
+
const content = fs34.readFileSync(filePath, "utf-8");
|
|
34718
34875
|
return JSON.parse(content);
|
|
34719
34876
|
} catch {
|
|
34720
34877
|
return defaultValue;
|
|
@@ -34769,7 +34926,7 @@ function isMetaTaskFalseCompletion(output) {
|
|
|
34769
34926
|
}
|
|
34770
34927
|
function reclassifyAsHumanDependent(taskId) {
|
|
34771
34928
|
try {
|
|
34772
|
-
let content =
|
|
34929
|
+
let content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
34773
34930
|
const taskPattern = new RegExp(`^- \\[[ x]\\] \`${taskId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\`.*$`, "m");
|
|
34774
34931
|
const match = content.match(taskPattern);
|
|
34775
34932
|
if (!match) return;
|
|
@@ -34782,7 +34939,7 @@ function reclassifyAsHumanDependent(taskId) {
|
|
|
34782
34939
|
const insertIndex = content.indexOf("\n", blockedIndex) + 1;
|
|
34783
34940
|
content = content.slice(0, insertIndex) + "\n" + uncheckedLine + content.slice(insertIndex);
|
|
34784
34941
|
}
|
|
34785
|
-
|
|
34942
|
+
fs34.writeFileSync(TODO_FILE, content);
|
|
34786
34943
|
log(`Reclassified task ${taskId} as human-dependent (moved to Blocked)`);
|
|
34787
34944
|
} catch (error) {
|
|
34788
34945
|
log(`Failed to reclassify task: ${error instanceof Error ? error.message : "Unknown"}`);
|
|
@@ -34790,7 +34947,7 @@ function reclassifyAsHumanDependent(taskId) {
|
|
|
34790
34947
|
}
|
|
34791
34948
|
function getMetaTaskFailureCount(taskId) {
|
|
34792
34949
|
try {
|
|
34793
|
-
const content =
|
|
34950
|
+
const content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
34794
34951
|
const escapedId = taskId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
34795
34952
|
const match = content.match(new RegExp(`\`${escapedId}\`.*\\[failed:(\\d+)\\]`));
|
|
34796
34953
|
return match ? parseInt(match[1], 10) : 0;
|
|
@@ -34800,7 +34957,7 @@ function getMetaTaskFailureCount(taskId) {
|
|
|
34800
34957
|
}
|
|
34801
34958
|
function incrementMetaTaskFailureCount(taskId) {
|
|
34802
34959
|
try {
|
|
34803
|
-
let content =
|
|
34960
|
+
let content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
34804
34961
|
const escapedId = taskId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
34805
34962
|
const linePattern = new RegExp(`^(- \\[[ x]\\] \`${escapedId}\`.*)$`, "m");
|
|
34806
34963
|
const lineMatch = content.match(linePattern);
|
|
@@ -34816,7 +34973,7 @@ function incrementMetaTaskFailureCount(taskId) {
|
|
|
34816
34973
|
newLine = `${line} [failed:${newCount}]`;
|
|
34817
34974
|
}
|
|
34818
34975
|
content = content.replace(line, newLine);
|
|
34819
|
-
|
|
34976
|
+
fs34.writeFileSync(TODO_FILE, content);
|
|
34820
34977
|
log(`Meta-task ${taskId} failure count: ${newCount}`);
|
|
34821
34978
|
return newCount;
|
|
34822
34979
|
} catch (error) {
|
|
@@ -34826,7 +34983,7 @@ function incrementMetaTaskFailureCount(taskId) {
|
|
|
34826
34983
|
}
|
|
34827
34984
|
function parseTodoFile() {
|
|
34828
34985
|
try {
|
|
34829
|
-
const content =
|
|
34986
|
+
const content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
34830
34987
|
const tasks = [];
|
|
34831
34988
|
let currentSection = "high_priority";
|
|
34832
34989
|
const lines = content.split("\n");
|
|
@@ -35247,18 +35404,18 @@ function autoRecoverDeferredParseFailures() {
|
|
|
35247
35404
|
async function generateMissingShipCards(ideas) {
|
|
35248
35405
|
const shippedIdeas = ideas.filter((i) => i.stage === "shipped");
|
|
35249
35406
|
if (shippedIdeas.length === 0) return;
|
|
35250
|
-
const assetsDir =
|
|
35251
|
-
const hasRegular =
|
|
35252
|
-
const hasBold =
|
|
35407
|
+
const assetsDir = path29.join(__dirname, "assets");
|
|
35408
|
+
const hasRegular = fs34.existsSync(path29.join(assetsDir, "Inter-Regular.ttf"));
|
|
35409
|
+
const hasBold = fs34.existsSync(path29.join(assetsDir, "Inter-Bold.ttf"));
|
|
35253
35410
|
if (!hasRegular && !hasBold) return;
|
|
35254
|
-
const visualsDir =
|
|
35411
|
+
const visualsDir = path29.join(DATA_DIR, "reports", "visuals");
|
|
35255
35412
|
let generated = 0;
|
|
35256
35413
|
for (const idea of shippedIdeas) {
|
|
35257
|
-
const cardPath =
|
|
35258
|
-
if (
|
|
35414
|
+
const cardPath = path29.join(visualsDir, `${idea.id}-card.png`);
|
|
35415
|
+
if (fs34.existsSync(cardPath)) continue;
|
|
35259
35416
|
if (idea.implementation?.preview_url) {
|
|
35260
|
-
const screenshotPath =
|
|
35261
|
-
if (!
|
|
35417
|
+
const screenshotPath = path29.join(DATA_DIR, "reports", `${idea.id}-screenshot.png`);
|
|
35418
|
+
if (!fs34.existsSync(screenshotPath)) {
|
|
35262
35419
|
try {
|
|
35263
35420
|
const { captureIdeaScreenshot: captureIdeaScreenshot2 } = await Promise.resolve().then(() => (init_screenshot(), screenshot_exports));
|
|
35264
35421
|
await captureIdeaScreenshot2(idea.id, idea.implementation.preview_url);
|
|
@@ -35359,11 +35516,37 @@ function detectMarketingTriggersIfNeeded(goals) {
|
|
|
35359
35516
|
}
|
|
35360
35517
|
return detectMarketingTriggers(goals);
|
|
35361
35518
|
}
|
|
35519
|
+
async function syncMarketingPlansToIdeas() {
|
|
35520
|
+
const { addMarketingIdeasToDashboard: addMarketingIdeasToDashboard2 } = await Promise.resolve().then(() => (init_marketing_integration(), marketing_integration_exports));
|
|
35521
|
+
const plansDir = path29.join(DATA_DIR, "marketing-plans");
|
|
35522
|
+
if (!fs34.existsSync(plansDir)) {
|
|
35523
|
+
return;
|
|
35524
|
+
}
|
|
35525
|
+
const planFiles = fs34.readdirSync(plansDir).filter((f) => f.endsWith(".json"));
|
|
35526
|
+
let totalAdded = 0;
|
|
35527
|
+
let totalSkipped = 0;
|
|
35528
|
+
for (const file of planFiles) {
|
|
35529
|
+
try {
|
|
35530
|
+
const planPath = path29.join(plansDir, file);
|
|
35531
|
+
const plan = JSON.parse(fs34.readFileSync(planPath, "utf-8"));
|
|
35532
|
+
if (plan.status === "approved" || plan.status === "active") {
|
|
35533
|
+
const result = await addMarketingIdeasToDashboard2(plan, DATA_DIR);
|
|
35534
|
+
totalAdded += result.added;
|
|
35535
|
+
totalSkipped += result.skipped;
|
|
35536
|
+
}
|
|
35537
|
+
} catch (err2) {
|
|
35538
|
+
log(`Failed to sync marketing plan ${file}: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
|
|
35539
|
+
}
|
|
35540
|
+
}
|
|
35541
|
+
if (totalAdded > 0) {
|
|
35542
|
+
log(`\u2713 Added ${totalAdded} marketing campaign ideas to dashboard (${totalSkipped} already existed)`);
|
|
35543
|
+
}
|
|
35544
|
+
}
|
|
35362
35545
|
function loadCodebaseSnapshot() {
|
|
35363
|
-
const snapshotPath =
|
|
35546
|
+
const snapshotPath = path29.join(DATA_DIR, "codebase-snapshot.json");
|
|
35364
35547
|
try {
|
|
35365
|
-
if (
|
|
35366
|
-
return JSON.parse(
|
|
35548
|
+
if (fs34.existsSync(snapshotPath)) {
|
|
35549
|
+
return JSON.parse(fs34.readFileSync(snapshotPath, "utf-8"));
|
|
35367
35550
|
}
|
|
35368
35551
|
} catch {
|
|
35369
35552
|
}
|
|
@@ -35918,9 +36101,9 @@ Focus on actionable, specific recommendations. Be concise.`;
|
|
|
35918
36101
|
}
|
|
35919
36102
|
function addTasksToTodo(tasks) {
|
|
35920
36103
|
if (tasks.length === 0) return;
|
|
35921
|
-
if (!
|
|
36104
|
+
if (!fs34.existsSync(TODO_FILE)) return;
|
|
35922
36105
|
try {
|
|
35923
|
-
let content =
|
|
36106
|
+
let content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
35924
36107
|
if (!content.includes("## High Priority (Do Now)")) {
|
|
35925
36108
|
log('TODO.md missing "## High Priority (Do Now)" section \u2014 creating it');
|
|
35926
36109
|
const scheduledIdx = content.indexOf("## Scheduled");
|
|
@@ -36020,15 +36203,15 @@ function addTasksToTodo(tasks) {
|
|
|
36020
36203
|
}
|
|
36021
36204
|
log(`Added TODO: ${task.id} \u2014 ${task.description}`);
|
|
36022
36205
|
}
|
|
36023
|
-
|
|
36206
|
+
fs34.writeFileSync(TODO_FILE, content);
|
|
36024
36207
|
} catch (error) {
|
|
36025
36208
|
log(`Failed to add tasks to TODO.md: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
36026
36209
|
}
|
|
36027
36210
|
}
|
|
36028
36211
|
function archiveCompletedTodos() {
|
|
36029
36212
|
try {
|
|
36030
|
-
if (!
|
|
36031
|
-
let content =
|
|
36213
|
+
if (!fs34.existsSync(TODO_FILE)) return;
|
|
36214
|
+
let content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
36032
36215
|
const lines = content.split("\n");
|
|
36033
36216
|
const completedLines = [];
|
|
36034
36217
|
const newLines = [];
|
|
@@ -36054,7 +36237,7 @@ function archiveCompletedTodos() {
|
|
|
36054
36237
|
const archiveBlock = completedLines.join("\n") + "\n";
|
|
36055
36238
|
content = content.slice(0, insertIndex) + archiveBlock + content.slice(insertIndex);
|
|
36056
36239
|
}
|
|
36057
|
-
|
|
36240
|
+
fs34.writeFileSync(TODO_FILE, content);
|
|
36058
36241
|
log(`Archived ${completedLines.length} completed TODO(s) to Completed This Week`);
|
|
36059
36242
|
} catch (error) {
|
|
36060
36243
|
log(`Failed to archive completed TODOs: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -36062,8 +36245,8 @@ function archiveCompletedTodos() {
|
|
|
36062
36245
|
}
|
|
36063
36246
|
function pruneStaleScheduledTodos(state) {
|
|
36064
36247
|
try {
|
|
36065
|
-
if (!
|
|
36066
|
-
const content =
|
|
36248
|
+
if (!fs34.existsSync(TODO_FILE)) return;
|
|
36249
|
+
const content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
36067
36250
|
const lines = content.split("\n");
|
|
36068
36251
|
const prunedIds = [];
|
|
36069
36252
|
const terminalIdeaIds = new Set(
|
|
@@ -36099,7 +36282,7 @@ function pruneStaleScheduledTodos(state) {
|
|
|
36099
36282
|
return true;
|
|
36100
36283
|
});
|
|
36101
36284
|
if (prunedIds.length === 0) return;
|
|
36102
|
-
|
|
36285
|
+
fs34.writeFileSync(TODO_FILE, newLines.join("\n"));
|
|
36103
36286
|
log(`Pruned ${prunedIds.length} stale TODO(s): ${prunedIds.join(", ")}`);
|
|
36104
36287
|
} catch (error) {
|
|
36105
36288
|
log(`Failed to prune stale TODOs: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -36107,8 +36290,8 @@ function pruneStaleScheduledTodos(state) {
|
|
|
36107
36290
|
}
|
|
36108
36291
|
function deduplicateActiveTodos() {
|
|
36109
36292
|
try {
|
|
36110
|
-
if (!
|
|
36111
|
-
const content =
|
|
36293
|
+
if (!fs34.existsSync(TODO_FILE)) return;
|
|
36294
|
+
const content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
36112
36295
|
const lines = content.split("\n");
|
|
36113
36296
|
const removedIds = [];
|
|
36114
36297
|
let currentSection = "";
|
|
@@ -36145,7 +36328,7 @@ function deduplicateActiveTodos() {
|
|
|
36145
36328
|
if (linesToRemove.size === 0) return;
|
|
36146
36329
|
const newLines = lines.filter((_, i) => !linesToRemove.has(i));
|
|
36147
36330
|
const cleaned = newLines.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
36148
|
-
|
|
36331
|
+
fs34.writeFileSync(TODO_FILE, cleaned);
|
|
36149
36332
|
log(`Deduplicated ${removedIds.length} TODO(s): ${removedIds.join(", ")}`);
|
|
36150
36333
|
} catch (error) {
|
|
36151
36334
|
log(`Failed to deduplicate TODOs: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -36153,8 +36336,8 @@ function deduplicateActiveTodos() {
|
|
|
36153
36336
|
}
|
|
36154
36337
|
function readMemoryContext() {
|
|
36155
36338
|
try {
|
|
36156
|
-
if (!
|
|
36157
|
-
const content =
|
|
36339
|
+
if (!fs34.existsSync(MEMORY_FILE)) return "";
|
|
36340
|
+
const content = fs34.readFileSync(MEMORY_FILE, "utf-8");
|
|
36158
36341
|
const MAX_TOTAL_LINES = 100;
|
|
36159
36342
|
const MIN_LEARNING_ENTRIES = 15;
|
|
36160
36343
|
const learningsSplit = content.indexOf("### Heartbeat Learnings");
|
|
@@ -36175,14 +36358,14 @@ function readMemoryContext() {
|
|
|
36175
36358
|
}
|
|
36176
36359
|
function appendToMemory(learnings) {
|
|
36177
36360
|
if (learnings.length === 0) return;
|
|
36178
|
-
if (!
|
|
36361
|
+
if (!fs34.existsSync(MEMORY_FILE)) return;
|
|
36179
36362
|
const MAX_LEARNINGS_PER_HEARTBEAT = 2;
|
|
36180
36363
|
let filtered = learnings.slice(0, MAX_LEARNINGS_PER_HEARTBEAT);
|
|
36181
36364
|
if (learnings.length > MAX_LEARNINGS_PER_HEARTBEAT) {
|
|
36182
36365
|
log(`Capped learnings from ${learnings.length} to ${MAX_LEARNINGS_PER_HEARTBEAT}`);
|
|
36183
36366
|
}
|
|
36184
36367
|
try {
|
|
36185
|
-
let content =
|
|
36368
|
+
let content = fs34.readFileSync(MEMORY_FILE, "utf-8");
|
|
36186
36369
|
const existingLines = content.split("\n").filter((l2) => l2.match(/^- \*\*/));
|
|
36187
36370
|
const KEY_PHRASE_PATTERN = /(?:idea-[a-f0-9]{8}|idea-[a-z0-9-]+|hyp-[a-z0-9-]+|goal-[a-z0-9-]+|pipeline starvation|critical path|batch triage|cooldown|decompos|timeout|inbox bloat|dedup|meta-task|heartbeat|autonomy gate|false completion)/gi;
|
|
36188
36371
|
filtered = filtered.filter((learning) => {
|
|
@@ -36232,7 +36415,7 @@ function appendToMemory(learnings) {
|
|
|
36232
36415
|
const subsectionEnd = subsectionEndMatch ? subsectionIndex + 22 + (subsectionEndMatch.index || 0) : insertPoint;
|
|
36233
36416
|
content = content.slice(0, subsectionEnd) + newEntries + "\n" + content.slice(subsectionEnd);
|
|
36234
36417
|
}
|
|
36235
|
-
|
|
36418
|
+
fs34.writeFileSync(MEMORY_FILE, content);
|
|
36236
36419
|
log(`Added ${filtered.length} learning(s) to MEMORY.md`);
|
|
36237
36420
|
}
|
|
36238
36421
|
} catch (error) {
|
|
@@ -36241,8 +36424,8 @@ function appendToMemory(learnings) {
|
|
|
36241
36424
|
}
|
|
36242
36425
|
function pruneMemoryLearnings() {
|
|
36243
36426
|
try {
|
|
36244
|
-
if (!
|
|
36245
|
-
const content =
|
|
36427
|
+
if (!fs34.existsSync(MEMORY_FILE)) return;
|
|
36428
|
+
const content = fs34.readFileSync(MEMORY_FILE, "utf-8");
|
|
36246
36429
|
const learningsHeader = "### Heartbeat Learnings";
|
|
36247
36430
|
const learningsIdx = content.indexOf(learningsHeader);
|
|
36248
36431
|
if (learningsIdx === -1) return;
|
|
@@ -36278,7 +36461,7 @@ function pruneMemoryLearnings() {
|
|
|
36278
36461
|
if (prunedCount === 0) return;
|
|
36279
36462
|
const newLearningsBody = headerLine + "\n\n" + finalEntries.join("\n") + "\n";
|
|
36280
36463
|
const newContent = beforeLearnings + newLearningsBody + afterLearnings;
|
|
36281
|
-
|
|
36464
|
+
fs34.writeFileSync(MEMORY_FILE, newContent);
|
|
36282
36465
|
log(`Pruned ${prunedCount} duplicate/stale learnings (${entryLines.length} \u2192 ${finalEntries.length})`);
|
|
36283
36466
|
} catch (error) {
|
|
36284
36467
|
log(`Failed to prune memory learnings: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -36397,7 +36580,7 @@ function detectTargetRepo(idea, config) {
|
|
|
36397
36580
|
const filesAnalyzed = idea.source.files_analyzed || [];
|
|
36398
36581
|
for (const file of filesAnalyzed) {
|
|
36399
36582
|
for (const repo of config.repos) {
|
|
36400
|
-
if (file.includes(repo.name) || file.includes(
|
|
36583
|
+
if (file.includes(repo.name) || file.includes(path29.basename(repo.path))) {
|
|
36401
36584
|
return repo;
|
|
36402
36585
|
}
|
|
36403
36586
|
}
|
|
@@ -36690,10 +36873,10 @@ function runTestsForRepo(workspacePath, testCommands, sourceRepoPath) {
|
|
|
36690
36873
|
const allOutput = [];
|
|
36691
36874
|
const env = { ...process.env };
|
|
36692
36875
|
if (sourceRepoPath) {
|
|
36693
|
-
const venvBin =
|
|
36694
|
-
if (
|
|
36876
|
+
const venvBin = path29.join(sourceRepoPath, ".venv", "bin");
|
|
36877
|
+
if (fs34.existsSync(venvBin)) {
|
|
36695
36878
|
env.PATH = `${venvBin}:${env.PATH}`;
|
|
36696
|
-
env.VIRTUAL_ENV =
|
|
36879
|
+
env.VIRTUAL_ENV = path29.join(sourceRepoPath, ".venv");
|
|
36697
36880
|
log(`Using venv from ${sourceRepoPath}/.venv`);
|
|
36698
36881
|
}
|
|
36699
36882
|
}
|
|
@@ -36746,7 +36929,7 @@ async function executeSingleSubTask(subTask, ideaId, idea, ideasData, autonomy,
|
|
|
36746
36929
|
});
|
|
36747
36930
|
const scopeBase64 = Buffer.from(scopePayload).toString("base64");
|
|
36748
36931
|
const timeoutMs = autonomy.max_sub_task_timeout_ms || 3e5;
|
|
36749
|
-
const workspacePath = options?.worktreePath ||
|
|
36932
|
+
const workspacePath = options?.worktreePath || path29.join(config.workspace_dir, targetRepo.name);
|
|
36750
36933
|
const executionStartTime = Date.now();
|
|
36751
36934
|
const updateSubTaskWithDetails = (status, updateOpts = {}) => {
|
|
36752
36935
|
const endTime = Date.now();
|
|
@@ -36988,7 +37171,7 @@ async function executeAutonomousImplementation(ideaId) {
|
|
|
36988
37171
|
if (autonomy.auto_merge) {
|
|
36989
37172
|
const branchName2 = idea.implementation.branch_name;
|
|
36990
37173
|
const defaultBranch2 = targetRepo2.default_branch || "main";
|
|
36991
|
-
const workspacePath =
|
|
37174
|
+
const workspacePath = path29.join(config.workspace_dir, targetRepo2.name);
|
|
36992
37175
|
if (branchName2) {
|
|
36993
37176
|
try {
|
|
36994
37177
|
log(`Auto-merging branch ${branchName2} into ${defaultBranch2}...`);
|
|
@@ -38571,7 +38754,7 @@ async function runAnalysis(type) {
|
|
|
38571
38754
|
}
|
|
38572
38755
|
function updateTodoFile(taskId, completed) {
|
|
38573
38756
|
try {
|
|
38574
|
-
let content =
|
|
38757
|
+
let content = fs34.readFileSync(TODO_FILE, "utf-8");
|
|
38575
38758
|
const uncheckedPattern = new RegExp(`- \\[ \\] \`${taskId}\``, "g");
|
|
38576
38759
|
const checkedPattern = new RegExp(`- \\[x\\] \`${taskId}\``, "g");
|
|
38577
38760
|
if (completed) {
|
|
@@ -38579,7 +38762,7 @@ function updateTodoFile(taskId, completed) {
|
|
|
38579
38762
|
} else {
|
|
38580
38763
|
content = content.replace(checkedPattern, `- [ ] \`${taskId}\``);
|
|
38581
38764
|
}
|
|
38582
|
-
|
|
38765
|
+
fs34.writeFileSync(TODO_FILE, content);
|
|
38583
38766
|
log(`Updated TODO.md: ${taskId} marked as ${completed ? "completed" : "pending"}`);
|
|
38584
38767
|
} catch (error) {
|
|
38585
38768
|
log(`Failed to update TODO.md: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -38698,8 +38881,8 @@ async function runSingleHeartbeat(beatNumber = 1) {
|
|
|
38698
38881
|
if (vibeResult.warning) log(vibeResult.warning);
|
|
38699
38882
|
log(`Vibe consumed (${vibeResult.vibes_remaining} remaining${vibeResult.plan ? `, plan: ${vibeResult.plan}` : ""}${vibeResult.offline ? ", offline" : ""})`);
|
|
38700
38883
|
}
|
|
38701
|
-
const commandsDir =
|
|
38702
|
-
if (!
|
|
38884
|
+
const commandsDir = path29.join(PROJECT_DIR, ".claude", "commands");
|
|
38885
|
+
if (!fs34.existsSync(commandsDir)) {
|
|
38703
38886
|
log("Slash commands missing \u2014 auto-scaffolding...");
|
|
38704
38887
|
scaffoldSlashCommands(PROJECT_DIR);
|
|
38705
38888
|
}
|
|
@@ -38760,6 +38943,9 @@ async function runSingleHeartbeat(beatNumber = 1) {
|
|
|
38760
38943
|
alerts.push(`MARKETING: ${t.description}`);
|
|
38761
38944
|
});
|
|
38762
38945
|
}
|
|
38946
|
+
if (!DRY_RUN) {
|
|
38947
|
+
await syncMarketingPlansToIdeas();
|
|
38948
|
+
}
|
|
38763
38949
|
if (!DRY_RUN) {
|
|
38764
38950
|
await generateMissingShipCards(state.ideas);
|
|
38765
38951
|
}
|
|
@@ -38909,7 +39095,7 @@ async function runSingleHeartbeat(beatNumber = 1) {
|
|
|
38909
39095
|
}
|
|
38910
39096
|
if (!DRY_RUN) {
|
|
38911
39097
|
const statusContent = generateStatusContent(state, alerts, taskToDo, reasoning, systemHealth);
|
|
38912
|
-
|
|
39098
|
+
fs34.writeFileSync(STATUS_FILE, statusContent);
|
|
38913
39099
|
log("Updated STATUS.md");
|
|
38914
39100
|
} else {
|
|
38915
39101
|
log("[DRY RUN] Would update STATUS.md");
|
|
@@ -39024,7 +39210,7 @@ ${"=".repeat(60)}
|
|
|
39024
39210
|
`);
|
|
39025
39211
|
}
|
|
39026
39212
|
function saveSessionToJson(summary) {
|
|
39027
|
-
const sessionsFile =
|
|
39213
|
+
const sessionsFile = path29.join(DATA_DIR, "heartbeat-sessions.json");
|
|
39028
39214
|
const sessionsData = loadJson(sessionsFile, { sessions: [] });
|
|
39029
39215
|
sessionsData.sessions.push(summary);
|
|
39030
39216
|
if (sessionsData.sessions.length > 50) {
|