From 731ced99d14ad6ddc435d0e85d725088b2498949 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 5 Aug 2026 16:17:53 +0300 Subject: [PATCH] Auto-deploy: 2026-08-05 16:17:53 --- background.js | 10 +- content.js | 189 ++------- cv_template.html | 208 ---------- deploy.sh | 9 + manifest.json | 2 + popup.html | 1 + popup.js | 43 +- post_feed.css | 71 ++++ post_feed.js | 80 ++-- profile.js | 70 +--- profile_data.js | 264 +++++++++++++ prompts.js | 391 +++++++++++++----- server/.env.example | 7 + server/cv_template.html | 260 ++++-------- server/cv_template_amman.html | 200 ---------- server/generate_cv.php | 589 +++++++++++++++++++--------- server/profile.json | 344 ++++++++++++++++ server/prompts/comment_prompt.txt | 89 ++++- server/prompts/repurpose_prompt.txt | 78 +++- sync_profile.js | 17 + 20 files changed, 1772 insertions(+), 1150 deletions(-) delete mode 100644 cv_template.html create mode 100644 profile_data.js delete mode 100644 server/cv_template_amman.html create mode 100644 server/profile.json create mode 100644 sync_profile.js diff --git a/background.js b/background.js index af0603f..8740da5 100644 --- a/background.js +++ b/background.js @@ -36,8 +36,12 @@ async function handleGeminiRequest({ apiKey, prompt, tab, action = 'generateText throw new Error('Daily limit reached (1,000 requests). Resets at midnight PT.'); } - // Truncate text โ€” 12k chars to fit list_analysis prompts - const maxChars = 12000; + // Safety valve only. Prompts already cap the job description at the source, + // because truncating here removes the END of the prompt โ€” which is where the + // job posting and the required output format sit. Gemini's context window is + // far larger than this; the limit exists to catch runaway input, not to shape + // normal requests. + const maxChars = 60000; const trimmedPrompt = prompt && prompt.length > maxChars ? prompt.substring(0, maxChars) + '\n\n[Truncated]' : prompt; @@ -70,7 +74,7 @@ async function handleGeminiRequest({ apiKey, prompt, tab, action = 'generateText if (action === 'generatePdf') { if (!data.pdf) throw new Error('Empty PDF response from server.'); await incrementUsage(); - return { pdf: data.pdf, filename: data.filename, fromCache: false }; + return { pdf: data.pdf, filename: data.filename, ats: data.ats || null, fromCache: false }; } else if (action === 'generateComment') { const comment = data.comment; if (!comment) { diff --git a/content.js b/content.js index 3a47648..f997a0f 100644 --- a/content.js +++ b/content.js @@ -345,6 +345,7 @@
+ @@ -353,6 +354,7 @@
Analysis + ATS Score Cover Letter CV Tips Q&A @@ -361,7 +363,7 @@
- ${['analysis', 'coverletter', 'cvtips', 'qa', 'benefits'].map(tab => ` + ${['analysis', 'ats', 'coverletter', 'cvtips', 'qa', 'benefits'].map(tab => `
${tabIcon(tab)}
@@ -388,7 +390,7 @@ } function tabIcon(tab) { - const icons = { analysis: '๐Ÿ“Š', coverletter: 'โœ‰๏ธ', cvtips: '๐Ÿ“', qa: 'โ“', benefits: 'โญ' }; + const icons = { analysis: '๐Ÿ“Š', ats: '๐ŸŽฏ', coverletter: 'โœ‰๏ธ', cvtips: '๐Ÿ“', qa: 'โ“', benefits: 'โญ' }; return icons[tab] || '๐Ÿ”'; } @@ -536,7 +538,11 @@ jobDescription: jobData.description, jobTitle: jobData.jobTitle || 'Job', action: 'generatePdf', - template: 'amman' + // Jordan-based postings get the conservative local framing; + // everything else gets the GCC/international framing. + template: /jordan|amman|ุงู„ุฃุฑุฏู†|ุนู…ู‘ุงู†|ุนู…ุงู†/i.test(jobData.location || '') + ? 'amman' + : 'default' } }, resolve); }); @@ -558,8 +564,20 @@ document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); - - showPanelToast(root, 'โœ… PDF Downloaded Successfully!', 'success'); + + // Surface what the tailoring actually achieved, and warn loudly if + // the server had to fall back because the model tried to fabricate. + const ats = response.data.ats || {}; + let msg = 'โœ… PDF downloaded'; + if (ats.score) msg += ` โ€” est. ATS match ${ats.score}%`; + if (ats.tailored === false) msg += ' (untailored fallback)'; + if (ats.violations && ats.violations.length) { + msg += ` โš ๏ธ blocked fabricated claims: ${ats.violations.join(', ')}`; + } + if (ats.missing && ats.missing.length) { + console.warn('[LJA] Job requirements not claimable from profile:', ats.missing); + } + showPanelToast(root, msg, 'success'); } else { throw new Error(response?.error || 'Failed to generate PDF from server'); } @@ -588,6 +606,7 @@ const loadingMsgs = { analysis: 'Analyzing job fit...', coverletter: 'Writing cover letter...', + ats: 'Scoring CV against ATS...', cvtips: 'Generating CV tips...', qa: 'Preparing Q&A answers...', benefits: 'Summarizing benefits...' @@ -669,142 +688,6 @@ } } - // โ”€โ”€โ”€ Prompt Builder โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - - function buildPrompt(tab, job, userProfile, language) { - const langInstruction = language === 'arabic' - ? 'Respond entirely in Arabic.' - : language === 'english' - ? 'Respond entirely in English.' - : 'Respond in the same language as the job posting (Arabic or English).'; - - const jobContext = ` -Job Title: ${job.jobTitle || 'Not specified'} -Company: ${job.company || 'Not specified'} -Location: ${job.location || 'Not specified'} -Description: -${job.description || 'No description available'} -${job.skills.length ? `\nRequired Skills: ${job.skills.join(', ')}` : ''}`.trim(); - - const profileBlock = `MY PROFILE:\n${userProfile}`; - - const prompts = { - analysis: `You are a senior career advisor specializing in tech roles in the Middle East. -${langInstruction} -Analyze this job posting against my profile. Be direct and honest. - -${profileBlock} - -JOB POSTING: -${jobContext} - -Provide in this exact format: -## MATCH SCORE: X/100 -Brief explanation. - -## โœ… STRONG MATCHES -- bullet points of matching skills/experience - -## โš ๏ธ GAPS -- bullet points of missing requirements or weak areas - -## ๐ŸŽฏ RECOMMENDATION -Apply / Apply with preparation / Skip โ€” with clear reason - -## ๐Ÿ’ก KEY SELLING POINTS -- what to emphasize when applying - -## ๐Ÿท๏ธ BEST CV VARIANT -Which of my CV variants (Solutions Architect / Flutter Developer / GIS Developer / FinTech Engineer) best fits and why`, - - coverletter: `Write a compelling, personalized cover letter for this position. -${langInstruction} - -${profileBlock} - -JOB: -${jobContext} - -Requirements: -- 3 paragraphs maximum -- Reference specific requirements from the job description -- Highlight my most relevant achievements with numbers -- Professional but confident tone โ€” not generic -- End with a strong call to action -- Do NOT use placeholder brackets โ€” write a complete ready-to-send letter -- Address to "Hiring Team" if no specific name is available -- Sign as "Hamza Ayed"`, - - cvtips: `Based on this job posting, provide specific CV optimization advice. -${langInstruction} - -${profileBlock} - -JOB: -${jobContext} - -Provide: -## ๐Ÿ“Œ SUGGESTED HEADLINE -A LinkedIn-style headline optimized for this specific role (max 120 characters) - -## ๐Ÿ“‹ SUGGESTED SUMMARY -3-4 sentence professional summary tailored for this role - -## ๐Ÿ”‘ KEYWORDS TO ADD -- specific keywords from the job to add to CV - -## โฌ†๏ธ EXPERIENCE TO PRIORITIZE -- which of my experiences to move to top or emphasize - -## โœ‚๏ธ WHAT TO DE-EMPHASIZE -- what to remove or minimize for this application - -## ๐ŸŽฏ SKILLS SECTION -- specific skills to list for this role`, - - qa: `I am applying for this job and need to answer application questions. -${langInstruction} -Answer each question based on my profile. Be specific, professional, and concise. - -${profileBlock} - -JOB: -${jobContext} - -${job.questions.length > 0 - ? 'QUESTIONS TO ANSWER:\n' + job.questions.map((q, i) => `${i + 1}. [${q.type}] ${q.question}`).join('\n') - : 'Generate answers for these common application questions:\n1. Why are you interested in this role?\n2. What is your relevant experience?\n3. What are your salary expectations?\n4. When can you start?\n5. Do you require visa sponsorship?\n6. What is your current notice period?'} - -For each question provide a clear, professional answer ready to paste. Keep answers concise (2-4 sentences for text fields).`, - - benefits: `Analyze this job posting and extract all benefits and advantages. -${langInstruction} - -JOB: -${jobContext} - -Provide: -## ๐Ÿ’ฐ COMPENSATION & BENEFITS -- salary, bonuses, equity if mentioned; otherwise state "Not specified" - -## ๐Ÿข WORK ARRANGEMENT -- remote/hybrid/onsite, flexibility, hours - -## ๐Ÿ“ˆ GROWTH OPPORTUNITIES -- career development, training, advancement potential - -## ๐ŸŒŸ COMPANY PERKS -- insurance, vacation, equipment, culture perks etc. - -## โš ๏ธ POTENTIAL CONCERNS -- red flags, very demanding requirements, unclear aspects - -## ๐Ÿ“Š OVERALL ATTRACTIVENESS: X/10 -Brief honest assessment of this opportunity for my profile` - }; - - return prompts[tab] || prompts.analysis; - } // โ”€โ”€โ”€ Markdown Renderer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -933,14 +816,28 @@ Brief honest assessment of this opportunity for my profile` // โ”€โ”€โ”€ Utilities โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Markers that identify a profile saved before the CV was rewritten around + // the Senior Mobile Architect positioning. A profile containing any of these + // is describing a person who no longer matches the master CV, so every prompt + // built from it would contradict the PDF that actually gets sent. + const STALE_PROFILE_MARKERS = [ + 'CTO & Technical Architect', + 'Senior Backend Engineer & Systems Architect', + 'IntaleqMaps', + 'hamzaayed.dev@gmail.com' + ]; + + function isStaleProfile(text) { + if (!text) return true; + return STALE_PROFILE_MARKERS.some(marker => text.includes(marker)); + } + function getSettings() { return new Promise(resolve => { chrome.storage.sync.get(['apiKey', 'userProfile', 'language'], (data) => { - if (data.userProfile && (data.userProfile.includes('hamzaayed@intaleqapp.com') || data.userProfile.includes('hamzaayedflutter@gmail.com') || data.userProfile.includes('hamzaayed@tripz-egypt.com'))) { - data.userProfile = data.userProfile - .replace(/hamzaayed@intaleqapp\.com/g, 'hamzaayed.dev@gmail.com') - .replace(/hamzaayedflutter@gmail\.com/g, 'hamzaayed.dev@gmail.com') - .replace(/hamzaayed@tripz-egypt\.com/g, 'hamzaayed.dev@gmail.com'); + if (isStaleProfile(data.userProfile) && typeof renderProfileText === 'function') { + console.warn('[LJA] Stored profile is out of date with the master CV โ€” migrating to profile_data.js.'); + data.userProfile = renderProfileText(); chrome.storage.sync.set({ userProfile: data.userProfile }); } resolve(data); diff --git a/cv_template.html b/cv_template.html deleted file mode 100644 index 0827364..0000000 --- a/cv_template.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - - -
-

Hamza Ayed

- -
{{JOB_HEADLINE}}
- -
- -
Professional Summary
- -

{{TAILORED_SUMMARY}}

- - - -
Technical Skills
- -

Core Focus: {{DYNAMIC_SKILLS}}

-

Backend & Architecture: PHP (Workerman), Node.js, NestJS, Python (FastAPI/Flask), REST APIs, WebSockets, Event-Driven Architecture

-

Infrastructure & Cloud: Docker, Linux, Nginx, Redis, CI/CD (GitHub Actions), AWS

-

Databases & Geospatial: PostgreSQL/PostGIS, MySQL, OpenStreetMap (OSM), GraphHopper, MapLibre GL, Spatial Queries

-

Mobile: Flutter/Dart, GetX, BLoC/Cubit

- -
Selected Technical Highlights
-
    -
  • Built proprietary OSM-based mapping infrastructure replacing Google Maps APIs
  • -
  • Engineered WebSocket-based real-time ride tracking systems
  • -
  • Developed offline-first mapping SDKs published on pub.dev and npm
  • -
  • Designed async AI document-processing pipelines using vision models
  • -
  • Optimized routing infrastructure using GraphHopper and PostGIS
  • -
- -
Professional Experience
- -
-
-
CTO & Technical Architect | Systems & Backend Focus โ€” Intaleq  | 
-
January 2024 โ€“ Present | Remote (MENA Region)
-
-
-
    -
  • Led the full-stack architecture of a smart transportation ecosystem supporting 1,800+ drivers and 2,500+ riders, building the core backend with PHP/Workerman.
  • -
  • Architected the proprietary mapping platform (IntaleqMaps), orchestrating GraphHopper routing, OSM tile serving, and PostGIS spatial queries via a NestJS API layer, saving $10,000+/month in operational costs.
  • -
-
- -
-
-
Founding Engineer & Lead Backend Architect โ€” Tripz Egypt  | 
-
Jan 2023 โ€“ Present | Cairo / Remote
-
-
-
    -
  • Co-founded and architected Egypt's homegrown ride-hailing platform, managing the entire distributed ecosystem with real-time tracking for 4,318 drivers and 2,464 riders.
  • -
  • Implemented robust event-driven architecture for real-time driver/rider matching and WebSockets-based route optimization.
  • -
  • Integrated local digital payment gateways and driver payout systems, supporting an 8% commission structure that drove a $0.78 Customer Acquisition Cost.
  • -
-
- -
-
-
Senior Systems Architect โ€” Mobile & Backend (Freelance)  | 
-
Jan 2017 โ€“ Dec 2023 | Jordan / Remote
-
-
-
    -
  • Delivered 25+ production enterprise applications across GIS, FinTech, and utilities, leading full lifecycle architecture for clients in the MENA region.
  • -
  • Developed Nabih (AI Smart Responder): designed and built an intelligent virtual assistant system connecting a Flutter mobile application (GetX/Cubit) with custom PHP backend APIs.
  • -
  • Developed Musadaq (AI-Powered Invoice Processing): engineered an automated KYC and invoice document processor using AI vision models and asynchronous processing pipelines.
  • -
-
- -
Notable Projects
-
    -
  • IntaleqMaps Engine: Engineered a proprietary mapping platform orchestrating GraphHopper routing, OSM tile serving, and PostGIS spatial queries via a NestJS API layer, replacing Google Maps API across the MENA region.
  • -
  • Nabih AI Virtual Assistant: Architected an automated customer support solution connecting a Flutter application (GetX/Cubit) to custom high-concurrency PHP backend APIs.
  • -
  • Musadaq AI Document Processor: Engineered an automated KYC and invoice document processing platform utilizing AI vision models and async processing pipelines.
  • -
  • Meta Ads Manager & SaaS Gateways: Deployed multi-tenant platforms using NestJS/React (Meta Ads Manager) and high-concurrency Node.js (WhatsApp bridge managing headless Puppeteer sessions in Docker) to automate operations.
  • -
- -
Open Source Contributions
-
    -
  • intaleq_maps (Flutter SDK): Published on pub.dev/packages/intaleq_maps. Custom map rendering, offline caching, and route plotting package optimized for low-bandwidth environments.
  • -
  • intaleq-maps-gl (NPM Library): Published on npmjs.com/package/intaleq-maps-gl. Web-based Mapbox GL compatible library optimized for custom OSM tiles and routing integration.
  • -
- -
Education & Certifications
-
    -
  • BS Mathematics (Applied Computing & Algorithms), Mutah University (2003โ€“2007)
  • -
  • Google Data Analytics Professional Certificate (Coursera)
  • -
  • IBM Data Analyst Professional Certificate (Coursera)
  • -
  • Meta APIs & Django Web Framework Course Certificates (Coursera)
  • -
  • AWS Cloud Practitioner & Cloud Architecture Fundamentals
  • -
- -
Languages
-

Arabic: Native | English: Professional Working Proficiency | Turkish: Limited Working Proficiency

- -
Availability & Work Authorization
-

- Available Immediately | Open to Remote, Hybrid & Relocation (GCC/Europe/MENA) | Valid Passport -

- - - diff --git a/deploy.sh b/deploy.sh index 14b21d6..d4d6bf3 100755 --- a/deploy.sh +++ b/deploy.sh @@ -2,6 +2,15 @@ echo "๐Ÿš€ Starting Deployment..." +# Keep the backend's copy of the profile in sync with profile_data.js. +# Skipping this ships a CV built from stale facts. +if command -v node >/dev/null 2>&1; then + echo "๐Ÿ”„ Syncing profile.json from profile_data.js..." + node sync_profile.js || { echo "โŒ Profile sync failed โ€” aborting deploy."; exit 1; } +else + echo "โš ๏ธ node not found โ€” skipping profile sync. Verify server/profile.json is current." +fi + # Add all changes git add . diff --git a/manifest.json b/manifest.json index 6d9427e..78945d6 100644 --- a/manifest.json +++ b/manifest.json @@ -22,6 +22,7 @@ "https://www.linkedin.com/jobs/*" ], "js": [ + "profile_data.js", "prompts.js", "content.js" ], @@ -50,6 +51,7 @@ "https://www.linkedin.com/search/results/all/*" ], "js": [ + "profile_data.js", "search_analyzer.js" ], "css": [ diff --git a/popup.html b/popup.html index a1d57e2..6b36b9a 100644 --- a/popup.html +++ b/popup.html @@ -527,6 +527,7 @@
+ diff --git a/popup.js b/popup.js index 47a271e..934d31b 100644 --- a/popup.js +++ b/popup.js @@ -39,12 +39,18 @@ function loadSettings() { document.getElementById('api-key-input').value = data.apiKey; setKeyStatus('ok'); } + // A profile saved before the CV was rewritten describes a different person + // than the PDF we now generate, so every prompt built from it would + // contradict the attached CV. Migrate it back to the source of truth. + const STALE_MARKERS = [ + 'CTO & Technical Architect', + 'Senior Backend Engineer & Systems Architect', + 'IntaleqMaps', + 'hamzaayed.dev@gmail.com' + ]; let profile = data.userProfile || DEFAULT_PROFILE; - if (profile.includes('hamzaayed@intaleqapp.com') || profile.includes('hamzaayedflutter@gmail.com') || profile.includes('hamzaayed@tripz-egypt.com')) { - profile = profile - .replace(/hamzaayed@intaleqapp\.com/g, 'hamzaayed.dev@gmail.com') - .replace(/hamzaayedflutter@gmail\.com/g, 'hamzaayed.dev@gmail.com') - .replace(/hamzaayed@tripz-egypt\.com/g, 'hamzaayed.dev@gmail.com'); + if (STALE_MARKERS.some(marker => profile.includes(marker))) { + profile = DEFAULT_PROFILE; chrome.storage.sync.set({ userProfile: profile }); } document.getElementById('profile-textarea').value = profile; @@ -173,6 +179,25 @@ document.getElementById('clear-all-btn').addEventListener('click', () => { // โ”€โ”€โ”€ Autofill Functionality โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ function parseProfileText(text) { + // When the textarea still holds the unmodified default, read the fields + // straight from the structured profile instead of regex-scraping the rendered + // text. Scraping only exists for the case where the user hand-edits it. + if (typeof PROFILE_DATA !== 'undefined' && text === DEFAULT_PROFILE) { + const id = PROFILE_DATA.identity; + const nameParts = id.fullName.split(' '); + return { + fullName: id.fullName, + firstName: nameParts[0] || '', + lastName: nameParts.slice(1).join(' ') || '', + email: id.email, + phone: id.phone, + linkedin: id.linkedin, + github: '', + summary: PROFILE_DATA.summary, + cvText: text + }; + } + const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/; const phoneRegex = /\+?[0-9]{1,4}[ \t.-]?[0-9]{3,4}[ \t.-]?[0-9]{3,4}/; const linkedinRegex = /(https?:\/\/)?(www\.)?linkedin\.com\/in\/[a-zA-Z0-9_-]+/; @@ -199,7 +224,13 @@ function parseProfileText(text) { let summary = ""; const summaryIdx = text.toLowerCase().indexOf("summary:"); if (summaryIdx !== -1) { - const skillsIdx = text.toLowerCase().indexOf("core skills:", summaryIdx); + // Stop at whichever section header comes next; the rendered profile uses + // "TARGET ROLES:", hand-written ones often use a skills header. + const lower = text.toLowerCase(); + const candidates = ['target roles:', 'technical skills:', 'core skills:', 'skills:'] + .map(h => lower.indexOf(h, summaryIdx)) + .filter(i => i !== -1); + const skillsIdx = candidates.length ? Math.min(...candidates) : -1; if (skillsIdx !== -1) { summary = text.substring(summaryIdx + 8, skillsIdx).trim(); } else { diff --git a/post_feed.css b/post_feed.css index 519fac1..dd4c6e6 100644 --- a/post_feed.css +++ b/post_feed.css @@ -156,6 +156,77 @@ border-color: rgba(108, 99, 255, 0.7); } +/* Credibility analysis panel (Arabic) */ +.lja-cb-analysis { + padding: 10px 14px; + background: rgba(108, 99, 255, 0.05); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.lja-cb-analysis-label { + font-size: 11px; + color: #ff8787; + margin-bottom: 4px; + font-weight: 600; +} + +.lja-cb-analysis-body { + font-size: 12px; + color: #e0deff; + line-height: 1.5; + text-align: right; + white-space: pre-wrap; +} + +/* Shown when the model judged the post not worth commenting on */ +.lja-cb-skip { + padding: 14px 16px; + font-size: 13px; + color: #ffd08a; + text-align: right; + line-height: 1.6; +} + +.lja-cb-actions button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* Rewrite variant */ +.lja-cb-header--rewrite { + background: linear-gradient(135deg, #FF6B6B 0%, #C92A2A 100%); +} + +.lja-cb-section-label { + padding: 12px 14px 6px; + font-size: 12px; + font-weight: 600; + color: #b0a8ff; + border-top: 1px solid rgba(255, 255, 255, 0.06); +} + +.lja-cb-text--tall { + min-height: 140px; + max-height: 320px; +} + +.lja-cb-image-prompt { + display: block; + width: 100%; + min-height: 80px; + max-height: 160px; + padding: 12px 16px; + background: rgba(0, 0, 0, 0.18); + border: none; + color: #c9c4ff; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 12px; + line-height: 1.55; + resize: vertical; + outline: none; + box-sizing: border-box; +} + /* Paste button โ€” primary emphasis */ .lja-cb-paste { background: linear-gradient(135deg, rgba(108,99,255,0.3), rgba(79,70,229,0.3)) !important; diff --git a/post_feed.js b/post_feed.js index 4a5c56d..63457f6 100644 --- a/post_feed.js +++ b/post_feed.js @@ -102,25 +102,28 @@ const isRTL = /[\u0600-\u06FF]/.test(commentText); + // The post text is written by a third party and flows through the model into + // this box, so nothing here may go through innerHTML. Interpolating it would + // let any post inject markup โ€” including a break-out โ€” into the + // extension's own UI on linkedin.com. + const skipped = !commentText.trim(); + const box = document.createElement('div'); box.className = BOX_CLASS; box.innerHTML = `
๐Ÿค– - Smart Comment & Analysis + Smart Comment & Analysis
-
-
๐Ÿ•ต๏ธ ุงู„ุชุญู„ูŠู„ ูˆุงู„ู…ุตุฏุงู‚ูŠุฉ:
-
- ${arabicSummary} -
+
+
๐Ÿ•ต๏ธ ุงู„ุชุญู„ูŠู„ ูˆุงู„ู…ุตุฏุงู‚ูŠุฉ:
+
- + +
@@ -128,6 +131,22 @@
`; + box.querySelector('.lja-cb-analysis-body').textContent = arabicSummary; + + const textArea = box.querySelector('.lja-cb-text'); + textArea.value = commentText; + textArea.dir = isRTL ? 'rtl' : 'ltr'; + textArea.style.textAlign = isRTL ? 'right' : 'left'; + + // The prompt deliberately returns an empty comment when it has nothing + // worth adding. Show the reasoning rather than an empty box to paste. + if (skipped) { + box.querySelector('.lja-cb-skip').hidden = false; + textArea.hidden = true; + box.querySelector('.lja-cb-copy').disabled = true; + box.querySelector('.lja-cb-paste').disabled = true; + } + box.querySelector('.lja-cb-close').addEventListener('click', () => box.remove()); box.querySelector('.lja-cb-copy').addEventListener('click', function () { @@ -305,38 +324,41 @@ imagePrompt = parts[1].trim(); } + // Same rule as the comment box: model output derived from a third-party post + // never touches innerHTML. Only the static shell does. const box = document.createElement('div'); box.className = BOX_CLASS; box.innerHTML = ` -
+
โœ๏ธ Rewritten Post
-
- Post Content: -
- - ${imagePrompt ? ` -
+ + + - - ` : ''} +
- ${imagePrompt ? '' : ''} +
`; + const postArea = box.querySelector('.lja-cb-text'); + postArea.value = postContent; + postArea.dir = isRTL ? 'rtl' : 'ltr'; + postArea.style.textAlign = isRTL ? 'right' : 'left'; + + if (imagePrompt) { + box.querySelector('.lja-cb-section-label--img').hidden = false; + const imgArea = box.querySelector('.lja-cb-image-prompt'); + imgArea.value = imagePrompt; + imgArea.hidden = false; + box.querySelector('.lja-cb-copy-img').hidden = false; + } + box.querySelector('.lja-cb-close').addEventListener('click', () => box.remove()); box.querySelector('.lja-cb-copy').addEventListener('click', function () { diff --git a/profile.js b/profile.js index a765d66..f52585e 100644 --- a/profile.js +++ b/profile.js @@ -1,62 +1,10 @@ -// profile.js โ€” Default user profile (pre-filled) -// This is loaded into the popup and used as the default profile text. +// profile.js โ€” Default user profile text shown in the popup. +// ============================================================================ +// DO NOT hardcode profile facts here. This file only RENDERS the single source +// of truth defined in profile_data.js. Edit that file instead. +// profile_data.js must be loaded before this script. +// ============================================================================ -const DEFAULT_PROFILE = `HAMZA AYED โ€” Senior Backend Engineer & Systems Architect - -SUMMARY: -Senior Backend Engineer and Systems Architect with experience building real-time transportation, mapping, and SaaS platforms across the MENA region. Specialized in high-concurrency PHP systems, distributed backend services, GIS infrastructure, and scalable APIs using Workerman, NestJS, Redis, PostgreSQL/PostGIS, Docker, and AWS. Built proprietary routing and mapping infrastructure integrating OpenStreetMap and GraphHopper for production ride-hailing environments. - -TECHNICAL SKILLS: -- Backend & Architecture: PHP (Workerman), Node.js, NestJS, Python (FastAPI/Flask), REST APIs, WebSockets, Event-Driven Architecture -- Infrastructure & Cloud: Docker, Linux, Nginx, Redis, CI/CD (GitHub Actions), AWS -- Databases & Geospatial: PostgreSQL/PostGIS, MySQL, OpenStreetMap (OSM), GraphHopper, MapLibre GL, Spatial Queries -- Mobile: Flutter/Dart, GetX, BLoC/Cubit - -KEY ACHIEVEMENTS: -- Architected IntaleqMaps orchestrating GraphHopper routing, OSM tile serving, and PostGIS spatial queries via a NestJS API layer, saving $10,000+/month vs Google Maps API -- Created custom Flutter Mapping SDK (intaleq_maps) published on pub.dev and JS Library (intaleq-maps-gl) on NPM -- Designed and built the IntaleqMaps platform using NestJS and PostGIS, and core ecosystem backend using PHP/Workerman -- Designed custom FinTech payment infrastructure for markets with zero standard payment API access -- Built 4-app ecosystem: Rider App, Driver App, Admin Dashboard, Service Portal โ€” all from scratch -- Architected smart mobility platform serving 1,800+ drivers and 2,500+ riders across MENA -- Integrated AI vision models for invoice processing (Musadaq platform) -- Developed Nabih: AI-powered smart responder system orchestrating a Flutter application (GetX/Cubit) with a PHP backend API for automated customer query resolution -- Built AI-Powered ATS Automation Chrome Extension: Real-time LinkedIn DOM scraping, Gemini 1.5 Pro, PHP-based PDF generation -- 51 professional certifications (Google, IBM, Meta via Coursera) -- $0.78 Driver Customer Acquisition Cost in the competitive Egyptian market -- Government-accredited โ€” only licensed mobility platform in its market - -EXPERIENCE: -- CTO & Technical Architect | Systems & Backend Focus โ€” Intaleq | Jan 2024โ€“Present | Remote (MENA Region) - โ†’ Led full-stack architecture of smart transportation ecosystem: 1,800+ drivers, 2,500+ riders. Designed core backend in PHP/Workerman and mapping platform backend in NestJS. Created proprietary mapping, routing, and payment infrastructure. -- Founding Engineer & Lead Backend Architect โ€” Tripz Egypt | Jan 2023โ€“Present | Cairo/Remote - โ†’ Built ride-hailing platform from zero: 8 ride types, 8% driver commission (lowest in market). Full-stack: Flutter apps, PHP backend, WebSockets, event-driven architecture, payment integrations. $0.78 CAC. -- Senior Systems Architect โ€” Mobile & Backend (Freelance) | Jan 2017โ€“Dec 2023 | Jordan/Remote - โ†’ 25+ production apps across MENA clients spanning healthcare, logistics, sports, HR, and utilities. - -EDUCATION: -- BS Mathematics (Applied Computing & Algorithms), Mutah University, 2003โ€“2007 (Very Good) -- Google Data Analytics Professional Certificate (Coursera) -- IBM Data Analyst Professional Certificate (Coursera) -- Meta APIs & Django Web Framework Course Certifications (Coursera) -- AWS Cloud Practitioner & Cloud Architecture Fundamentals - -LANGUAGES: Arabic (Native), English (Professional Working Proficiency) -LOCATION: Amman, Jordan | Open to Remote, Hybrid, On-site (Middle East & Gulf) -CONTACT: hamzaayed.dev@gmail.com | +962 79 858 3052 | linkedin.com/in/hamza-ayed | github.com/Hamza-Ayed | intaleqapp.com/hamza.html (Portfolio) - -TARGET ROLES: -1. Technical Architect / Solutions Architect (distributed systems, cloud, GIS, FinTech) -2. Lead Software Engineer (full-stack, mobile ecosystems, backend) -3. GIS / Geospatial Developer (mapping, OSM, custom SDK, spatial data) -4. FinTech Integration Engineer (payments, wallets, gateways) -5. Engineering Manager / Mobile Architecture Lead - -NOTABLE PROJECTS: -- Intaleq Smart Mobility Platform โ€” Real-time ride-hailing with PHP/Workerman core backend, NestJS mapping engine, PostGIS spatial features, and proprietary payment infrastructure -- Tripz Egypt โ€” Ride-hailing platform with 8 ride types, AI-powered KYC, lowest commission model, WebSockets dispatcher -- Musadaq โ€” AI-powered invoice processing platform with vision models -- Nabih โ€” AI-powered smart responder assistant built with Flutter (GetX/Cubit) and PHP backend APIs -- IntaleqMaps SDK & JS GL โ€” Published mapping libraries on pub.dev + NPM (https://pub.dev/packages/intaleq_maps and https://libraries.io/npm/intaleq-maps-gl) -- LinkedIn Job Analyzer โ€” AI Chrome extension with real-time DOM scraping, Gemini 1.5 Pro, PHP PDF generation -- 25+ client apps: healthcare, sports news, HR systems, transit, utilities`; +const DEFAULT_PROFILE = (typeof renderProfileText === 'function') + ? renderProfileText() + : '/* profile_data.js failed to load โ€” check the script order in popup.html */'; diff --git a/profile_data.js b/profile_data.js new file mode 100644 index 0000000..1f261da --- /dev/null +++ b/profile_data.js @@ -0,0 +1,264 @@ +// profile_data.js โ€” SINGLE SOURCE OF TRUTH +// ============================================================================ +// Every prompt, CV, cover letter and comment in this extension derives from +// this file. Nothing else may hardcode job titles, metrics, dates or contacts. +// +// Synced from: cvs/ATS-safe rรฉsumรฉ template.pdf (master CV). +// If the master CV changes, change THIS file โ€” then nothing else needs editing. +// A mirror copy lives at server/profile.json for the PHP backend. +// ============================================================================ + +const PROFILE_DATA = { + identity: { + fullName: 'Hamza Ayed', + primaryTitle: 'Senior Mobile Architect', + positioningLine: 'Ride-Hailing & Real-Time Systems ยท FinTech in Emerging Markets', + location: 'Amman, Jordan', + phone: '+962 798 583 052', + email: 'hamzaayedflutter@gmail.com', + linkedin: 'linkedin.com/in/hamza-ayed', + portfolio: 'intaleqapp.com/hamza.html', + productSite: 'tripz-egypt.com', + languages: 'Arabic (Native), English (Professional Working Proficiency)', + availability: 'Available immediately. Open to Remote, Hybrid and Relocation (GCC / MENA / Europe).' + }, + + // Titles the CV/headline generator is ALLOWED to use. Anything outside this + // list is a fabrication risk โ€” see integrity rules in prompts.js. + allowedTitles: [ + 'Senior Mobile Architect', + 'Senior Mobile Engineer', + 'Lead Mobile Engineer', + 'Senior Flutter Developer', + 'Lead Flutter Engineer', + 'Mobile Technical Lead', + 'Founding Engineer (Mobile)', + 'Senior Software Engineer (Mobile)' + ], + + // Titles that would overstate or misrepresent the record. + forbiddenTitles: [ + 'CTO', + 'Solutions Architect', + 'Senior AI Developer', + 'Machine Learning Engineer', + 'Data Scientist', + 'DevOps Architect', + 'Engineering Manager' + ], + + summary: + 'Senior Mobile Architect and Founding Engineer with proven experience building and launching two ' + + 'production-grade ride-hailing platforms (Tripz โ€” Egypt, Intaleq โ€” Syria) from zero to live operations. ' + + 'Designed and delivered full mobility ecosystems including Rider, Driver, Admin and Customer Support ' + + 'applications, along with backend APIs, security layers, DevOps pipelines and fintech integrations. ' + + 'Specialized in real-time systems, self-hosted geo-services, high-latency optimization and ' + + 'infrastructure cost reduction.', + + targetRoles: [ + 'Senior / Lead Mobile Engineer', + 'Mobile Architect', + 'Founding Engineer (Mobility, Logistics, FinTech)' + ], + + // Headline metrics โ€” these are the numbers that carry the CV. + headlineMetrics: [ + { value: '5,275', label: 'Riders acquired across 2 launches' }, + { value: '5,750+', label: 'Drivers onboarded' }, + { value: '~80%', label: 'Map infrastructure cost cut' }, + { value: '8 apps', label: 'Shipped across 2 platforms' } + ], + + achievementsAtAGlance: [ + 'Tripz (Egypt): Scaled platform to 2,464 riders and 4,318 drivers. Built and launched 4 distinct apps ' + + 'plus full backend in under 4 months with near-zero map infrastructure cost.', + 'Intaleq (Syria): Acquired 2,811 riders and 1,440 drivers in the first 2 months. Engineered a custom ' + + 'operational payment layer to bypass API limitations in a highly constrained market.' + ], + + // Grouped exactly as the master CV groups them. The ATS keyword selector may + // only choose from these values. + skills: { + 'Mobile & Architecture': [ + 'Flutter', 'Dart', 'GetX', 'Provider', 'Clean Architecture', + 'Offline-First Design', 'REST APIs', 'State Management', 'Native Android (Java)', + 'iOS / Android Release Management' + ], + 'Real-Time & Geo Systems': [ + 'WebSockets', 'Real-Time Dispatching', 'Live Tracking', 'Self-Hosted Maps', + 'Map Tiles', 'Polylines', 'Routing Engines', 'Geospatial Queries', + 'High-Latency Network Optimization' + ], + 'Backend & DevOps': [ + 'PHP', 'Laravel', 'MySQL', 'Docker', 'Linux', 'Nginx', + 'CI/CD (Gitea)', 'Infrastructure Cost Optimization', 'Self-Hosted Infrastructure' + ], + 'Security & FinTech': [ + 'Payment Gateway Integration (7+ providers)', 'HMAC Security', 'RASP', + 'Fake GPS Detection', 'Anti-Tampering', 'KYC Automation' + ] + }, + + experience: [ + { + company: 'Intaleq', + title: 'Senior Mobile Systems Architect', + location: 'MENA Region (Remote)', + start: 'Jan 2025', + end: 'Present', + // Tagged so the tailoring engine can pick the 3-4 most job-relevant + // bullets instead of always printing the same ones. + bullets: [ + { text: 'Launched a ride-hailing platform in a high-constraint market (Syria), reaching 2,811 riders and 1,440 drivers within the first two months.', tags: ['leadership', 'delivery', 'mobility', 'scale'] }, + { text: 'Designed and implemented Rider, Driver, Admin and Customer Support applications.', tags: ['mobile', 'flutter', 'architecture'] }, + { text: 'Built a custom payment helper application to integrate legacy, non-API local payment gateways, enabling reliable driver balance top-ups despite infrastructure limitations.', tags: ['fintech', 'payments', 'integration'] }, + { text: 'Leveraged fully self-hosted map tiles and routing engines to eliminate dependency on expensive third-party mapping APIs, keeping operational costs near zero.', tags: ['geo', 'cost', 'infrastructure'] }, + { text: 'Designed an offline-first caching mechanism ensuring data integrity and uninterrupted driverโ€“rider interactions during unstable network conditions.', tags: ['mobile', 'offline', 'reliability'] }, + { text: 'Optimized deployment cycles by 40% through a secure, self-hosted CI/CD pipeline using Gitea, ensuring data sovereignty and rapid release efficiency.', tags: ['devops', 'cicd', 'security'] } + ] + }, + { + company: 'Tripz', + title: 'Founding Mobile Architect', + location: 'Cairo, Egypt (Remote)', + start: 'Jan 2024', + end: 'Dec 2024', + bullets: [ + { text: 'Built and launched a complete ride-hailing ecosystem (Rider, Driver, Admin, Customer Support apps plus backend API) in under four months, scaling to 2,464 riders and 4,318 drivers.', tags: ['delivery', 'leadership', 'mobility', 'scale', 'flutter'] }, + { text: 'Designed real-time dispatch and live tracking using WebSockets, optimized for high-latency MENA network environments.', tags: ['realtime', 'websockets', 'geo'] }, + { text: 'Reduced map infrastructure expenses by approximately 80% versus third-party API pricing by self-hosting map tiles, routing logic and polyline generation on dedicated servers.', tags: ['geo', 'cost', 'infrastructure'] }, + { text: 'Replaced traditional call-center hardware by engineering a secure, mobile-first Customer Support application, enabling support agents to operate entirely remotely.', tags: ['mobile', 'product', 'cost'] }, + { text: 'Automated driver onboarding through AI-powered KYC integration, reducing manual review time and minimizing fraudulent registrations.', tags: ['ai', 'automation', 'fintech', 'security'] }, + { text: 'Implemented API-level HMAC security, anti-tampering mechanisms and Fake GPS detection to protect geo-spatial and financial data.', tags: ['security', 'geo', 'fintech'] }, + { text: 'Collaborated closely with business stakeholders and operations teams to align product and architecture decisions with on-the-ground market realities.', tags: ['leadership', 'product', 'communication'] } + ] + }, + { + company: 'Global Tech Projects', + title: 'Lead Mobile Engineer (Flutter / Native)', + location: 'Freelance / Consultant', + start: 'Jan 2017', + end: 'Dec 2023', + bullets: [ + { text: 'Delivered 30+ production-grade mobile applications across e-commerce, delivery, healthcare and news sectors, acting as technical decision-maker for early-stage clients.', tags: ['delivery', 'leadership', 'mobile', 'scale'] }, + { text: 'Migrated legacy Android (Java) applications to Flutter, cutting maintenance complexity and delivery timelines by roughly 50%.', tags: ['flutter', 'migration', 'mobile'] }, + { text: 'Built custom Flutter plugins and tailored API layers to integrate with complex legacy backend systems.', tags: ['flutter', 'integration', 'architecture'] } + ] + } + ], + + education: { + degree: 'BSc Mathematics', + institution: "Mu'tah University, Jordan", + note: 'Applied mathematical foundations to state-machine design and geo-spatial routing.' + }, + + certifications: [ + 'IBM Data Analyst Professional Certificate', + 'Google Data Analytics Professional Certificate', + 'IBM Data Analytics with Excel and R Specialization' + ], + + // โ”€โ”€ Honest capability boundaries โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Used to stop the model inventing experience. Keep this brutally accurate: + // it is the difference between a CV that survives a technical screen and one + // that does not. + boundaries: { + strongIn: [ + 'Flutter / Dart mobile architecture at production scale', + 'Real-time systems: WebSockets, dispatching, live tracking', + 'Self-hosted geo infrastructure: tiles, routing, polylines', + 'Payment gateway integration and constrained-market fintech workarounds', + 'Mobile-layer security: HMAC, anti-tampering, Fake GPS detection', + 'PHP / Laravel / MySQL backend supporting mobile products', + 'Docker, Linux, Nginx, self-hosted CI/CD' + ], + limitedIn: [ + 'AI is limited to INTEGRATING third-party vision/LLM APIs (KYC automation, Gemini API). ' + + 'No model training, no MLOps, no deep learning research.', + 'Cloud experience is self-hosted/VPS-centric โ€” not deep AWS/GCP/Azure managed-service experience.', + 'No Kubernetes in production.', + 'No native Swift/SwiftUI or Kotlin-first development โ€” Flutter is the primary mobile path.', + 'No data engineering (Spark, Hadoop, Airflow, warehouses).' + ], + neverClaim: [ + 'TensorFlow', 'PyTorch', 'Scikit-learn', 'Spark', 'Hadoop', 'Kubernetes', + 'MLOps', 'model training', 'NLP research', 'Swift', 'SwiftUI', 'Kotlin Multiplatform', + 'blockchain', 'Salesforce', 'SAP' + ] + }, + + // Voice rules โ€” applied to cover letters, LinkedIn comments and posts. + voice: { + tone: 'Operator-level, calm, direct, evidence-first. Writes like someone who has shipped, not someone selling.', + banned: [ + 'I am writing to apply', 'I leverage my expertise', 'passionate about', + 'proven track record', 'dynamic environment', 'synergy', 'game-changer', + 'disruptive', 'serving millions', 'in the digital age', 'Great post', + 'Well said', 'Couldn\'t agree more', 'This resonates', + 'ููŠ ุงู„ุนุตุฑ ุงู„ุฑู‚ู…ูŠ', 'ู…ู…ุง ู„ุง ุดูƒ ููŠู‡', 'ุฎุชุงู…ุงู‹', 'ูŠุนุฏ ู…ู† ุฃู‡ู…' + ], + neverMention: [ + 'military history or background', + 'sanctions or politically sensitive framing of the Syria market', + 'exact client names under NDA' + ] + } +}; + +// Renders the structured profile into the plain-text block that prompts consume. +function renderProfileText(p = PROFILE_DATA) { + const id = p.identity; + const lines = []; + + lines.push(`${id.fullName.toUpperCase()} โ€” ${id.primaryTitle}`); + lines.push(id.positioningLine); + lines.push(''); + lines.push('SUMMARY:'); + lines.push(p.summary); + lines.push(''); + lines.push('TARGET ROLES: ' + p.targetRoles.join(' | ')); + lines.push(''); + lines.push('HEADLINE METRICS:'); + p.headlineMetrics.forEach(m => lines.push(`- ${m.value} โ€” ${m.label}`)); + lines.push(''); + lines.push('AT A GLANCE:'); + p.achievementsAtAGlance.forEach(a => lines.push(`- ${a}`)); + lines.push(''); + lines.push('TECHNICAL SKILLS:'); + Object.entries(p.skills).forEach(([group, items]) => { + lines.push(`- ${group}: ${items.join(', ')}`); + }); + lines.push(''); + lines.push('EXPERIENCE:'); + p.experience.forEach(job => { + lines.push(`- ${job.title} โ€” ${job.company} | ${job.start} โ€“ ${job.end} | ${job.location}`); + job.bullets.forEach(b => lines.push(` โ€ข ${b.text}`)); + }); + lines.push(''); + lines.push('EDUCATION:'); + lines.push(`- ${p.education.degree}, ${p.education.institution}. ${p.education.note}`); + p.certifications.forEach(c => lines.push(`- ${c}`)); + lines.push(''); + lines.push('CAPABILITY BOUNDARIES (do not overstate):'); + lines.push(' STRONG IN:'); + p.boundaries.strongIn.forEach(s => lines.push(` + ${s}`)); + lines.push(' LIMITED / NOT EXPERIENCED IN:'); + p.boundaries.limitedIn.forEach(s => lines.push(` - ${s}`)); + lines.push(' NEVER CLAIM: ' + p.boundaries.neverClaim.join(', ')); + lines.push(''); + lines.push(`LANGUAGES: ${id.languages}`); + lines.push(`LOCATION: ${id.location} โ€” ${id.availability}`); + lines.push(`CONTACT: ${id.email} | ${id.phone} | ${id.linkedin} | ${id.portfolio}`); + + return lines.join('\n'); +} + +// Flat list of every legitimate skill token, for ATS keyword matching. +function allSkillTokens(p = PROFILE_DATA) { + return Object.values(p.skills).flat(); +} + +if (typeof module !== 'undefined' && module.exports) { + module.exports = { PROFILE_DATA, renderProfileText, allSkillTokens }; +} diff --git a/prompts.js b/prompts.js index c075e2f..5195d19 100644 --- a/prompts.js +++ b/prompts.js @@ -1,38 +1,134 @@ -// prompts.js โ€” All AI prompts v3 -// LANGUAGE RULES: Analysis = match job language. Everything else = ENGLISH ALWAYS. +// prompts.js โ€” All AI prompts v4 (ATS-engineered) +// ============================================================================ +// LANGUAGE RULES: Analysis = Arabic. Everything else = ENGLISH ALWAYS. +// +// All identity facts come from PROFILE_DATA (profile_data.js). Nothing in this +// file may hardcode a job title, a metric, a date or a contact detail. +// ============================================================================ + +// โ”€โ”€โ”€ ATS methodology block โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// This is the part that actually moves the needle with parsers and recruiters. +// It is injected into every CV/keyword-facing prompt. +const ATS_METHOD = ` +ATS METHODOLOGY โ€” APPLY ALL OF THIS: + +1. EXACT-TERM MIRRORING (most important rule) + ATS matching is literal, not semantic. If the job says "React Native", writing + "cross-platform mobile" scores ZERO. Copy the job posting's EXACT wording for + any skill I genuinely have. Do not paraphrase, do not use a synonym, do not + pluralise differently. + +2. DUAL FORM FOR ACRONYMS + Write both forms on first use so either query matches: + "Continuous Integration / Continuous Deployment (CI/CD)", "Application + Programming Interface (API)", "Know Your Customer (KYC)". + +3. TITLE ALIGNMENT + The CV headline should closely mirror the job's title when that title is an + honest description of my record. Recruiters and ATS both weight title match + heavily. Never invent seniority. + +4. KEYWORD FREQUENCY + A critical keyword should appear 2-3 times across the document (summary, + skills, and at least one experience bullet) โ€” never stuffed, always inside a + real sentence that describes something I actually did. + +5. BULLET FORMULA (XYZ) + Every bullet: "Accomplished [X] as measured by [Y] by doing [Z]." + Start with a strong past-tense verb. Lead with the outcome, not the task. + Prefer a real number over an adjective every single time. + +6. HARD SKILLS BEAT SOFT SKILLS + Prioritise tools, languages, frameworks, protocols and platforms. Soft skills + ("team player", "communicator") are near-worthless to an ATS โ€” include only + where the job explicitly names them as a requirement. + +7. PARSER SAFETY + No tables, no columns, no text boxes, no graphics, no headers/footers, no + icons or symbols in place of words. Standard section headings only + ("Professional Summary", "Technical Skills", "Professional Experience", + "Education"). Dates as "MMM YYYY โ€“ MMM YYYY". + +8. HONEST GAP HANDLING + If the job requires something I do not have, do NOT insert it. Instead find + the closest genuine adjacent experience and name it accurately.`; + +// โ”€โ”€โ”€ Integrity rules, generated from the profile โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +function buildIntegrityRules(P) { + const b = P.boundaries; + return ` +STRICT INTEGRITY RULES โ€” VIOLATING ANY OF THESE INVALIDATES THE OUTPUT: + +1. NEVER invent a skill, tool, framework, employer, metric or certification that + is not present in MY PROFESSIONAL PROFILE below. +2. NEVER use these titles: ${P.forbiddenTitles.join(', ')}. + Allowed titles only: ${P.allowedTitles.join(', ')}. +3. NEVER claim any of the following โ€” I have no production experience with them: + ${b.neverClaim.join(', ')}. +4. MY GENUINE STRENGTHS (lead with these): +${b.strongIn.map(s => ' โ€ข ' + s).join('\n')} +5. MY HONEST LIMITS (acknowledge, never paper over): +${b.limitedIn.map(s => ' โ€ข ' + s).join('\n')} +6. Every number I use must be one of the real metrics in my profile. Do not + round up, do not extrapolate, do not invent new ones. +7. Do not mention: ${P.voice.neverMention.join('; ')}. +8. Banned phrasing (reads as AI-generated or as filler): + ${P.voice.banned.join(' / ')}.`; +} function buildPromptV2(tab, job, userProfile, language) { - // Force Arabic for the analysis tab to ensure full comprehension - const analysisLang = 'Respond ENTIRELY in Arabic. All headings, explanations, and bullets MUST be in Arabic. and RTL'; + const P = (typeof PROFILE_DATA !== 'undefined') ? PROFILE_DATA : null; + if (!P) { + console.error('[LJA] profile_data.js not loaded โ€” prompts will be degraded.'); + } + + const id = P ? P.identity : {}; + const analysisLang = + 'Respond ENTIRELY in Arabic. All headings, explanations and bullets MUST be in Arabic, and RTL.'; + + // Cap the description here rather than letting the transport truncate the + // whole prompt. Truncation at the transport layer cuts from the END, which is + // exactly where the job posting and the required output format live. + const MAX_DESCRIPTION_CHARS = 7000; + const rawDescription = job.description || 'No description available'; + const description = rawDescription.length > MAX_DESCRIPTION_CHARS + ? rawDescription.slice(0, MAX_DESCRIPTION_CHARS) + '\n[description truncated]' + : rawDescription; const ctx = [ 'Job Title: ' + (job.jobTitle || 'Not specified'), 'Company: ' + (job.company || 'Not specified'), 'Location: ' + (job.location || 'Not specified'), 'Type: ' + (job.jobType || 'Not specified'), - 'Description:\n' + (job.description || 'No description available'), - job.skills.length ? 'Required Skills: ' + job.skills.join(', ') : '' + 'Description:\n' + description, + (job.skills && job.skills.length) ? 'Required Skills: ' + job.skills.join(', ') : '' ].filter(Boolean).join('\n'); - const prof = 'MY PROFESSIONAL PROFILE:\n' + userProfile; + // Prefer the stored/edited profile; fall back to the rendered source of truth. + const profileText = (userProfile && userProfile.trim()) + ? userProfile + : (typeof renderProfileText === 'function' ? renderProfileText() : ''); + + const prof = 'MY PROFESSIONAL PROFILE:\n' + profileText; const co = job.company || 'this company'; - const loc = job.location || 'Middle East'; + const STRICT_RULES = P ? buildIntegrityRules(P) : ''; - const STRICT_RULES = ` -STRICT INTEGRITY RULES โ€” VIOLATING THESE IS FORBIDDEN: -1. NEVER invent skills, tools, frameworks, or certifications not explicitly listed in MY PROFESSIONAL PROFILE. -2. If the job requires a skill I do not have (e.g., TensorFlow, PyTorch, Scikit-learn, Spark, Hadoop, Kubernetes, deep NLP, generative AI model training), ACKNOWLEDGE THE GAP honestly. Do NOT fabricate experience. -3. My AI experience is LIMITED to: AI vision models for document processing (Musadaq), AI smart responder (Nabih), Gemini API integration (LinkedIn extension), and Python backend automation. I do NOT have MLOps pipeline experience, model training, or deep learning research. -4. My TRUE core stack is: PHP (Workerman), Node.js, NestJS, Python (FastAPI/Flask), PostgreSQL/PostGIS, Docker, Flutter โ€” NOT data science or ML engineering. -5. Always prioritize my REAL architecture achievements (IntaleqMaps, Tripz, 25+ apps, $10K/month savings) over generic AI buzzwords.`; + const Pr = {}; - const P = {}; + // โ”€โ”€ TAB: Analysis (Arabic) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + Pr.analysis = `You are an elite career strategist and a hiring manager who has +screened thousands of engineering CVs. Evaluate this job against my profile with +brutal honesty and EXTREME brevity. - P.analysis = `You are an elite career strategist. ${analysisLang} -Evaluate this job against my profile with brutal honesty and EXTREME brevity. -DO NOT recount my history, military background, or summarize my profile. Keep it actionable and short. -CRITICAL RULE: The user is a Senior Backend Engineer and Technical Lead who built and scaled production systems from zero. Position his 0-to-1, hands-on delivery experience as a massive advantage for any engineering team. Do NOT downgrade his technical leadership. + +CRITICAL FRAMING: I am a Senior Mobile Architect and Founding Engineer who has +taken two ride-hailing platforms from zero to live operations, owning mobile, +backend, geo-infrastructure, security and DevOps. Position that end-to-end 0-to-1 +ownership as the core advantage. Do NOT downgrade it to "just a Flutter developer". +Equally: do NOT inflate it into architecture-astronaut or CTO language. + +DO NOT recount my history or summarise my profile back to me. Be actionable. ${STRICT_RULES} @@ -41,25 +137,32 @@ ${prof} JOB POSTING: ${ctx} -Respond in this EXACT concise structure: +Respond in this EXACT structure: -## ๐ŸŽฏ VERDICT: [YES - APPLY / NO - SKIP] -[One short sentence explaining why] +## ๐ŸŽฏ ุงู„ู‚ุฑุงุฑ: [ุชู‚ุฏู‘ู… / ู„ุง ุชุชู‚ุฏู‘ู…] +[ุณุทุฑ ูˆุงุญุฏ ู‚ุตูŠุฑ ูŠุดุฑุญ ุงู„ุณุจุจ] -## โœ… WHY IT FITS (If Yes) -- [Bullet point 1] -- [Bullet point 2] -- [Bullet point 3] +## ๐Ÿ“Š ู†ุณุจุฉ ุงู„ู…ุทุงุจู‚ุฉ ุงู„ุชู‚ุฏูŠุฑูŠุฉ: X% +[ุณุทุฑ ูˆุงุญุฏ: ุนู„ู‰ ุฃูŠ ุฃุณุงุณ ุญุณุจุชู‡ุง] -## โŒ WHY IT MIGHT NOT FIT (If No or Maybe) -- [Bullet point 1 - e.g. location, specific missing skill] -- [Bullet point 2] +## โœ… ู†ู‚ุงุท ุงู„ู‚ูˆุฉ ู‡ู†ุง +- [ู†ู‚ุทุฉ 1 โ€” ุงุฑุจุทู‡ุง ุจู…ุชุทู„ุจ ู…ุญุฏุฏ ู…ู† ุงู„ุฅุนู„ุงู†] +- [ู†ู‚ุทุฉ 2] +- [ู†ู‚ุทุฉ 3] -## ๐Ÿ“ CV TWEAKS (If Yes) -- [What to emphasize in the CV] -- [What to de-emphasize]`; +## โŒ ุงู„ูุฌูˆุงุช ุงู„ุญู‚ูŠู‚ูŠุฉ +- [ูุฌูˆุฉ 1 โ€” ูˆู…ุฏู‰ ุฎุทูˆุฑุชู‡ุง: ู‚ุงุชู„ุฉ / ู‚ุงุจู„ุฉ ู„ู„ู…ุนุงู„ุฌุฉ / ุดูƒู„ูŠุฉ] +- [ูุฌูˆุฉ 2] - P.coverletter = `You are an expert career writer. Write a COMPLETE, READY-TO-SEND cover letter. +## ๐Ÿ“ ุชุนุฏูŠู„ุงุช ุงู„ุณูŠููŠ ุงู„ู…ุทู„ูˆุจุฉ +- [ู…ุง ุงู„ุฐูŠ ูŠุฌุจ ุฅุจุฑุงุฒู‡] +- [ู…ุง ุงู„ุฐูŠ ูŠุฌุจ ุชู‚ู„ูŠุตู‡] + +## ๐Ÿ”‘ ูƒู„ู…ุงุช ู…ูุชุงุญูŠุฉ ู†ุงู‚ุตุฉ ูŠุฌุจ ุฅุถุงูุชู‡ุง ุญุฑููŠุงู‹ +- [4-6 ูƒู„ู…ุงุช ู…ู†ุณูˆุฎุฉ ุญุฑููŠุงู‹ ู…ู† ุงู„ุฅุนู„ุงู†]`; + + // โ”€โ”€ TAB: Cover letter โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + Pr.coverletter = `You are an expert career writer. Write a COMPLETE, READY-TO-SEND cover letter. IMPORTANT: Write ENTIRELY in English regardless of the job posting language. ${STRICT_RULES} @@ -73,27 +176,36 @@ FORMAT โ€” follow EXACTLY: Dear ${co} Team, -[PARAGRAPH 1 โ€” HOOK (3 sentences max): Compelling connection between my background and this role. Mention the company by name. NO generic "I am writing to apply" openers.] +[PARAGRAPH 1 โ€” HOOK (3 sentences max): A specific connection between my record and +this role. Name the company. Open with something I BUILT, not with an intention to +apply.] -[PARAGRAPH 2 โ€” PROOF (4-5 sentences): Match 3-4 job requirements to my REAL achievements with NUMBERS. Use "At Intaleq, I..." or "When building Tripz, I..." Be concrete.] +[PARAGRAPH 2 โ€” PROOF (4-5 sentences): Match 3-4 of this job's stated requirements +to my REAL achievements, each with a number. Use "At Intaleq, I..." / "Building +Tripz, I...". Mirror the job's exact technical vocabulary where it matches my real +skills.] -[PARAGRAPH 3 โ€” CLOSE (2-3 sentences): Enthusiasm for THIS company. Confident call to action requesting an interview.] +[PARAGRAPH 3 โ€” CLOSE (2-3 sentences): Why THIS company specifically. Confident, +non-needy call to action requesting a conversation.] Best regards, -Hamza Ayed -hamzaayed.dev@gmail.com -+962 79 858 3052 +${id.fullName} +${id.email} +${id.phone} CRITICAL RULES: - MUST be in English -- NO brackets or placeholders โ€” use ACTUAL names and data -- Every sentence specific to THIS job at ${co} +- NO brackets or placeholders in the final output โ€” use ACTUAL names and data +- Every sentence must be unusable for any other job posting - Under 300 words total -- Tone MUST be professional, direct, and confident. Focus on delivery, impact, and hands-on engineering. DO NOT mention military history. DO NOT use buzzwords like "sanctioned markets", "serving millions", "disruptive".`; +- Tone: ${P ? P.voice.tone : 'direct and evidence-first'}`; - P.cvtips = `You are a LinkedIn optimization expert and ATS specialist. + // โ”€โ”€ TAB: CV tips / LinkedIn optimisation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + Pr.cvtips = `You are an ATS specialist and LinkedIn optimisation expert. IMPORTANT: Respond ENTIRELY in English regardless of the job posting language. +${ATS_METHOD} + ${STRICT_RULES} ${prof} @@ -104,53 +216,126 @@ ${ctx} Respond EXACTLY: ## LINKEDIN HEADLINE -[One headline, EXACTLY under 80 characters, ATS-optimized. Format like: Senior Backend Engineer & Technical Lead | PHP, NestJS, Docker | Building for Scale] +[One headline, UNDER 220 characters (LinkedIn's real limit โ€” use the room). +Format: | <3-4 exact hard skills from the posting that I +genuinely have> | <one proof point with a number>] ## PROFESSIONAL SUMMARY -[3-4 sentences tailored summary ready to paste โ€” in English. CRITICAL: Focus on delivery, cost optimization, and hands-on engineering. Use a professional, direct, no-fluff tone. Emphasize impact: what you built, numbers you achieved, problems you solved. DO NOT use academic or AI-generated phrases like 'I leverage my expertise'. DO NOT invent industries like 'banking' unless they are in MY PROFILE.] +[3-4 sentences, ready to paste. Lead with what I built and the numbers. Mirror the +job's exact terminology. No academic phrasing, no "I leverage my expertise".] -## ATS KEYWORDS TO ADD -- [10-15 specific keywords from this job that MUST appear in CV] +## ATS KEYWORDS โ€” EXACT MATCH REQUIRED +[Table-free list of 12-15 keywords copied VERBATIM from the job posting that I can +honestly claim. Mark each: (HAVE) if already in my CV, (ADD) if missing.] + +## KEYWORDS I CANNOT HONESTLY CLAIM +[Anything the job wants that I genuinely lack โ€” with the nearest adjacent real +experience I can offer instead. Be blunt.] ## EXPERIENCE TO LEAD WITH -- [Which role first and which 3-4 bullet points to highlight] +- [Which role first, and the 3-4 exact bullets to surface โ€” quote them from my profile] -## REMOVE OR MINIMIZE -- [What to de-emphasize for this application] +## REMOVE OR MINIMISE +- [What to cut for this specific application and why] -## SKILLS SECTION (priority order) -1. [Most important skill] +## SKILLS SECTION (priority order, exact job wording) +1. [Most important] 2. [Second] -3. [Continue 8-10 skills] +3. [Continue to 10] ## NEW BULLET POINTS TO ADD -- [2-3 new achievement bullets ready to paste into CV โ€” in English]`; +- [2-3 new bullets in XYZ format, built ONLY from real achievements in my profile, + rewritten to carry this job's exact keywords]`; - const dynamicQuestions = job.questions && job.questions.length > 0 + // โ”€โ”€ TAB: ATS match report (new) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + Pr.ats = `You are an ATS simulation engine. Score my CV against this job the way +a real applicant tracking system would, then tell me exactly how to close the gap. +Respond ENTIRELY in English. + +${ATS_METHOD} + +${STRICT_RULES} + +${prof} + +JOB: +${ctx} + +Respond EXACTLY: + +## OVERALL ATS SCORE: X/100 +[One line on how that breaks down] + +## SCORE BREAKDOWN +- Hard skill match: X/40 +- Title / seniority match: X/20 +- Domain & industry match: X/15 +- Years of experience match: X/15 +- Education & certification match: X/10 + +## MUST-HAVE REQUIREMENTS โ€” VERDICT PER ITEM +[For every hard requirement in the posting: | Requirement | MET / PARTIAL / MISSING | Evidence from my profile | +Write as plain lines, not a markdown table โ€” tables break ATS parsing when copied.] + +## EXACT PHRASES TO INSERT +[5-8 lines, each one a sentence I can paste verbatim into my CV. Each must be +TRUE per my profile and carry a keyword copied exactly from the posting.] + +## DISQUALIFIERS +[Anything that would auto-reject me: location, work authorisation, hard years +requirement, mandatory certification. If none, say "None detected".] + +## VERDICT +[APPLY AS-IS / APPLY AFTER EDITS / SKIP] โ€” one sentence of reasoning.`; + + // โ”€โ”€ TAB: Application form Q&A โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const dynamicQuestions = (job.questions && job.questions.length > 0) ? job.questions.map((q, i) => `${i + 1}. "${q.question}" [type: ${q.type}]`).join('\n') - : '1. "Why are you interested in this role?" [type: text]\n2. "What is your relevant experience?" [type: text]\n3. "What are your salary expectations?" [type: text]\n4. "When can you start?" [type: text]\n5. "Do you require visa sponsorship?" [type: text]'; + : '1. "Why are you interested in this role?" [type: text]\n' + + '2. "What is your relevant experience?" [type: text]\n' + + '3. "What are your salary expectations?" [type: text]\n' + + '4. "When can you start?" [type: text]\n' + + '5. "Do you require visa sponsorship?" [type: text]'; - P.qa = `You are a form-filling assistant. Your ONLY job is to answer application form questions. + Pr.qa = `You are a form-filling assistant. Your ONLY job is to answer application +form questions accurately. -STRICT RULES: -- Return ONLY a raw JSON object. Nothing else. -- Do NOT write cover letters, introductions, paragraphs, or markdown. -- Do NOT use headers like "###" or "##". -- Do NOT use code blocks like \`\`\`json. -- For number/numeric questions: answer with JUST a number (e.g. "6"). -- For yes/no or select questions: answer with JUST "Yes" or "No". -- For salary questions: Output ONLY a number. IMPORTANT: Adapt the number intelligently to the job's likely currency (e.g., if UAE/Saudi: ~30000 AED/SAR. If remote USD: ~6000 USD). -- For text questions: answer in 1 short sentence max. -- Keys must be the EXACT question text. +STRICT OUTPUT RULES: +- Return ONLY a raw JSON object. Nothing else. No markdown, no code fences, no prose. +- Keys must be the EXACT question text as given. +- Numeric questions: answer with JUST a number (e.g. "8"). +- Yes/No or select questions: answer with JUST "Yes" or "No". +- Text questions: 1 short sentence, maximum. -MY PERSONAL DETAILS & PREFERENCES (Use these for answers): -- Location: Amman, Jordan -- Phone / Country Code: +962 -- Notice Period / Start Date: Available Immediately -- Salary Expectations: Competitive / Negotiable (If forced to give a number, calculate based on the job's currency as instructed above). +YEARS-OF-EXPERIENCE RULES (compute, do not guess): +- Total professional software experience: since Jan 2017. +- Flutter / mobile: since Jan 2017 (Flutter-primary since ~2019). +- Ride-hailing / real-time mobility systems: since Jan 2024. +- Payment / fintech integration: since Jan 2024. +- If asked about a technology I do NOT have (see NEVER CLAIM), answer "0". + +SALARY RULES: +- Output ONLY a number, matching the job's likely currency: + UAE โ†’ AED monthly, Saudi โ†’ SAR monthly, Jordan โ†’ JOD monthly, + Egypt โ†’ EGP monthly, remote/international โ†’ USD monthly. +- Anchor on senior mobile architect market rate for that location. Do not lowball, + do not fantasise. If the field allows text, prefer "Negotiable". + +MY PERSONAL DETAILS: +- Full name: ${id.fullName} +- Location: ${id.location} +- Phone: ${id.phone} +- Email: ${id.email} +- LinkedIn: ${id.linkedin} +- Notice period / start date: Available immediately +- Work authorisation: Jordanian citizen, valid passport. Requires sponsorship for + GCC/EU roles. Fully authorised for remote contract work. +- Willing to relocate: Yes (GCC / MENA / Europe) + +${STRICT_RULES} MY PROFILE: -${userProfile} +${profileText} JOB: ${ctx} @@ -158,14 +343,15 @@ ${ctx} QUESTIONS TO ANSWER: ${dynamicQuestions} -RESPOND WITH ONLY THIS FORMAT (raw JSON, no wrapping, answering ONLY the questions listed above): +RESPOND WITH ONLY THIS FORMAT (raw JSON, answering ONLY the questions listed above): { "question 1 text here": "concise answer", "question 2 text here": "concise answer" }`; - P.benefits = `You are a career analyst specializing in tech compensation in MENA. -IMPORTANT: Respond ENTIRELY in English regardless of the job posting language. + // โ”€โ”€ TAB: Benefits / compensation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + Pr.benefits = `You are a career analyst specialising in tech compensation across +MENA and the GCC. Respond ENTIRELY in English. ${prof} @@ -176,41 +362,60 @@ Respond EXACTLY: ## COMPENSATION ESTIMATE - **Mentioned salary:** [exact text or "Not disclosed"] -- **Market estimate:** [realistic range in USD based on role, seniority, location] -- **Benefits listed:** [bonuses, equity, insurance] +- **Market estimate:** [realistic monthly range in LOCAL currency + USD equivalent, + for MY seniority in THIS location] +- **My realistic ask:** [a specific number to open with, and my walk-away floor] +- **Benefits listed:** [bonuses, equity, insurance, relocation] ## WORK SETUP - **Type:** [Remote / Hybrid / On-site] -- **Location:** [where + relocation support] +- **Location & relocation:** [where + whether sponsorship/relocation is offered] +- **Visa reality:** [as a Jordanian citizen โ€” what this role actually requires] ## CAREER VALUE - **Growth potential:** [what this leads to in 2-3 years] -- **Skills I will gain:** [new skills/tech] -- **Resume value:** [how it improves my CV] +- **Skills I would gain:** [specifically what is new versus my current stack] +- **CV value:** [does this brand/domain strengthen my next move] ## WHAT IS ATTRACTIVE -- [3-5 appealing aspects for MY profile] +- [3-5 points specific to MY profile] ## RED FLAGS -- [Concerning requirements or red flags] +- [Concerning requirements, vague scope, unrealistic stack breadth, salary + omission, churn signals. If none, say so.] ## OVERALL RATING: X/10 **Worth applying?** [YES / MAYBE / NO] [2-3 sentence honest assessment]`; - P.list_analysis = `You are an AI pre-screening jobs. -I will give you a JSON array of jobs (Title, Company). -My stack: Flutter, Python (FastAPI), PHP, Node.js, GIS, Technical Architect. -I am actively seeking Senior Engineer, Tech Lead, or Architect roles. -Reject Java, C#, C++, .NET, or pure Product Management roles. + + // โ”€โ”€ TAB: Bulk list pre-screen โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const targetRoles = P ? P.targetRoles.join(', ') : 'Senior/Lead Mobile Engineer'; + const coreStack = P ? Object.values(P.skills).flat().slice(0, 18).join(', ') : ''; + + Pr.list_analysis = `You are pre-screening a list of jobs on my behalf. + +MY PROFILE IN ONE LINE: ${P ? P.identity.primaryTitle : 'Senior Mobile Architect'} โ€” ${P ? P.identity.positioningLine : ''} +MY CORE STACK: ${coreStack} +ROLES I WANT: ${targetRoles} + +SCORING RULES: +- YES โ†’ mobile/Flutter-centric, or real-time/mobility/fintech systems at senior level. +- MAYBE โ†’ adjacent (general senior software, backend-heavy with a mobile component, + or a strong company where the stack is learnable). +- NO โ†’ primarily Java/C#/C++/.NET/Go backend, pure data science or ML, native + iOS-only (Swift) or Android-only (Kotlin) requirements, pure product management, + QA, or junior/mid-level roles. + +I will give you a JSON array of jobs (Title, Company). JOBS LIST: ${job.listData} -Respond ONLY with a raw JSON array of objects, one for each job, containing: +Respond ONLY with a raw JSON array, one object per job: [ - { "index": number, "verdict": "YES" | "NO" | "MAYBE", "reason": "Short reason" } + { "index": number, "verdict": "YES" | "NO" | "MAYBE", "reason": "Short reason, max 8 words" } ] -Do not wrap in markdown \`\`\`json blocks.`; +Do not wrap in markdown code fences.`; - return P[tab] || P.analysis; + return Pr[tab] || Pr.analysis; } diff --git a/server/.env.example b/server/.env.example index 1cedebe..4ba0015 100644 --- a/server/.env.example +++ b/server/.env.example @@ -1 +1,8 @@ GEMINI_API_KEY="your_api_key_here" + +# Model overrides (optional). +# CV tailoring decides whether an application lands, so it uses the stronger +# model. Comments and post rewrites are high-volume, so they stay on the cheap one. +GEMINI_MODEL_CV="gemini-flash-latest" +GEMINI_MODEL_TEXT="gemini-flash-latest" +GEMINI_MODEL_LIGHT="gemini-flash-lite-latest" diff --git a/server/cv_template.html b/server/cv_template.html index 0827364..5bb4e35 100644 --- a/server/cv_template.html +++ b/server/cv_template.html @@ -3,206 +3,112 @@ <head> <meta charset="UTF-8"> <style> - /* - * CSS Optimized for Dompdf / mPDF / TCPDF - * Avoids flexbox, uses standard block/inline styling for flawless PDF generation. - */ - body { - font-family: Arial, Helvetica, sans-serif; + /* ========================================================================== + * ATS-SAFE CV TEMPLATE + * Rules enforced by this stylesheet: + * - Single column. No floats, no tables, no absolute positioning. + * Multi-column layouts are the #1 cause of scrambled ATS text extraction. + * - No headers/footers, no text boxes, no images, no icons. + * - Standard, machine-recognisable section headings. + * - Standard fonts only (Helvetica/Arial), ligatures disabled so that + * Dompdf does not emit glyphs that break text copy-out. + * - Contact details and URLs are PLAIN TEXT, not anchor-wrapped labels: + * many parsers read the visible text, not the href. + * ========================================================================== */ + body { + font-family: Helvetica, Arial, sans-serif; font-variant-ligatures: none; font-feature-settings: "liga" 0, "clig" 0; - letter-spacing: 0px; + letter-spacing: 0; text-rendering: geometricPrecision; - font-size: 13px; - color: #222; - line-height: 1.4; - margin: 0; - padding: 15px 30px; - } - - .header { - text-align: center; - border-bottom: 2px solid #1a237e; - padding-bottom: 10px; - margin-bottom: 15px; - } - - h1 { - font-size: 28px; - color: #1a237e; - margin: 0 0 5px 0; - text-transform: uppercase; - letter-spacing: 1px; - } - - .headline { - font-size: 16px; - font-weight: bold; - color: #424242; - margin-bottom: 5px; - } - - .contact { - font-size: 12px; - color: #555; + font-size: 10.5pt; + color: #1a1a1a; + line-height: 1.42; + margin: 0; + padding: 34px 40px; } - .section-title { - font-size: 15px; - font-weight: bold; - color: #1a237e; - border-bottom: 1px solid #ddd; - margin-top: 15px; - margin-bottom: 6px; - padding-bottom: 4px; - text-transform: uppercase; + h1 { + font-size: 20pt; + color: #111; + margin: 0 0 4px 0; letter-spacing: 0.5px; - } - - p { margin: 0 0 6px 0; text-align: justify; } - - .job-block { margin-bottom: 10px; } - - .job-header { - width: 100%; - margin-bottom: 5px; - display: table; /* ู‡ุฐุง ูŠุญู„ ู…ุดุงูƒู„ Dompdf */ - } - - .job-title { - font-weight: bold; - font-size: 13.5px; /* ุชุตุบูŠุฑ ุทููŠู ุฌุฏุงู‹ */ - color: #222; - float: left; - width: 70%; /* ุฅุฌุจุงุฑ ุงู„ุนู†ูˆุงู† ุนู„ู‰ ุนุฏู… ุชุฌุงูˆุฒ 70% ู…ู† ุงู„ุณุทุฑ */ - line-height: 1.3; - } - - .job-meta { - font-size: 11px; - color: #1a237e; font-weight: bold; - float: right; - width: 28%; /* ุชุฎุตูŠุต ู…ุณุงุญุฉ ุขู…ู†ุฉ ู„ู„ุชุงุฑูŠุฎ */ - text-align: right; } - - .clear { clear: both; } - + .headline { + font-size: 11.5pt; + font-weight: bold; + color: #333; + margin: 0 0 6px 0; + } + .contact { + font-size: 9.5pt; + color: #333; + line-height: 1.55; + margin-bottom: 14px; + padding-bottom: 10px; + border-bottom: 1.5px solid #222; + } - ul { margin: 4px 0 8px 0; padding-left: 20px; } - li { margin-bottom: 4px; text-align: justify; } + h2 { + font-size: 11pt; + font-weight: bold; + color: #111; + border-bottom: 1px solid #bbb; + margin: 16px 0 7px 0; + padding-bottom: 3px; + text-transform: uppercase; + letter-spacing: 0.6px; + } + + p { margin: 0 0 6px 0; text-align: left; } + + .job { margin-bottom: 11px; } + + /* Each field on its own line, in reading order. No float, no table. + A parser walking the DOM gets: title, then employer/location, then dates. */ + .job-title { font-weight: bold; font-size: 10.5pt; color: #111; margin: 0; } + .job-company { font-size: 10pt; color: #222; margin: 1px 0 0 0; } + .job-dates { font-size: 9.5pt; color: #444; margin: 1px 0 3px 0; } + + ul { margin: 3px 0 7px 0; padding-left: 17px; } + li { margin-bottom: 3px; text-align: left; } + + .skill-line { margin: 0 0 4px 0; } + .label { font-weight: bold; } </style> </head> <body> - <div class="header"> - <h1>Hamza Ayed</h1> - <!-- PHP WILL INJECT THE AI-GENERATED HEADLINE HERE --> - <div class="headline">{{JOB_HEADLINE}}</div> - <div class="contact"> - Amman, Jordan | +962 79 858 3052 | <a href="mailto:hamzaayed.dev@gmail.com" style="color: #555; text-decoration: none;">hamzaayed.dev@gmail.com</a><br> - <a href="https://linkedin.com/in/hamza-ayed" style="color: #555; text-decoration: none; font-weight: bold;">linkedin.com/in/hamza-ayed</a> | - <a href="https://github.com/Hamza-Ayed" style="color: #555; text-decoration: none; font-weight: bold;">github.com/Hamza-Ayed</a> | - <a href="https://intaleqapp.com/hamza.html" style="color: #1a237e; text-decoration: underline; font-weight: bold;">intaleqapp.com/hamza.html (Portfolio)</a> - </div> - </div> + <h1>{{FULL_NAME}}</h1> + <div class="headline">{{JOB_HEADLINE}}</div> + <div class="contact">{{CONTACT_BLOCK}}</div> - <div class="section-title">Professional Summary</div> - <!-- PHP WILL INJECT THE AI-GENERATED SUMMARY HERE --> + <h2>Professional Summary</h2> <p>{{TAILORED_SUMMARY}}</p> + <h2>Key Achievements</h2> + {{ACHIEVEMENTS_BLOCK}} + <h2>Technical Skills</h2> + <p class="skill-line"><span class="label">Core Focus for This Role:</span> {{DYNAMIC_SKILLS}}</p> + {{SKILLS_BLOCK}} - <div class="section-title">Technical Skills</div> - <!-- PHP WILL INJECT ATS KEYWORDS HERE --> - <p><strong>Core Focus:</strong> {{DYNAMIC_SKILLS}}</p> - <p><strong>Backend & Architecture:</strong> PHP (Workerman), Node.js, NestJS, Python (FastAPI/Flask), REST APIs, WebSockets, Event-Driven Architecture</p> - <p><strong>Infrastructure & Cloud:</strong> Docker, Linux, Nginx, Redis, CI/CD (GitHub Actions), AWS</p> - <p><strong>Databases & Geospatial:</strong> PostgreSQL/PostGIS, MySQL, OpenStreetMap (OSM), GraphHopper, MapLibre GL, Spatial Queries</p> - <p><strong>Mobile:</strong> Flutter/Dart, GetX, BLoC/Cubit</p> + <h2>Professional Experience</h2> + {{EXPERIENCE_BLOCK}} - <div class="section-title">Selected Technical Highlights</div> - <ul> - <li>Built proprietary OSM-based mapping infrastructure replacing Google Maps APIs</li> - <li>Engineered WebSocket-based real-time ride tracking systems</li> - <li>Developed offline-first mapping SDKs published on pub.dev and npm</li> - <li>Designed async AI document-processing pipelines using vision models</li> - <li>Optimized routing infrastructure using GraphHopper and PostGIS</li> - </ul> + <h2>Education</h2> + {{EDUCATION_BLOCK}} - <div class="section-title">Professional Experience</div> + <h2>Certifications</h2> + {{CERTIFICATIONS_BLOCK}} - <div class="job-block"> - <div class="job-header"> - <div class="job-title">CTO & Technical Architect | Systems & Backend Focus โ€” Intaleq  | </div> - <div class="job-meta">January 2024 โ€“ Present | Remote (MENA Region)</div> - <div class="clear"></div> - </div> - <ul> - <li>Led the full-stack architecture of a smart transportation ecosystem supporting 1,800+ drivers and 2,500+ riders, building the core backend with PHP/Workerman.</li> - <li>Architected the proprietary mapping platform (IntaleqMaps), orchestrating GraphHopper routing, OSM tile serving, and PostGIS spatial queries via a NestJS API layer, saving $10,000+/month in operational costs.</li> - </ul> - </div> - - <div class="job-block"> - <div class="job-header"> - <div class="job-title">Founding Engineer & Lead Backend Architect โ€” Tripz Egypt  | </div> - <div class="job-meta">Jan 2023 โ€“ Present | Cairo / Remote</div> - <div class="clear"></div> - </div> - <ul> - <li>Co-founded and architected Egypt's homegrown ride-hailing platform, managing the entire distributed ecosystem with real-time tracking for 4,318 drivers and 2,464 riders.</li> - <li>Implemented robust event-driven architecture for real-time driver/rider matching and WebSockets-based route optimization.</li> - <li>Integrated local digital payment gateways and driver payout systems, supporting an 8% commission structure that drove a $0.78 Customer Acquisition Cost.</li> - </ul> - </div> - - <div class="job-block"> - <div class="job-header"> - <div class="job-title">Senior Systems Architect โ€” Mobile & Backend (Freelance)  | </div> - <div class="job-meta">Jan 2017 โ€“ Dec 2023 | Jordan / Remote</div> - <div class="clear"></div> - </div> - <ul> - <li>Delivered 25+ production enterprise applications across GIS, FinTech, and utilities, leading full lifecycle architecture for clients in the MENA region.</li> - <li>Developed Nabih (AI Smart Responder): designed and built an intelligent virtual assistant system connecting a Flutter mobile application (GetX/Cubit) with custom PHP backend APIs.</li> - <li>Developed Musadaq (AI-Powered Invoice Processing): engineered an automated KYC and invoice document processor using AI vision models and asynchronous processing pipelines.</li> - </ul> - </div> - - <div class="section-title">Notable Projects</div> - <ul> - <li><strong>IntaleqMaps Engine:</strong> Engineered a proprietary mapping platform orchestrating GraphHopper routing, OSM tile serving, and PostGIS spatial queries via a NestJS API layer, replacing Google Maps API across the MENA region.</li> - <li><strong>Nabih AI Virtual Assistant:</strong> Architected an automated customer support solution connecting a Flutter application (GetX/Cubit) to custom high-concurrency PHP backend APIs.</li> - <li><strong>Musadaq AI Document Processor:</strong> Engineered an automated KYC and invoice document processing platform utilizing AI vision models and async processing pipelines.</li> - <li><strong>Meta Ads Manager & SaaS Gateways:</strong> Deployed multi-tenant platforms using NestJS/React (Meta Ads Manager) and high-concurrency Node.js (WhatsApp bridge managing headless Puppeteer sessions in Docker) to automate operations.</li> - </ul> - - <div class="section-title">Open Source Contributions</div> - <ul> - <li><strong>intaleq_maps (Flutter SDK):</strong> Published on <a href="https://pub.dev/packages/intaleq_maps" style="color: #1a237e; text-decoration: none; font-weight: bold;">pub.dev/packages/intaleq_maps</a>. Custom map rendering, offline caching, and route plotting package optimized for low-bandwidth environments.</li> - <li><strong>intaleq-maps-gl (NPM Library):</strong> Published on <a href="https://libraries.io/npm/intaleq-maps-gl" style="color: #1a237e; text-decoration: none; font-weight: bold;">npmjs.com/package/intaleq-maps-gl</a>. Web-based Mapbox GL compatible library optimized for custom OSM tiles and routing integration.</li> - </ul> - - <div class="section-title">Education & Certifications</div> - <ul> - <li><strong>BS Mathematics (Applied Computing & Algorithms)</strong>, Mutah University (2003โ€“2007)</li> - <li><strong>Google Data Analytics</strong> Professional Certificate (Coursera)</li> - <li><strong>IBM Data Analyst</strong> Professional Certificate (Coursera)</li> - <li><strong>Meta APIs & Django Web Framework</strong> Course Certificates (Coursera)</li> - <li><strong>AWS Cloud Practitioner & Cloud Architecture Fundamentals</strong></li> - </ul> - - <div class="section-title">Languages</div> - <p><strong>Arabic:</strong> Native | <strong>English:</strong> Professional Working Proficiency | <strong>Turkish:</strong> Limited Working Proficiency</p> - - <div class="section-title">Availability & Work Authorization</div> - <p style="text-align: center; font-weight: bold; color: #1a237e; margin-top: 10px;"> - Available Immediately | Open to Remote, Hybrid & Relocation (GCC/Europe/MENA) | Valid Passport - </p> + <h2>Additional Information</h2> + <p class="skill-line"><span class="label">Languages:</span> {{LANGUAGES}}</p> + <p class="skill-line"><span class="label">Location:</span> {{LOCATION}}</p> + <p class="skill-line"><span class="label">Availability:</span> {{AVAILABILITY}}</p> </body> </html> diff --git a/server/cv_template_amman.html b/server/cv_template_amman.html deleted file mode 100644 index f69e05f..0000000 --- a/server/cv_template_amman.html +++ /dev/null @@ -1,200 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<meta charset="UTF-8"> -<style> - body { - font-family: Arial, Helvetica, sans-serif; - font-variant-ligatures: none; - font-feature-settings: "liga" 0, "clig" 0; - letter-spacing: 0px; - text-rendering: geometricPrecision; - font-size: 13px; - color: #222; - line-height: 1.4; - margin: 0; - padding: 15px 30px; - } - - .header { - text-align: center; - border-bottom: 2px solid #1a237e; - padding-bottom: 10px; - margin-bottom: 15px; - } - - h1 { - font-size: 28px; - color: #1a237e; - margin: 0 0 5px 0; - text-transform: uppercase; - letter-spacing: 1px; - } - - .headline { - font-size: 16px; - font-weight: bold; - color: #424242; - margin-bottom: 5px; - } - - .contact { - font-size: 12px; - color: #555; - } - - .section-title { - font-size: 15px; - font-weight: bold; - color: #1a237e; - border-bottom: 1px solid #ddd; - margin-top: 15px; - margin-bottom: 6px; - padding-bottom: 4px; - text-transform: uppercase; - letter-spacing: 0.5px; - } - - p { margin: 0 0 6px 0; text-align: justify; } - - .job-block { margin-bottom: 10px; } - - .job-header { - width: 100%; - margin-bottom: 5px; - display: table; - } - - .job-title { - font-weight: bold; - font-size: 13.5px; - color: #222; - float: left; - width: 70%; - line-height: 1.3; - } - - .job-meta { - font-size: 11px; - color: #1a237e; - font-weight: bold; - float: right; - width: 28%; - text-align: right; - } - - .clear { clear: both; } - - ul { margin: 4px 0 8px 0; padding-left: 20px; } - li { margin-bottom: 4px; text-align: justify; } -</style> -</head> -<body> - - <div class="header"> - <h1>Hamza Ayed</h1> - <div class="headline">{{JOB_HEADLINE}}</div> - <div class="contact"> - Amman, Jordan | +962 79 858 3052 | <a href="mailto:hamzaayed.dev@gmail.com" style="color: #555; text-decoration: none;">hamzaayed.dev@gmail.com</a><br> - <a href="https://linkedin.com/in/hamza-ayed" style="color: #555; text-decoration: none; font-weight: bold;">linkedin.com/in/hamza-ayed</a> | - <a href="https://github.com/Hamza-Ayed" style="color: #555; text-decoration: none; font-weight: bold;">github.com/Hamza-Ayed</a> | - <a href="https://intaleqapp.com/hamza.html" style="color: #1a237e; text-decoration: underline; font-weight: bold;">intaleqapp.com/hamza.html (Portfolio)</a> - </div> - </div> - - <div class="section-title">Professional Summary</div> - <p>{{TAILORED_SUMMARY}}</p> - - <div class="section-title">Technical Skills</div> - <p><strong>Core Focus:</strong> {{DYNAMIC_SKILLS}}</p> - <p><strong>Backend & Architecture:</strong> PHP (Workerman), Node.js, NestJS, Python (FastAPI/Flask), REST APIs, WebSockets, Microservices</p> - <p><strong>Infrastructure & Cloud:</strong> Docker, Linux, Nginx, Redis, CI/CD (GitHub Actions), AWS</p> - <p><strong>Databases:</strong> PostgreSQL, MySQL, Redis, Database Design & Query Optimization</p> - <p><strong>Geospatial (Supplemental):</strong> PostGIS, OpenStreetMap (OSM), GraphHopper, MapLibre GL, Spatial Queries</p> - <p><strong>Mobile:</strong> Flutter/Dart, GetX, BLoC/Cubit</p> - - <div class="section-title">Impact & Delivery</div> - <ul> - <li>Built and scaled platforms serving 6,000+ active users across two markets with zero downtime deployments</li> - <li>Engineered proprietary OSM-based mapping infrastructure replacing Google Maps APIs across the MENA region โ€” saving $10,000+/month in operational costs</li> - <li>Designed high-concurrency WebSocket systems handling real-time operations at scale</li> - <li>Delivered 25+ production applications across healthcare, logistics, fintech, and utilities for MENA clients</li> - <li>Published open-source mapping SDKs on pub.dev and npm, used in production environments</li> - </ul> - - <div class="section-title">Professional Experience</div> - - <div class="job-block"> - <div class="job-header"> - <div class="job-title">Technical Lead & Backend Engineer โ€” Intaleq  | </div> - <div class="job-meta">January 2024 โ€“ Present | Amman, Jordan</div> - <div class="clear"></div> - </div> - <ul> - <li>Built full-stack transportation platform from zero to production: PHP/Workerman backend, Flutter mobile apps, WebSocket-based real-time dispatcher โ€” serving 1,800+ active drivers.</li> - <li>Developed proprietary mapping platform (IntaleqMaps) orchestrating GraphHopper routing, OSM tile serving, and PostGIS spatial queries via a NestJS API layer โ€” reducing mapping API costs by $10,000+/month.</li> - <li>Designed event-driven architecture for real-time driver/rider matching, live tracking, and automated fare calculation.</li> - </ul> - </div> - - <div class="job-block"> - <div class="job-header"> - <div class="job-title">Lead Backend Engineer โ€” Tripz Egypt  | </div> - <div class="job-meta">Jan 2023 โ€“ Present | Cairo / Remote</div> - <div class="clear"></div> - </div> - <ul> - <li>Built complete ride-hailing platform: PHP backend system, Flutter rider and driver apps, real-time WebSocket communication layer, and local payment gateway integrations.</li> - <li>Engineered event-driven driver/rider matching system serving 4,318 drivers with a $0.78 customer acquisition cost โ€” the lowest in the Egyptian market.</li> - <li>Integrated local digital payment gateways and designed automated driver payout infrastructure supporting an 8% commission model.</li> - </ul> - </div> - - <div class="job-block"> - <div class="job-header"> - <div class="job-title">Senior Backend Engineer โ€” Freelance  | </div> - <div class="job-meta">Jan 2017 โ€“ Dec 2023 | Jordan / Remote</div> - <div class="clear"></div> - </div> - <ul> - <li>Delivered 25+ applications for MENA clients across healthcare, logistics, HR technology, sports platforms, and utility management systems โ€” full lifecycle from architecture to deployment.</li> - <li>Built automated customer support platform: designed Flutter application (GetX/Cubit) connected to high-concurrency PHP backend APIs handling real-time query resolution.</li> - <li>Developed document processing system: engineered async processing pipelines using vision models for automated KYC and invoice data extraction.</li> - </ul> - </div> - - <div class="section-title">Notable Projects</div> - <ul> - <li><strong>IntaleqMaps Engine:</strong> Proprietary mapping platform orchestrating GraphHopper routing, OSM tile serving, and PostGIS spatial queries via a NestJS API layer โ€” replacing Google Maps API across the MENA region.</li> - <li><strong>Tripz Egypt Platform:</strong> End-to-end ride-hailing system with 8 ride types, real-time matching, custom payment infrastructure, and AI-powered KYC verification.</li> - <li><strong>Customer Support Automation Platform:</strong> Built an automated query resolution system connecting a Flutter application (GetX/Cubit) to high-concurrency PHP backend APIs.</li> - <li><strong>Document Processing Platform:</strong> Engineered automated KYC and invoice processing using vision models and asynchronous processing pipelines.</li> - <li><strong>Meta Ads Manager (SaaS):</strong> Deployed multi-tenant platform using NestJS/React for automated ad campaign management.</li> - <li><strong>WhatsApp Bridge:</strong> High-concurrency Node.js service managing headless Puppeteer sessions in Docker for automated messaging operations.</li> - </ul> - - <div class="section-title">Open Source Contributions</div> - <ul> - <li><strong>intaleq_maps (Flutter SDK):</strong> Published on <a href="https://pub.dev/packages/intaleq_maps" style="color: #1a237e; text-decoration: none; font-weight: bold;">pub.dev/packages/intaleq_maps</a>. Custom map rendering, offline caching, and route plotting package optimized for low-bandwidth environments.</li> - <li><strong>intaleq-maps-gl (NPM Library):</strong> Published on <a href="https://libraries.io/npm/intaleq-maps-gl" style="color: #1a237e; text-decoration: none; font-weight: bold;">npmjs.com/package/intaleq-maps-gl</a>. Web-based Mapbox GL compatible library optimized for custom OSM tiles and routing integration.</li> - </ul> - - <div class="section-title">Education & Certifications</div> - <ul> - <li><strong>BS Mathematics (Applied Computing & Algorithms)</strong>, Mutah University (2003โ€“2007)</li> - <li><strong>Google Data Analytics</strong> Professional Certificate (Coursera)</li> - <li><strong>IBM Data Analyst</strong> Professional Certificate (Coursera)</li> - <li><strong>Meta APIs & Django Web Framework</strong> Course Certificates (Coursera)</li> - <li><strong>AWS Cloud Practitioner & Cloud Architecture Fundamentals</strong></li> - </ul> - - <div class="section-title">Languages</div> - <p><strong>Arabic:</strong> Native | <strong>English:</strong> Professional Working Proficiency | <strong>Turkish:</strong> Limited Working Proficiency</p> - - <div class="section-title">Availability</div> - <p style="text-align: center; font-weight: bold; color: #1a237e; margin-top: 10px;"> - Based in Amman, Jordan โ€” Available Immediately | Open to On-site, Hybrid & Remote Roles in Jordan & MENA - </p> - -</body> -</html> \ No newline at end of file diff --git a/server/generate_cv.php b/server/generate_cv.php index 5b57d49..3cfbcf4 100644 --- a/server/generate_cv.php +++ b/server/generate_cv.php @@ -1,6 +1,10 @@ <?php // ============================================================================ // Dynamic ATS-Optimized CV Generator & AI Proxy (Backend) +// ---------------------------------------------------------------------------- +// All CV facts come from profile.json, which is generated from profile_data.js +// (run `node sync_profile.js` after editing the profile). Nothing in this file +// hardcodes a job title, a metric, a date or a contact detail. // ============================================================================ header('Access-Control-Allow-Origin: *'); @@ -24,282 +28,479 @@ use Dompdf\Dompdf; use Dompdf\Options; use Dotenv\Dotenv; -// Path to the .env file located outside the document root for security -// Assuming script is in /home/user/htdocs/domain.com/cv/server/ -// and .env is in /home/user/ +// .env lives outside the document root. $envPath = realpath(__DIR__ . '/../../../..'); - if ($envPath && file_exists($envPath . '/.env')) { $dotenv = Dotenv::createImmutable($envPath); $dotenv->load(); } $rawData = file_get_contents('php://input'); -$data = json_decode($rawData, true); - +$data = json_decode($rawData, true) ?: []; $action = $data['action'] ?? 'generateText'; -// Prioritize API key from .env over frontend payload $apiKey = $_ENV['GEMINI_API_KEY'] ?? getenv('GEMINI_API_KEY') ?: ($data['apiKey'] ?? ''); - if (empty($apiKey)) { http_response_code(400); - echo json_encode(["error" => "Missing apiKey. Please set GEMINI_API_KEY in .env or pass it in request."]); + echo json_encode(["error" => "Missing apiKey. Set GEMINI_API_KEY in .env or pass it in the request."]); exit; } -// Standardized on gemini-flash-lite-latest due to quota limits -$model = "gemini-flash-lite-latest"; -$geminiUrl = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key=" . $apiKey; +// โ”€โ”€ Model selection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// CV tailoring is the one call where output quality directly determines whether +// an application lands, so it gets the stronger model. Comments and post +// rewrites are high-volume and low-stakes, so they stay on the cheap model. +$MODELS = [ + 'generatePdf' => $_ENV['GEMINI_MODEL_CV'] ?? 'gemini-flash-latest', + 'generateText' => $_ENV['GEMINI_MODEL_TEXT'] ?? 'gemini-flash-latest', + 'generateComment' => $_ENV['GEMINI_MODEL_LIGHT'] ?? 'gemini-flash-lite-latest', + 'repurposePost' => $_ENV['GEMINI_MODEL_LIGHT'] ?? 'gemini-flash-lite-latest', +]; -// ========================================== -// ACTION 1: Generate ATS PDF CV -// ========================================== -if ($action === 'generatePdf') { - $jobDescription = $data['jobDescription'] ?? ''; - $template = $data['template'] ?? 'default'; // 'amman' or 'default' +function geminiUrl(string $model, string $apiKey): string { + return "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key=" . $apiKey; +} - // --- Amman Market Prompt (Senior Backend Engineer & Technical Lead) --- - if ($template === 'amman') { - $prompt = "You are an expert ATS CV tailor for the Jordan/Amman local tech market. Read the following job description and generate tailored content. - -STRICT INTEGRITY RULES: -1. NEVER invent skills I do not have. My TRUE technical stack is: PHP (Workerman), Node.js, NestJS, Python (FastAPI/Flask), PostgreSQL/PostGIS, Docker, Flutter, GetX, BLoC, WebSockets, OpenStreetMap. -2. Do NOT add data science skills like TensorFlow, PyTorch, Scikit-learn, Hadoop, or MLOps. -3. My title MUST be aligned with 'Senior Backend Engineer', 'Technical Lead', or 'Lead Backend Engineer'. NEVER use 'Solutions Architect', 'AI Developer', or 'CTO'. - -Return ONLY a valid JSON object with EXACTLY three keys: 'headline', 'summary', and 'skills'. -The 'headline' should be a clean, confident title based on my TRUE skills โ€” matching Amman market expectations (Senior/Lead Backend Engineer). -The 'summary' should open with a hook about building and scaling production systems in the MENA region, emphasizing delivery, cost optimization, and hands-on engineering. Connect my real achievements to the job requirements. Keep it professional and direct โ€” NO academic fluff, NO 'I leverage my expertise' phrases. -The 'skills' should be a comma-separated list of 10 highly relevant ATS keywords from MY ACTUAL SKILLS that match the job. Prioritize backend, API, database, and infrastructure skills over GIS. -Do NOT use markdown blocks like ```json, just return raw JSON text. - -Job Description: -" . substr($jobDescription, 0, 4000); - } - // --- Default Prompt (Current โ€” Enterprise/GCC) --- - else { - $prompt = "You are an expert ATS CV tailor. Read the following job description and generate tailored content for my CV to maximize my chances of getting an interview. - -STRICT INTEGRITY RULES: -1. NEVER invent skills I do not have. My TRUE technical stack is: PHP (Workerman), Node.js, NestJS, Python (FastAPI/Flask), PostgreSQL/PostGIS, Docker, Flutter, GetX, BLoC, WebSockets, OpenStreetMap. -2. Do NOT add data science skills like TensorFlow, PyTorch, Scikit-learn, Hadoop, or MLOps. -3. My title MUST be aligned with 'Solutions Architect', 'Senior Backend Engineer', or 'Senior Mobile Engineer'. NEVER title me 'Senior AI Developer'. - -Return ONLY a valid JSON object with EXACTLY three keys: 'headline', 'summary', and 'skills'. -The 'headline' should be a clean, confident title based on my TRUE skills. -The 'summary' MUST open with exactly this hook: 'Built two production ride-hailing platforms from zero to thousands of users, on proprietary infrastructure, in high-complexity and emerging markets.' Then use the next 2 sentences to seamlessly tie my relevant TRUE skills to the job description requirements. -The 'skills' should be a comma-separated list of 10 highly relevant ATS keywords from MY ACTUAL SKILLS that match the job. -Do NOT use markdown blocks like ```json, just return raw JSON text. - -Job Description: -" . substr($jobDescription, 0, 4000); - } - - $payload = json_encode([ - "contents" => [["parts" => [["text" => $prompt]]]], - "generationConfig" => ["temperature" => 0.2, "responseMimeType" => "application/json"] - ]); - - $ch = curl_init($geminiUrl); +function callGemini(string $url, array $payload): array { + $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + curl_setopt($ch, CURLOPT_TIMEOUT, 120); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlErr = curl_error($ch); curl_close($ch); + return ['body' => $response, 'code' => $httpCode, 'error' => $curlErr]; +} - if ($httpCode !== 200) { +function loadProfile(): array { + $path = __DIR__ . '/profile.json'; + if (!file_exists($path)) { http_response_code(500); - echo json_encode(["error" => "Gemini API Error", "details" => json_decode($response)]); + echo json_encode(["error" => "profile.json missing. Run 'node sync_profile.js' and redeploy."]); + exit; + } + $profile = json_decode(file_get_contents($path), true); + if (!is_array($profile)) { + http_response_code(500); + echo json_encode(["error" => "profile.json is not valid JSON."]); + exit; + } + return $profile; +} + +function e(?string $s): string { + return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); +} + +// ============================================================================ +// ACTION 1: Generate ATS PDF CV +// ============================================================================ +if ($action === 'generatePdf') { + $jobDescription = trim($data['jobDescription'] ?? ''); + $jobTitle = trim($data['jobTitle'] ?? ''); + // 'amman' biases toward the local Jordanian market; 'default' toward + // GCC / international / remote. It changes EMPHASIS ONLY โ€” never facts. + $market = ($data['template'] ?? 'default') === 'amman' ? 'amman' : 'default'; + + if ($jobDescription === '') { + http_response_code(400); + echo json_encode(["error" => "jobDescription is required to tailor the CV."]); exit; } - $responseData = json_decode($response, true); + $profile = loadProfile(); + $id = $profile['identity']; + $bounds = $profile['boundaries']; + + // โ”€โ”€ Build the reference blocks the model must work from โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + $skillsRef = ''; + foreach ($profile['skills'] as $group => $items) { + $skillsRef .= "- {$group}: " . implode(', ', $items) . "\n"; + } + + $experienceRef = ''; + foreach ($profile['experience'] as $ri => $role) { + $experienceRef .= "ROLE INDEX {$ri}: {$role['title']} โ€” {$role['company']} ({$role['start']} โ€“ {$role['end']}, {$role['location']})\n"; + foreach ($role['bullets'] as $bi => $bullet) { + $experienceRef .= " [{$ri}.{$bi}] {$bullet['text']}\n"; + } + } + + $marketGuidance = $market === 'amman' + ? "TARGET MARKET: Jordan / Amman local tech market. Titles here are conservative โ€” " . + "prefer 'Senior Mobile Engineer', 'Lead Mobile Engineer' or 'Mobile Technical Lead' " . + "over 'Architect' unless the posting itself uses 'Architect'. Emphasise hands-on " . + "delivery, cost control and breadth. State availability for on-site/hybrid in Amman." + : "TARGET MARKET: GCC / international / remote. 'Senior Mobile Architect' and " . + "'Founding Engineer' land well here. Emphasise 0-to-1 platform ownership, scale " . + "metrics, real-time systems and infrastructure cost reduction. Note openness to " . + "relocation and remote work."; + + $prompt = <<<PROMPT +You are an expert ATS rรฉsumรฉ tailor. Rewrite my CV content so it scores as highly +as possible against the job description below WITHOUT stating anything untrue. + +{$marketGuidance} + +=== ATS METHODOLOGY โ€” APPLY ALL OF IT === +1. EXACT-TERM MIRRORING: ATS matching is literal. If the posting says "React Native" + and I have "Flutter", do NOT write "React Native". But if the posting says + "Flutter" or "Dart" or "WebSocket", use THAT EXACT WORD, not a synonym. +2. DUAL FORM FOR ACRONYMS: e.g. "Continuous Integration / Continuous Deployment (CI/CD)". +3. TITLE ALIGNMENT: the headline should mirror the posting's title when that title + honestly describes my record and appears in ALLOWED TITLES. +4. KEYWORD FREQUENCY: a critical keyword should appear 2-3 times across summary, + skills and at least one experience bullet โ€” always inside a real sentence. +5. BULLET FORMULA: "Accomplished [X] as measured by [Y] by doing [Z]." Strong + past-tense verb first. Outcome before task. A number beats an adjective. +6. HARD SKILLS OVER SOFT SKILLS. + +=== STRICT INTEGRITY RULES โ€” VIOLATION INVALIDATES THE OUTPUT === +A. NEVER invent a skill, employer, metric, date or certification. +B. ALLOWED TITLES ONLY: {allowed} +C. FORBIDDEN TITLES: {forbidden} +D. NEVER CLAIM (no production experience): {never} +E. Every number must be copied exactly from MY REAL DATA. No rounding, no new numbers. +F. When rewriting an experience bullet you may change the WORDING to carry the + job's keywords, but the FACTS, NUMBERS and SCOPE must remain identical to the + original bullet you were given. + +=== MY REAL DATA === +NAME: {$id['fullName']} +CURRENT POSITIONING: {$id['primaryTitle']} โ€” {$id['positioningLine']} +MASTER SUMMARY: {$profile['summary']} + +SKILLS (you may only select from these): +{$skillsRef} +EXPERIENCE (you may only rewrite these exact bullets): +{$experienceRef} +HONEST LIMITS: {limits} + +=== TARGET JOB === +Title: {$jobTitle} +Description: +{jobdesc} + +=== RETURN FORMAT === +Return ONLY raw JSON (no markdown fences) with EXACTLY these keys: +{ + "headline": "One line. Title mirroring the job + 3-4 exact hard skills I truly have.", + "summary": "3-4 sentences. Opens with what I built and the numbers. Mirrors the job's exact terminology.", + "core_skills": "Comma-separated list of exactly 10 keywords copied verbatim from the job posting that I genuinely have.", + "roles": [ + { + "role_index": 0, + "bullet_ids": ["0.0", "0.2"], + "bullets": ["Rewritten text of bullet 0.0", "Rewritten text of bullet 0.2"] + } + ], + "ats_keywords_matched": ["exact terms from the posting that now appear in the CV"], + "ats_keywords_missing": ["requirements from the posting I genuinely cannot claim"], + "estimated_ats_score": 0 +} + +RULES FOR "roles": +- Include EVERY role index that exists in MY REAL DATA, in the same order. +- For each role select the 3-4 bullets MOST relevant to this job (fewer for the + oldest role). "bullets" must align 1:1 with "bullet_ids". +- Each rewritten bullet must stay factually identical to its original. +PROMPT; + + $prompt = str_replace( + ['{allowed}', '{forbidden}', '{never}', '{limits}', '{jobdesc}'], + [ + implode(', ', $profile['allowedTitles']), + implode(', ', $profile['forbiddenTitles']), + implode(', ', $bounds['neverClaim']), + implode(' | ', $bounds['limitedIn']), + mb_substr($jobDescription, 0, 6000) + ], + $prompt + ); + + $res = callGemini(geminiUrl($MODELS['generatePdf'], $apiKey), [ + "contents" => [["parts" => [["text" => $prompt]]]], + "generationConfig" => [ + "temperature" => 0.25, + "maxOutputTokens" => 4096, + "responseMimeType" => "application/json" + ] + ]); + + if ($res['code'] !== 200) { + http_response_code(502); + echo json_encode(["error" => "Gemini API Error", "details" => json_decode($res['body'])]); + exit; + } + + $responseData = json_decode($res['body'], true); $aiText = $responseData['candidates'][0]['content']['parts'][0]['text'] ?? '{}'; - $aiText = str_replace(['```json', '```'], '', $aiText); - $parsedJson = json_decode(trim($aiText), true); + $aiText = trim(str_replace(['```json', '```'], '', $aiText)); + $ai = json_decode($aiText, true) ?: []; - $defaultHeadline = ($template === 'amman') - ? "Senior Backend Engineer & Technical Lead" - : "Solutions Architect & Technical Leader"; - $headline = $parsedJson['headline'] ?? $defaultHeadline; - $summary = $parsedJson['summary'] ?? "Experienced professional with a strong background in software engineering."; - $skills = $parsedJson['skills'] ?? "Architecture, APIs, Cloud, Backend Systems, System Design"; + // โ”€โ”€ Server-side integrity guard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // The model is instructed not to fabricate, but instruction-following is not + // a guarantee. Anything it produces is scanned for terms I have no right to + // claim; a hit means we fall back to the untailored master content rather + // than ship a CV that will fail a technical screen. + $violations = []; + $haystack = strtolower(json_encode($ai, JSON_UNESCAPED_UNICODE)); + foreach (array_merge($bounds['neverClaim'], $profile['forbiddenTitles']) as $banned) { + if (strpos($haystack, strtolower($banned)) !== false) { + $violations[] = $banned; + } + } + $integrityClean = empty($violations); - $templateFile = ($template === 'amman') - ? __DIR__ . '/cv_template_amman.html' - : __DIR__ . '/cv_template.html'; - $html = file_get_contents($templateFile); - $html = str_replace('{{JOB_HEADLINE}}', htmlspecialchars($headline), $html); - $html = str_replace('{{TAILORED_SUMMARY}}', htmlspecialchars($summary), $html); - $html = str_replace('{{DYNAMIC_SKILLS}}', htmlspecialchars($skills), $html); + $headline = ($integrityClean && !empty($ai['headline'])) + ? $ai['headline'] + : $id['primaryTitle'] . ' โ€” ' . $id['positioningLine']; + + $summary = ($integrityClean && !empty($ai['summary'])) + ? $ai['summary'] + : $profile['summary']; + + if ($integrityClean && !empty($ai['core_skills'])) { + $coreSkills = $ai['core_skills']; + } else { + // Fallback spreads across every skill group. Taking the first N of the + // flattened list would just duplicate the "Mobile & Architecture" line + // verbatim, wasting the most valuable slot on the page. + $picked = []; + foreach ($profile['skills'] as $items) { + $picked = array_merge($picked, array_slice($items, 0, 3)); + } + $coreSkills = implode(', ', array_slice($picked, 0, 10)); + } + + // โ”€โ”€ Render blocks โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // Contact details sit on separate block-level lines with explicit + // separators. A bare <br> can collapse during text extraction and glue the + // email to the LinkedIn URL, which loses the recruiter both. + $contactBlock = + '<div>' . e($id['location']) . '  |  ' . e($id['phone']) + . '  |  ' . e($id['email']) . '</div>' + . '<div>' . e($id['linkedin']) . '  |  ' . e($id['portfolio']) + . '  |  ' . e($id['productSite']) . '</div>'; + + // Metrics are rendered as one linear list. The master CV shows them as a + // four-column band, which looks good to a human and extracts as garbage in + // an ATS โ€” this is the parser-safe equivalent of the same information. + $achievements = '<ul>'; + foreach ($profile['headlineMetrics'] as $m) { + $achievements .= '<li><strong>' . e($m['value']) . '</strong> โ€” ' . e($m['label']) . '</li>'; + } + foreach ($profile['achievementsAtAGlance'] as $a) { + $achievements .= '<li>' . e($a) . '</li>'; + } + $achievements .= '</ul>'; + + $skillsBlock = ''; + foreach ($profile['skills'] as $group => $items) { + $skillsBlock .= '<p class="skill-line"><span class="label">' . e($group) . ':</span> ' + . e(implode(', ', $items)) . '</p>'; + } + + // Index the AI's per-role selections so we can fall back role-by-role. + $aiRoles = []; + if ($integrityClean && !empty($ai['roles']) && is_array($ai['roles'])) { + foreach ($ai['roles'] as $r) { + if (isset($r['role_index'])) { + $aiRoles[(int)$r['role_index']] = $r; + } + } + } + + $experienceBlock = ''; + foreach ($profile['experience'] as $ri => $role) { + $experienceBlock .= '<div class="job">'; + $experienceBlock .= '<p class="job-title">' . e($role['title']) . '</p>'; + $experienceBlock .= '<p class="job-company">' . e($role['company']) . ' โ€” ' . e($role['location']) . '</p>'; + $experienceBlock .= '<p class="job-dates">' . e($role['start']) . ' โ€“ ' . e($role['end']) . '</p>'; + + // Prefer the tailored rewrite; fall back to the master bullets whenever + // the model returned nothing usable for this role. + $bullets = []; + if (isset($aiRoles[$ri]['bullets']) && is_array($aiRoles[$ri]['bullets'])) { + foreach ($aiRoles[$ri]['bullets'] as $b) { + $b = trim((string)$b); + if ($b !== '') $bullets[] = $b; + } + } + if (empty($bullets)) { + foreach ($role['bullets'] as $b) { + $bullets[] = $b['text']; + } + } + + $experienceBlock .= '<ul>'; + foreach ($bullets as $b) { + $experienceBlock .= '<li>' . e($b) . '</li>'; + } + $experienceBlock .= '</ul></div>'; + } + + $edu = $profile['education']; + $educationBlock = '<p>' . e($edu['degree']) . ' โ€” ' . e($edu['institution']) . '. ' . e($edu['note']) . '</p>'; + + $certBlock = '<ul>'; + foreach ($profile['certifications'] as $c) { + $certBlock .= '<li>' . e($c) . '</li>'; + } + $certBlock .= '</ul>'; + + $availability = $market === 'amman' + ? 'Based in Amman, Jordan. Available immediately for on-site, hybrid and remote roles.' + : $id['availability']; + + $html = file_get_contents(__DIR__ . '/cv_template.html'); + $html = strtr($html, [ + '{{FULL_NAME}}' => e($id['fullName']), + '{{JOB_HEADLINE}}' => e($headline), + '{{CONTACT_BLOCK}}' => $contactBlock, + '{{TAILORED_SUMMARY}}' => e($summary), + '{{ACHIEVEMENTS_BLOCK}}' => $achievements, + '{{DYNAMIC_SKILLS}}' => e($coreSkills), + '{{SKILLS_BLOCK}}' => $skillsBlock, + '{{EXPERIENCE_BLOCK}}' => $experienceBlock, + '{{EDUCATION_BLOCK}}' => $educationBlock, + '{{CERTIFICATIONS_BLOCK}}' => $certBlock, + '{{LANGUAGES}}' => e($id['languages']), + '{{LOCATION}}' => e($id['location']), + '{{AVAILABILITY}}' => e($availability), + ]); try { $options = new Options(); $options->set('isHtml5ParserEnabled', true); $options->set('defaultFont', 'Helvetica'); $dompdf = new Dompdf($options); - $dompdf->loadHtml($html); + $dompdf->loadHtml($html, 'UTF-8'); $dompdf->setPaper('A4', 'portrait'); $dompdf->render(); - $pdfOutput = $dompdf->output(); - $rawJobTitle = $data['jobTitle'] ?? 'Job'; - $safeJobTitle = preg_replace('/[^a-zA-Z0-9\-_]/', '_', $rawJobTitle); - $safeJobTitle = trim($safeJobTitle, '_'); - $fileName = "Hamza_Ayed - {$safeJobTitle}.pdf"; + + // ATS parsers key on the filename too โ€” "Name - Role.pdf" reads cleanly. + $safeName = preg_replace('/[^a-zA-Z0-9\-_ ]/', '', $id['fullName']); + $safeTitle = preg_replace('/[^a-zA-Z0-9\-_ ]/', '', $jobTitle ?: 'CV'); + $safeTitle = trim(preg_replace('/\s+/', ' ', $safeTitle)); + $fileName = trim("{$safeName} - {$safeTitle}") . '.pdf'; echo json_encode([ - "success" => true, - "pdf" => base64_encode($pdfOutput), - "filename" => $fileName + "success" => true, + "pdf" => base64_encode($pdfOutput), + "filename" => $fileName, + "ats" => [ + "score" => $ai['estimated_ats_score'] ?? null, + "matched" => $ai['ats_keywords_matched'] ?? [], + "missing" => $ai['ats_keywords_missing'] ?? [], + "tailored" => $integrityClean && !empty($aiRoles), + "violations" => $violations, + ] ]); - } catch (Exception $e) { + } catch (Exception $ex) { http_response_code(500); - echo json_encode(["error" => "PDF Generation Failed", "details" => $e->getMessage()]); + echo json_encode(["error" => "PDF Generation Failed", "details" => $ex->getMessage()]); } exit; } -// ========================================== -// ACTION 2: Standard Proxy (Text generation) -// ========================================== +// ============================================================================ +// ACTION 2: Standard Proxy (text generation) +// ============================================================================ if ($action === 'generateText') { $prompt = $data['prompt'] ?? ''; - - $payload = json_encode([ - "contents" => [["parts" => [["text" => $prompt]]]], - "generationConfig" => ["temperature" => 0.7, "maxOutputTokens" => 8192] - ]); - - $ch = curl_init($geminiUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if ($httpCode !== 200) { - http_response_code($httpCode); - echo $response; + if (trim($prompt) === '') { + http_response_code(400); + echo json_encode(["error" => "prompt is required."]); exit; } - // Pass the exact Gemini response back to the extension - echo $response; + $res = callGemini(geminiUrl($MODELS['generateText'], $apiKey), [ + "contents" => [["parts" => [["text" => $prompt]]]], + "generationConfig" => ["temperature" => 0.6, "maxOutputTokens" => 8192] + ]); + + if ($res['code'] !== 200) { + http_response_code($res['code'] ?: 502); + echo $res['body'] ?: json_encode(["error" => $res['error'] ?: 'Upstream failure']); + exit; + } + echo $res['body']; exit; } -// ========================================== -// ACTION 3: Smart Comment Generator -// ========================================== -if ($action === 'generateComment') { - $postText = substr($data['postText'] ?? '', 0, 3000); +// ============================================================================ +// ACTION 3 & 4: Prompt-file backed generators (comment / repurpose) +// ============================================================================ +$fileBacked = [ + 'generateComment' => ['file' => 'comment_prompt.txt', 'key' => 'comment', 'tokens' => 700, 'temp' => 0.8], + 'repurposePost' => ['file' => 'repurpose_prompt.txt', 'key' => 'result', 'tokens' => 1200, 'temp' => 0.85], +]; - if (empty($postText)) { +if (isset($fileBacked[$action])) { + $cfg = $fileBacked[$action]; + $postText = mb_substr($data['postText'] ?? '', 0, 3000); + + if (trim($postText) === '') { http_response_code(400); echo json_encode(["error" => "postText is required."]); exit; } - $promptFile = __DIR__ . '/prompts/comment_prompt.txt'; + $promptFile = __DIR__ . '/prompts/' . $cfg['file']; if (!file_exists($promptFile)) { http_response_code(500); - echo json_encode(["error" => "Comment prompt file not found on server."]); + echo json_encode(["error" => "Prompt file {$cfg['file']} not found on server."]); exit; } + // Give the writer prompts the real identity instead of a stale copy baked + // into the text file. {{PROFILE}} is optional in the template. + $profile = loadProfile(); $promptTemplate = file_get_contents($promptFile); - $prompt = str_replace('{{POST_TEXT}}', $postText, $promptTemplate); - - $payload = json_encode([ - "contents" => [["parts" => [["text" => $prompt]]]], - "generationConfig" => ["temperature" => 0.75, "maxOutputTokens" => 256] + $prompt = strtr($promptTemplate, [ + '{{POST_TEXT}}' => $postText, + '{{PROFILE}}' => $profile['profileText'] ?? '', + '{{VOICE}}' => $profile['voice']['tone'] ?? '', + '{{BANNED}}' => implode(' / ', $profile['voice']['banned'] ?? []), ]); - $ch = curl_init($geminiUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if ($httpCode !== 200) { - http_response_code(500); - echo json_encode(["error" => "Gemini API Error", "details" => json_decode($response)]); - exit; + $genConfig = [ + "temperature" => $cfg['temp'], + "maxOutputTokens" => $cfg['tokens'] + ]; + // The comment generator must return strict JSON; the repurposer returns prose. + if ($action === 'generateComment') { + $genConfig['responseMimeType'] = 'application/json'; } - $responseData = json_decode($response, true); - $commentText = trim($responseData['candidates'][0]['content']['parts'][0]['text'] ?? ''); - - if (empty($commentText)) { - http_response_code(500); - echo json_encode(["error" => "Empty comment from AI."]); - exit; - } - - echo json_encode(["success" => true, "comment" => $commentText]); - exit; -} - -// ========================================== -// ACTION 4: Repurpose Post Generator -// ========================================== -if ($action === 'repurposePost') { - $postText = substr($data['postText'] ?? '', 0, 3000); - - if (empty($postText)) { - http_response_code(400); - echo json_encode(["error" => "postText is required."]); - exit; - } - - $promptFile = __DIR__ . '/prompts/repurpose_prompt.txt'; - if (!file_exists($promptFile)) { - http_response_code(500); - echo json_encode(["error" => "Repurpose prompt file not found on server."]); - exit; - } - - $promptTemplate = file_get_contents($promptFile); - $prompt = str_replace('{{POST_TEXT}}', $postText, $promptTemplate); - - $payload = json_encode([ + $res = callGemini(geminiUrl($MODELS[$action], $apiKey), [ "contents" => [["parts" => [["text" => $prompt]]]], - "generationConfig" => ["temperature" => 0.75, "maxOutputTokens" => 800] + "generationConfig" => $genConfig ]); - $ch = curl_init($geminiUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if ($httpCode !== 200) { - http_response_code(500); - echo json_encode(["error" => "Gemini API Error", "details" => json_decode($response)]); + if ($res['code'] !== 200) { + http_response_code(502); + echo json_encode(["error" => "Gemini API Error", "details" => json_decode($res['body'])]); exit; } - $responseData = json_decode($response, true); - $resultText = trim($responseData['candidates'][0]['content']['parts'][0]['text'] ?? ''); + $responseData = json_decode($res['body'], true); + $text = trim($responseData['candidates'][0]['content']['parts'][0]['text'] ?? ''); - if (empty($resultText)) { - http_response_code(500); + if ($text === '') { + http_response_code(502); echo json_encode(["error" => "Empty result from AI."]); exit; } - echo json_encode(["success" => true, "result" => $resultText]); + echo json_encode(["success" => true, $cfg['key'] => $text]); exit; } + +http_response_code(400); +echo json_encode(["error" => "Unknown action: " . $action]); diff --git a/server/profile.json b/server/profile.json new file mode 100644 index 0000000..443f380 --- /dev/null +++ b/server/profile.json @@ -0,0 +1,344 @@ +{ + "identity": { + "fullName": "Hamza Ayed", + "primaryTitle": "Senior Mobile Architect", + "positioningLine": "Ride-Hailing & Real-Time Systems ยท FinTech in Emerging Markets", + "location": "Amman, Jordan", + "phone": "+962 798 583 052", + "email": "hamzaayedflutter@gmail.com", + "linkedin": "linkedin.com/in/hamza-ayed", + "portfolio": "intaleqapp.com/hamza.html", + "productSite": "tripz-egypt.com", + "languages": "Arabic (Native), English (Professional Working Proficiency)", + "availability": "Available immediately. Open to Remote, Hybrid and Relocation (GCC / MENA / Europe)." + }, + "allowedTitles": [ + "Senior Mobile Architect", + "Senior Mobile Engineer", + "Lead Mobile Engineer", + "Senior Flutter Developer", + "Lead Flutter Engineer", + "Mobile Technical Lead", + "Founding Engineer (Mobile)", + "Senior Software Engineer (Mobile)" + ], + "forbiddenTitles": [ + "CTO", + "Solutions Architect", + "Senior AI Developer", + "Machine Learning Engineer", + "Data Scientist", + "DevOps Architect", + "Engineering Manager" + ], + "summary": "Senior Mobile Architect and Founding Engineer with proven experience building and launching two production-grade ride-hailing platforms (Tripz โ€” Egypt, Intaleq โ€” Syria) from zero to live operations. Designed and delivered full mobility ecosystems including Rider, Driver, Admin and Customer Support applications, along with backend APIs, security layers, DevOps pipelines and fintech integrations. Specialized in real-time systems, self-hosted geo-services, high-latency optimization and infrastructure cost reduction.", + "targetRoles": [ + "Senior / Lead Mobile Engineer", + "Mobile Architect", + "Founding Engineer (Mobility, Logistics, FinTech)" + ], + "headlineMetrics": [ + { + "value": "5,275", + "label": "Riders acquired across 2 launches" + }, + { + "value": "5,750+", + "label": "Drivers onboarded" + }, + { + "value": "~80%", + "label": "Map infrastructure cost cut" + }, + { + "value": "8 apps", + "label": "Shipped across 2 platforms" + } + ], + "achievementsAtAGlance": [ + "Tripz (Egypt): Scaled platform to 2,464 riders and 4,318 drivers. Built and launched 4 distinct apps plus full backend in under 4 months with near-zero map infrastructure cost.", + "Intaleq (Syria): Acquired 2,811 riders and 1,440 drivers in the first 2 months. Engineered a custom operational payment layer to bypass API limitations in a highly constrained market." + ], + "skills": { + "Mobile & Architecture": [ + "Flutter", + "Dart", + "GetX", + "Provider", + "Clean Architecture", + "Offline-First Design", + "REST APIs", + "State Management", + "Native Android (Java)", + "iOS / Android Release Management" + ], + "Real-Time & Geo Systems": [ + "WebSockets", + "Real-Time Dispatching", + "Live Tracking", + "Self-Hosted Maps", + "Map Tiles", + "Polylines", + "Routing Engines", + "Geospatial Queries", + "High-Latency Network Optimization" + ], + "Backend & DevOps": [ + "PHP", + "Laravel", + "MySQL", + "Docker", + "Linux", + "Nginx", + "CI/CD (Gitea)", + "Infrastructure Cost Optimization", + "Self-Hosted Infrastructure" + ], + "Security & FinTech": [ + "Payment Gateway Integration (7+ providers)", + "HMAC Security", + "RASP", + "Fake GPS Detection", + "Anti-Tampering", + "KYC Automation" + ] + }, + "experience": [ + { + "company": "Intaleq", + "title": "Senior Mobile Systems Architect", + "location": "MENA Region (Remote)", + "start": "Jan 2025", + "end": "Present", + "bullets": [ + { + "text": "Launched a ride-hailing platform in a high-constraint market (Syria), reaching 2,811 riders and 1,440 drivers within the first two months.", + "tags": [ + "leadership", + "delivery", + "mobility", + "scale" + ] + }, + { + "text": "Designed and implemented Rider, Driver, Admin and Customer Support applications.", + "tags": [ + "mobile", + "flutter", + "architecture" + ] + }, + { + "text": "Built a custom payment helper application to integrate legacy, non-API local payment gateways, enabling reliable driver balance top-ups despite infrastructure limitations.", + "tags": [ + "fintech", + "payments", + "integration" + ] + }, + { + "text": "Leveraged fully self-hosted map tiles and routing engines to eliminate dependency on expensive third-party mapping APIs, keeping operational costs near zero.", + "tags": [ + "geo", + "cost", + "infrastructure" + ] + }, + { + "text": "Designed an offline-first caching mechanism ensuring data integrity and uninterrupted driverโ€“rider interactions during unstable network conditions.", + "tags": [ + "mobile", + "offline", + "reliability" + ] + }, + { + "text": "Optimized deployment cycles by 40% through a secure, self-hosted CI/CD pipeline using Gitea, ensuring data sovereignty and rapid release efficiency.", + "tags": [ + "devops", + "cicd", + "security" + ] + } + ] + }, + { + "company": "Tripz", + "title": "Founding Mobile Architect", + "location": "Cairo, Egypt (Remote)", + "start": "Jan 2024", + "end": "Dec 2024", + "bullets": [ + { + "text": "Built and launched a complete ride-hailing ecosystem (Rider, Driver, Admin, Customer Support apps plus backend API) in under four months, scaling to 2,464 riders and 4,318 drivers.", + "tags": [ + "delivery", + "leadership", + "mobility", + "scale", + "flutter" + ] + }, + { + "text": "Designed real-time dispatch and live tracking using WebSockets, optimized for high-latency MENA network environments.", + "tags": [ + "realtime", + "websockets", + "geo" + ] + }, + { + "text": "Reduced map infrastructure expenses by approximately 80% versus third-party API pricing by self-hosting map tiles, routing logic and polyline generation on dedicated servers.", + "tags": [ + "geo", + "cost", + "infrastructure" + ] + }, + { + "text": "Replaced traditional call-center hardware by engineering a secure, mobile-first Customer Support application, enabling support agents to operate entirely remotely.", + "tags": [ + "mobile", + "product", + "cost" + ] + }, + { + "text": "Automated driver onboarding through AI-powered KYC integration, reducing manual review time and minimizing fraudulent registrations.", + "tags": [ + "ai", + "automation", + "fintech", + "security" + ] + }, + { + "text": "Implemented API-level HMAC security, anti-tampering mechanisms and Fake GPS detection to protect geo-spatial and financial data.", + "tags": [ + "security", + "geo", + "fintech" + ] + }, + { + "text": "Collaborated closely with business stakeholders and operations teams to align product and architecture decisions with on-the-ground market realities.", + "tags": [ + "leadership", + "product", + "communication" + ] + } + ] + }, + { + "company": "Global Tech Projects", + "title": "Lead Mobile Engineer (Flutter / Native)", + "location": "Freelance / Consultant", + "start": "Jan 2017", + "end": "Dec 2023", + "bullets": [ + { + "text": "Delivered 30+ production-grade mobile applications across e-commerce, delivery, healthcare and news sectors, acting as technical decision-maker for early-stage clients.", + "tags": [ + "delivery", + "leadership", + "mobile", + "scale" + ] + }, + { + "text": "Migrated legacy Android (Java) applications to Flutter, cutting maintenance complexity and delivery timelines by roughly 50%.", + "tags": [ + "flutter", + "migration", + "mobile" + ] + }, + { + "text": "Built custom Flutter plugins and tailored API layers to integrate with complex legacy backend systems.", + "tags": [ + "flutter", + "integration", + "architecture" + ] + } + ] + } + ], + "education": { + "degree": "BSc Mathematics", + "institution": "Mu'tah University, Jordan", + "note": "Applied mathematical foundations to state-machine design and geo-spatial routing." + }, + "certifications": [ + "IBM Data Analyst Professional Certificate", + "Google Data Analytics Professional Certificate", + "IBM Data Analytics with Excel and R Specialization" + ], + "boundaries": { + "strongIn": [ + "Flutter / Dart mobile architecture at production scale", + "Real-time systems: WebSockets, dispatching, live tracking", + "Self-hosted geo infrastructure: tiles, routing, polylines", + "Payment gateway integration and constrained-market fintech workarounds", + "Mobile-layer security: HMAC, anti-tampering, Fake GPS detection", + "PHP / Laravel / MySQL backend supporting mobile products", + "Docker, Linux, Nginx, self-hosted CI/CD" + ], + "limitedIn": [ + "AI is limited to INTEGRATING third-party vision/LLM APIs (KYC automation, Gemini API). No model training, no MLOps, no deep learning research.", + "Cloud experience is self-hosted/VPS-centric โ€” not deep AWS/GCP/Azure managed-service experience.", + "No Kubernetes in production.", + "No native Swift/SwiftUI or Kotlin-first development โ€” Flutter is the primary mobile path.", + "No data engineering (Spark, Hadoop, Airflow, warehouses)." + ], + "neverClaim": [ + "TensorFlow", + "PyTorch", + "Scikit-learn", + "Spark", + "Hadoop", + "Kubernetes", + "MLOps", + "model training", + "NLP research", + "Swift", + "SwiftUI", + "Kotlin Multiplatform", + "blockchain", + "Salesforce", + "SAP" + ] + }, + "voice": { + "tone": "Operator-level, calm, direct, evidence-first. Writes like someone who has shipped, not someone selling.", + "banned": [ + "I am writing to apply", + "I leverage my expertise", + "passionate about", + "proven track record", + "dynamic environment", + "synergy", + "game-changer", + "disruptive", + "serving millions", + "in the digital age", + "Great post", + "Well said", + "Couldn't agree more", + "This resonates", + "ููŠ ุงู„ุนุตุฑ ุงู„ุฑู‚ู…ูŠ", + "ู…ู…ุง ู„ุง ุดูƒ ููŠู‡", + "ุฎุชุงู…ุงู‹", + "ูŠุนุฏ ู…ู† ุฃู‡ู…" + ], + "neverMention": [ + "military history or background", + "sanctions or politically sensitive framing of the Syria market", + "exact client names under NDA" + ] + }, + "_generated": "AUTO-GENERATED from profile_data.js โ€” do not edit by hand. Run: node sync_profile.js", + "_generatedAt": "2026-08-05T13:17:53.677Z", + "profileText": "HAMZA AYED โ€” Senior Mobile Architect\nRide-Hailing & Real-Time Systems ยท FinTech in Emerging Markets\n\nSUMMARY:\nSenior Mobile Architect and Founding Engineer with proven experience building and launching two production-grade ride-hailing platforms (Tripz โ€” Egypt, Intaleq โ€” Syria) from zero to live operations. Designed and delivered full mobility ecosystems including Rider, Driver, Admin and Customer Support applications, along with backend APIs, security layers, DevOps pipelines and fintech integrations. Specialized in real-time systems, self-hosted geo-services, high-latency optimization and infrastructure cost reduction.\n\nTARGET ROLES: Senior / Lead Mobile Engineer | Mobile Architect | Founding Engineer (Mobility, Logistics, FinTech)\n\nHEADLINE METRICS:\n- 5,275 โ€” Riders acquired across 2 launches\n- 5,750+ โ€” Drivers onboarded\n- ~80% โ€” Map infrastructure cost cut\n- 8 apps โ€” Shipped across 2 platforms\n\nAT A GLANCE:\n- Tripz (Egypt): Scaled platform to 2,464 riders and 4,318 drivers. Built and launched 4 distinct apps plus full backend in under 4 months with near-zero map infrastructure cost.\n- Intaleq (Syria): Acquired 2,811 riders and 1,440 drivers in the first 2 months. Engineered a custom operational payment layer to bypass API limitations in a highly constrained market.\n\nTECHNICAL SKILLS:\n- Mobile & Architecture: Flutter, Dart, GetX, Provider, Clean Architecture, Offline-First Design, REST APIs, State Management, Native Android (Java), iOS / Android Release Management\n- Real-Time & Geo Systems: WebSockets, Real-Time Dispatching, Live Tracking, Self-Hosted Maps, Map Tiles, Polylines, Routing Engines, Geospatial Queries, High-Latency Network Optimization\n- Backend & DevOps: PHP, Laravel, MySQL, Docker, Linux, Nginx, CI/CD (Gitea), Infrastructure Cost Optimization, Self-Hosted Infrastructure\n- Security & FinTech: Payment Gateway Integration (7+ providers), HMAC Security, RASP, Fake GPS Detection, Anti-Tampering, KYC Automation\n\nEXPERIENCE:\n- Senior Mobile Systems Architect โ€” Intaleq | Jan 2025 โ€“ Present | MENA Region (Remote)\n โ€ข Launched a ride-hailing platform in a high-constraint market (Syria), reaching 2,811 riders and 1,440 drivers within the first two months.\n โ€ข Designed and implemented Rider, Driver, Admin and Customer Support applications.\n โ€ข Built a custom payment helper application to integrate legacy, non-API local payment gateways, enabling reliable driver balance top-ups despite infrastructure limitations.\n โ€ข Leveraged fully self-hosted map tiles and routing engines to eliminate dependency on expensive third-party mapping APIs, keeping operational costs near zero.\n โ€ข Designed an offline-first caching mechanism ensuring data integrity and uninterrupted driverโ€“rider interactions during unstable network conditions.\n โ€ข Optimized deployment cycles by 40% through a secure, self-hosted CI/CD pipeline using Gitea, ensuring data sovereignty and rapid release efficiency.\n- Founding Mobile Architect โ€” Tripz | Jan 2024 โ€“ Dec 2024 | Cairo, Egypt (Remote)\n โ€ข Built and launched a complete ride-hailing ecosystem (Rider, Driver, Admin, Customer Support apps plus backend API) in under four months, scaling to 2,464 riders and 4,318 drivers.\n โ€ข Designed real-time dispatch and live tracking using WebSockets, optimized for high-latency MENA network environments.\n โ€ข Reduced map infrastructure expenses by approximately 80% versus third-party API pricing by self-hosting map tiles, routing logic and polyline generation on dedicated servers.\n โ€ข Replaced traditional call-center hardware by engineering a secure, mobile-first Customer Support application, enabling support agents to operate entirely remotely.\n โ€ข Automated driver onboarding through AI-powered KYC integration, reducing manual review time and minimizing fraudulent registrations.\n โ€ข Implemented API-level HMAC security, anti-tampering mechanisms and Fake GPS detection to protect geo-spatial and financial data.\n โ€ข Collaborated closely with business stakeholders and operations teams to align product and architecture decisions with on-the-ground market realities.\n- Lead Mobile Engineer (Flutter / Native) โ€” Global Tech Projects | Jan 2017 โ€“ Dec 2023 | Freelance / Consultant\n โ€ข Delivered 30+ production-grade mobile applications across e-commerce, delivery, healthcare and news sectors, acting as technical decision-maker for early-stage clients.\n โ€ข Migrated legacy Android (Java) applications to Flutter, cutting maintenance complexity and delivery timelines by roughly 50%.\n โ€ข Built custom Flutter plugins and tailored API layers to integrate with complex legacy backend systems.\n\nEDUCATION:\n- BSc Mathematics, Mu'tah University, Jordan. Applied mathematical foundations to state-machine design and geo-spatial routing.\n- IBM Data Analyst Professional Certificate\n- Google Data Analytics Professional Certificate\n- IBM Data Analytics with Excel and R Specialization\n\nCAPABILITY BOUNDARIES (do not overstate):\n STRONG IN:\n + Flutter / Dart mobile architecture at production scale\n + Real-time systems: WebSockets, dispatching, live tracking\n + Self-hosted geo infrastructure: tiles, routing, polylines\n + Payment gateway integration and constrained-market fintech workarounds\n + Mobile-layer security: HMAC, anti-tampering, Fake GPS detection\n + PHP / Laravel / MySQL backend supporting mobile products\n + Docker, Linux, Nginx, self-hosted CI/CD\n LIMITED / NOT EXPERIENCED IN:\n - AI is limited to INTEGRATING third-party vision/LLM APIs (KYC automation, Gemini API). No model training, no MLOps, no deep learning research.\n - Cloud experience is self-hosted/VPS-centric โ€” not deep AWS/GCP/Azure managed-service experience.\n - No Kubernetes in production.\n - No native Swift/SwiftUI or Kotlin-first development โ€” Flutter is the primary mobile path.\n - No data engineering (Spark, Hadoop, Airflow, warehouses).\n NEVER CLAIM: TensorFlow, PyTorch, Scikit-learn, Spark, Hadoop, Kubernetes, MLOps, model training, NLP research, Swift, SwiftUI, Kotlin Multiplatform, blockchain, Salesforce, SAP\n\nLANGUAGES: Arabic (Native), English (Professional Working Proficiency)\nLOCATION: Amman, Jordan โ€” Available immediately. Open to Remote, Hybrid and Relocation (GCC / MENA / Europe).\nCONTACT: hamzaayedflutter@gmail.com | +962 798 583 052 | linkedin.com/in/hamza-ayed | intaleqapp.com/hamza.html" +} \ No newline at end of file diff --git a/server/prompts/comment_prompt.txt b/server/prompts/comment_prompt.txt index 064bae2..3e46761 100644 --- a/server/prompts/comment_prompt.txt +++ b/server/prompts/comment_prompt.txt @@ -1,21 +1,78 @@ -You are a Solutions Architect and Engineering Leader. Read the following LinkedIn post and generate a JSON response. +You are writing LinkedIn comments as Hamza Ayed. Read the post below and return a JSON response. -STRICT RULES FOR THE COMMENT: -- Max 3 sentences. It MUST flow naturally as a single, coherent, human-written comment. Do NOT output disconnected, bullet-point-like statements. -- Tone: Calm, authoritative, direct, and operator-level. Avoid corporate jargon, fluff, ego, preaching, or generic praise clichรฉs (never start with "Great post", "Well said", etc.). -- INDUSTRY RELEVANCE (CRITICAL): Do NOT mention ride-hailing, mobility, GIS, or transportation unless the post is explicitly and directly about maps, routing, logistics, or transportation. For general posts (like team management, system design, workplace culture, innovation), comment from a general systems architect/tech lead perspective. -- TONE & STYLE: Write a comment that encourages professional discussion and attracts profile visits. -- LANGUAGE: Match the language of the post. If the post is in Arabic, write the comment in Arabic. If the post is in English, write the comment in English. -- Output the comment exactly as plain text in the JSON string (no markdown formatting inside). +=== WHO YOU ARE === +{{PROFILE}} -STRICT RULES FOR THE ARABIC SUMMARY & CREDIBILITY CHECK: -- Analyze the post objectively. No flattery. No automatic agreement. -- Detect any exaggerated metrics, vanity metrics, logical flaws, or emotional manipulation (e.g., VC/startup hustle culture tropes). -- Provide a 2-3 sentence Arabic response. The first sentence should summarize the post. The following sentences MUST be a brutal, honest credibility assessment pointing out risks or flaws in the author's logic. (Note: The summary and credibility check MUST ALWAYS be in Arabic, regardless of the post's language). +=== VOICE === +{{VOICE}} -Return ONLY a valid JSON object with EXACTLY two keys: -1. "arabic_summary": The objective summary and credibility assessment in Arabic. -2. "comment": The comment following the rules above (matching the post's language: Arabic if the post is Arabic, English if the post is English). +You have shipped things. You are not selling, not networking, not performing. +You comment because you have something specific to add โ€” or because something in +the post is wrong and worth naming politely. -POST TEXT: +=== THE COMMENT โ€” HARD RULES === + +1. LENGTH: 2-3 sentences. One coherent paragraph that reads like a person typed + it in one go. Never a list, never disconnected statements, never a mini-essay. + +2. IT MUST ADD SOMETHING. Every comment has to do exactly one of these: + (a) contribute a concrete detail, constraint or trade-off the post missed; + (b) share a specific thing you saw when you did this yourself; + (c) ask a sharp, genuine question that moves the discussion forward; + (d) respectfully complicate a claim that is too clean to be true. + If you cannot do any of these honestly, set "comment" to "" (empty string) and + explain why in the Arabic analysis. A skipped comment is better than a generic + one โ€” generic comments actively damage a technical reputation. + +3. NEVER OPEN WITH PRAISE. Banned openers include: "Great post", "Well said", + "Couldn't agree more", "This resonates", "Spot on", "So true", "Love this", + "Thanks for sharing", "ูƒู„ุงู… ุณู„ูŠู…", "ุฃุญุณู†ุช", "ุจุงู„ุถุจุท", "ู…ู‚ุงู„ ุฑุงุฆุน". + Open on the substance instead. + +4. NO SELF-PROMOTION. Do not name Tripz, Intaleq, your job title, your metrics or + your portfolio. Your credibility must come from the precision of what you say, + not from a credential drop. At most you may say "when I built dispatch systems" + as context โ€” never as a boast, and only when directly relevant. + +5. DOMAIN DISCIPLINE (critical): Do NOT bring up ride-hailing, mobility, mapping, + GIS, dispatch or transportation UNLESS the post is explicitly about routing, + logistics, maps or transport. For general posts (management, hiring, system + design, culture, career advice), comment as an experienced engineer, full stop. + +6. LANGUAGE: Match the post exactly. Arabic post โ†’ Arabic comment (natural, + professional, conversational Arabic โ€” not translated-English Arabic). + English post โ†’ English comment. + +7. BANNED PHRASING (instant fail): {{BANNED}} + Also banned: em-dash-heavy rhetorical constructions, "it's not X, it's Y" + parallelism, rhetorical questions used as filler, and any sentence that would + survive unchanged under a different post. + +8. NO hashtags. NO emojis. NO markdown. Plain text only. + +9. NO FABRICATION. Never invent a statistic, a benchmark, a study or an anecdote + that did not happen. + +=== THE ARABIC ANALYSIS โ€” HARD RULES === + +Always in Arabic, regardless of the post's language. 3-4 sentences total: +- Sentence 1: what the post actually claims, stripped of rhetoric. +- Sentences 2+: a blunt credibility assessment. Name specifically: + * unverifiable or vanity metrics + * survivorship bias, or a sample of one presented as a rule + * hustle-culture / VC-narrative framing + * logical gaps, false dichotomies, correlation sold as causation + * whether this is a disguised sales pitch or recruitment ad +- If the post is genuinely solid, say so plainly and say why โ€” do not manufacture + criticism. Honesty runs in both directions. +- Close with a one-word engagement call: "ูŠุณุชุงู‡ู„ ุชุนู„ูŠู‚" or "ุชุฌุงู‡ู„ู‡". + +=== OUTPUT === +Return ONLY a valid JSON object with EXACTLY these two keys: +{ + "arabic_summary": "ุงู„ุชุญู„ูŠู„ ูˆุงู„ู…ุตุฏุงู‚ูŠุฉ ุจุงู„ุนุฑุจูŠุฉ", + "comment": "the comment in the post's language, or empty string if not worth commenting" +} + +=== POST TEXT === {{POST_TEXT}} diff --git a/server/prompts/repurpose_prompt.txt b/server/prompts/repurpose_prompt.txt index a28481b..eba69d0 100644 --- a/server/prompts/repurpose_prompt.txt +++ b/server/prompts/repurpose_prompt.txt @@ -1,21 +1,65 @@ -You are Hamza Ayed, a Senior Solutions Architect and technical leader. -Your task is to REPURPOSE an existing LinkedIn post into an entirely new, original post written in your unique voice. +You are Hamza Ayed. Take the post below and write an entirely NEW, original post +in your own voice on the same underlying topic. -YOUR BACKGROUND & PHILOSOPHY: -- Built "Intaleq" and "Tripz" platforms from scratch. -- Created "Intaleq Maps" to bypass Google Maps, saving massive costs and achieving independence. -- You believe in building independent infrastructure, cost-efficiency, and tech sovereignty rather than relying entirely on 3rd parties. +=== WHO YOU ARE === +{{PROFILE}} -STRICT RULES: -1. CORE CONCEPT: Extract the main idea from the provided post, but write a COMPLETELY NEW post. Do not just summarize. Reframe it from your Architect/Leadership perspective. -2. LANGUAGE: Match the language of the original post. If Arabic, use conversational, engaging, professional Arabic (AVOID AI clichรฉs like "ููŠ ุงู„ุนุตุฑ ุงู„ุฑู‚ู…ูŠ", "ู…ู…ุง ู„ุง ุดูƒ ููŠู‡", "ูŠุนุฏ", "ุฎุชุงู…ุงู‹"). Write naturally like a seasoned expert. -3. STRUCTURE: - - Hook: A strong opening statement (no emojis in the very first sentence). - - Body: Your perspective on the topic. Subtly weave in your philosophy if relevant (without revealing secrets). - - Conclusion: End with a strong closing thought or an open question to encourage engagement. -4. IMAGE PROMPT: At the very end of your response, leave two blank lines, then output EXACTLY this header: "--- IMAGE PROMPT ---", followed by a highly detailed, descriptive prompt in English for an AI image generator (like Midjourney or DALL-E) to create a stunning, professional accompanying visual for your post. -5. NO EMOJIS in the image prompt section. You may use a few appropriate emojis in the main post text. -6. OUTPUT FORMAT: Return the new post text, then the image prompt section. Nothing else. +=== VOICE === +{{VOICE}} -POST TO REPURPOSE: +=== YOUR PHILOSOPHY (weave in only when genuinely relevant) === +- You build independent infrastructure instead of renting it. Self-hosted maps, + routing and CI/CD were cost decisions before they were ideological ones. +- You launch in constrained markets โ€” unstable networks, missing payment APIs, + low-end devices. Constraints are the interesting part of the engineering. +- You are suspicious of advice that has never survived contact with production. +- You measure things. A claim without a number is an opinion. + +=== HARD RULES === + +1. ORIGINAL, NOT A SUMMARY. Extract the underlying idea, then write something new + from your own angle. If you find yourself restating the original's structure, + start over. Someone who read both posts should not feel they read the same one. + +2. NO FABRICATION. You may only reference achievements, numbers and projects that + appear in WHO YOU ARE above. Never invent a metric, a client, a benchmark or a + war story. If the topic needs an example you do not have, write about the + principle instead of faking the anecdote. + +3. LANGUAGE: Match the original post. Arabic โ†’ natural, professional, + conversational Arabic. Write like a working engineer talking, not like a + translated press release. + +4. BANNED PHRASING: {{BANNED}} + Also avoid: opening with a one-word sentence for drama, "Here's the thing", + "Let that sink in", numbered listicles of obvious advice, and any closing line + that asks for engagement in a transparently manufactured way. + +5. STRUCTURE: + - HOOK: a concrete, specific opening statement. No emoji in the first sentence. + A real observation beats a provocation. + - BODY: your actual position, with the reasoning visible. Name the trade-off, + not just the conclusion. Short paragraphs โ€” LinkedIn is read on phones. + - CLOSE: a genuine question or a sharp closing thought. It should be a question + you would actually want answered. + +6. LENGTH: 120-220 words. Long enough to say something, short enough to be read. + +7. NO hashtags. At most 1-2 emoji in the body, and only if they earn their place. + +8. NEVER reveal proprietary implementation details, client names, or anything + under NDA. + +=== IMAGE PROMPT === +After the post, leave two blank lines, then output EXACTLY this header: +--- IMAGE PROMPT --- +Then a detailed English prompt for an AI image generator: a professional, +editorial-quality visual that matches the post's idea. Describe subject, +composition, lighting, colour palette and mood. No text or lettering in the +image. No emojis in this section. + +=== OUTPUT === +The new post text, then the image prompt section. Nothing else. + +=== POST TO REPURPOSE === {{POST_TEXT}} diff --git a/sync_profile.js b/sync_profile.js new file mode 100644 index 0000000..1a86c8a --- /dev/null +++ b/sync_profile.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node +// sync_profile.js โ€” regenerate server/profile.json from profile_data.js +// Run this after ANY edit to profile_data.js: node sync_profile.js + +const fs = require('fs'); +const path = require('path'); +const { PROFILE_DATA, renderProfileText } = require('./profile_data.js'); + +const target = path.join(__dirname, 'server', 'profile.json'); +const payload = Object.assign({}, PROFILE_DATA, { + _generated: 'AUTO-GENERATED from profile_data.js โ€” do not edit by hand. Run: node sync_profile.js', + _generatedAt: new Date().toISOString(), + profileText: renderProfileText() +}); + +fs.writeFileSync(target, JSON.stringify(payload, null, 2)); +console.log(`โœ… Synced ${target} (${fs.statSync(target).size} bytes)`);