feat(ai): optimize tool loops, fix deployments, and integrate new onboarding flow
This commit is contained in:
22
vibn-frontend/docs/scripts/check-db-via-api.js
Normal file
22
vibn-frontend/docs/scripts/check-db-via-api.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// Use the existing API endpoint to check the database
|
||||
const projectId = 'Sih2VRdZeBglpVNc4eFS'; // Your project ID
|
||||
|
||||
async function checkDB() {
|
||||
try {
|
||||
// Try to fetch extraction handoff
|
||||
const response = await fetch(`http://localhost:3000/api/projects/${projectId}/extraction-handoff`);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('\n=== EXTRACTION HANDOFF ===');
|
||||
console.log(JSON.stringify(data.handoff, null, 2));
|
||||
} else {
|
||||
console.log('Extraction handoff response:', response.status, await response.text());
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
checkDB();
|
||||
74
vibn-frontend/docs/scripts/check-documents.js
Normal file
74
vibn-frontend/docs/scripts/check-documents.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const admin = require('firebase-admin');
|
||||
|
||||
// Initialize Firebase Admin
|
||||
const serviceAccount = require('./vibn-firebase-admin-key.json');
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount)
|
||||
});
|
||||
|
||||
const db = admin.firestore();
|
||||
|
||||
async function checkDocuments() {
|
||||
try {
|
||||
// Get the most recent project
|
||||
const projectsSnapshot = await db.collection('projects')
|
||||
.orderBy('createdAt', 'desc')
|
||||
.limit(1)
|
||||
.get();
|
||||
|
||||
if (projectsSnapshot.empty) {
|
||||
console.log('No projects found');
|
||||
return;
|
||||
}
|
||||
|
||||
const project = projectsSnapshot.docs[0];
|
||||
const projectId = project.id;
|
||||
const projectData = project.data();
|
||||
|
||||
console.log('\n=== PROJECT INFO ===');
|
||||
console.log('Project ID:', projectId);
|
||||
console.log('Project Name:', projectData.name);
|
||||
console.log('Current Phase:', projectData.currentPhase);
|
||||
|
||||
// Check knowledge_items
|
||||
console.log('\n=== KNOWLEDGE ITEMS ===');
|
||||
const knowledgeSnapshot = await db.collection('knowledge_items')
|
||||
.where('projectId', '==', projectId)
|
||||
.get();
|
||||
|
||||
console.log('Total knowledge items:', knowledgeSnapshot.size);
|
||||
|
||||
knowledgeSnapshot.docs.forEach((doc, idx) => {
|
||||
const data = doc.data();
|
||||
console.log(`\n[${idx + 1}] ${doc.id}`);
|
||||
console.log(' Title:', data.title);
|
||||
console.log(' Source Type:', data.sourceType);
|
||||
console.log(' Content Length:', data.content?.length || 0);
|
||||
console.log(' Created:', data.createdAt?.toDate?.() || 'unknown');
|
||||
});
|
||||
|
||||
// Check extraction handoff
|
||||
console.log('\n=== EXTRACTION HANDOFF ===');
|
||||
const extractionHandoff = projectData.phaseData?.phaseHandoffs?.extraction;
|
||||
if (extractionHandoff) {
|
||||
console.log('Phase:', extractionHandoff.phase);
|
||||
console.log('Ready:', extractionHandoff.readyForNextPhase);
|
||||
console.log('Confidence:', extractionHandoff.confidence);
|
||||
console.log('Problems:', extractionHandoff.confirmed?.problems?.length || 0);
|
||||
console.log('Features:', extractionHandoff.confirmed?.features?.length || 0);
|
||||
console.log('Users:', extractionHandoff.confirmed?.targetUsers?.length || 0);
|
||||
console.log('Missing:', extractionHandoff.missing);
|
||||
console.log('Questions:', extractionHandoff.questionsForUser);
|
||||
} else {
|
||||
console.log('No extraction handoff found');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
checkDocuments();
|
||||
33
vibn-frontend/docs/scripts/check-phase.js
Normal file
33
vibn-frontend/docs/scripts/check-phase.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const admin = require('firebase-admin');
|
||||
const serviceAccount = require('./serviceAccountKey.json');
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount)
|
||||
});
|
||||
|
||||
const db = admin.firestore();
|
||||
|
||||
async function checkPhase() {
|
||||
const projectId = 'ivmEUWmdBrY9M5QDuBsJ';
|
||||
const projectDoc = await db.collection('projects').doc(projectId).get();
|
||||
|
||||
if (!projectDoc.exists) {
|
||||
console.log('Project not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = projectDoc.data();
|
||||
console.log('\n=== PROJECT PHASE DATA ===');
|
||||
console.log('Current Phase:', data.currentPhase);
|
||||
console.log('Phase Status:', data.phaseStatus);
|
||||
console.log('Phase Data:', JSON.stringify(data.phaseData, null, 2));
|
||||
console.log('\n=== EXPECTED vs ACTUAL ===');
|
||||
console.log('Expected Phase: gathering');
|
||||
console.log('Actual Phase:', data.currentPhase);
|
||||
console.log('Match:', data.currentPhase === 'gathering' ? '✅ YES' : '❌ NO');
|
||||
}
|
||||
|
||||
checkPhase().then(() => process.exit(0)).catch(err => {
|
||||
console.error('Error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
13
vibn-frontend/docs/scripts/fix_auth_redirect.js
Normal file
13
vibn-frontend/docs/scripts/fix_auth_redirect.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const file = 'app/auth/page.tsx';
|
||||
let code = fs.readFileSync(file, 'utf8');
|
||||
|
||||
// Currently it routes to `/${workspace}/projects`
|
||||
// We will route them to `/onboarding`
|
||||
code = code.replace(
|
||||
'router.push(`/${workspace}/projects`);',
|
||||
'router.push(`/onboarding`);'
|
||||
);
|
||||
|
||||
fs.writeFileSync(file, code);
|
||||
10
vibn-frontend/docs/scripts/fix_click.js
Normal file
10
vibn-frontend/docs/scripts/fix_click.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const fs = require('fs');
|
||||
const file = 'app/(onboarding)/onboarding/page.tsx';
|
||||
let code = fs.readFileSync(file, 'utf8');
|
||||
|
||||
code = code.replace(
|
||||
'const btn = document.querySelector(".btn-primary:not([disabled])") as HTMLElement;\n if (btn)(".btn-primary:not([disabled])");\n if (btn) btn.click();',
|
||||
'const btn = document.querySelector(".btn-primary:not([disabled])") as HTMLElement;\n if (btn) btn.click();'
|
||||
);
|
||||
|
||||
fs.writeFileSync(file, code);
|
||||
32
vibn-frontend/docs/scripts/fix_mcp.js
Normal file
32
vibn-frontend/docs/scripts/fix_mcp.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const path = 'lib/auth/workspace-auth.ts';
|
||||
let content = fs.readFileSync(path, 'utf8');
|
||||
|
||||
const replacement = `if (!principal) {
|
||||
if (isDevBypass) {
|
||||
const userRow = await queryOne<{ id: string }>(
|
||||
\`SELECT id FROM fs_users WHERE data->>'email' = $1 LIMIT 1\`,
|
||||
[process.env.NEXT_PUBLIC_DEV_LOCAL_AUTH_EMAIL]
|
||||
);
|
||||
if (userRow) {
|
||||
const workspace = await getWorkspaceByOwner(userRow.id);
|
||||
if (workspace) {
|
||||
return { source: 'api_key', workspace, userId: userRow.id, apiKeyId: 'dev_bypass' };
|
||||
}
|
||||
}
|
||||
}
|
||||
return NextResponse.json({ error: 'Invalid or revoked API key' }, { status: 401 });
|
||||
}
|
||||
if (!matchesTarget(principal.workspace, opts)) {
|
||||
return NextResponse.json({ error: 'API key not authorized for this workspace' }, { status: 403 });
|
||||
}
|
||||
return principal;
|
||||
}`;
|
||||
|
||||
content = content.replace(
|
||||
/if \(\!principal && !isDevBypass\) \{[\s\S]*?return principal;\n \}\n \}/m,
|
||||
replacement
|
||||
);
|
||||
|
||||
fs.writeFileSync(path, content);
|
||||
22
vibn-frontend/docs/scripts/fix_primitives_errors.js
Normal file
22
vibn-frontend/docs/scripts/fix_primitives_errors.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const dir = 'app/(onboarding)/onboarding';
|
||||
|
||||
// Replace Arrow in onboarding-primitives.tsx
|
||||
let primCode = fs.readFileSync(path.join(dir, 'onboarding-primitives.tsx'), 'utf8');
|
||||
primCode = primCode.replace(
|
||||
/<Arrow size=\{13\} \/>/g,
|
||||
'<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>'
|
||||
);
|
||||
fs.writeFileSync(path.join(dir, 'onboarding-primitives.tsx'), primCode);
|
||||
|
||||
// Add Field to imports
|
||||
const filesToFix = ['onboarding-consultant.tsx', 'onboarding-entrepreneur.tsx', 'onboarding-owner.tsx'];
|
||||
for (const f of filesToFix) {
|
||||
let code = fs.readFileSync(path.join(dir, f), 'utf8');
|
||||
if (!code.includes('Field,') && !code.includes(', Field')) {
|
||||
code = code.replace(/import \{ ([^}]+) \} from "\.\/onboarding-primitives";/, 'import { $1, Field } from "./onboarding-primitives";');
|
||||
}
|
||||
fs.writeFileSync(path.join(dir, f), code);
|
||||
}
|
||||
12
vibn-frontend/docs/scripts/fix_primitives_errors2.js
Normal file
12
vibn-frontend/docs/scripts/fix_primitives_errors2.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const dir = 'app/(onboarding)/onboarding';
|
||||
let code = fs.readFileSync(path.join(dir, 'onboarding-build.tsx'), 'utf8');
|
||||
|
||||
code = code.replace(
|
||||
/<Arrow size=\{13\} \/>/g,
|
||||
'<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>'
|
||||
);
|
||||
|
||||
fs.writeFileSync(path.join(dir, 'onboarding-build.tsx'), code);
|
||||
16
vibn-frontend/docs/scripts/fix_window_assign.js
Normal file
16
vibn-frontend/docs/scripts/fix_window_assign.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const dir = 'app/(onboarding)/onboarding';
|
||||
const files = fs.readdirSync(dir).filter(f => f.endsWith('.tsx'));
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dir, file);
|
||||
let code = fs.readFileSync(filePath, 'utf8');
|
||||
|
||||
// Remove Object.assign(window, {...}) lines since these are now proper React imports
|
||||
code = code.replace(/Object\.assign\(window,\s*\{[\s\S]*?\}\);/g, '');
|
||||
|
||||
fs.writeFileSync(filePath, code);
|
||||
}
|
||||
console.log("Removed global window assignments from child components.");
|
||||
15
vibn-frontend/docs/scripts/format_onboarding.js
Normal file
15
vibn-frontend/docs/scripts/format_onboarding.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const fs = require('fs');
|
||||
const file = 'app/(onboarding)/onboarding/page.tsx';
|
||||
let code = fs.readFileSync(file, 'utf8');
|
||||
|
||||
// Prepend "use client" and imports
|
||||
const header = `"use client";\n\nimport React, { useState, useEffect, useMemo, Fragment } from "react";\nimport "./onboarding.css";\n\n`;
|
||||
|
||||
// Add export default to the function
|
||||
code = code.replace('function OnboardingApp() {', 'export default function OnboardingApp() {');
|
||||
|
||||
// Remove ReactDOM.createRoot
|
||||
code = code.replace('ReactDOM.createRoot(document.getElementById("root")).render(<OnboardingApp />);', '');
|
||||
|
||||
fs.writeFileSync(file, header + code);
|
||||
console.log("Reformatted page.tsx");
|
||||
26
vibn-frontend/docs/scripts/index.js
Normal file
26
vibn-frontend/docs/scripts/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const { onRequest } = require('firebase-functions/v2/https');
|
||||
const next = require('next');
|
||||
|
||||
const app = next({
|
||||
dev: false,
|
||||
conf: { distDir: '.next' },
|
||||
});
|
||||
|
||||
const handle = app.getRequestHandler();
|
||||
|
||||
// Prepare the app on module load
|
||||
const prepared = app.prepare();
|
||||
|
||||
exports.nextjsFunc = onRequest(
|
||||
{
|
||||
region: 'us-central1',
|
||||
memory: '2GiB',
|
||||
timeoutSeconds: 300,
|
||||
maxInstances: 10,
|
||||
minInstances: 0,
|
||||
},
|
||||
async (req, res) => {
|
||||
await prepared;
|
||||
return handle(req, res);
|
||||
}
|
||||
);
|
||||
43
vibn-frontend/docs/scripts/mcp-server.js
Normal file
43
vibn-frontend/docs/scripts/mcp-server.js
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Vibn MCP Server Entry Point
|
||||
*
|
||||
* This script starts the Vibn MCP server which exposes project data
|
||||
* and capabilities to AI assistants through the Model Context Protocol.
|
||||
*
|
||||
* Usage:
|
||||
* npm run mcp:server
|
||||
*
|
||||
* Or add to your AI assistant's MCP configuration:
|
||||
* {
|
||||
* "mcpServers": {
|
||||
* "vibn": {
|
||||
* "command": "node",
|
||||
* "args": ["path/to/vibn-frontend/mcp-server.js"]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
// Load environment variables
|
||||
require('dotenv').config({ path: path.join(__dirname, '.env.local') });
|
||||
|
||||
// Run the TypeScript server using tsx
|
||||
const server = spawn('npx', ['tsx', path.join(__dirname, 'lib/mcp/server.ts')], {
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
server.on('error', (error) => {
|
||||
console.error('Failed to start MCP server:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
server.on('exit', (code) => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
|
||||
269
vibn-frontend/docs/scripts/mcp-zed-bridge.js
Executable file
269
vibn-frontend/docs/scripts/mcp-zed-bridge.js
Executable file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Zed MCP bridge — translates JSON-RPC 2.0 (Zed's MCP client) to Vibn's
|
||||
* simpler {tool, params} HTTP API at /api/mcp.
|
||||
*
|
||||
* Usage:
|
||||
* node mcp-zed-bridge.js
|
||||
*
|
||||
* Env:
|
||||
* VIBN_MCP_URL — defaults to https://vibnai.com/api/mcp
|
||||
* VIBN_API_KEY — your vibn_sk_... token
|
||||
*/
|
||||
|
||||
const VIBNDEV_MCP_URL =
|
||||
process.env.VIBN_MCP_URL || "https://vibnai.com/api/mcp";
|
||||
const VIBNDEV_API_KEY = process.env.VIBN_API_KEY || "";
|
||||
|
||||
if (!VIBNDEV_API_KEY) {
|
||||
process.stderr.write("VIBN_API_KEY is required\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ── JSON-RPC helpers ─────────────────────────────────────────────────
|
||||
|
||||
let requestId = 0;
|
||||
|
||||
function sendJson(obj) {
|
||||
process.stdout.write(JSON.stringify(obj) + "\n");
|
||||
}
|
||||
|
||||
function log(msg) {
|
||||
process.stderr.write(`[vibn-bridge] ${msg}\n`);
|
||||
}
|
||||
|
||||
// ── Tool list from the API capability descriptor ──────────────────────
|
||||
|
||||
const TOOLS = [
|
||||
"workspace.describe",
|
||||
"gitea.credentials",
|
||||
"projects.list",
|
||||
"projects.get",
|
||||
"project.recent_errors",
|
||||
"project.error_detail",
|
||||
"project.error_resolve",
|
||||
"apps.list",
|
||||
"apps.get",
|
||||
"apps.create",
|
||||
"apps.update",
|
||||
"apps.rewire_git",
|
||||
"apps.delete",
|
||||
"apps.deploy",
|
||||
"apps.deployments",
|
||||
"apps.domains.list",
|
||||
"apps.domains.set",
|
||||
"apps.logs",
|
||||
"apps.exec",
|
||||
"apps.volumes.list",
|
||||
"apps.volumes.wipe",
|
||||
"apps.containers.up",
|
||||
"apps.containers.ps",
|
||||
"apps.repair",
|
||||
"apps.templates.list",
|
||||
"apps.templates.search",
|
||||
"apps.envs.list",
|
||||
"apps.envs.upsert",
|
||||
"apps.envs.delete",
|
||||
"databases.list",
|
||||
"databases.create",
|
||||
"databases.get",
|
||||
"databases.update",
|
||||
"databases.delete",
|
||||
"auth.list",
|
||||
"auth.create",
|
||||
"auth.delete",
|
||||
"domains.search",
|
||||
"domains.list",
|
||||
"domains.get",
|
||||
"domains.register",
|
||||
"domains.attach",
|
||||
"storage.describe",
|
||||
"storage.provision",
|
||||
"storage.inject_env",
|
||||
"gitea.repos.list",
|
||||
"gitea.repo.get",
|
||||
"gitea.repo.create",
|
||||
"gitea.file.read",
|
||||
"gitea.file.write",
|
||||
"gitea.file.delete",
|
||||
"gitea.branches.list",
|
||||
"gitea.branch.create",
|
||||
"devcontainer.ensure",
|
||||
"devcontainer.status",
|
||||
"devcontainer.suspend",
|
||||
"shell.exec",
|
||||
"fs.read",
|
||||
"fs.write",
|
||||
"fs.edit",
|
||||
"fs.list",
|
||||
"fs.tree",
|
||||
"fs.delete",
|
||||
"fs.glob",
|
||||
"fs.grep",
|
||||
"dev_server.start",
|
||||
"browser.console",
|
||||
"browser.navigate",
|
||||
"dev_server.stop",
|
||||
"dev_server.list",
|
||||
"dev_server.logs",
|
||||
"ship",
|
||||
].map((name) => ({
|
||||
name,
|
||||
description: `Vibn workspace tool: ${name}`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
projectId: { type: "string", description: "Vibn project ID" },
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// ── MCP protocol handlers ────────────────────────────────────────────
|
||||
|
||||
async function handleInitialize(id) {
|
||||
sendJson({
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: {
|
||||
protocolVersion: "2024-11-05",
|
||||
capabilities: { tools: {} },
|
||||
serverInfo: { name: "vibn-mcp-bridge", version: "1.0.0" },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function handleToolsList(id) {
|
||||
sendJson({
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: { tools: TOOLS },
|
||||
});
|
||||
}
|
||||
|
||||
async function handleToolsCall(id, params) {
|
||||
const { name, arguments: args } = params;
|
||||
|
||||
try {
|
||||
const res = await fetch(VIBNDEV_MCP_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${VIBNDEV_API_KEY}`,
|
||||
},
|
||||
body: JSON.stringify({ tool: name, params: args }),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
sendJson({
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(data, null, 2),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
sendJson({
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(data, null, 2),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
sendJson({
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error: ${err.message}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Main loop ─────────────────────────────────────────────────────────
|
||||
|
||||
let buffer = "";
|
||||
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
buffer += chunk;
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() || "";
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
let msg;
|
||||
try {
|
||||
msg = JSON.parse(line);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!msg.jsonrpc || !msg.method) continue;
|
||||
const id = msg.id;
|
||||
|
||||
// Wrap in async IIFE so unhandled rejections don't crash the bridge
|
||||
(async () => {
|
||||
try {
|
||||
switch (msg.method) {
|
||||
case "initialize":
|
||||
await handleInitialize(id);
|
||||
break;
|
||||
case "notifications/initialized":
|
||||
// No response needed
|
||||
break;
|
||||
case "tools/list":
|
||||
await handleToolsList(id);
|
||||
break;
|
||||
case "tools/call":
|
||||
await handleToolsCall(id, msg.params);
|
||||
break;
|
||||
default:
|
||||
sendJson({
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
error: {
|
||||
code: -32601,
|
||||
message: `Method not found: ${msg.method}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
log(`Error handling ${msg.method}: ${err.message}`);
|
||||
sendJson({
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
error: { code: -32603, message: `Internal error: ${err.message}` },
|
||||
});
|
||||
}
|
||||
})();
|
||||
}
|
||||
});
|
||||
|
||||
process.stdin.on("end", () => {
|
||||
log("stdin closed, exiting");
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
log(`bridge started, proxying to ${VIBNDEV_MCP_URL}`);
|
||||
11
vibn-frontend/docs/scripts/patch_local.js
Normal file
11
vibn-frontend/docs/scripts/patch_local.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const file = 'app/(onboarding)/onboarding/page.tsx';
|
||||
let code = fs.readFileSync(file, 'utf8');
|
||||
|
||||
code = code.replace(
|
||||
'try { return localStorage.getItem("vibn:firstName") || ""; } catch { return ""; }',
|
||||
'try { return typeof window !== "undefined" ? localStorage.getItem("vibn:firstName") || "" : ""; } catch { return ""; }'
|
||||
);
|
||||
|
||||
fs.writeFileSync(file, code);
|
||||
15
vibn-frontend/docs/scripts/patch_window.js
Normal file
15
vibn-frontend/docs/scripts/patch_window.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const file = 'app/(onboarding)/onboarding/page.tsx';
|
||||
let code = fs.readFileSync(file, 'utf8');
|
||||
|
||||
code = code.replace(
|
||||
'const close = () => { window.location.href = "index.html"; };',
|
||||
'const close = () => { if (typeof window !== "undefined") window.location.href = "/"; };'
|
||||
);
|
||||
code = code.replace(
|
||||
'const openChat = () => { window.location.href = "index.html"; };',
|
||||
'const openChat = () => { if (typeof window !== "undefined") window.location.href = "/"; };'
|
||||
);
|
||||
|
||||
fs.writeFileSync(file, code);
|
||||
137
vibn-frontend/docs/scripts/setup-e2e-test.sh
Executable file
137
vibn-frontend/docs/scripts/setup-e2e-test.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Helper script to set up and run E2E test
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " E2E TEST SETUP HELPER"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
echo -e "${BLUE}Step 1: Check if server is running...${NC}"
|
||||
if curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 | grep -q "200\|404"; then
|
||||
echo -e "${GREEN}✓ Server is running on http://localhost:3000${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Server not running. Starting it now...${NC}"
|
||||
npm run dev &
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Step 2: Get your credentials${NC}"
|
||||
echo ""
|
||||
echo -e "${CYAN}Please follow these steps:${NC}"
|
||||
echo ""
|
||||
echo "1. Open your browser and go to:"
|
||||
echo " ${BLUE}http://localhost:3000${NC}"
|
||||
echo ""
|
||||
echo "2. Sign in and create/open a project"
|
||||
echo ""
|
||||
echo "3. Go to AI Chat page"
|
||||
echo ""
|
||||
echo "4. Open DevTools (F12 or Cmd+Option+I)"
|
||||
echo ""
|
||||
echo "5. Go to Network tab"
|
||||
echo ""
|
||||
echo "6. Send any message (e.g., 'test')"
|
||||
echo ""
|
||||
echo "7. Find the request to '/api/ai/chat'"
|
||||
echo ""
|
||||
echo "8. Click it → Headers tab"
|
||||
echo ""
|
||||
echo "9. Copy the 'Authorization' header value"
|
||||
echo " (It looks like: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ij...)"
|
||||
echo ""
|
||||
echo "10. Copy your project ID from the URL"
|
||||
echo " (Format: /workspace/project/{PROJECT_ID}/v_ai_chat)"
|
||||
echo ""
|
||||
echo ""
|
||||
echo -e "${YELLOW}Ready to input your credentials?${NC}"
|
||||
read -p "Press Enter when ready..."
|
||||
echo ""
|
||||
|
||||
# Get auth token
|
||||
echo -e "${CYAN}Paste your Authorization header (including 'Bearer '):${NC}"
|
||||
read -r AUTH_TOKEN
|
||||
echo ""
|
||||
|
||||
# Get project ID
|
||||
echo -e "${CYAN}Paste your Project ID:${NC}"
|
||||
read -r PROJECT_ID
|
||||
echo ""
|
||||
|
||||
# Validate inputs
|
||||
if [ -z "$AUTH_TOKEN" ]; then
|
||||
echo -e "${YELLOW}⚠ Auth token is empty. Exiting.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$PROJECT_ID" ]; then
|
||||
echo -e "${YELLOW}⚠ Project ID is empty. Exiting.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Save to temporary file for reuse
|
||||
cat > .e2e-test-env << EOF
|
||||
export AUTH_TOKEN='$AUTH_TOKEN'
|
||||
export PROJECT_ID='$PROJECT_ID'
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Credentials saved to .e2e-test-env${NC}"
|
||||
echo ""
|
||||
|
||||
# Test connection
|
||||
echo -e "${BLUE}Step 3: Testing connection...${NC}"
|
||||
response=$(curl -s -X POST "http://localhost:3000/api/ai/chat" \
|
||||
-H "Authorization: $AUTH_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"projectId\":\"$PROJECT_ID\",\"message\":\"test\"}")
|
||||
|
||||
if echo "$response" | jq -e '.reply' > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ Connection successful!${NC}"
|
||||
reply=$(echo "$response" | jq -r '.reply' | head -c 100)
|
||||
echo -e "${CYAN}AI Response:${NC} $reply..."
|
||||
echo ""
|
||||
elif echo "$response" | jq -e '.error' > /dev/null 2>&1; then
|
||||
error=$(echo "$response" | jq -r '.error')
|
||||
echo -e "${YELLOW}⚠ API Error: $error${NC}"
|
||||
echo "Please check your credentials and try again."
|
||||
exit 1
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Unexpected response${NC}"
|
||||
echo "$response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}=========================================="
|
||||
echo " READY TO RUN E2E TEST"
|
||||
echo "==========================================${NC}"
|
||||
echo ""
|
||||
echo "Credentials saved! You can now run:"
|
||||
echo ""
|
||||
echo -e "${CYAN} source .e2e-test-env${NC}"
|
||||
echo -e "${CYAN} ./test-e2e-collector.sh${NC}"
|
||||
echo ""
|
||||
echo "Or simply:"
|
||||
echo ""
|
||||
echo -e "${CYAN} AUTH_TOKEN='$AUTH_TOKEN' PROJECT_ID='$PROJECT_ID' ./test-e2e-collector.sh${NC}"
|
||||
echo ""
|
||||
|
||||
# Offer to run now
|
||||
echo -e "${YELLOW}Run E2E test now? (y/n)${NC}"
|
||||
read -r run_now
|
||||
|
||||
if [ "$run_now" = "y" ] || [ "$run_now" = "Y" ]; then
|
||||
echo ""
|
||||
export AUTH_TOKEN
|
||||
export PROJECT_ID
|
||||
./test-e2e-collector.sh
|
||||
fi
|
||||
|
||||
161
vibn-frontend/docs/scripts/test-actual-user-flow.sh
Executable file
161
vibn-frontend/docs/scripts/test-actual-user-flow.sh
Executable file
@@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test script simulating actual user conversation flow
|
||||
# Project: Dr Dave EMR (Rcj5OY2xpQFHAzqUyMim)
|
||||
# Context: GitHub connected, no documents uploaded
|
||||
|
||||
set -e
|
||||
|
||||
PROJECT_ID="Rcj5OY2xpQFHAzqUyMim"
|
||||
BASE_URL="http://localhost:3000"
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Testing Actual User Conversation Flow${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Get Firebase token
|
||||
echo -e "${YELLOW}Step 1: Get Firebase Auth Token${NC}"
|
||||
|
||||
if [ -z "$FIREBASE_TOKEN" ]; then
|
||||
echo "Please provide your Firebase ID token:"
|
||||
echo "(or set FIREBASE_TOKEN environment variable)"
|
||||
read -r FIREBASE_TOKEN
|
||||
fi
|
||||
|
||||
if [ -z "$FIREBASE_TOKEN" ]; then
|
||||
echo -e "${RED}Error: Firebase token is required${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Token received${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to send chat message
|
||||
send_message() {
|
||||
local message="$1"
|
||||
local step_name="$2"
|
||||
|
||||
echo -e "${YELLOW}${step_name}${NC}"
|
||||
echo -e "User: ${message}"
|
||||
|
||||
local response=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${FIREBASE_TOKEN}" \
|
||||
-d "{
|
||||
\"message\": \"${message}\",
|
||||
\"projectId\": \"${PROJECT_ID}\"
|
||||
}")
|
||||
|
||||
local ai_reply=$(echo "$response" | jq -r '.reply // .error // "No reply"')
|
||||
echo -e "AI: ${ai_reply}"
|
||||
echo ""
|
||||
|
||||
# Return the full response for checking
|
||||
echo "$response"
|
||||
}
|
||||
|
||||
# Function to check project phase
|
||||
check_project_phase() {
|
||||
local step_name="$1"
|
||||
|
||||
echo -e "${YELLOW}${step_name}${NC}"
|
||||
|
||||
# Get project data from Firestore via API
|
||||
local project_data=$(curl -s "${BASE_URL}/api/projects/${PROJECT_ID}" \
|
||||
-H "Authorization: Bearer ${FIREBASE_TOKEN}")
|
||||
|
||||
local current_phase=$(echo "$project_data" | jq -r '.currentPhase // "unknown"')
|
||||
local handoff_ready=$(echo "$project_data" | jq -r '.phaseData.phaseHandoffs.collector.readyForNextPhase // false')
|
||||
local has_extraction=$(echo "$project_data" | jq -r '.phaseData.phaseHandoffs.extraction // "null"')
|
||||
|
||||
echo "Current Phase: ${current_phase}"
|
||||
echo "Collector Ready: ${handoff_ready}"
|
||||
echo "Has Extraction Handoff: $([ "$has_extraction" != "null" ] && echo "YES" || echo "NO")"
|
||||
echo ""
|
||||
|
||||
echo "$project_data"
|
||||
}
|
||||
|
||||
# Simulate the actual user conversation
|
||||
echo -e "${BLUE}Starting conversation simulation...${NC}"
|
||||
echo ""
|
||||
|
||||
# Message 1: Initial greeting (AI should welcome and ask what they have)
|
||||
response1=$(send_message "Hello" "Message 1: Initial greeting")
|
||||
sleep 2
|
||||
|
||||
# Message 2: User mentions GitHub repo
|
||||
response2=$(send_message "I have a GitHub repo connected" "Message 2: User mentions GitHub")
|
||||
sleep 2
|
||||
|
||||
# Message 3: User confirms they have everything
|
||||
response3=$(send_message "that's everything" "Message 3: User confirms ready")
|
||||
sleep 2
|
||||
|
||||
# Check if handoff was triggered
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Checking Handoff Contract${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
project_state=$(check_project_phase "Step 1: Check project state immediately after 'that's everything'")
|
||||
|
||||
# Wait a bit for async extraction to run
|
||||
echo -e "${YELLOW}Waiting 5 seconds for backend extraction to complete...${NC}"
|
||||
sleep 5
|
||||
|
||||
project_state_after=$(check_project_phase "Step 2: Check project state after backend extraction")
|
||||
|
||||
# Extract key values
|
||||
current_phase=$(echo "$project_state_after" | jq -r '.currentPhase // "unknown"')
|
||||
ready_for_next=$(echo "$project_state_after" | jq -r '.phaseData.phaseHandoffs.collector.readyForNextPhase // false')
|
||||
has_extraction=$(echo "$project_state_after" | jq -r '.phaseData.phaseHandoffs.extraction // "null"')
|
||||
|
||||
# Message 4: Send another message to see if AI is in extraction review mode
|
||||
response4=$(send_message "what did you find?" "Message 4: Ask about findings (should be in extraction_review mode)")
|
||||
|
||||
# Verify results
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Test Results${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
if [ "$ready_for_next" = "true" ]; then
|
||||
echo -e "${GREEN}✓ Collector handoff.readyForNextPhase = true${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Collector handoff.readyForNextPhase = false (expected true)${NC}"
|
||||
fi
|
||||
|
||||
if [ "$has_extraction" != "null" ]; then
|
||||
echo -e "${GREEN}✓ Extraction handoff exists${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Extraction handoff missing${NC}"
|
||||
fi
|
||||
|
||||
if [ "$current_phase" = "extraction_review" ]; then
|
||||
echo -e "${GREEN}✓ Phase transitioned to extraction_review${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Phase is '${current_phase}' (expected 'extraction_review')${NC}"
|
||||
fi
|
||||
|
||||
# Check if AI's final response mentions "processing" or "analyzing"
|
||||
if echo "$response4" | jq -r '.reply' | grep -qi "processing\|analyzing\|let me analyze"; then
|
||||
echo -e "${RED}✗ AI is still saying it's processing/analyzing (should present results)${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓ AI is not hallucinating processing state${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Full Project State${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo "$project_state_after" | jq '.'
|
||||
|
||||
167
vibn-frontend/docs/scripts/test-backend-extraction.sh
Executable file
167
vibn-frontend/docs/scripts/test-backend-extraction.sh
Executable file
@@ -0,0 +1,167 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test Backend-Led Extraction Flow
|
||||
# Tests the complete flow: create → collect → backend extract → review
|
||||
|
||||
set -e
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
BASE_URL="http://localhost:3000"
|
||||
TOKEN="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4MDI5MzRmZTBlZWM0NmE1ZWQwMDA2ZDE0YTFiYWIwMWUzNDUwODMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiTWFyayBIZW5kZXJzb24iLCJwaWN0dXJlIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzUzOTU0MjEzP3Y9NCIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9nZW4tbGFuZy1jbGllbnQtMDk4MDA3OTQxMCIsImF1ZCI6Imdlbi1sYW5nLWNsaWVudC0wOTgwMDc5NDEwIiwiYXV0aF90aW1lIjoxNzYzMzI1MDEyLCJ1c2VyX2lkIjoiMmhDdmdXQzJaV2RJMGVlTm5SQVM3SWVKcmg1MiIsInN1YiI6IjJoQ3ZnV0MyWldkSTBlZU5uUkFTN0llSnJoNTIiLCJpYXQiOjE3NjM0MjI1NDUsImV4cCI6MTc2MzQyNjE0NSwiZW1haWwiOiJtYXJrQGdldGFjcXVpcmVkLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnaXRodWIuY29tIjpbIjUzOTU0MjEzIl0sImVtYWlsIjpbIm1hcmtAZ2V0YWNxdWlyZWQuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ2l0aHViLmNvbSJ9fQ.TpMOORDnPUKkbLlg-KtYBmbarEjAijJ3W4vN8tWT6OslOfwaeDJAtPXIahyQk38UvKY4ZGognQG6t-laSATB8yIC8IdkYbD699axfPSGQqC8Lbux1P6YrFKOPLGDD2XemBtJ-Gb5Ql-nK_DbXKAmygLxIwz019XpLJEucGkBPAN_Rj2xC7125DVexkDSIb6ZnbLiDgCpR_IkImyQb08tqlOoBiHVUa-4VGDhraoBPACJfQXwPToJ1W3nhBiVtMvSq7s_Ekd8Otn8AB_1teu5lxC-rhLdgJuNrmlxO-H6xIMBFZ72bwq7wrvdWd_EijqFQCU99oEhphTNoISoJ3wK-g"
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Backend Extraction Flow Test${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
# Step 1: Create Project
|
||||
echo -e "${YELLOW}[Step 1]${NC} Creating new project..."
|
||||
|
||||
RANDOM_ID=$RANDOM
|
||||
PROJECT_NAME="Backend Extract Test ${RANDOM_ID}"
|
||||
|
||||
CREATE_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/projects/create" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectName\": \"${PROJECT_NAME}\",
|
||||
\"projectType\": \"scratch\",
|
||||
\"slug\": \"backend-extract-test-${RANDOM_ID}\",
|
||||
\"product\": {
|
||||
\"name\": \"${PROJECT_NAME}\"
|
||||
}
|
||||
}")
|
||||
|
||||
PROJECT_ID=$(echo "$CREATE_RESPONSE" | jq -r '.projectId // empty')
|
||||
|
||||
if [ -z "$PROJECT_ID" ]; then
|
||||
echo -e "${RED}✗ Failed to create project${NC}"
|
||||
echo "Response: $CREATE_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Project created: ${PROJECT_ID}${NC}\n"
|
||||
|
||||
# Step 2: Upload a test document
|
||||
echo -e "${YELLOW}[Step 2]${NC} Uploading test document..."
|
||||
|
||||
TEST_DOC_PATH="/tmp/test-doc-$RANDOM.md"
|
||||
cat > "$TEST_DOC_PATH" << 'EOF'
|
||||
# Product Requirements: TaskFlow
|
||||
|
||||
## Problem
|
||||
Freelancers and small agencies struggle to manage multiple client projects simultaneously. They lose track of deadlines, forget client requests, and spend too much time on administrative work instead of billable hours.
|
||||
|
||||
## Target Users
|
||||
- Freelance designers and developers
|
||||
- Small creative agencies (2-10 people)
|
||||
- Solo consultants managing multiple clients
|
||||
|
||||
## Core Features
|
||||
1. **Client Dashboard**: One view per client showing all active projects
|
||||
2. **Smart Task Capture**: Quickly log tasks from emails, calls, or chat
|
||||
3. **Automated Reminders**: AI suggests when to follow up based on task age
|
||||
4. **Time Tracking**: Simple timer integrated into task view
|
||||
5. **Client Portal**: Clients can see progress without asking for updates
|
||||
|
||||
## Technical Constraints
|
||||
- Must work offline (PWA)
|
||||
- Mobile-first design
|
||||
- Budget: $10k development budget
|
||||
- Timeline: Launch MVP in 8 weeks
|
||||
|
||||
## Business Model
|
||||
- $15/month per user
|
||||
- 14-day free trial
|
||||
- Annual plans at 20% discount
|
||||
EOF
|
||||
|
||||
UPLOAD_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/projects/${PROJECT_ID}/knowledge/upload-document" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-F "file=@${TEST_DOC_PATH}")
|
||||
|
||||
UPLOAD_SUCCESS=$(echo "$UPLOAD_RESPONSE" | jq -r '.success // false')
|
||||
|
||||
if [ "$UPLOAD_SUCCESS" != "true" ]; then
|
||||
echo -e "${RED}✗ Document upload failed${NC}"
|
||||
echo "Response: $UPLOAD_RESPONSE"
|
||||
rm -f "$TEST_DOC_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Document uploaded${NC}"
|
||||
rm -f "$TEST_DOC_PATH"
|
||||
|
||||
# Step 3: Tell AI about docs and confirm ready
|
||||
echo -e "\n${YELLOW}[Step 3]${NC} Telling AI about documents..."
|
||||
|
||||
CHAT1=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectId\": \"${PROJECT_ID}\",
|
||||
\"message\": \"I uploaded a requirements document\"
|
||||
}")
|
||||
|
||||
echo -e "${GREEN}✓ AI acknowledged${NC}"
|
||||
|
||||
# Step 4: Say "that's everything" to trigger extraction
|
||||
echo -e "\n${YELLOW}[Step 4]${NC} Confirming ready for extraction..."
|
||||
|
||||
CHAT2=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectId\": \"${PROJECT_ID}\",
|
||||
\"message\": \"Yes, that's everything. Please analyze it.\"
|
||||
}")
|
||||
|
||||
echo -e "${GREEN}✓ Extraction triggered${NC}"
|
||||
echo -e "${YELLOW}⏳ Backend extraction running (this takes 10-30 seconds)...${NC}\n"
|
||||
|
||||
# Wait for extraction to complete
|
||||
sleep 15
|
||||
|
||||
# Step 5: Check if extraction completed
|
||||
echo -e "${YELLOW}[Step 5]${NC} Checking extraction results..."
|
||||
|
||||
# Send a message to see if AI presents results
|
||||
CHAT3=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectId\": \"${PROJECT_ID}\",
|
||||
\"message\": \"What did you find?\"
|
||||
}")
|
||||
|
||||
AI_REPLY=$(echo "$CHAT3" | jq -r '.reply // empty')
|
||||
|
||||
# Check if AI is still saying "processing"
|
||||
if echo "$AI_REPLY" | grep -qi "processing\|synthesizing\|analyzing"; then
|
||||
echo -e "${RED}✗ AI still says 'processing' - extraction may not have completed${NC}"
|
||||
echo -e "\nAI Response:"
|
||||
echo "$AI_REPLY"
|
||||
echo -e "\n${YELLOW}Try waiting a bit longer, then check: ${BASE_URL}/default/project/${PROJECT_ID}/v_ai_chat${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if AI is presenting results
|
||||
if echo "$AI_REPLY" | grep -qi "found\|identified\|problems\|features\|users"; then
|
||||
echo -e "${GREEN}✓ AI is presenting extraction results!${NC}"
|
||||
echo -e "\n${BLUE}AI Response:${NC}"
|
||||
echo "$AI_REPLY" | fold -w 80 -s
|
||||
echo -e "\n${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN}✓ Backend extraction working!${NC}"
|
||||
echo -e "${GREEN}========================================${NC}\n"
|
||||
echo -e "View full chat: ${BASE_URL}/default/project/${PROJECT_ID}/v_ai_chat"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Unclear if extraction worked${NC}"
|
||||
echo -e "\nAI Response:"
|
||||
echo "$AI_REPLY"
|
||||
echo -e "\n${YELLOW}Check manually: ${BASE_URL}/default/project/${PROJECT_ID}/v_ai_chat${NC}"
|
||||
fi
|
||||
|
||||
303
vibn-frontend/docs/scripts/test-e2e-collector.sh
Executable file
303
vibn-frontend/docs/scripts/test-e2e-collector.sh
Executable file
@@ -0,0 +1,303 @@
|
||||
#!/bin/bash
|
||||
|
||||
# End-to-End Collector Flow Test
|
||||
# Simulates a real user journey from welcome → document upload → GitHub → extension → handoff
|
||||
|
||||
# set -e # Don't exit on error - show all test results
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
BASE_URL="http://localhost:3000"
|
||||
API_BASE="$BASE_URL/api"
|
||||
|
||||
# You MUST set these from a real logged-in session
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " E2E COLLECTOR FLOW TEST"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo -e "${YELLOW}SETUP REQUIRED:${NC}"
|
||||
echo "1. Log into http://localhost:3000"
|
||||
echo "2. Open DevTools → Network tab"
|
||||
echo "3. Send a test message in AI Chat"
|
||||
echo "4. Copy the 'Authorization: Bearer XXX' header"
|
||||
echo "5. Create a project and copy the projectId"
|
||||
echo ""
|
||||
echo -e "${CYAN}Then run:${NC}"
|
||||
echo " export AUTH_TOKEN='your-token-here'"
|
||||
echo " export PROJECT_ID='your-project-id-here'"
|
||||
echo " ./test-e2e-collector.sh"
|
||||
echo ""
|
||||
|
||||
if [ -z "$AUTH_TOKEN" ] || [ -z "$PROJECT_ID" ]; then
|
||||
echo -e "${RED}ERROR: AUTH_TOKEN and PROJECT_ID must be set${NC}"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test results
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
FAILED_TESTS=()
|
||||
|
||||
# Helper functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
((TESTS_PASSED++))
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
((TESTS_FAILED++))
|
||||
FAILED_TESTS+=("$1")
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_response() {
|
||||
echo -e "${CYAN}[RESPONSE]${NC} $1"
|
||||
}
|
||||
|
||||
# Send a chat message and check response
|
||||
send_chat_message() {
|
||||
local message="$1"
|
||||
local expected_keywords="$2"
|
||||
|
||||
log_info "Sending: \"$message\""
|
||||
|
||||
response=$(curl -s -X POST "$API_BASE/ai/chat" \
|
||||
-H "Authorization: Bearer $AUTH_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"projectId\":\"$PROJECT_ID\",\"message\":\"$message\"}")
|
||||
|
||||
http_code=$?
|
||||
|
||||
if [ $http_code -ne 0 ]; then
|
||||
log_error "Failed to send message: $message"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check for error in response
|
||||
if echo "$response" | jq -e '.error' > /dev/null 2>&1; then
|
||||
error_msg=$(echo "$response" | jq -r '.error')
|
||||
log_error "API Error: $error_msg"
|
||||
echo "$response" | jq '.' 2>/dev/null || echo "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract reply
|
||||
reply=$(echo "$response" | jq -r '.reply' 2>/dev/null)
|
||||
|
||||
if [ -z "$reply" ] || [ "$reply" = "null" ]; then
|
||||
log_error "No reply received"
|
||||
echo "$response" | jq '.' 2>/dev/null || echo "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_response "$(echo "$reply" | head -c 200)..."
|
||||
|
||||
# Check for expected keywords
|
||||
if [ -n "$expected_keywords" ]; then
|
||||
all_found=true
|
||||
IFS='|' read -ra KEYWORDS <<< "$expected_keywords"
|
||||
for keyword in "${KEYWORDS[@]}"; do
|
||||
if echo "$reply" | grep -qi "$keyword"; then
|
||||
log_success "Response contains: '$keyword'"
|
||||
else
|
||||
log_error "Response missing expected keyword: '$keyword'"
|
||||
all_found=false
|
||||
fi
|
||||
done
|
||||
|
||||
if $all_found; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check collector handoff state
|
||||
check_handoff_state() {
|
||||
local expected_docs="$1"
|
||||
local expected_github="$2"
|
||||
local expected_extension="$3"
|
||||
|
||||
log_info "Checking collector handoff state..."
|
||||
|
||||
# This would require Firestore access or an API endpoint
|
||||
# For now, we'll just log what we expect
|
||||
log_info "Expected state:"
|
||||
echo " - hasDocuments: $expected_docs"
|
||||
echo " - githubConnected: $expected_github"
|
||||
echo " - extensionLinked: $expected_extension"
|
||||
|
||||
# TODO: Add actual Firestore check or API endpoint
|
||||
log_warning "Handoff state check not implemented (would need Firestore access)"
|
||||
}
|
||||
|
||||
# Simulate document upload
|
||||
simulate_document_upload() {
|
||||
local filename="$1"
|
||||
|
||||
log_info "Simulating upload: $filename"
|
||||
|
||||
# Create a temporary test file
|
||||
temp_file=$(mktemp)
|
||||
echo "This is a test document for QA purposes.
|
||||
|
||||
Project Overview:
|
||||
- We're building a SaaS platform
|
||||
- Target users: Small businesses
|
||||
- Key features: User management, billing, analytics
|
||||
|
||||
Technical Stack:
|
||||
- Frontend: React, Next.js
|
||||
- Backend: Node.js, PostgreSQL
|
||||
- Infrastructure: AWS
|
||||
|
||||
This is test content for $filename" > "$temp_file"
|
||||
|
||||
# Upload via API
|
||||
response=$(curl -s -X POST "$API_BASE/projects/$PROJECT_ID/knowledge/upload-document" \
|
||||
-H "Authorization: Bearer $AUTH_TOKEN" \
|
||||
-F "file=@$temp_file;filename=$filename")
|
||||
|
||||
rm "$temp_file"
|
||||
|
||||
if echo "$response" | jq -e '.success' > /dev/null 2>&1; then
|
||||
knowledge_id=$(echo "$response" | jq -r '.knowledgeItemId')
|
||||
log_success "Uploaded: $filename (ID: $knowledge_id)"
|
||||
return 0
|
||||
else
|
||||
error_msg=$(echo "$response" | jq -r '.error' 2>/dev/null || echo "Unknown error")
|
||||
log_error "Upload failed for $filename: $error_msg"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main test flow
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " RUNNING E2E COLLECTOR TESTS"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Project ID: $PROJECT_ID"
|
||||
echo ""
|
||||
|
||||
# Step 1: Initial greeting (auto-sent "Hello")
|
||||
log_info "=== STEP 1: Welcome Message ==="
|
||||
send_chat_message "Hello" "Welcome|Step 1|Step 2|Step 3|documents|GitHub|extension"
|
||||
sleep 2
|
||||
|
||||
# Step 2: Upload documents
|
||||
log_info ""
|
||||
log_info "=== STEP 2: Upload Documents ==="
|
||||
|
||||
docs=(
|
||||
"project-overview.md"
|
||||
"user-stories.md"
|
||||
"technical-requirements.md"
|
||||
"api-specification.md"
|
||||
"database-schema.md"
|
||||
"ui-mockups.md"
|
||||
"business-requirements.md"
|
||||
"deployment-plan.md"
|
||||
)
|
||||
|
||||
for doc in "${docs[@]}"; do
|
||||
simulate_document_upload "$doc"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Step 3: Tell AI about documents
|
||||
log_info ""
|
||||
log_info "=== STEP 3: Inform AI About Documents ==="
|
||||
send_chat_message "I just uploaded 8 documents about my project" "uploaded|document"
|
||||
sleep 2
|
||||
|
||||
# Step 4: Connect GitHub
|
||||
log_info ""
|
||||
log_info "=== STEP 4: GitHub Connection ==="
|
||||
send_chat_message "Yes, I have a GitHub repo. It's called myuser/my-saas-app" "GitHub|repo|connected"
|
||||
sleep 2
|
||||
|
||||
# Note: Actual GitHub connection requires OAuth flow
|
||||
log_warning "GitHub OAuth flow requires manual browser interaction"
|
||||
log_info "In real testing, user would click 'Connect GitHub' button"
|
||||
|
||||
# Step 5: Extension
|
||||
log_info ""
|
||||
log_info "=== STEP 5: Extension Installation ==="
|
||||
send_chat_message "I want to install the browser extension" "extension|install|capture"
|
||||
sleep 2
|
||||
|
||||
# Step 6: Confirm ready
|
||||
log_info ""
|
||||
log_info "=== STEP 6: Confirm Everything ==="
|
||||
send_chat_message "Yes, that's everything I have for now" "everything|analyze|dig"
|
||||
sleep 2
|
||||
|
||||
# Step 7: Check for auto-transition
|
||||
log_info ""
|
||||
log_info "=== STEP 7: Verify Auto-Transition ==="
|
||||
send_chat_message "What do you need from me?" "extraction|review|important|V1"
|
||||
sleep 2
|
||||
|
||||
# Step 8: Check handoff state
|
||||
log_info ""
|
||||
log_info "=== STEP 8: Verify Handoff State ==="
|
||||
check_handoff_state "true" "true" "false"
|
||||
|
||||
# Results
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " TEST RESULTS"
|
||||
echo "=========================================="
|
||||
echo -e "${GREEN}Passed:${NC} $TESTS_PASSED"
|
||||
echo -e "${RED}Failed:${NC} $TESTS_FAILED"
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_FAILED -gt 0 ]; then
|
||||
echo -e "${RED}Failed checks:${NC}"
|
||||
for test in "${FAILED_TESTS[@]}"; do
|
||||
echo " - $test"
|
||||
done
|
||||
echo ""
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}✅ E2E COLLECTOR FLOW COMPLETE!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Open http://localhost:3000 in browser"
|
||||
echo "2. Navigate to the project"
|
||||
echo "3. Check AI Chat page - verify checklist shows:"
|
||||
echo " ✅ Documents uploaded (8)"
|
||||
echo " ✅ GitHub connected"
|
||||
echo " ⭕ Extension linked"
|
||||
echo "4. Verify mode switched to 'Extraction Review'"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Run tests
|
||||
cd "$(dirname "$0")"
|
||||
main
|
||||
|
||||
62
vibn-frontend/docs/scripts/test-gemini-3-global.js
Normal file
62
vibn-frontend/docs/scripts/test-gemini-3-global.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const { VertexAI } = require('@google-cloud/vertexai');
|
||||
|
||||
async function testGemini3Global() {
|
||||
console.log('🧪 Testing Gemini 3 Pro Preview in GLOBAL location...\n');
|
||||
|
||||
const vertexAI = new VertexAI({
|
||||
project: 'gen-lang-client-0980079410',
|
||||
location: 'global', // ← KEY: Use 'global' not regional!
|
||||
});
|
||||
|
||||
console.log('Model: gemini-3-pro-preview');
|
||||
console.log('Location: global');
|
||||
console.log('Project: gen-lang-client-0980079410\n');
|
||||
|
||||
try {
|
||||
const model = vertexAI.getGenerativeModel({
|
||||
model: 'gemini-3-pro-preview',
|
||||
systemInstruction: 'You are a helpful AI assistant.',
|
||||
generationConfig: {
|
||||
temperature: 1.0,
|
||||
responseMimeType: 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
console.log('⏳ Sending test request...\n');
|
||||
|
||||
const response = await model.generateContent({
|
||||
contents: [{
|
||||
role: 'user',
|
||||
parts: [{
|
||||
text: 'Return JSON with this exact structure: {"status": "success", "model": "gemini-3-pro-preview", "message": "Gemini 3 is working!", "features": ["thinking_mode", "1M_context", "multimodal"]}'
|
||||
}],
|
||||
}],
|
||||
});
|
||||
|
||||
const text = response.response?.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
||||
console.log('✅ RAW RESPONSE:\n', text, '\n');
|
||||
|
||||
const parsed = JSON.parse(text);
|
||||
|
||||
if (parsed.status === 'success') {
|
||||
console.log('🎉🎉🎉 SUCCESS! GEMINI 3 PRO PREVIEW IS WORKING! 🎉🎉🎉\n');
|
||||
console.log('✅ Model:', parsed.model);
|
||||
console.log('✅ Message:', parsed.message);
|
||||
console.log('✅ Features:', parsed.features?.join(', '));
|
||||
console.log('\n🚀 You have full access to Gemini 3 Pro Preview!');
|
||||
console.log('📊 1M token context | Thinking mode | Multimodal');
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error.message);
|
||||
if (error.message.includes('404')) {
|
||||
console.log('\n💡 Model still not accessible. You may need to:');
|
||||
console.log(' 1. Enable the model in the console');
|
||||
console.log(' 2. Accept usage terms');
|
||||
console.log(' 3. Wait for API access approval');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
testGemini3Global();
|
||||
39
vibn-frontend/docs/scripts/test-gemini-3-simple.js
Normal file
39
vibn-frontend/docs/scripts/test-gemini-3-simple.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const { VertexAI } = require('@google-cloud/vertexai');
|
||||
|
||||
async function testSimple() {
|
||||
console.log('🧪 Testing Gemini 3 Pro Preview (simple text)...\n');
|
||||
|
||||
const vertexAI = new VertexAI({
|
||||
project: 'gen-lang-client-0980079410',
|
||||
location: 'global',
|
||||
});
|
||||
|
||||
try {
|
||||
const model = vertexAI.getGenerativeModel({
|
||||
model: 'gemini-3-pro-preview',
|
||||
generationConfig: { temperature: 1.0 },
|
||||
});
|
||||
|
||||
console.log('⏳ Sending simple text request...\n');
|
||||
|
||||
const response = await model.generateContent({
|
||||
contents: [{
|
||||
role: 'user',
|
||||
parts: [{ text: 'Say "Hello from Gemini 3!" in exactly those words.' }],
|
||||
}],
|
||||
});
|
||||
|
||||
const text = response.response?.candidates?.[0]?.content?.parts?.[0]?.text || 'No response';
|
||||
console.log('✅ Response:', text, '\n');
|
||||
|
||||
if (text.includes('Gemini 3') || text.includes('Hello')) {
|
||||
console.log('🎉 SUCCESS! Gemini 3 Pro Preview is responding!');
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Full error:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
testSimple();
|
||||
56
vibn-frontend/docs/scripts/test-handoff-persistence.sh
Executable file
56
vibn-frontend/docs/scripts/test-handoff-persistence.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test Collector Handoff Persistence
|
||||
# Sends messages and checks if handoff data is being persisted
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
PROJECT_ID="lyOZxelSkjAB6XisIzup"
|
||||
BASE_URL="http://localhost:3000"
|
||||
TOKEN="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4MDI5MzRmZTBlZWM0NmE1ZWQwMDA2ZDE0YTFiYWIwMWUzNDUwODMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiTWFyayBIZW5kZXJzb24iLCJwaWN0dXJlIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzUzOTU0MjEzP3Y9NCIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9nZW4tbGFuZy1jbGllbnQtMDk4MDA3OTQxMCIsImF1ZCI6Imdlbi1sYW5nLWNsaWVudC0wOTgwMDc5NDEwIiwiYXV0aF90aW1lIjoxNzYzMzI1MDEyLCJ1c2VyX2lkIjoiMmhDdmdXQzJaV2RJMGVlTm5SQVM3SWVKcmg1MiIsInN1YiI6IjJoQ3ZnV0MyWldkSTBlZU5uUkFTN0llSnJoNTIiLCJpYXQiOjE3NjM0MjI1NDUsImV4cCI6MTc2MzQyNjE0NSwiZW1haWwiOiJtYXJrQGdldGFjcXVpcmVkLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnaXRodWIuY29tIjpbIjUzOTU0MjEzIl0sImVtYWlsIjpbIm1hcmtAZ2V0YWNxdWlyZWQuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ2l0aHViLmNvbSJ9fQ.TpMOORDnPUKkbLlg-KtYBmbarEjAijJ3W4vN8tWT6OslOfwaeDJAtPXIahyQk38UvKY4ZGognQG6t-laSATB8yIC8IdkYbD699axfPSGQqC8Lbux1P6YrFKOPLGDD2XemBtJ-Gb5Ql-nK_DbXKAmygLxIwz019XpLJEucGkBPAN_Rj2xC7125DVexkDSIb6ZnbLiDgCpR_IkImyQb08tqlOoBiHVUa-4VGDhraoBPACJfQXwPToJ1W3nhBiVtMvSq7s_Ekd8Otn8AB_1teu5lxC-rhLdgJuNrmlxO-H6xIMBFZ72bwq7wrvdWd_EijqFQCU99oEhphTNoISoJ3wK-g"
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Testing Handoff Persistence${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
# Send a message and capture full response with verbose logging
|
||||
echo -e "${YELLOW}[Test]${NC} Sending message to AI with verbose logging enabled...\n"
|
||||
|
||||
# Enable curl verbose output and check server response
|
||||
curl -v -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"projectId": "'"${PROJECT_ID}"'", "message": "I have uploaded 1 document. I do not have GitHub or the extension yet."}' \
|
||||
2>&1 | tee /tmp/curl-output.txt
|
||||
|
||||
echo -e "\n\n${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Analysis${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
# Check if the response contains expected fields
|
||||
if grep -q '"mode":"collector_mode"' /tmp/curl-output.txt; then
|
||||
echo -e "${GREEN}✓ Mode is collector_mode${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Mode not detected${NC}"
|
||||
fi
|
||||
|
||||
if grep -q '"projectPhase"' /tmp/curl-output.txt; then
|
||||
echo -e "${GREEN}✓ Project phase returned${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Project phase not returned${NC}"
|
||||
fi
|
||||
|
||||
# Look for any indication of handoff data in server logs
|
||||
echo -e "\n${YELLOW}Note:${NC} The collectorHandoff data is persisted to Firestore,"
|
||||
echo -e "but not returned in the API response. This is expected."
|
||||
echo -e "\nTo verify persistence, check:"
|
||||
echo -e " 1. Server console logs for '[AI Chat] Collector handoff persisted'"
|
||||
echo -e " 2. Firebase Console: projects/${PROJECT_ID}/phaseData/phaseHandoffs/collector"
|
||||
echo -e " 3. UI checklist: ${BASE_URL}/default/project/${PROJECT_ID}/v_ai_chat\n"
|
||||
|
||||
rm -f /tmp/curl-output.txt
|
||||
|
||||
216
vibn-frontend/docs/scripts/test-simplified-flow.sh
Executable file
216
vibn-frontend/docs/scripts/test-simplified-flow.sh
Executable file
@@ -0,0 +1,216 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simplified E2E Test - Create Project → AI Chat → Collector Flow
|
||||
# This tests the new streamlined project creation flow
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
BASE_URL="http://localhost:3000"
|
||||
TOKEN="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4MDI5MzRmZTBlZWM0NmE1ZWQwMDA2ZDE0YTFiYWIwMWUzNDUwODMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiTWFyayBIZW5kZXJzb24iLCJwaWN0dXJlIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzUzOTU0MjEzP3Y9NCIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9nZW4tbGFuZy1jbGllbnQtMDk4MDA3OTQxMCIsImF1ZCI6Imdlbi1sYW5nLWNsaWVudC0wOTgwMDc5NDEwIiwiYXV0aF90aW1lIjoxNzYzMzI1MDEyLCJ1c2VyX2lkIjoiMmhDdmdXQzJaV2RJMGVlTm5SQVM3SWVKcmg1MiIsInN1YiI6IjJoQ3ZnV0MyWldkSTBlZU5uUkFTN0llSnJoNTIiLCJpYXQiOjE3NjM0MjI1NDUsImV4cCI6MTc2MzQyNjE0NSwiZW1haWwiOiJtYXJrQGdldGFjcXVpcmVkLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnaXRodWIuY29tIjpbIjUzOTU0MjEzIl0sImVtYWlsIjpbIm1hcmtAZ2V0YWNxdWlyZWQuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ2l0aHViLmNvbSJ9fQ.TpMOORDnPUKkbLlg-KtYBmbarEjAijJ3W4vN8tWT6OslOfwaeDJAtPXIahyQk38UvKY4ZGognQG6t-laSATB8yIC8IdkYbD699axfPSGQqC8Lbux1P6YrFKOPLGDD2XemBtJ-Gb5Ql-nK_DbXKAmygLxIwz019XpLJEucGkBPAN_Rj2xC7125DVexkDSIb6ZnbLiDgCpR_IkImyQb08tqlOoBiHVUa-4VGDhraoBPACJfQXwPToJ1W3nhBiVtMvSq7s_Ekd8Otn8AB_1teu5lxC-rhLdgJuNrmlxO-H6xIMBFZ72bwq7wrvdWd_EijqFQCU99oEhphTNoISoJ3wK-g"
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Simplified Project Creation Test${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
# Step 1: Create Project
|
||||
echo -e "${YELLOW}[Step 1]${NC} Creating new project..."
|
||||
|
||||
RANDOM_ID=$RANDOM
|
||||
PROJECT_NAME="E2E Test ${RANDOM_ID}"
|
||||
|
||||
CREATE_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/projects/create" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectName\": \"${PROJECT_NAME}\",
|
||||
\"projectType\": \"scratch\",
|
||||
\"slug\": \"e2e-test-${RANDOM_ID}\",
|
||||
\"product\": {
|
||||
\"name\": \"${PROJECT_NAME}\"
|
||||
}
|
||||
}")
|
||||
|
||||
PROJECT_ID=$(echo "$CREATE_RESPONSE" | jq -r '.projectId // empty')
|
||||
|
||||
if [ -z "$PROJECT_ID" ]; then
|
||||
echo -e "${RED}✗ Failed to create project${NC}"
|
||||
echo "Response: $CREATE_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Project created: ${PROJECT_ID}${NC}\n"
|
||||
|
||||
# Step 2: Send first message to AI (should trigger welcome)
|
||||
echo -e "${YELLOW}[Step 2]${NC} Sending first message to AI..."
|
||||
|
||||
AI_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectId\": \"${PROJECT_ID}\",
|
||||
\"message\": \"Hello\"
|
||||
}")
|
||||
|
||||
AI_REPLY=$(echo "$AI_RESPONSE" | jq -r '.reply // empty')
|
||||
|
||||
if [ -z "$AI_REPLY" ]; then
|
||||
echo -e "${RED}✗ Failed to get AI response${NC}"
|
||||
echo "Response: $AI_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ AI responded${NC}"
|
||||
echo -e " Reply preview: ${AI_REPLY:0:100}...\n"
|
||||
|
||||
# Step 3: Check conversation history
|
||||
echo -e "${YELLOW}[Step 3]${NC} Checking conversation history..."
|
||||
|
||||
HISTORY_RESPONSE=$(curl -s -X GET "${BASE_URL}/api/ai/conversation?projectId=${PROJECT_ID}" \
|
||||
-H "Authorization: ${TOKEN}")
|
||||
|
||||
MESSAGE_COUNT=$(echo "$HISTORY_RESPONSE" | jq '.messages | length')
|
||||
|
||||
if [ "$MESSAGE_COUNT" -lt 2 ]; then
|
||||
echo -e "${RED}✗ Conversation history incomplete (found ${MESSAGE_COUNT} messages)${NC}"
|
||||
echo "Response: $HISTORY_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Conversation history persisted (${MESSAGE_COUNT} messages)${NC}\n"
|
||||
|
||||
# Step 4: Check collector handoff state
|
||||
echo -e "${YELLOW}[Step 4]${NC} Checking collector handoff state..."
|
||||
|
||||
HANDOFF_RESPONSE=$(curl -s -X GET "${BASE_URL}/api/ai/conversation?projectId=${PROJECT_ID}" \
|
||||
-H "Authorization: ${TOKEN}")
|
||||
|
||||
# The handoff data should be in the project document, let's just verify the conversation loaded
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Collector handoff endpoint accessible${NC}\n"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ Could not verify handoff state directly${NC}\n"
|
||||
fi
|
||||
|
||||
# Step 5: Simulate user saying they'll upload docs
|
||||
echo -e "${YELLOW}[Step 5]${NC} Simulating user interaction (uploading docs)..."
|
||||
|
||||
DOCS_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectId\": \"${PROJECT_ID}\",
|
||||
\"message\": \"I am going to upload some docs\"
|
||||
}")
|
||||
|
||||
DOCS_REPLY=$(echo "$DOCS_RESPONSE" | jq -r '.reply // empty')
|
||||
|
||||
if [ -z "$DOCS_REPLY" ]; then
|
||||
echo -e "${RED}✗ Failed to get AI response${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ AI acknowledged document upload${NC}"
|
||||
echo -e " Reply preview: ${DOCS_REPLY:0:100}...\n"
|
||||
|
||||
# Step 6: Simulate uploading a document
|
||||
echo -e "${YELLOW}[Step 6]${NC} Simulating document upload..."
|
||||
|
||||
# Create a test file
|
||||
TEST_DOC_PATH="/tmp/test-document-$RANDOM.md"
|
||||
cat > "$TEST_DOC_PATH" << 'EOF'
|
||||
# Test Product Requirements
|
||||
|
||||
## Overview
|
||||
This is a test document for the E2E flow.
|
||||
|
||||
## Features
|
||||
- Feature 1: User authentication
|
||||
- Feature 2: Dashboard
|
||||
- Feature 3: Analytics
|
||||
|
||||
## Tech Stack
|
||||
- Next.js
|
||||
- TypeScript
|
||||
- Firebase
|
||||
EOF
|
||||
|
||||
UPLOAD_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/projects/${PROJECT_ID}/knowledge/upload-document" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-F "file=@${TEST_DOC_PATH}")
|
||||
|
||||
UPLOAD_SUCCESS=$(echo "$UPLOAD_RESPONSE" | jq -r '.success // false')
|
||||
|
||||
if [ "$UPLOAD_SUCCESS" != "true" ]; then
|
||||
echo -e "${RED}✗ Document upload failed${NC}"
|
||||
echo "Response: $UPLOAD_RESPONSE"
|
||||
rm -f "$TEST_DOC_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Document uploaded successfully${NC}"
|
||||
rm -f "$TEST_DOC_PATH"
|
||||
|
||||
# Get the knowledge item ID
|
||||
KNOWLEDGE_ITEM_ID=$(echo "$UPLOAD_RESPONSE" | jq -r '.knowledgeItemId // empty')
|
||||
echo -e " Knowledge item: ${KNOWLEDGE_ITEM_ID}\n"
|
||||
|
||||
# Step 7: Tell AI about the upload
|
||||
echo -e "${YELLOW}[Step 7]${NC} Informing AI about uploaded document..."
|
||||
|
||||
INFORM_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectId\": \"${PROJECT_ID}\",
|
||||
\"message\": \"I just uploaded a requirements document\"
|
||||
}")
|
||||
|
||||
INFORM_REPLY=$(echo "$INFORM_RESPONSE" | jq -r '.reply // empty')
|
||||
|
||||
if [ -z "$INFORM_REPLY" ]; then
|
||||
echo -e "${RED}✗ Failed to get AI response${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ AI acknowledged upload${NC}"
|
||||
echo -e " Reply preview: ${INFORM_REPLY:0:100}...\n"
|
||||
|
||||
# Step 8: Check final conversation state
|
||||
echo -e "${YELLOW}[Step 8]${NC} Checking final conversation state..."
|
||||
|
||||
FINAL_HISTORY=$(curl -s -X GET "${BASE_URL}/api/ai/conversation?projectId=${PROJECT_ID}" \
|
||||
-H "Authorization: ${TOKEN}")
|
||||
|
||||
FINAL_MESSAGE_COUNT=$(echo "$FINAL_HISTORY" | jq '.messages | length')
|
||||
|
||||
if [ "$FINAL_MESSAGE_COUNT" -lt 6 ]; then
|
||||
echo -e "${RED}✗ Final conversation history incomplete (found ${FINAL_MESSAGE_COUNT} messages)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Final conversation history complete (${FINAL_MESSAGE_COUNT} messages)${NC}\n"
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${GREEN}✓ All tests passed!${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
echo -e "Test Summary:"
|
||||
echo -e " • Project ID: ${PROJECT_ID}"
|
||||
echo -e " • Total Messages: ${FINAL_MESSAGE_COUNT}"
|
||||
echo -e " • Documents Uploaded: 1"
|
||||
echo -e " • Knowledge Items: ${KNOWLEDGE_ITEM_ID}"
|
||||
echo -e ""
|
||||
echo -e "Next Steps:"
|
||||
echo -e " 1. Open ${BASE_URL}/default/project/${PROJECT_ID}/v_ai_chat"
|
||||
echo -e " 2. Verify the collector checklist shows 1 document"
|
||||
echo -e " 3. Continue the conversation to test GitHub/extension flow"
|
||||
echo -e ""
|
||||
|
||||
342
vibn-frontend/docs/scripts/test-table-stakes.sh
Executable file
342
vibn-frontend/docs/scripts/test-table-stakes.sh
Executable file
@@ -0,0 +1,342 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Table Stakes Features - Automated QA Test Script
|
||||
# Tests all implemented features end-to-end
|
||||
|
||||
# set -e # Don't exit on error - show all test results
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
BASE_URL="http://localhost:3000"
|
||||
API_BASE="$BASE_URL/api"
|
||||
TEST_PROJECT_NAME="QA Test Project $(date +%s)"
|
||||
TEST_WORKSPACE="test-workspace"
|
||||
|
||||
# Test results
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
FAILED_TESTS=()
|
||||
|
||||
# Helper functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
((TESTS_PASSED++))
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
((TESTS_FAILED++))
|
||||
FAILED_TESTS+=("$1")
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if server is running
|
||||
check_server() {
|
||||
log_info "Checking if server is running at $BASE_URL..."
|
||||
if curl -s -o /dev/null -w "%{http_code}" "$BASE_URL" | grep -q "200\|404"; then
|
||||
log_success "Server is running"
|
||||
return 0
|
||||
else
|
||||
log_error "Server is not running at $BASE_URL"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 1: Create a new project
|
||||
test_create_project() {
|
||||
log_info "Test 1: Creating new project..."
|
||||
|
||||
# Note: This requires authentication, so we'll check the endpoint exists
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$API_BASE/projects/create" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}')
|
||||
|
||||
if [ "$response" = "401" ]; then
|
||||
log_success "Project creation endpoint exists (401 Unauthorized as expected without token)"
|
||||
else
|
||||
log_warning "Project creation endpoint returned $response (expected 401)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 2: Check extensionLinked field initialization
|
||||
test_extension_field() {
|
||||
log_info "Test 2: Checking extensionLinked field in project creation..."
|
||||
|
||||
# Check the code has the field
|
||||
if grep -q "extensionLinked: false" app/api/projects/create/route.ts; then
|
||||
log_success "extensionLinked field present in project creation"
|
||||
else
|
||||
log_error "extensionLinked field missing from project creation"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 3: Check collector handoff type includes 'collector'
|
||||
test_collector_handoff_type() {
|
||||
log_info "Test 3: Checking collector handoff type definition..."
|
||||
|
||||
if grep -q "'collector' |" lib/server/chat-context.ts; then
|
||||
log_success "Collector phase included in phaseHandoffs type"
|
||||
else
|
||||
log_error "Collector phase missing from phaseHandoffs type"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 4: Check auto-transition logic exists
|
||||
test_auto_transition() {
|
||||
log_info "Test 4: Checking auto-transition logic..."
|
||||
|
||||
if grep -q "Auto-transitioning project to extraction phase" app/api/ai/chat/route.ts; then
|
||||
log_success "Auto-transition logic present in chat route"
|
||||
else
|
||||
log_error "Auto-transition logic missing from chat route"
|
||||
fi
|
||||
|
||||
if grep -q "currentPhase === 'analyzed'" lib/server/chat-mode-resolver.ts; then
|
||||
log_success "Mode resolver checks currentPhase field"
|
||||
else
|
||||
log_error "Mode resolver doesn't check currentPhase"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 5: Check extension linking API exists
|
||||
test_extension_api() {
|
||||
log_info "Test 5: Checking extension linking API..."
|
||||
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$API_BASE/extension/link-project" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}')
|
||||
|
||||
if [ "$response" = "401" ]; then
|
||||
log_success "Extension link API exists (401 Unauthorized as expected)"
|
||||
else
|
||||
log_warning "Extension link API returned $response (expected 401)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 6: Check chunking API exists
|
||||
test_chunking_api() {
|
||||
log_info "Test 6: Checking extraction chunking API..."
|
||||
|
||||
# Check if the file exists
|
||||
if [ -f "app/api/projects/[projectId]/knowledge/chunk-insight/route.ts" ]; then
|
||||
log_success "Chunk insight API endpoint file exists"
|
||||
else
|
||||
log_error "Chunk insight API endpoint missing"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 7: Check auto-chunking is disabled on upload
|
||||
test_no_auto_chunking() {
|
||||
log_info "Test 7: Checking auto-chunking is disabled on upload..."
|
||||
|
||||
if grep -q "Store whole document as single knowledge_item" app/api/projects/[projectId]/knowledge/upload-document/route.ts; then
|
||||
log_success "Upload stores whole document (no auto-chunking)"
|
||||
else
|
||||
log_error "Upload may still be auto-chunking"
|
||||
fi
|
||||
|
||||
if grep -q "// import { chunkDocument }" app/api/projects/[projectId]/knowledge/upload-document/route.ts; then
|
||||
log_success "chunkDocument import commented out"
|
||||
else
|
||||
log_warning "chunkDocument import may still be active"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 8: Check collector checklist component exists
|
||||
test_checklist_component() {
|
||||
log_info "Test 8: Checking collector checklist component..."
|
||||
|
||||
if [ -f "components/ai/collector-checklist.tsx" ]; then
|
||||
log_success "Collector checklist component exists"
|
||||
else
|
||||
log_error "Collector checklist component missing"
|
||||
fi
|
||||
|
||||
if grep -q "CollectorChecklist" app/[workspace]/project/[projectId]/v_ai_chat/page.tsx; then
|
||||
log_success "Checklist integrated into AI chat page"
|
||||
else
|
||||
log_error "Checklist not integrated into AI chat page"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 9: Check Gemini role mapping fix
|
||||
test_gemini_role_fix() {
|
||||
log_info "Test 9: Checking Gemini role mapping fix..."
|
||||
|
||||
if grep -q "role === 'assistant' ? 'model'" lib/ai/gemini-client.ts; then
|
||||
log_success "Gemini client translates assistant → model role"
|
||||
else
|
||||
log_error "Gemini client missing role translation"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 10: Check conversation history persistence
|
||||
test_conversation_history() {
|
||||
log_info "Test 10: Checking conversation history loading..."
|
||||
|
||||
if grep -q "Load existing conversation history" app/api/ai/chat/route.ts; then
|
||||
log_success "Chat route loads conversation history"
|
||||
else
|
||||
log_error "Chat route doesn't load conversation history"
|
||||
fi
|
||||
|
||||
if grep -q "conversationHistory.map" app/api/ai/chat/route.ts; then
|
||||
log_success "Conversation history included in messages"
|
||||
else
|
||||
log_error "Conversation history not included in messages"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 11: Check extensionLinked in context
|
||||
test_extension_in_context() {
|
||||
log_info "Test 11: Checking extensionLinked in project context..."
|
||||
|
||||
if grep -q "extensionLinked?: boolean" lib/server/chat-context.ts; then
|
||||
log_success "extensionLinked field in ProjectChatContext type"
|
||||
else
|
||||
log_error "extensionLinked field missing from context type"
|
||||
fi
|
||||
|
||||
if grep -q "extensionLinked: projectData.extensionLinked" lib/server/chat-context.ts; then
|
||||
log_success "extensionLinked passed to AI in context"
|
||||
else
|
||||
log_error "extensionLinked not passed to AI"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 12: Check collector prompt mentions extensionLinked
|
||||
test_collector_prompt() {
|
||||
log_info "Test 12: Checking collector prompt..."
|
||||
|
||||
if grep -q "projectContext.project.extensionLinked" lib/ai/prompts/collector.ts; then
|
||||
log_success "Collector prompt checks extensionLinked field"
|
||||
else
|
||||
log_error "Collector prompt doesn't check extensionLinked"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 13: Check UI text update
|
||||
test_ui_text() {
|
||||
log_info "Test 13: Checking UI text reflects no auto-chunking..."
|
||||
|
||||
if grep -q "stored for the Extractor AI to review" app/[workspace]/project/[projectId]/context/page.tsx; then
|
||||
log_success "UI text updated (no mention of auto-chunking)"
|
||||
else
|
||||
log_error "UI text may still mention auto-chunking"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 14: Verify linter passes
|
||||
test_linter() {
|
||||
log_info "Test 14: Running linter check..."
|
||||
|
||||
# This is a simplified check - just verify key files exist and are not empty
|
||||
files_to_check=(
|
||||
"app/api/ai/chat/route.ts"
|
||||
"lib/server/chat-context.ts"
|
||||
"lib/ai/gemini-client.ts"
|
||||
"components/ai/collector-checklist.tsx"
|
||||
)
|
||||
|
||||
all_good=true
|
||||
for file in "${files_to_check[@]}"; do
|
||||
if [ ! -f "$file" ]; then
|
||||
log_error "File missing: $file"
|
||||
all_good=false
|
||||
elif [ ! -s "$file" ]; then
|
||||
log_error "File is empty: $file"
|
||||
all_good=false
|
||||
fi
|
||||
done
|
||||
|
||||
if $all_good; then
|
||||
log_success "All critical files exist and are not empty"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 15: Check documentation exists
|
||||
test_documentation() {
|
||||
log_info "Test 15: Checking documentation..."
|
||||
|
||||
docs=(
|
||||
"TABLE_STAKES_IMPLEMENTATION.md"
|
||||
"QA_FIXES_APPLIED.md"
|
||||
"PROJECT_CREATION_FIX.md"
|
||||
"UPLOAD_CHUNKING_REMOVED.md"
|
||||
)
|
||||
|
||||
for doc in "${docs[@]}"; do
|
||||
if [ -f "$doc" ]; then
|
||||
log_success "Documentation exists: $doc"
|
||||
else
|
||||
log_warning "Documentation missing: $doc"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Main test execution
|
||||
main() {
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " TABLE STAKES FEATURES - QA TEST SUITE"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_server
|
||||
echo ""
|
||||
|
||||
test_create_project
|
||||
test_extension_field
|
||||
test_collector_handoff_type
|
||||
test_auto_transition
|
||||
test_extension_api
|
||||
test_chunking_api
|
||||
test_no_auto_chunking
|
||||
test_checklist_component
|
||||
test_gemini_role_fix
|
||||
test_conversation_history
|
||||
test_extension_in_context
|
||||
test_collector_prompt
|
||||
test_ui_text
|
||||
test_linter
|
||||
test_documentation
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " TEST RESULTS"
|
||||
echo "=========================================="
|
||||
echo -e "${GREEN}Passed:${NC} $TESTS_PASSED"
|
||||
echo -e "${RED}Failed:${NC} $TESTS_FAILED"
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_FAILED -gt 0 ]; then
|
||||
echo -e "${RED}Failed tests:${NC}"
|
||||
for test in "${FAILED_TESTS[@]}"; do
|
||||
echo " - $test"
|
||||
done
|
||||
echo ""
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}✅ ALL TESTS PASSED!${NC}"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Run tests
|
||||
cd "$(dirname "$0")"
|
||||
main
|
||||
|
||||
105
vibn-frontend/docs/scripts/test-vision-flow.sh
Executable file
105
vibn-frontend/docs/scripts/test-vision-flow.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test Vision Flow - Automated testing of 3-question flow
|
||||
# Usage: ./test-vision-flow.sh <projectId>
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "❌ Error: Please provide a projectId"
|
||||
echo "Usage: ./test-vision-flow.sh <projectId>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROJECT_ID="$1"
|
||||
WORKSPACE="marks-account"
|
||||
API_URL="http://localhost:3000/api/ai/chat"
|
||||
|
||||
# Load test questions
|
||||
Q1=$(grep "^q1=" .test-questions | cut -d'=' -f2-)
|
||||
Q2=$(grep "^q2=" .test-questions | cut -d'=' -f2-)
|
||||
Q3=$(grep "^q3=" .test-questions | cut -d'=' -f2-)
|
||||
|
||||
echo "🧪 Testing Vision Flow"
|
||||
echo "Project ID: $PROJECT_ID"
|
||||
echo ""
|
||||
echo "Questions loaded:"
|
||||
echo " Q1: ${Q1:0:50}..."
|
||||
echo " Q2: ${Q2:0:50}..."
|
||||
echo " Q3: ${Q3:0:50}..."
|
||||
echo ""
|
||||
|
||||
# Function to send chat message
|
||||
send_message() {
|
||||
local message="$1"
|
||||
local step="$2"
|
||||
|
||||
echo "[$step] Sending: ${message:0:60}..."
|
||||
|
||||
response=$(curl -s -X POST "$API_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"message\": \"$message\",
|
||||
\"projectId\": \"$PROJECT_ID\",
|
||||
\"workspaceId\": \"$WORKSPACE\"
|
||||
}")
|
||||
|
||||
# Check for error
|
||||
if echo "$response" | grep -q '"error"'; then
|
||||
echo "❌ Error response:"
|
||||
echo "$response" | jq '.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract reply
|
||||
reply=$(echo "$response" | jq -r '.reply // .message // "No reply"')
|
||||
echo "[$step] AI Reply: ${reply:0:80}..."
|
||||
echo ""
|
||||
|
||||
# Small delay between messages
|
||||
sleep 2
|
||||
}
|
||||
|
||||
echo "🚀 Starting test..."
|
||||
echo ""
|
||||
|
||||
# Send Q1
|
||||
send_message "$Q1" "Q1"
|
||||
|
||||
# Send Q2
|
||||
send_message "$Q2" "Q2"
|
||||
|
||||
# Send Q3
|
||||
send_message "$Q3" "Q3"
|
||||
|
||||
echo "✅ All 3 questions sent!"
|
||||
echo ""
|
||||
echo "🔍 Checking logs for success markers..."
|
||||
sleep 2
|
||||
|
||||
# Check terminal logs for success
|
||||
TERMINAL_LOG="$HOME/.cursor/projects/Users-markhenderson-ai-proxy/terminals/13.txt"
|
||||
|
||||
if tail -100 "$TERMINAL_LOG" | grep -q "All 3 vision answers complete"; then
|
||||
echo "✅ SUCCESS: Found 'All 3 vision answers complete' in logs"
|
||||
else
|
||||
echo "❌ FAILED: Did not find success marker in logs"
|
||||
echo ""
|
||||
echo "Last 20 lines of log:"
|
||||
tail -20 "$TERMINAL_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if tail -100 "$TERMINAL_LOG" | grep -q "readyForMVP.*true\|readyForExtraction.*true"; then
|
||||
echo "✅ SUCCESS: MVP generation flag set"
|
||||
else
|
||||
echo "⚠️ WARNING: MVP generation flag not found in logs"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 Vision flow test completed!"
|
||||
echo ""
|
||||
echo "📊 Check Firestore to verify data was saved:"
|
||||
echo " Project: $PROJECT_ID"
|
||||
echo " Expected fields: visionAnswers.q1, q2, q3, allAnswered, readyForMVP"
|
||||
|
||||
84
vibn-frontend/docs/scripts/test-vision-simple.sh
Executable file
84
vibn-frontend/docs/scripts/test-vision-simple.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple Vision Flow Test
|
||||
# Usage: ./test-vision-simple.sh <projectId>
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "❌ Usage: ./test-vision-simple.sh <projectId>"
|
||||
echo ""
|
||||
echo "1. Create a new project in the browser"
|
||||
echo "2. Copy its project ID from the URL"
|
||||
echo "3. Run: ./test-vision-simple.sh YOUR_PROJECT_ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROJECT_ID="$1"
|
||||
WORKSPACE="marks-account"
|
||||
API_URL="http://localhost:3000/api/ai/chat"
|
||||
|
||||
# Load test questions
|
||||
Q1=$(grep "^q1=" .test-questions | cut -d'=' -f2-)
|
||||
Q2=$(grep "^q2=" .test-questions | cut -d'=' -f2-)
|
||||
Q3=$(grep "^q3=" .test-questions | cut -d'=' -f2-)
|
||||
|
||||
echo "🧪 Vision Flow Test"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Project: $PROJECT_ID"
|
||||
echo ""
|
||||
|
||||
# Function to send message and check response
|
||||
send_and_check() {
|
||||
local msg="$1"
|
||||
local step="$2"
|
||||
|
||||
echo "[$step] Sending..."
|
||||
|
||||
response=$(curl -s -X POST "$API_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @- <<EOF
|
||||
{
|
||||
"message": $(echo "$msg" | jq -Rs .),
|
||||
"projectId": "$PROJECT_ID",
|
||||
"workspaceId": "$WORKSPACE"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
if echo "$response" | jq -e '.error' > /dev/null 2>&1; then
|
||||
echo "❌ Error: $(echo "$response" | jq -r '.error')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ [$step] Sent"
|
||||
sleep 3
|
||||
}
|
||||
|
||||
echo "Sending Q1..."
|
||||
send_and_check "$Q1" "Q1"
|
||||
|
||||
echo "Sending Q2..."
|
||||
send_and_check "$Q2" "Q2"
|
||||
|
||||
echo "Sending Q3..."
|
||||
send_and_check "$Q3" "Q3"
|
||||
|
||||
echo ""
|
||||
echo "🔍 Checking logs..."
|
||||
sleep 2
|
||||
|
||||
TERMINAL_LOG="$HOME/.cursor/projects/Users-markhenderson-ai-proxy/terminals/13.txt"
|
||||
|
||||
if tail -100 "$TERMINAL_LOG" | grep -q "All 3 vision answers complete"; then
|
||||
echo "✅ SUCCESS: All 3 vision answers stored!"
|
||||
echo "✅ SUCCESS: MVP generation triggered!"
|
||||
else
|
||||
echo "❌ FAILED: Did not find success marker"
|
||||
echo ""
|
||||
echo "Last vision-related logs:"
|
||||
tail -50 "$TERMINAL_LOG" | grep -E "vision|Q1|Q2|Q3|allAnswered"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 Test PASSED!"
|
||||
|
||||
37
vibn-frontend/docs/scripts/test_auth.js
Normal file
37
vibn-frontend/docs/scripts/test_auth.js
Normal file
@@ -0,0 +1,37 @@
|
||||
require('dotenv').config({ path: '.env.local' });
|
||||
const { Pool } = require('pg');
|
||||
|
||||
async function test() {
|
||||
console.log("BYPASS_AUTH:", process.env.NEXT_PUBLIC_DEV_BYPASS_PROJECT_AUTH);
|
||||
console.log("LOCAL_EMAIL:", process.env.NEXT_PUBLIC_DEV_LOCAL_AUTH_EMAIL);
|
||||
|
||||
if (!process.env.DATABASE_URL) {
|
||||
console.log("No DATABASE_URL in .env.local");
|
||||
return;
|
||||
}
|
||||
|
||||
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||
|
||||
try {
|
||||
const res1 = await pool.query("SELECT id FROM fs_users WHERE data->>'email' = $1 LIMIT 1", [process.env.NEXT_PUBLIC_DEV_LOCAL_AUTH_EMAIL]);
|
||||
if (res1.rows.length === 0) {
|
||||
console.log("FAIL: No user found for email", process.env.NEXT_PUBLIC_DEV_LOCAL_AUTH_EMAIL);
|
||||
return;
|
||||
}
|
||||
const userId = res1.rows[0].id;
|
||||
console.log("User ID:", userId);
|
||||
|
||||
const res2 = await pool.query("SELECT id, slug FROM fs_workspaces WHERE owner_id = $1 LIMIT 1", [userId]);
|
||||
if (res2.rows.length === 0) {
|
||||
console.log("FAIL: User owns no workspaces.");
|
||||
return;
|
||||
}
|
||||
console.log("Workspace found:", res2.rows[0]);
|
||||
console.log("SUCCESS: The bypass SHOULD work.");
|
||||
} catch (e) {
|
||||
console.log("ERROR:", e.message);
|
||||
} finally {
|
||||
pool.end();
|
||||
}
|
||||
}
|
||||
test();
|
||||
25
vibn-frontend/docs/scripts/test_auth2.js
Normal file
25
vibn-frontend/docs/scripts/test_auth2.js
Normal file
@@ -0,0 +1,25 @@
|
||||
require('dotenv').config({ path: '.env.local' });
|
||||
const { Pool } = require('pg');
|
||||
|
||||
async function test() {
|
||||
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||
|
||||
try {
|
||||
const res1 = await pool.query("SELECT id FROM fs_users WHERE data->>'email' = $1 LIMIT 1", [process.env.NEXT_PUBLIC_DEV_LOCAL_AUTH_EMAIL]);
|
||||
const userId = res1.rows[0].id;
|
||||
|
||||
// The table is vibn_workspaces, not fs_workspaces
|
||||
const res2 = await pool.query("SELECT id, slug FROM vibn_workspaces WHERE owner_user_id = $1 LIMIT 1", [userId]);
|
||||
if (res2.rows.length === 0) {
|
||||
console.log("FAIL: User owns no workspaces.");
|
||||
return;
|
||||
}
|
||||
console.log("Workspace found:", res2.rows[0]);
|
||||
console.log("SUCCESS: The bypass SHOULD work.");
|
||||
} catch (e) {
|
||||
console.log("ERROR:", e.message);
|
||||
} finally {
|
||||
pool.end();
|
||||
}
|
||||
}
|
||||
test();
|
||||
13
vibn-frontend/docs/scripts/test_mcp_fetch.js
Normal file
13
vibn-frontend/docs/scripts/test_mcp_fetch.js
Normal file
@@ -0,0 +1,13 @@
|
||||
async function test() {
|
||||
const res = await fetch('http://localhost:3000/api/mcp', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer vibn_sk_fake'
|
||||
},
|
||||
body: JSON.stringify({ action: 'workspace.describe' })
|
||||
});
|
||||
console.log("Status:", res.status);
|
||||
console.log("Body:", await res.text());
|
||||
}
|
||||
test();
|
||||
27
vibn-frontend/docs/scripts/trigger-extraction.js
Normal file
27
vibn-frontend/docs/scripts/trigger-extraction.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// Manually trigger extraction to see what happens
|
||||
const projectId = 'Sih2VRdZeBglpVNc4eFS';
|
||||
|
||||
async function triggerExtraction() {
|
||||
try {
|
||||
console.log('Triggering extraction for project:', projectId);
|
||||
console.log('Watch the terminal running "npm run dev" for logs...\n');
|
||||
|
||||
const response = await fetch(`http://localhost:3000/api/projects/${projectId}/run-extraction`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer fake-token-for-local-dev' // Will fail auth but trigger logging
|
||||
}
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
console.log('Response status:', response.status);
|
||||
console.log('Response body:', text);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
triggerExtraction();
|
||||
|
||||
89
vibn-frontend/docs/scripts/verify-handoff-simple.sh
Executable file
89
vibn-frontend/docs/scripts/verify-handoff-simple.sh
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple Handoff Verification - Uses API endpoint to check handoff state
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
PROJECT_ID="lyOZxelSkjAB6XisIzup"
|
||||
BASE_URL="http://localhost:3000"
|
||||
TOKEN="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4MDI5MzRmZTBlZWM0NmE1ZWQwMDA2ZDE0YTFiYWIwMWUzNDUwODMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiTWFyayBIZW5kZXJzb24iLCJwaWN0dXJlIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzUzOTU0MjEzP3Y9NCIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9nZW4tbGFuZy1jbGllbnQtMDk4MDA3OTQxMCIsImF1ZCI6Imdlbi1sYW5nLWNsaWVudC0wOTgwMDc5NDEwIiwiYXV0aF90aW1lIjoxNzYzMzI1MDEyLCJ1c2VyX2lkIjoiMmhDdmdXQzJaV2RJMGVlTm5SQVM3SWVKcmg1MiIsInN1YiI6IjJoQ3ZnV0MyWldkSTBlZU5uUkFTN0llSnJoNTIiLCJpYXQiOjE3NjM0MjI1NDUsImV4cCI6MTc2MzQyNjE0NSwiZW1haWwiOiJtYXJrQGdldGFjcXVpcmVkLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnaXRodWIuY29tIjpbIjUzOTU0MjEzIl0sImVtYWlsIjpbIm1hcmtAZ2V0YWNxdWlyZWQuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ2l0aHViLmNvbSJ9fQ.TpMOORDnPUKkbLlg-KtYBmbarEjAijJ3W4vN8tWT6OslOfwaeDJAtPXIahyQk38UvKY4ZGognQG6t-laSATB8yIC8IdkYbD699axfPSGQqC8Lbux1P6YrFKOPLGDD2XemBtJ-Gb5Ql-nK_DbXKAmygLxIwz019XpLJEucGkBPAN_Rj2xC7125DVexkDSIb6ZnbLiDgCpR_IkImyQb08tqlOoBiHVUa-4VGDhraoBPACJfQXwPToJ1W3nhBiVtMvSq7s_Ekd8Otn8AB_1teu5lxC-rhLdgJuNrmlxO-H6xIMBFZ72bwq7wrvdWd_EijqFQCU99oEhphTNoISoJ3wK-g"
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Collector Handoff Verification${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
# Send a test message that should trigger handoff update
|
||||
echo -e "${YELLOW}[Step 1]${NC} Sending message to check handoff state..."
|
||||
|
||||
CHAT_RESPONSE=$(curl -s -X POST "${BASE_URL}/api/ai/chat" \
|
||||
-H "Authorization: ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"projectId\": \"${PROJECT_ID}\",
|
||||
\"message\": \"What do you know about my project so far?\"
|
||||
}")
|
||||
|
||||
AI_REPLY=$(echo "$CHAT_RESPONSE" | jq -r '.reply // empty')
|
||||
|
||||
if [ -z "$AI_REPLY" ]; then
|
||||
echo -e "${RED}✗ Failed to get AI response${NC}"
|
||||
echo "Response: $CHAT_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ AI responded${NC}"
|
||||
echo -e "\nAI Reply:\n${AI_REPLY}\n"
|
||||
|
||||
# Check the conversation for handoff indicators
|
||||
echo -e "${YELLOW}[Step 2]${NC} Checking conversation history..."
|
||||
|
||||
HISTORY=$(curl -s -X GET "${BASE_URL}/api/ai/conversation?projectId=${PROJECT_ID}" \
|
||||
-H "Authorization: ${TOKEN}")
|
||||
|
||||
MESSAGE_COUNT=$(echo "$HISTORY" | jq '.messages | length')
|
||||
echo -e "${GREEN}✓ Found ${MESSAGE_COUNT} messages in history${NC}\n"
|
||||
|
||||
# Look for handoff indicators in AI responses
|
||||
echo -e "${YELLOW}[Step 3]${NC} Analyzing AI responses for handoff tracking...\n"
|
||||
|
||||
HAS_DOC_MENTION=$(echo "$AI_REPLY" | grep -i "document\|uploaded" && echo "yes" || echo "no")
|
||||
HAS_GITHUB_MENTION=$(echo "$AI_REPLY" | grep -i "github\|repo" && echo "yes" || echo "no")
|
||||
HAS_EXTENSION_MENTION=$(echo "$AI_REPLY" | grep -i "extension\|browser" && echo "yes" || echo "no")
|
||||
|
||||
if [ "$HAS_DOC_MENTION" = "yes" ]; then
|
||||
echo -e "${GREEN}✓ AI is tracking documents${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ AI doesn't mention documents in response${NC}"
|
||||
fi
|
||||
|
||||
if [ "$HAS_GITHUB_MENTION" = "yes" ]; then
|
||||
echo -e "${GREEN}✓ AI is tracking GitHub connection${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ AI doesn't mention GitHub in response${NC}"
|
||||
fi
|
||||
|
||||
if [ "$HAS_EXTENSION_MENTION" = "yes" ]; then
|
||||
echo -e "${GREEN}✓ AI is tracking browser extension${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ AI doesn't mention extension in response${NC}"
|
||||
fi
|
||||
|
||||
echo -e "\n${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Summary${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
echo -e "Project ID: ${PROJECT_ID}"
|
||||
echo -e "Total Messages: ${MESSAGE_COUNT}"
|
||||
echo -e "\nTo manually verify handoff data in Firebase Console:"
|
||||
echo -e " 1. Go to Firestore"
|
||||
echo -e " 2. Open projects/${PROJECT_ID}"
|
||||
echo -e " 3. Check phaseData.phaseHandoffs.collector"
|
||||
echo -e ""
|
||||
echo -e "Or view in UI:"
|
||||
echo -e " ${BASE_URL}/default/project/${PROJECT_ID}/v_ai_chat"
|
||||
echo -e " (Look for the checklist in the left sidebar)\n"
|
||||
|
||||
111
vibn-frontend/docs/scripts/verify-handoff.sh
Executable file
111
vibn-frontend/docs/scripts/verify-handoff.sh
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Verify Collector Handoff Data
|
||||
# This script checks if the collector handoff contract is properly persisted
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
PROJECT_ID="lyOZxelSkjAB6XisIzup"
|
||||
TOKEN="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4MDI5MzRmZTBlZWM0NmE1ZWQwMDA2ZDE0YTFiYWIwMWUzNDUwODMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiTWFyayBIZW5kZXJzb24iLCJwaWN0dXJlIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzUzOTU0MjEzP3Y9NCIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9nZW4tbGFuZy1jbGllbnQtMDk4MDA3OTQxMCIsImF1ZCI6Imdlbi1sYW5nLWNsaWVudC0wOTgwMDc5NDEwIiwiYXV0aF90aW1lIjoxNzYzMzI1MDEyLCJ1c2VyX2lkIjoiMmhDdmdXQzJaV2RJMGVlTm5SQVM3SWVKcmg1MiIsInN1YiI6IjJoQ3ZnV0MyWldkSTBlZU5uUkFTN0llSnJoNTIiLCJpYXQiOjE3NjM0MjI1NDUsImV4cCI6MTc2MzQyNjE0NSwiZW1haWwiOiJtYXJrQGdldGFjcXVpcmVkLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnaXRodWIuY29tIjpbIjUzOTU0MjEzIl0sImVtYWlsIjpbIm1hcmtAZ2V0YWNxdWlyZWQuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ2l0aHViLmNvbSJ9fQ.TpMOORDnPUKkbLlg-KtYBmbarEjAijJ3W4vN8tWT6OslOfwaeDJAtPXIahyQk38UvKY4ZGognQG6t-laSATB8yIC8IdkYbD699axfPSGQqC8Lbux1P6YrFKOPLGDD2XemBtJ-Gb5Ql-nK_DbXKAmygLxIwz019XpLJEucGkBPAN_Rj2xC7125DVexkDSIb6ZnbLiDgCpR_IkImyQb08tqlOoBiHVUa-4VGDhraoBPACJfQXwPToJ1W3nhBiVtMvSq7s_Ekd8Otn8AB_1teu5lxC-rhLdgJuNrmlxO-H6xIMBFZ72bwq7wrvdWd_EijqFQCU99oEhphTNoISoJ3wK-g"
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Verifying Collector Handoff Data${NC}"
|
||||
echo -e "${BLUE}========================================${NC}\n"
|
||||
|
||||
# Create a temp Node.js script to check Firestore directly
|
||||
cat > /tmp/check-handoff.js << 'EOFJS'
|
||||
const admin = require('firebase-admin');
|
||||
|
||||
// Initialize Firebase Admin
|
||||
const serviceAccount = require(process.env.HOME + '/ai-proxy/vibn-frontend/gen-lang-client-0980079410-firebase-adminsdk-fbsvc-c0e0cffc47.json');
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
storageBucket: 'gen-lang-client-0980079410.firebasestorage.app'
|
||||
});
|
||||
|
||||
const db = admin.firestore();
|
||||
|
||||
async function checkHandoff() {
|
||||
const projectId = process.argv[2];
|
||||
|
||||
console.log(`\nChecking project: ${projectId}\n`);
|
||||
|
||||
// 1. Check project document for phaseData
|
||||
const projectDoc = await db.collection('projects').doc(projectId).get();
|
||||
|
||||
if (!projectDoc.exists) {
|
||||
console.log('❌ Project not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const projectData = projectDoc.data();
|
||||
|
||||
console.log('📋 Project Phase Info:');
|
||||
console.log(` Current Phase: ${projectData.currentPhase || 'not set'}`);
|
||||
console.log(` Phase Status: ${projectData.phaseStatus || 'not set'}`);
|
||||
|
||||
// 2. Check for collector handoff data
|
||||
if (projectData.phaseData && projectData.phaseData.phaseHandoffs && projectData.phaseData.phaseHandoffs.collector) {
|
||||
const handoff = projectData.phaseData.phaseHandoffs.collector;
|
||||
console.log('\n✅ Collector Handoff Data Found:');
|
||||
console.log(JSON.stringify(handoff, null, 2));
|
||||
|
||||
// Verify the handoff contract
|
||||
console.log('\n📊 Handoff Contract Status:');
|
||||
console.log(` ✓ Has Documents: ${handoff.confirmed?.hasDocuments || false}`);
|
||||
console.log(` ✓ Document Count: ${handoff.confirmed?.documentCount || 0}`);
|
||||
console.log(` ✓ GitHub Connected: ${handoff.confirmed?.githubConnected || false}`);
|
||||
console.log(` ✓ Extension Linked: ${handoff.confirmed?.extensionLinked || false}`);
|
||||
console.log(` ✓ Ready for Extraction: ${handoff.readyForNextPhase || false}`);
|
||||
|
||||
if (handoff.readyForNextPhase) {
|
||||
console.log('\n🎉 Collector phase is complete and ready for extraction!');
|
||||
} else {
|
||||
console.log('\n⏳ Collector phase is still in progress');
|
||||
}
|
||||
} else {
|
||||
console.log('\n⚠️ No collector handoff data found');
|
||||
console.log(' This might be normal if the AI hasn\'t returned structured handoff data yet');
|
||||
}
|
||||
|
||||
// 3. Check knowledge items
|
||||
const knowledgeSnapshot = await db.collection('knowledge_items')
|
||||
.where('projectId', '==', projectId)
|
||||
.get();
|
||||
|
||||
console.log(`\n📚 Knowledge Items: ${knowledgeSnapshot.size} found`);
|
||||
|
||||
if (knowledgeSnapshot.size > 0) {
|
||||
knowledgeSnapshot.forEach(doc => {
|
||||
const data = doc.data();
|
||||
console.log(` - ${data.title || 'Untitled'} (${data.sourceType})`);
|
||||
});
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
checkHandoff().catch(err => {
|
||||
console.error('Error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
EOFJS
|
||||
|
||||
echo -e "${YELLOW}Checking Firestore for handoff data...${NC}\n"
|
||||
|
||||
# Run the Node.js script
|
||||
cd /Users/markhenderson/ai-proxy/vibn-frontend && node /tmp/check-handoff.js "$PROJECT_ID"
|
||||
|
||||
# Clean up
|
||||
rm -f /tmp/check-handoff.js
|
||||
|
||||
echo -e "\n${BLUE}========================================${NC}"
|
||||
|
||||
Reference in New Issue
Block a user