#!/usr/bin/env python3 """ Context Manager Create, update, and manage per-project context files. Each website has its own context/ folder with brand voice, keywords, and guidelines. """ import os import json import argparse from pathlib import Path from datetime import datetime from typing import Dict, List, Optional class ContextManager: """Manage per-project context files""" def __init__(self, project_path: str): self.project_path = project_path self.context_path = os.path.join(project_path, 'context') # Ensure context directory exists os.makedirs(self.context_path, exist_ok=True) def create_context(self, industry: str = 'general', audience: str = 'Thai audience', formality: str = 'normal') -> Dict[str, str]: """Create complete context structure for new project""" created_files = {} # 1. brand-voice.md brand_voice_content = self._generate_brand_voice(industry, audience, formality) brand_voice_path = os.path.join(self.context_path, 'brand-voice.md') with open(brand_voice_path, 'w', encoding='utf-8') as f: f.write(brand_voice_content) created_files['brand-voice.md'] = brand_voice_path # 2. target-keywords.md keywords_content = self._generate_target_keywords(industry) keywords_path = os.path.join(self.context_path, 'target-keywords.md') with open(keywords_path, 'w', encoding='utf-8') as f: f.write(keywords_content) created_files['target-keywords.md'] = keywords_path # 3. seo-guidelines.md seo_guidelines = self._generate_seo_guidelines() seo_guidelines_path = os.path.join(self.context_path, 'seo-guidelines.md') with open(seo_guidelines_path, 'w', encoding='utf-8') as f: f.write(seo_guidelines) created_files['seo-guidelines.md'] = seo_guidelines_path # 4. internal-links-map.md links_map = "# Internal Links Map\n\nAdd your priority pages here:\n\n## Homepage\n- URL: /\n- Priority: High\n\n## Key Pages\n- Add your key pages here...\n" links_map_path = os.path.join(self.context_path, 'internal-links-map.md') with open(links_map_path, 'w', encoding='utf-8') as f: f.write(links_map) created_files['internal-links-map.md'] = links_map_path # 5. data-services.json data_services = { 'ga4': {'enabled': False, 'property_id': '', 'credentials_path': ''}, 'gsc': {'enabled': False, 'site_url': '', 'credentials_path': ''}, 'dataforseo': {'enabled': False, 'login': '', 'password': ''}, 'umami': {'enabled': False, 'api_url': '', 'api_key': ''} } data_services_path = os.path.join(self.context_path, 'data-services.json') with open(data_services_path, 'w', encoding='utf-8') as f: json.dump(data_services, f, indent=2) created_files['data-services.json'] = data_services_path # 6. style-guide.md style_guide = self._generate_style_guide() style_guide_path = os.path.join(self.context_path, 'style-guide.md') with open(style_guide_path, 'w', encoding='utf-8') as f: f.write(style_guide) created_files['style-guide.md'] = style_guide_path return created_files def _generate_brand_voice(self, industry: str, audience: str, formality: str) -> str: """Generate brand-voice.md template""" formality_th = { 'casual': 'กันเอง (Casual)', 'normal': 'ปกติ (Normal)', 'formal': 'เป็นทางการ (Formal)' }.get(formality, 'ปกติ (Normal)') return f"""# Brand Voice & Messaging **Industry:** {industry} **Target Audience:** {audience} **Default Formality:** {formality_th} **Created:** {datetime.now().strftime('%Y-%m-%d')} --- ## Voice Pillars ### 1. เป็นกันเอง (Friendly) - **What it means**: พูดเหมือนเพื่อนช่วยเพื่อน ไม่ทางการเกินไป - **Example**: "มาเริ่มกันเลย! ไม่ต้องรอให้พร้อม 100%" - **Avoid**: ภาษาทางการแบบเอกสารราชการ ### 2. น่าเชื่อถือ (Trustworthy) - **What it means**: ให้ข้อมูลที่ถูกต้อง มีหลักฐานรองรับ - **Example**: "จากการทดสอบ เราพบว่า..." - **Avoid**: อ้างอิงไม่มีแหล่งที่มา ### 3. มีประโยชน์ (Helpful) - **What it means**: มุ่งให้ค่ากับผู้อ่าน ช่วยแก้ปัญหา - **Example**: "ทำตามขั้นตอนนี้ คุณจะได้..." - **Avoid**: ขายของเกินไปโดยไม่ให้คุณค่า --- ## Tone Guidelines ### General Tone พูดแบบเพื่อนที่หวังดี อธิบายเรื่องยากให้ง่าย ### By Content Type **How-To Guides**: - ใช้ภาษาง่ายๆ - เป็นขั้นตอน - มีตัวอย่างประกอบ **Review Content**: - เปรียบเทียบตรงไปตรงมา - มีข้อมูลสนับสนุน - บอกข้อดีข้อเสีย **News/Updates**: - กระชับ ได้ใจความ - เน้นข้อมูลสำคัญ - อัปเดตทันทีที่มีข้อมูลใหม่ --- ## Formality Level **Default**: {formality_th} **Social Media**: กันเอง (Casual) - ใช้คำฟุ่มเฟือยได้บ้าง **Blog**: ปกติ (Normal) - อ่านง่ายแต่ยังคงความน่าเชื่อถือ **Product Pages**: ปกติถึงเป็นทางการเล็กน้อย - ให้ความน่าเชื่อถือ --- ## Messaging Framework ### Core Messages 1. **แก้ปัญหาจริง**: เน้นแก้ปัญหาที่ลูกค้าเจอจริง 2. **ไม่ซับซ้อน**: อธิบายเรื่องยากให้ง่าย 3. **น่าเชื่อถือ**: มีหลักฐาน ข้อมูลรองรับ ### Value Propositions **For Beginners**: เริ่มต้นง่าย ไม่ต้องมีพื้นฐานก็ทำได้ **For Professionals**: เครื่องมือครบ จบในที่เดียว --- ## Writing Examples ### Excellent Voice ✅ "มาเริ่ม podcast กันเลย! ไม่ต้องรอให้พร้อม 100% แค่มีไอเดียดีๆ กับไมค์หนึ่งอัน คุณก็เริ่มต้นได้แล้ว ส่วนเรื่องเทคนิคที่เหลือ เราช่วยคุณเอง" **Why this works**: - เป็นกันเอง - ให้กำลังใจ - ไม่ข่มขู่ด้วยความยาก ### Not Our Voice ❌ "การดำเนินการสร้าง podcast จำเป็นต้องมีการเตรียมการอย่างรอบคอบและใช้อุปกรณ์ที่มีคุณภาพสูง" **Why this fails**: - เป็นทางการเกินไป - ดูน่ากลัว - ไม่เป็นมิตร --- **Last Updated:** {datetime.now().strftime('%Y-%m-%d')} """ def _generate_target_keywords(self, industry: str) -> str: """Generate target-keywords.md template""" return f"""# Target Keywords **Industry:** {industry} **Created:** {datetime.now().strftime('%Y-%m-%d')} --- ## Primary Keyword Clusters ### Cluster 1: [Main Topic] **Intent:** Commercial Investigation **Keywords (Thai)**: - [Keyword 1] - [Keyword 2] - [Keyword 3] **Keywords (English)**: - [Keyword 1] - [Keyword 2] - [Keyword 3] **Search Volume:** TBD (research needed) **Difficulty:** Medium --- ### Cluster 2: [Secondary Topic] [Same structure] --- ## Keyword Mapping | Keyword | Intent | Priority | Target URL | |---------|--------|----------|------------| | [keyword] | Commercial | High | /page | | [keyword] | Informational | Medium | /blog | --- **Notes:** - Update keyword data from GSC monthly - Add new clusters as business expands - Track ranking performance **Last Updated:** {datetime.now().strftime('%Y-%m-%d')} """ def _generate_seo_guidelines(self) -> str: """Generate seo-guidelines.md""" return f"""# SEO Guidelines (Thai-Specific) **Created:** {datetime.now().strftime('%Y-%m-%d')} --- ## Content Requirements ### Word Count - **Thai:** 1,500-3,000 words - **English:** 2,000-3,000 words ### Keyword Density - **Thai:** 1.0-1.5% - **English:** 1.5-2.0% ### Readability - **Thai Grade Level:** ม.6-ม.12 - **Avg Sentence Length:** 15-25 words (Thai) - **Formality:** Auto-detect from brand-voice.md --- ## Meta Elements ### Title Tag - **Length:** 50-60 characters - **Must include:** Primary keyword - **Format:** [Keyword]: [Benefit] | [Brand] ### Meta Description - **Length:** 150-160 characters - **Must include:** Keyword + CTA - **Format:** [Problem]? [Solution]. [CTA]. ### URL Slug - **Format:** lowercase-with-hyphens - **Thai:** Keep Thai or use transliteration - **Max:** 5 words --- ## Content Structure ### Headings - **H1:** 1 per page, includes keyword - **H2:** 4-7 per article - **H3:** As needed for subsections ### Internal Links - **Minimum:** 3 per article - **Maximum:** 7 per article - **Anchor text:** Descriptive with keywords ### External Links - **Minimum:** 2 per article - **Authority sources only** - **No competitor links** --- ## Images ### Requirements - **Alt text:** Descriptive with keywords - **File names:** descriptive-name.jpg - **Compression:** WebP preferred - **Size:** Optimized for web --- ## Quality Checklist Before publishing: - [ ] Keyword in H1 - [ ] Keyword in first 100 words - [ ] Keyword in 2+ H2s - [ ] Keyword density 1.0-1.5% (Thai) - [ ] 3-5 internal links - [ ] 2-3 external authority links - [ ] Meta title 50-60 chars - [ ] Meta description 150-160 chars - [ ] Images have alt text - [ ] Readability checked --- **Last Updated:** {datetime.now().strftime('%Y-%m-%d')} """ def _generate_style_guide(self) -> str: """Generate style-guide.md""" return f"""# Writing Style Guide **Created:** {datetime.now().strftime('%Y-%m-%d')} --- ## General Principles 1. **Clear over clever** - ความชัดเจนสำคัญกว่าการเล่นคำ 2. **Helpful over promotional** - ให้ค่ามากกว่าขาย 3. **Conversational over formal** - พูดคุยมากกว่าทางการ --- ## Sentence Structure ### Thai Sentences - **Average:** 15-25 words - **Active voice:** 80%+ - **Short paragraphs:** 2-4 sentences ### Formatting - **Use bullets:** For lists of 3+ items - **Use bold:** For key concepts - **Use white space:** Generously --- ## Word Choice ### Use This, Not That | Say This | Not That | |----------|----------| | เริ่มเลย | ดำเนินการเริ่มต้น | | ง่ายมาก | ไม่มีความซับซ้อน whatsoever | | ช่วยคุณ | ให้ความช่วยเหลือแก่ท่าน | --- ## Examples ### Good Introduction "คุณกำลังมองหาวิธีเริ่มต้น podcast ใช่ไหม? บทความนี้จะบอกทุกอย่างที่ต้องรู้ ตั้งแต่การเลือกอุปกรณ์จนถึงการเผยแพร่" **Why it works:** - ตรงประเด็น - บอกสิ่งที่ผู้อ่านจะได้ - อ่านเข้าใจง่าย --- ## Thai-Specific Guidelines ### Particles - Use ครับ/ค่ะ appropriately - Don't overuse นะ, จ้ะ in formal content - Match formality level to content type ### Transliteration - Use consistent Thai spelling for English terms - Example: "podcast" = "พ็อดคาสท์" (not พอดแคสต์, พ็อดคาสต์) --- **Last Updated:** {datetime.now().strftime('%Y-%m-%d')} """ def main(): """Main entry point""" parser = argparse.ArgumentParser( description='Manage per-project context files' ) parser.add_argument( '--action', choices=['create', 'analyze', 'update-keywords'], default='create', help='Action to perform' ) parser.add_argument( '--create', action='store_true', help='Create context files (shortcut for --action create)' ) parser.add_argument( '--project', '-p', required=True, help='Path to project folder' ) parser.add_argument( '--industry', '-i', default='general', help='Industry (for create action)' ) parser.add_argument( '--audience', '-a', default='Thai audience', help='Target audience (for create action)' ) parser.add_argument( '--formality', '-f', choices=['casual', 'normal', 'formal'], default='normal', help='Formality level (for create action)' ) args = parser.parse_args() # Handle --create shortcut if args.create: args.action = 'create' # Initialize manager print(f"\n📝 Context Manager") print(f"Project: {args.project}\n") manager = ContextManager(args.project) if args.action == 'create': print(f"Creating context files...") print(f"Industry: {args.industry}") print(f"Audience: {args.audience}") print(f"Formality: {args.formality}\n") created = manager.create_context(args.industry, args.audience, args.formality) print(f"\n✅ Context created successfully!") print(f"\n📁 Created files:") for filename, path in created.items(): print(f" ✓ {filename}") print(f"\n📍 Location: {manager.context_path}") print(f"\nNext steps:") print(f" 1. Customize brand-voice.md with your actual voice") print(f" 2. Add target keywords based on your research") print(f" 3. Configure analytics in data-services.json") print() elif args.action == 'analyze': print("Content analysis not yet implemented.") print("This will analyze existing content and update context files.") print() elif args.action == 'update-keywords': print("Keyword update not yet implemented.") print("This will update keywords from GSC data.") print() if __name__ == '__main__': main()