vibebusiness 1.2.89 → 1.2.91
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 +37 -37
- 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/briefing/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 +4 -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/briefing/page_client-reference-manifest.js +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.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/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +1 -1
- package/.next/standalone/.next/server/app/kanban/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/kanban/page_client-reference-manifest.js +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 +2 -2
- 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 +2 -2
- 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 +17 -17
- package/.next/standalone/.next/server/chunks/7151.js +7 -7
- 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/pages-manifest.json +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/data/codebase-snapshot.json +44 -61
- package/.next/standalone/data/ideas.json +729 -0
- package/.next/standalone/data/sessions.json +32 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/analyze.ts +35 -4
- package/.next/standalone/templates/commands/growth-activate.md +64 -0
- package/.next/standalone/templates/commands/vision.md +68 -0
- package/.next/static/chunks/147-271b001e9a7f5e6c.js +1 -0
- package/.next/static/chunks/159-82cfa8b54cbd2c11.js +1 -0
- package/.next/static/chunks/47-bab84ed5cfb8fd9d.js +1 -0
- package/.next/static/chunks/879-ae588e66e8e0b1d0.js +1 -0
- package/.next/static/chunks/app/briefing/page-a38c83adfb5ef570.js +1 -0
- package/.next/static/chunks/app/goals/[id]/page-ce4e225f25e7294d.js +1 -0
- package/.next/static/chunks/app/ideas/[id]/page-6b28cf279c62a569.js +1 -0
- package/.next/static/chunks/app/roadmap/[id]/page-381809badf3910a3.js +1 -0
- package/.next/static/chunks/app/social/page-2d71af5da2476063.js +1 -0
- package/.next/static/chunks/app/updates/new/page-0acea8b7d66043a5.js +1 -0
- package/dist/scripts/analyze.js +525 -76
- package/dist/scripts/chat.js +10 -0
- package/dist/scripts/generate-idea.js +3 -0
- package/dist/scripts/heartbeat.js +1896 -353
- package/dist/scripts/implement.js +3 -0
- package/dist/scripts/init.js +3 -0
- package/dist/scripts/scan.js +3 -0
- package/dist/scripts/social-routine.js +3 -0
- package/package.json +1 -1
- package/templates/commands/growth-activate.md +64 -0
- package/templates/commands/vision.md +68 -0
- package/.next/static/chunks/147-b00f2ac2bbec93ae.js +0 -1
- package/.next/static/chunks/159-4ce492ccac6de8f7.js +0 -1
- package/.next/static/chunks/47-eba0f8b4f9b17641.js +0 -1
- package/.next/static/chunks/879-7fbd95e93ddc4636.js +0 -1
- package/.next/static/chunks/app/briefing/page-683aba0c52e910d7.js +0 -1
- package/.next/static/chunks/app/goals/[id]/page-40818dc7e710eeda.js +0 -1
- package/.next/static/chunks/app/ideas/[id]/page-5f33e0ecf590ddec.js +0 -1
- package/.next/static/chunks/app/roadmap/[id]/page-9ba8a537e30c633c.js +0 -1
- package/.next/static/chunks/app/social/page-21daeca0cf8af46b.js +0 -1
- package/.next/static/chunks/app/updates/new/page-ac5b966024ce0ddc.js +0 -1
- /package/.next/static/{UaMf1fmArAE78vyk_zQVK → ixOS0rCuseEALBC-pPhha}/_buildManifest.js +0 -0
- /package/.next/static/{UaMf1fmArAE78vyk_zQVK → ixOS0rCuseEALBC-pPhha}/_ssgManifest.js +0 -0
package/dist/scripts/analyze.js
CHANGED
|
@@ -103,7 +103,7 @@ var require_package = __commonJS({
|
|
|
103
103
|
var require_main = __commonJS({
|
|
104
104
|
"node_modules/dotenv/lib/main.js"(exports2, module2) {
|
|
105
105
|
"use strict";
|
|
106
|
-
var
|
|
106
|
+
var fs7 = require("fs");
|
|
107
107
|
var path4 = require("path");
|
|
108
108
|
var os = require("os");
|
|
109
109
|
var crypto3 = require("crypto");
|
|
@@ -242,7 +242,7 @@ var require_main = __commonJS({
|
|
|
242
242
|
if (options && options.path && options.path.length > 0) {
|
|
243
243
|
if (Array.isArray(options.path)) {
|
|
244
244
|
for (const filepath of options.path) {
|
|
245
|
-
if (
|
|
245
|
+
if (fs7.existsSync(filepath)) {
|
|
246
246
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
247
247
|
}
|
|
248
248
|
}
|
|
@@ -252,7 +252,7 @@ var require_main = __commonJS({
|
|
|
252
252
|
} else {
|
|
253
253
|
possibleVaultPath = path4.resolve(process.cwd(), ".env.vault");
|
|
254
254
|
}
|
|
255
|
-
if (
|
|
255
|
+
if (fs7.existsSync(possibleVaultPath)) {
|
|
256
256
|
return possibleVaultPath;
|
|
257
257
|
}
|
|
258
258
|
return null;
|
|
@@ -305,7 +305,7 @@ var require_main = __commonJS({
|
|
|
305
305
|
const parsedAll = {};
|
|
306
306
|
for (const path5 of optionPaths) {
|
|
307
307
|
try {
|
|
308
|
-
const parsed = DotenvModule.parse(
|
|
308
|
+
const parsed = DotenvModule.parse(fs7.readFileSync(path5, { encoding }));
|
|
309
309
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
310
310
|
} catch (e) {
|
|
311
311
|
if (debug) {
|
|
@@ -494,7 +494,7 @@ module.exports = __toCommonJS(analyze_exports);
|
|
|
494
494
|
|
|
495
495
|
// scripts/analyze.ts
|
|
496
496
|
var import_child_process2 = require("child_process");
|
|
497
|
-
var
|
|
497
|
+
var fs6 = __toESM(require("fs"));
|
|
498
498
|
var path3 = __toESM(require("path"));
|
|
499
499
|
|
|
500
500
|
// node_modules/uuid/dist/esm-node/rng.js
|
|
@@ -1241,8 +1241,198 @@ function detectBYOKKeys() {
|
|
|
1241
1241
|
}
|
|
1242
1242
|
return null;
|
|
1243
1243
|
}
|
|
1244
|
+
function detectProvider(config) {
|
|
1245
|
+
const cli = detectClaudeCLI(config?.claudePath);
|
|
1246
|
+
if (cli.found) {
|
|
1247
|
+
return {
|
|
1248
|
+
provider: "claude-cli",
|
|
1249
|
+
version: cli.version,
|
|
1250
|
+
message: `Found Claude Code CLI${cli.version ? ` (${cli.version})` : ""}. Using your Claude subscription for AI reasoning.`
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
const byok = detectBYOKKeys();
|
|
1254
|
+
if (byok) {
|
|
1255
|
+
const providerNames = {
|
|
1256
|
+
"claude-cli": "Claude CLI",
|
|
1257
|
+
"anthropic-api": "Anthropic API",
|
|
1258
|
+
"openai-api": "OpenAI API",
|
|
1259
|
+
"google-api": "Google AI API"
|
|
1260
|
+
};
|
|
1261
|
+
return {
|
|
1262
|
+
provider: byok.provider,
|
|
1263
|
+
message: `Using ${providerNames[byok.provider]} key for AI reasoning.`
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
provider: "claude-cli",
|
|
1268
|
+
// default, will fail at invocation
|
|
1269
|
+
message: "No AI provider found. Install Claude Code CLI or set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY)."
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1244
1272
|
var CONFIG_DIR = path.join(process.env.HOME || "", ".ai-analyst");
|
|
1245
1273
|
var PROVIDER_CONFIG_FILE = path.join(CONFIG_DIR, "provider.json");
|
|
1274
|
+
function loadProviderConfig() {
|
|
1275
|
+
try {
|
|
1276
|
+
if (fs2.existsSync(PROVIDER_CONFIG_FILE)) {
|
|
1277
|
+
return JSON.parse(fs2.readFileSync(PROVIDER_CONFIG_FILE, "utf-8"));
|
|
1278
|
+
}
|
|
1279
|
+
} catch {
|
|
1280
|
+
}
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1283
|
+
function invokeClaudeCLI(options, claudePath) {
|
|
1284
|
+
const bin = claudePath || "claude";
|
|
1285
|
+
const args = ["--print"];
|
|
1286
|
+
if (options.model) {
|
|
1287
|
+
args.push("--model", options.model);
|
|
1288
|
+
}
|
|
1289
|
+
if (options.jsonSchema) {
|
|
1290
|
+
args.push("--output-format", "json", "--json-schema", JSON.stringify(options.jsonSchema));
|
|
1291
|
+
}
|
|
1292
|
+
if (options.claudeFlags) {
|
|
1293
|
+
args.push(...options.claudeFlags);
|
|
1294
|
+
}
|
|
1295
|
+
const useStdin = options.useStdin !== false && options.prompt.length > 1e4;
|
|
1296
|
+
if (!useStdin) {
|
|
1297
|
+
args.push("-p", options.prompt);
|
|
1298
|
+
}
|
|
1299
|
+
const startTime = Date.now();
|
|
1300
|
+
const timeoutMs = options.timeoutMs || 3e5;
|
|
1301
|
+
return new Promise((resolve) => {
|
|
1302
|
+
const child = (0, import_child_process.spawn)(bin, args, {
|
|
1303
|
+
cwd: options.cwd || process.cwd(),
|
|
1304
|
+
env: process.env,
|
|
1305
|
+
stdio: useStdin ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
|
|
1306
|
+
});
|
|
1307
|
+
let stdout = "";
|
|
1308
|
+
let stderr = "";
|
|
1309
|
+
child.stdout?.on("data", (data) => {
|
|
1310
|
+
stdout += data.toString();
|
|
1311
|
+
});
|
|
1312
|
+
child.stderr?.on("data", (data) => {
|
|
1313
|
+
stderr += data.toString();
|
|
1314
|
+
});
|
|
1315
|
+
if (useStdin && child.stdin) {
|
|
1316
|
+
child.stdin.write(options.prompt);
|
|
1317
|
+
child.stdin.end();
|
|
1318
|
+
}
|
|
1319
|
+
const timeout = setTimeout(() => {
|
|
1320
|
+
child.kill("SIGTERM");
|
|
1321
|
+
resolve({
|
|
1322
|
+
output: stdout,
|
|
1323
|
+
provider: "claude-cli",
|
|
1324
|
+
durationMs: Date.now() - startTime,
|
|
1325
|
+
error: `Timeout after ${timeoutMs}ms`
|
|
1326
|
+
});
|
|
1327
|
+
}, timeoutMs);
|
|
1328
|
+
child.on("close", (code) => {
|
|
1329
|
+
clearTimeout(timeout);
|
|
1330
|
+
resolve({
|
|
1331
|
+
output: stdout.trim(),
|
|
1332
|
+
provider: "claude-cli",
|
|
1333
|
+
durationMs: Date.now() - startTime,
|
|
1334
|
+
error: code !== 0 ? `Claude CLI exited with code ${code}: ${stderr}` : void 0
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
child.on("error", (err) => {
|
|
1338
|
+
clearTimeout(timeout);
|
|
1339
|
+
resolve({
|
|
1340
|
+
output: "",
|
|
1341
|
+
provider: "claude-cli",
|
|
1342
|
+
durationMs: Date.now() - startTime,
|
|
1343
|
+
error: `Failed to spawn Claude CLI: ${err.message}`
|
|
1344
|
+
});
|
|
1345
|
+
});
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
async function invokeAnthropicAPI(options) {
|
|
1349
|
+
const startTime = Date.now();
|
|
1350
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
1351
|
+
if (!apiKey) {
|
|
1352
|
+
return {
|
|
1353
|
+
output: "",
|
|
1354
|
+
provider: "anthropic-api",
|
|
1355
|
+
durationMs: 0,
|
|
1356
|
+
error: "ANTHROPIC_API_KEY not set"
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
try {
|
|
1360
|
+
const model = options.model || "claude-sonnet-4-6";
|
|
1361
|
+
let prompt = options.prompt;
|
|
1362
|
+
if (options.jsonSchema) {
|
|
1363
|
+
prompt += `
|
|
1364
|
+
|
|
1365
|
+
IMPORTANT: Respond with valid JSON matching this schema:
|
|
1366
|
+
${JSON.stringify(options.jsonSchema, null, 2)}
|
|
1367
|
+
|
|
1368
|
+
Output ONLY the JSON object, no markdown code blocks or other text.`;
|
|
1369
|
+
} else if (options.expectJson) {
|
|
1370
|
+
prompt += "\n\nIMPORTANT: Respond with valid JSON only. No markdown code blocks or other text.";
|
|
1371
|
+
}
|
|
1372
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
1373
|
+
method: "POST",
|
|
1374
|
+
headers: {
|
|
1375
|
+
"Content-Type": "application/json",
|
|
1376
|
+
"x-api-key": apiKey,
|
|
1377
|
+
"anthropic-version": "2023-06-01"
|
|
1378
|
+
},
|
|
1379
|
+
body: JSON.stringify({
|
|
1380
|
+
model,
|
|
1381
|
+
max_tokens: 16384,
|
|
1382
|
+
messages: [{ role: "user", content: prompt }]
|
|
1383
|
+
}),
|
|
1384
|
+
signal: AbortSignal.timeout(options.timeoutMs || 3e5)
|
|
1385
|
+
});
|
|
1386
|
+
if (!response.ok) {
|
|
1387
|
+
const errorText = await response.text();
|
|
1388
|
+
return {
|
|
1389
|
+
output: "",
|
|
1390
|
+
provider: "anthropic-api",
|
|
1391
|
+
durationMs: Date.now() - startTime,
|
|
1392
|
+
error: `Anthropic API error ${response.status}: ${errorText}`
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
const data = await response.json();
|
|
1396
|
+
const text = data.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
1397
|
+
return {
|
|
1398
|
+
output: text,
|
|
1399
|
+
provider: "anthropic-api",
|
|
1400
|
+
durationMs: Date.now() - startTime
|
|
1401
|
+
};
|
|
1402
|
+
} catch (err) {
|
|
1403
|
+
return {
|
|
1404
|
+
output: "",
|
|
1405
|
+
provider: "anthropic-api",
|
|
1406
|
+
durationMs: Date.now() - startTime,
|
|
1407
|
+
error: `Anthropic API call failed: ${err.message}`
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
async function invokeAI(options) {
|
|
1412
|
+
const savedConfig = loadProviderConfig();
|
|
1413
|
+
const detection = detectProvider(savedConfig || void 0);
|
|
1414
|
+
switch (detection.provider) {
|
|
1415
|
+
case "claude-cli":
|
|
1416
|
+
return invokeClaudeCLI(options, savedConfig?.claudePath);
|
|
1417
|
+
case "anthropic-api":
|
|
1418
|
+
return invokeAnthropicAPI(options);
|
|
1419
|
+
case "openai-api":
|
|
1420
|
+
case "google-api":
|
|
1421
|
+
return {
|
|
1422
|
+
output: "",
|
|
1423
|
+
provider: detection.provider,
|
|
1424
|
+
durationMs: 0,
|
|
1425
|
+
error: `${detection.provider} provider not yet implemented. Use Claude CLI or set ANTHROPIC_API_KEY.`
|
|
1426
|
+
};
|
|
1427
|
+
default:
|
|
1428
|
+
return {
|
|
1429
|
+
output: "",
|
|
1430
|
+
provider: "claude-cli",
|
|
1431
|
+
durationMs: 0,
|
|
1432
|
+
error: detection.message
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1246
1436
|
function requireClaudeCLI(feature) {
|
|
1247
1437
|
const cli = detectClaudeCLI();
|
|
1248
1438
|
if (cli.found) {
|
|
@@ -1265,6 +1455,291 @@ Or set an API key: export ANTHROPIC_API_KEY=sk-ant-...`
|
|
|
1265
1455
|
);
|
|
1266
1456
|
}
|
|
1267
1457
|
|
|
1458
|
+
// scripts/lib/vision/define.ts
|
|
1459
|
+
var fs4 = __toESM(require("fs"));
|
|
1460
|
+
|
|
1461
|
+
// scripts/lib/paths.ts
|
|
1462
|
+
var path2 = __toESM(require("path"));
|
|
1463
|
+
var fs3 = __toESM(require("fs"));
|
|
1464
|
+
function findProjectRoot() {
|
|
1465
|
+
const cwd = process.cwd();
|
|
1466
|
+
if (fs3.existsSync(path2.join(cwd, "data", "config.json"))) {
|
|
1467
|
+
return cwd;
|
|
1468
|
+
}
|
|
1469
|
+
try {
|
|
1470
|
+
const entries = fs3.readdirSync(cwd, { withFileTypes: true });
|
|
1471
|
+
for (const entry of entries) {
|
|
1472
|
+
if (entry.isDirectory()) {
|
|
1473
|
+
const candidate = path2.join(cwd, entry.name, "data", "config.json");
|
|
1474
|
+
if (fs3.existsSync(candidate)) {
|
|
1475
|
+
return path2.join(cwd, entry.name);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
} catch {
|
|
1480
|
+
}
|
|
1481
|
+
let dir = cwd;
|
|
1482
|
+
for (let i = 0; i < 5; i++) {
|
|
1483
|
+
const parent = path2.dirname(dir);
|
|
1484
|
+
if (parent === dir) break;
|
|
1485
|
+
if (fs3.existsSync(path2.join(parent, "data", "config.json"))) {
|
|
1486
|
+
return parent;
|
|
1487
|
+
}
|
|
1488
|
+
dir = parent;
|
|
1489
|
+
}
|
|
1490
|
+
return cwd;
|
|
1491
|
+
}
|
|
1492
|
+
var PROJECT_DIR = findProjectRoot();
|
|
1493
|
+
var DATA_DIR = path2.join(PROJECT_DIR, "data");
|
|
1494
|
+
var STATUS_FILE = path2.join(PROJECT_DIR, "STATUS.md");
|
|
1495
|
+
var TODO_FILE = path2.join(PROJECT_DIR, "TODO.md");
|
|
1496
|
+
var MEMORY_FILE = path2.join(PROJECT_DIR, "MEMORY.md");
|
|
1497
|
+
var IDEAS_FILE = path2.join(DATA_DIR, "ideas.json");
|
|
1498
|
+
var GOALS_FILE = path2.join(DATA_DIR, "goals.json");
|
|
1499
|
+
var SESSIONS_FILE = path2.join(DATA_DIR, "sessions.json");
|
|
1500
|
+
var CONFIG_FILE = path2.join(DATA_DIR, "config.json");
|
|
1501
|
+
var BUSINESS_CONTEXT_FILE = path2.join(DATA_DIR, "business-context.json");
|
|
1502
|
+
var HYPOTHESES_FILE = path2.join(DATA_DIR, "hypotheses.json");
|
|
1503
|
+
var IMPLEMENTATIONS_FILE = path2.join(DATA_DIR, "implementations.json");
|
|
1504
|
+
var ROADMAP_FILE = path2.join(DATA_DIR, "roadmap.json");
|
|
1505
|
+
var PRODUCT_VISION_FILE = path2.join(DATA_DIR, "product-vision.json");
|
|
1506
|
+
var COMPETITORS_FILE = path2.join(DATA_DIR, "competitors.json");
|
|
1507
|
+
var POSITIONING_FILE = path2.join(DATA_DIR, "positioning.json");
|
|
1508
|
+
var PAGES_FILE = path2.join(DATA_DIR, "pages.json");
|
|
1509
|
+
var PAYMENTS_FILE = path2.join(DATA_DIR, "payments.json");
|
|
1510
|
+
var POSTHOG_FILE = path2.join(DATA_DIR, "posthog.json");
|
|
1511
|
+
var SOCIAL_FILE = path2.join(DATA_DIR, "social.json");
|
|
1512
|
+
var MARKETING_STRATEGIES_FILE = path2.join(DATA_DIR, "marketing-strategies.json");
|
|
1513
|
+
var CAMPAIGNS_DIR = path2.join(DATA_DIR, "campaigns");
|
|
1514
|
+
var TEMPLATES_DIR = path2.join(__dirname, "..", "..", "templates");
|
|
1515
|
+
var COMMAND_TEMPLATES_DIR = path2.join(TEMPLATES_DIR, "commands");
|
|
1516
|
+
|
|
1517
|
+
// scripts/lib/vision/define.ts
|
|
1518
|
+
var PROJECT_DIR2 = PRODUCT_VISION_FILE.replace(/\/data\/product-vision\.json$/, "");
|
|
1519
|
+
function loadJson(filePath, defaultValue) {
|
|
1520
|
+
try {
|
|
1521
|
+
if (!fs4.existsSync(filePath)) return defaultValue;
|
|
1522
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
1523
|
+
} catch {
|
|
1524
|
+
return defaultValue;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
function saveJson(filePath, data) {
|
|
1528
|
+
fs4.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
1529
|
+
}
|
|
1530
|
+
function loadProductVision() {
|
|
1531
|
+
if (!fs4.existsSync(PRODUCT_VISION_FILE)) return null;
|
|
1532
|
+
try {
|
|
1533
|
+
return JSON.parse(fs4.readFileSync(PRODUCT_VISION_FILE, "utf-8"));
|
|
1534
|
+
} catch {
|
|
1535
|
+
return null;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
async function generateProductVision() {
|
|
1539
|
+
const positioning = loadJson(POSITIONING_FILE, null);
|
|
1540
|
+
const businessContext = loadJson(BUSINESS_CONTEXT_FILE, null);
|
|
1541
|
+
const competitors = loadJson(COMPETITORS_FILE, null);
|
|
1542
|
+
const existingVision = loadProductVision();
|
|
1543
|
+
const prompt = buildVisionPrompt(positioning, businessContext, competitors, existingVision);
|
|
1544
|
+
const aiResult = await invokeAI({
|
|
1545
|
+
prompt,
|
|
1546
|
+
cwd: PROJECT_DIR2,
|
|
1547
|
+
timeoutMs: 18e4,
|
|
1548
|
+
useStdin: true,
|
|
1549
|
+
expectJson: true
|
|
1550
|
+
});
|
|
1551
|
+
if (aiResult.error || !aiResult.output.trim()) {
|
|
1552
|
+
console.error(`Vision generation failed: ${aiResult.error || "empty output"}`);
|
|
1553
|
+
return null;
|
|
1554
|
+
}
|
|
1555
|
+
let vision = null;
|
|
1556
|
+
try {
|
|
1557
|
+
const raw = aiResult.output.trim();
|
|
1558
|
+
const jsonMatch = raw.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
1559
|
+
const jsonStr = jsonMatch ? jsonMatch[1].trim() : raw;
|
|
1560
|
+
vision = JSON.parse(jsonStr);
|
|
1561
|
+
} catch {
|
|
1562
|
+
console.error("Failed to parse vision JSON from AI output");
|
|
1563
|
+
return null;
|
|
1564
|
+
}
|
|
1565
|
+
vision.last_updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
1566
|
+
vision.version = existingVision ? (existingVision.version || 0) + 1 : 1;
|
|
1567
|
+
saveJson(PRODUCT_VISION_FILE, vision);
|
|
1568
|
+
return vision;
|
|
1569
|
+
}
|
|
1570
|
+
async function deriveHypothesesFromVision(vision) {
|
|
1571
|
+
const prompt = `You are a product strategist. Given this product vision, derive 5-8 testable business hypotheses.
|
|
1572
|
+
|
|
1573
|
+
PRODUCT VISION:
|
|
1574
|
+
- One-liner: ${vision.one_liner}
|
|
1575
|
+
- Focus metric: ${vision.focus_metric}
|
|
1576
|
+
- Best-fit customers: ${vision.obviously_awesome.best_fit_customers.primary}
|
|
1577
|
+
- Value delivered: ${vision.obviously_awesome.value_delivered.join(", ")}
|
|
1578
|
+
- Problem (external): ${vision.storybrand.problem.external}
|
|
1579
|
+
- Problem (internal): ${vision.storybrand.problem.internal}
|
|
1580
|
+
- Success transformation: ${vision.storybrand.success.transformation}
|
|
1581
|
+
- Anti-goals: ${vision.anti_goals.join(", ")}
|
|
1582
|
+
|
|
1583
|
+
Each hypothesis should be:
|
|
1584
|
+
- Specific and falsifiable ("If we build X, then Y will happen")
|
|
1585
|
+
- Tied to a funnel stage (acquisition, engagement, conversion, monetization)
|
|
1586
|
+
- Have clear validation criteria
|
|
1587
|
+
|
|
1588
|
+
Respond with JSON only (no markdown):
|
|
1589
|
+
[
|
|
1590
|
+
{
|
|
1591
|
+
"title": "Short title",
|
|
1592
|
+
"statement": "If we [action], then [outcome] because [reason]",
|
|
1593
|
+
"description": "Detailed explanation",
|
|
1594
|
+
"funnel_stage": "acquisition|engagement|conversion|monetization",
|
|
1595
|
+
"priority": "critical|high|medium|low",
|
|
1596
|
+
"effort_to_test": "xs|s|m|l|xl",
|
|
1597
|
+
"validation_criteria": ["criteria 1", "criteria 2"]
|
|
1598
|
+
}
|
|
1599
|
+
]`;
|
|
1600
|
+
const aiResult = await invokeAI({
|
|
1601
|
+
prompt,
|
|
1602
|
+
cwd: PROJECT_DIR2,
|
|
1603
|
+
timeoutMs: 12e4,
|
|
1604
|
+
useStdin: true,
|
|
1605
|
+
expectJson: true
|
|
1606
|
+
});
|
|
1607
|
+
if (aiResult.error || !aiResult.output.trim()) {
|
|
1608
|
+
console.error(`Hypothesis derivation failed: ${aiResult.error || "empty output"}`);
|
|
1609
|
+
return [];
|
|
1610
|
+
}
|
|
1611
|
+
try {
|
|
1612
|
+
const raw = aiResult.output.trim();
|
|
1613
|
+
const jsonMatch = raw.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
1614
|
+
const jsonStr = jsonMatch ? jsonMatch[1].trim() : raw;
|
|
1615
|
+
return JSON.parse(jsonStr);
|
|
1616
|
+
} catch {
|
|
1617
|
+
console.error("Failed to parse hypotheses JSON");
|
|
1618
|
+
return [];
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
function buildVisionPrompt(positioning, businessContext, competitors, existingVision) {
|
|
1622
|
+
const sections = [];
|
|
1623
|
+
sections.push(`You are a product strategist. Generate a structured product vision using two proven frameworks:
|
|
1624
|
+
|
|
1625
|
+
1. **Obviously Awesome** (April Dunford) \u2014 5-step positioning:
|
|
1626
|
+
- Competitive alternatives: What would customers do if you didn't exist?
|
|
1627
|
+
- Unique attributes: What capabilities do you have that alternatives lack?
|
|
1628
|
+
- Value: What value do those attributes enable?
|
|
1629
|
+
- Best-fit customers: Who cares most about that value?
|
|
1630
|
+
- Market category: What context makes your value obvious?
|
|
1631
|
+
|
|
1632
|
+
2. **StoryBrand** (Donald Miller) \u2014 7-part narrative:
|
|
1633
|
+
- Character: The customer \u2014 who are they, what do they want?
|
|
1634
|
+
- Problem: External (tangible), Internal (emotional), Philosophical (why it matters)
|
|
1635
|
+
- Guide: You \u2014 empathy + authority
|
|
1636
|
+
- Plan: 3 simple steps to succeed
|
|
1637
|
+
- Call to Action: Direct CTA + transitional CTA
|
|
1638
|
+
- Failure: What happens if they don't act
|
|
1639
|
+
- Success: The transformation (before \u2192 after)`);
|
|
1640
|
+
if (businessContext) {
|
|
1641
|
+
sections.push(`
|
|
1642
|
+
BUSINESS CONTEXT:
|
|
1643
|
+
Product: ${businessContext.product?.name || "Unknown"} \u2014 ${businessContext.product?.summary || ""}
|
|
1644
|
+
Funnels: ${JSON.stringify(businessContext.funnels?.map((f) => ({ name: f.name, status: f.status })) || [])}
|
|
1645
|
+
Monetization: MRR ${businessContext.monetization?.current_mrr || 0}, ${businessContext.monetization?.paying_lawyers || 0} paying customers
|
|
1646
|
+
Key constraints: ${businessContext.key_constraints?.join(", ") || "none"}`);
|
|
1647
|
+
}
|
|
1648
|
+
if (positioning) {
|
|
1649
|
+
sections.push(`
|
|
1650
|
+
EXISTING POSITIONING:
|
|
1651
|
+
Tagline: ${positioning.current?.tagline || "none"}
|
|
1652
|
+
Value prop: ${positioning.current?.value_proposition || "none"}
|
|
1653
|
+
Target audience: ${JSON.stringify(positioning.current?.target_audience || {})}
|
|
1654
|
+
Differentiators: ${positioning.current?.differentiators?.join(", ") || "none"}`);
|
|
1655
|
+
}
|
|
1656
|
+
if (competitors?.competitors?.length) {
|
|
1657
|
+
const topCompetitors = competitors.competitors.slice(0, 5);
|
|
1658
|
+
sections.push(`
|
|
1659
|
+
COMPETITORS:
|
|
1660
|
+
${topCompetitors.map((c) => `- ${c.name}: ${c.positioning?.value_proposition || "unknown"}`).join("\n")}`);
|
|
1661
|
+
}
|
|
1662
|
+
if (existingVision) {
|
|
1663
|
+
sections.push(`
|
|
1664
|
+
EXISTING VISION (v${existingVision.version}, refresh this):
|
|
1665
|
+
One-liner: ${existingVision.one_liner}
|
|
1666
|
+
Focus metric: ${existingVision.focus_metric}`);
|
|
1667
|
+
}
|
|
1668
|
+
sections.push(`
|
|
1669
|
+
Respond with JSON only (no markdown code blocks, just raw JSON):
|
|
1670
|
+
{
|
|
1671
|
+
"last_updated": "${(/* @__PURE__ */ new Date()).toISOString()}",
|
|
1672
|
+
"version": ${existingVision ? (existingVision.version || 0) + 1 : 1},
|
|
1673
|
+
"obviously_awesome": {
|
|
1674
|
+
"competitive_alternatives": ["alt1", "alt2", ...],
|
|
1675
|
+
"unique_attributes": ["attr1", "attr2", ...],
|
|
1676
|
+
"value_delivered": ["value1", "value2", ...],
|
|
1677
|
+
"best_fit_customers": { "primary": "...", "characteristics": ["...", "..."] },
|
|
1678
|
+
"market_category": "...",
|
|
1679
|
+
"positioning_statement": "..."
|
|
1680
|
+
},
|
|
1681
|
+
"storybrand": {
|
|
1682
|
+
"character": { "identity": "...", "desire": "..." },
|
|
1683
|
+
"problem": { "external": "...", "internal": "...", "philosophical": "..." },
|
|
1684
|
+
"guide": { "empathy": "...", "authority": "..." },
|
|
1685
|
+
"plan": ["step 1", "step 2", "step 3"],
|
|
1686
|
+
"call_to_action": { "direct": "...", "transitional": "..." },
|
|
1687
|
+
"failure": "...",
|
|
1688
|
+
"success": { "transformation": "...", "before": "...", "after": "..." }
|
|
1689
|
+
},
|
|
1690
|
+
"one_liner": "For [best_fit_customers] who [problem], [product] is the [category] that [value]. Unlike [alternatives], we [unique_attributes].",
|
|
1691
|
+
"focus_metric": "The ONE metric that proves this vision is working",
|
|
1692
|
+
"anti_goals": ["Things we explicitly will NOT build"]
|
|
1693
|
+
}`);
|
|
1694
|
+
return sections.join("\n");
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// scripts/lib/vision/hypotheses.ts
|
|
1698
|
+
var fs5 = __toESM(require("fs"));
|
|
1699
|
+
function loadJson2(filePath, defaultValue) {
|
|
1700
|
+
try {
|
|
1701
|
+
if (!fs5.existsSync(filePath)) return defaultValue;
|
|
1702
|
+
return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
|
|
1703
|
+
} catch {
|
|
1704
|
+
return defaultValue;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
function saveJson2(filePath, data) {
|
|
1708
|
+
fs5.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
1709
|
+
}
|
|
1710
|
+
async function seedHypothesesFromVision(vision) {
|
|
1711
|
+
const v = vision || loadProductVision();
|
|
1712
|
+
if (!v) {
|
|
1713
|
+
console.error("No product vision found. Run /vision first.");
|
|
1714
|
+
return [];
|
|
1715
|
+
}
|
|
1716
|
+
const derived = await deriveHypothesesFromVision(v);
|
|
1717
|
+
if (derived.length === 0) return [];
|
|
1718
|
+
const store = loadJson2(HYPOTHESES_FILE, { hypotheses: [] });
|
|
1719
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1720
|
+
const newHypotheses = derived.map((h, i) => ({
|
|
1721
|
+
id: `hyp-vision-${Date.now().toString(36)}-${i}`,
|
|
1722
|
+
created_at: now,
|
|
1723
|
+
updated_at: now,
|
|
1724
|
+
title: h.title,
|
|
1725
|
+
statement: h.statement,
|
|
1726
|
+
description: h.description,
|
|
1727
|
+
funnel_stage: h.funnel_stage,
|
|
1728
|
+
priority: h.priority || "medium",
|
|
1729
|
+
status: "stated",
|
|
1730
|
+
evidence_for: [],
|
|
1731
|
+
evidence_against: [],
|
|
1732
|
+
validation_criteria: h.validation_criteria || [],
|
|
1733
|
+
validation_ideas: [],
|
|
1734
|
+
reference_model: "product-vision",
|
|
1735
|
+
effort_to_test: h.effort_to_test || "m",
|
|
1736
|
+
tags: ["vision-derived"]
|
|
1737
|
+
}));
|
|
1738
|
+
store.hypotheses.push(...newHypotheses);
|
|
1739
|
+
saveJson2(HYPOTHESES_FILE, store);
|
|
1740
|
+
return newHypotheses;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1268
1743
|
// scripts/lib/logger.ts
|
|
1269
1744
|
function logError(context, error) {
|
|
1270
1745
|
const entry = {
|
|
@@ -1585,59 +2060,6 @@ function formatValueSummary(ideas, analysisType, durationSeconds, repoName) {
|
|
|
1585
2060
|
}
|
|
1586
2061
|
}
|
|
1587
2062
|
|
|
1588
|
-
// scripts/lib/paths.ts
|
|
1589
|
-
var path2 = __toESM(require("path"));
|
|
1590
|
-
var fs3 = __toESM(require("fs"));
|
|
1591
|
-
function findProjectRoot() {
|
|
1592
|
-
const cwd = process.cwd();
|
|
1593
|
-
if (fs3.existsSync(path2.join(cwd, "data", "config.json"))) {
|
|
1594
|
-
return cwd;
|
|
1595
|
-
}
|
|
1596
|
-
try {
|
|
1597
|
-
const entries = fs3.readdirSync(cwd, { withFileTypes: true });
|
|
1598
|
-
for (const entry of entries) {
|
|
1599
|
-
if (entry.isDirectory()) {
|
|
1600
|
-
const candidate = path2.join(cwd, entry.name, "data", "config.json");
|
|
1601
|
-
if (fs3.existsSync(candidate)) {
|
|
1602
|
-
return path2.join(cwd, entry.name);
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
} catch {
|
|
1607
|
-
}
|
|
1608
|
-
let dir = cwd;
|
|
1609
|
-
for (let i = 0; i < 5; i++) {
|
|
1610
|
-
const parent = path2.dirname(dir);
|
|
1611
|
-
if (parent === dir) break;
|
|
1612
|
-
if (fs3.existsSync(path2.join(parent, "data", "config.json"))) {
|
|
1613
|
-
return parent;
|
|
1614
|
-
}
|
|
1615
|
-
dir = parent;
|
|
1616
|
-
}
|
|
1617
|
-
return cwd;
|
|
1618
|
-
}
|
|
1619
|
-
var PROJECT_DIR = findProjectRoot();
|
|
1620
|
-
var DATA_DIR = path2.join(PROJECT_DIR, "data");
|
|
1621
|
-
var STATUS_FILE = path2.join(PROJECT_DIR, "STATUS.md");
|
|
1622
|
-
var TODO_FILE = path2.join(PROJECT_DIR, "TODO.md");
|
|
1623
|
-
var MEMORY_FILE = path2.join(PROJECT_DIR, "MEMORY.md");
|
|
1624
|
-
var IDEAS_FILE = path2.join(DATA_DIR, "ideas.json");
|
|
1625
|
-
var GOALS_FILE = path2.join(DATA_DIR, "goals.json");
|
|
1626
|
-
var SESSIONS_FILE = path2.join(DATA_DIR, "sessions.json");
|
|
1627
|
-
var CONFIG_FILE = path2.join(DATA_DIR, "config.json");
|
|
1628
|
-
var BUSINESS_CONTEXT_FILE = path2.join(DATA_DIR, "business-context.json");
|
|
1629
|
-
var HYPOTHESES_FILE = path2.join(DATA_DIR, "hypotheses.json");
|
|
1630
|
-
var IMPLEMENTATIONS_FILE = path2.join(DATA_DIR, "implementations.json");
|
|
1631
|
-
var ROADMAP_FILE = path2.join(DATA_DIR, "roadmap.json");
|
|
1632
|
-
var COMPETITORS_FILE = path2.join(DATA_DIR, "competitors.json");
|
|
1633
|
-
var POSITIONING_FILE = path2.join(DATA_DIR, "positioning.json");
|
|
1634
|
-
var PAGES_FILE = path2.join(DATA_DIR, "pages.json");
|
|
1635
|
-
var PAYMENTS_FILE = path2.join(DATA_DIR, "payments.json");
|
|
1636
|
-
var POSTHOG_FILE = path2.join(DATA_DIR, "posthog.json");
|
|
1637
|
-
var SOCIAL_FILE = path2.join(DATA_DIR, "social.json");
|
|
1638
|
-
var TEMPLATES_DIR = path2.join(__dirname, "..", "..", "templates");
|
|
1639
|
-
var COMMAND_TEMPLATES_DIR = path2.join(TEMPLATES_DIR, "commands");
|
|
1640
|
-
|
|
1641
2063
|
// scripts/analyze.ts
|
|
1642
2064
|
function parseArgs() {
|
|
1643
2065
|
const args = process.argv.slice(2);
|
|
@@ -1647,7 +2069,7 @@ function parseArgs() {
|
|
|
1647
2069
|
for (const arg of args) {
|
|
1648
2070
|
if (arg.startsWith("--type=")) {
|
|
1649
2071
|
const value = arg.split("=")[1];
|
|
1650
|
-
if (["quick", "deep", "metrics", "seo", "research", "growth"].includes(value)) {
|
|
2072
|
+
if (["quick", "deep", "metrics", "seo", "research", "growth", "vision"].includes(value)) {
|
|
1651
2073
|
type = value;
|
|
1652
2074
|
}
|
|
1653
2075
|
} else if (arg.startsWith("--topic=")) {
|
|
@@ -1659,35 +2081,35 @@ function parseArgs() {
|
|
|
1659
2081
|
return { type, topic, context };
|
|
1660
2082
|
}
|
|
1661
2083
|
function loadConfig() {
|
|
1662
|
-
if (!
|
|
2084
|
+
if (!fs6.existsSync(CONFIG_FILE)) {
|
|
1663
2085
|
throw new Error("Config file not found. Please ensure data/config.json exists.");
|
|
1664
2086
|
}
|
|
1665
|
-
return JSON.parse(
|
|
2087
|
+
return JSON.parse(fs6.readFileSync(CONFIG_FILE, "utf-8"));
|
|
1666
2088
|
}
|
|
1667
2089
|
function loadBusinessContext() {
|
|
1668
|
-
if (!
|
|
2090
|
+
if (!fs6.existsSync(BUSINESS_CONTEXT_FILE)) {
|
|
1669
2091
|
return void 0;
|
|
1670
2092
|
}
|
|
1671
2093
|
try {
|
|
1672
|
-
return JSON.parse(
|
|
2094
|
+
return JSON.parse(fs6.readFileSync(BUSINESS_CONTEXT_FILE, "utf-8"));
|
|
1673
2095
|
} catch {
|
|
1674
2096
|
return void 0;
|
|
1675
2097
|
}
|
|
1676
2098
|
}
|
|
1677
2099
|
function loadIdeas() {
|
|
1678
|
-
if (!
|
|
2100
|
+
if (!fs6.existsSync(IDEAS_FILE)) {
|
|
1679
2101
|
return { ideas: [] };
|
|
1680
2102
|
}
|
|
1681
|
-
return JSON.parse(
|
|
2103
|
+
return JSON.parse(fs6.readFileSync(IDEAS_FILE, "utf-8"));
|
|
1682
2104
|
}
|
|
1683
2105
|
function saveIdeas(data) {
|
|
1684
2106
|
atomicWriteFileSync(IDEAS_FILE, JSON.stringify(data, null, 2));
|
|
1685
2107
|
}
|
|
1686
2108
|
function loadSessions() {
|
|
1687
|
-
if (!
|
|
2109
|
+
if (!fs6.existsSync(SESSIONS_FILE)) {
|
|
1688
2110
|
return { sessions: [] };
|
|
1689
2111
|
}
|
|
1690
|
-
return JSON.parse(
|
|
2112
|
+
return JSON.parse(fs6.readFileSync(SESSIONS_FILE, "utf-8"));
|
|
1691
2113
|
}
|
|
1692
2114
|
function saveSessions(data) {
|
|
1693
2115
|
atomicWriteFileSync(SESSIONS_FILE, JSON.stringify(data, null, 2));
|
|
@@ -1773,8 +2195,8 @@ function flushSessionLogs(sessionId) {
|
|
|
1773
2195
|
}
|
|
1774
2196
|
function loadGoalsContext() {
|
|
1775
2197
|
try {
|
|
1776
|
-
if (!
|
|
1777
|
-
const goalsData = JSON.parse(
|
|
2198
|
+
if (!fs6.existsSync(GOALS_FILE)) return "";
|
|
2199
|
+
const goalsData = JSON.parse(fs6.readFileSync(GOALS_FILE, "utf-8"));
|
|
1778
2200
|
const goals = goalsData.goals || [];
|
|
1779
2201
|
if (goals.length === 0) return "";
|
|
1780
2202
|
return goals.map((g) => {
|
|
@@ -1792,8 +2214,8 @@ function linkIdeasToHypotheses(ideas) {
|
|
|
1792
2214
|
const ideasWithHypothesis = ideas.filter((i) => i.hypothesis_id);
|
|
1793
2215
|
if (ideasWithHypothesis.length === 0) return;
|
|
1794
2216
|
try {
|
|
1795
|
-
if (!
|
|
1796
|
-
const data = JSON.parse(
|
|
2217
|
+
if (!fs6.existsSync(HYPOTHESES_FILE)) return;
|
|
2218
|
+
const data = JSON.parse(fs6.readFileSync(HYPOTHESES_FILE, "utf-8"));
|
|
1797
2219
|
const hypotheses = data.hypotheses || [];
|
|
1798
2220
|
let changed = false;
|
|
1799
2221
|
for (const idea of ideasWithHypothesis) {
|
|
@@ -1817,8 +2239,8 @@ function linkIdeasToHypotheses(ideas) {
|
|
|
1817
2239
|
}
|
|
1818
2240
|
function matchNewIdeasToEpics(newIdeas, ideasData) {
|
|
1819
2241
|
try {
|
|
1820
|
-
if (!
|
|
1821
|
-
const roadmapData = JSON.parse(
|
|
2242
|
+
if (!fs6.existsSync(ROADMAP_FILE)) return;
|
|
2243
|
+
const roadmapData = JSON.parse(fs6.readFileSync(ROADMAP_FILE, "utf-8"));
|
|
1822
2244
|
const epics = roadmapData.epics || [];
|
|
1823
2245
|
if (epics.length === 0) return;
|
|
1824
2246
|
let matched = 0;
|
|
@@ -1852,8 +2274,8 @@ function matchNewIdeasToEpics(newIdeas, ideasData) {
|
|
|
1852
2274
|
}
|
|
1853
2275
|
function loadHypothesesContext() {
|
|
1854
2276
|
try {
|
|
1855
|
-
if (!
|
|
1856
|
-
const data = JSON.parse(
|
|
2277
|
+
if (!fs6.existsSync(HYPOTHESES_FILE)) return "";
|
|
2278
|
+
const data = JSON.parse(fs6.readFileSync(HYPOTHESES_FILE, "utf-8"));
|
|
1857
2279
|
const hypotheses = data.hypotheses || [];
|
|
1858
2280
|
if (hypotheses.length === 0) return "";
|
|
1859
2281
|
return hypotheses.filter((h) => h.status === "stated" || h.status === "testing").map(
|
|
@@ -2160,7 +2582,7 @@ Return ONLY this JSON structure (no markdown code blocks):
|
|
|
2160
2582
|
const jsonMatch = output.match(/\{[\s\S]*\}/);
|
|
2161
2583
|
if (jsonMatch) {
|
|
2162
2584
|
const snapshot = JSON.parse(jsonMatch[0]);
|
|
2163
|
-
|
|
2585
|
+
fs6.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2));
|
|
2164
2586
|
console.log(`Codebase snapshot saved to ${snapshotPath}`);
|
|
2165
2587
|
}
|
|
2166
2588
|
} catch (error) {
|
|
@@ -2183,6 +2605,33 @@ async function main() {
|
|
|
2183
2605
|
if (type === "research" && topic) {
|
|
2184
2606
|
console.log(`Research topic: ${topic}`);
|
|
2185
2607
|
}
|
|
2608
|
+
if (type === "vision") {
|
|
2609
|
+
console.log("Generating product vision (Obviously Awesome + StoryBrand)...\n");
|
|
2610
|
+
const vision = await generateProductVision();
|
|
2611
|
+
if (!vision) {
|
|
2612
|
+
console.error("Vision generation failed");
|
|
2613
|
+
process.exit(1);
|
|
2614
|
+
}
|
|
2615
|
+
console.log(`
|
|
2616
|
+
\u2713 Vision saved to data/product-vision.json (v${vision.version})`);
|
|
2617
|
+
console.log(` One-liner: ${vision.one_liner}`);
|
|
2618
|
+
console.log(` Focus metric: ${vision.focus_metric}`);
|
|
2619
|
+
console.log(` Anti-goals: ${vision.anti_goals.join(", ")}
|
|
2620
|
+
`);
|
|
2621
|
+
console.log("Deriving hypotheses from vision...");
|
|
2622
|
+
const hypotheses = await seedHypothesesFromVision(vision);
|
|
2623
|
+
if (hypotheses.length > 0) {
|
|
2624
|
+
console.log(`\u2713 ${hypotheses.length} hypotheses seeded to data/hypotheses.json
|
|
2625
|
+
`);
|
|
2626
|
+
for (const h of hypotheses) {
|
|
2627
|
+
console.log(` - [${h.funnel_stage}] ${h.title}`);
|
|
2628
|
+
}
|
|
2629
|
+
} else {
|
|
2630
|
+
console.log(" No hypotheses generated (you can add them manually)");
|
|
2631
|
+
}
|
|
2632
|
+
console.log("\n=== Vision Analysis Complete ===");
|
|
2633
|
+
return;
|
|
2634
|
+
}
|
|
2186
2635
|
const config = loadConfig();
|
|
2187
2636
|
console.log(`Analyzing ${config.repos.length} repositories...`);
|
|
2188
2637
|
const session = createSession(type, config.repos.map((r) => r.name));
|