Skill: theshadowrose/faq-forge (version 1.0.1)

--- name: "FAQ Forge Customer Knowledge Base Builder" description: "Build, maintain, and publish professional FAQ documentation. Create, organize, and publish customer-facing knowledge bases. Reduce support burden by answering common questions before they're asked." author: "@TheShadowRose" version: "1.0.0" tags: ["faq", "documentation", "knowledge-base", "support", "customer-service", "publishing"] license: "MIT" --- # FAQ Forge Customer Knowledge Base Builder Build, maintain, and publish professional FAQ documentation. Create, organize, and publish customer-facing knowledge bases. Reduce support burden by answering common questions before they're asked. --- **Build, maintain, and publish professional customer FAQ documentation** FAQ Forge is a complete FAQ management system that helps you create, organize, and publish customer-facing knowledge bases. Reduce support burden by answering common questions before they're asked. **Author:** Shadow Rose **License:** MIT **Quality:** quality-verified --- ## What It Does - ✅ **Manage Q&A pairs** with categories, tags, and priorities - 🔍 **Search** across all questions and answers - 📊 **Track** which questions are viewed most (analytics-ready) - 🌐 **Publish** to professional HTML, Markdown, or plain text - 📥 **Import** from existing markdown documentation - 🚀 **Templates** for common business types (SaaS, e-commerce, freelance, digital products) - 🔗 **Link** related questions together - 💬 **Feedback** system to mark questions needing improvement - 🏷️ **Multi-product** support (separate FAQ sections per product) --- ## Quick Start ### 1. Install (No Dependencies!) ```bash # Clone or download FAQ Forge cd faq-forge # Make scripts executable (optional) chmod +x *.py ``` **That's it!** FAQ Forge uses only Python standard library—no pip installs required. ### 2. Create Your First FAQ **Option A: Start from a template** ```bash # See available templates python faq_templates.py list # Apply a template (digital products, SaaS, freelance, or e-commerce) python faq_templates.py apply digital-products ``` **Option B: Add questions manually** ```bash # Add a question python faq_forge.py add \ "How do I reset my password?" \ "Click 'Forgot Password' on the login page and follow the email instructions." \ --category "Account Management" \ --tags "password,account,login" \ --priority "high" ``` ### 3. Publish Your FAQ ```bash # Generate a professional HTML page python faq_publish.py html faq.html # Or markdown python faq_publish.py markdown faq.md # Or plain text python faq_publish.py text faq.txt ``` Open `faq.html` in your browser—you have a complete, searchable FAQ ready to publish! --- ## Core Features ### 📝 FAQ Management **Add questions:** ```bash python faq_forge.py add \ "What payment methods do you accept?" \ "We accept Visa, Mastercard, PayPal, and Apple Pay." \ --category "Billing" \ --tags "payment,billing" \ --priority "high" ``` **Update answers:** ```bash python faq_forge.py update how-do-i-reset-my-password \ --answer "New updated answer with more detail." ``` **Search:** ```bash # Search all questions python faq_forge.py search "password" # Filter by category python faq_forge.py search --category "Billing" # Filter by tags python faq_forge.py search --tags "payment,refund" # Filter by priority python faq_forge.py search --priority "high" # Combine filters python faq_forge.py search "refund" --category "Billing" --priority "high" ``` **List all questions:** ```bash python faq_forge.py list python faq_forge.py list --product "pro-plan" ``` **Delete questions:** ```bash python faq_forge.py delete question-id ``` ### 🔗 Related Questions Link related questions together: ```bash python faq_forge.py relate \ how-do-i-reset-my-password \ i-forgot-my-username ``` Related questions appear as links in the published HTML. ### 💬 Feedback & Improvement Tracking Mark questions that need attention: ```bash # Mark as needing improvement python faq_forge.py feedback question-id --needs-improvement \ --notes "Add more detail about mobile app" # Mark as outdated python faq_forge.py feedback question-id --outdated \ --notes "Update for new payment system" ``` Track which questions need review: ```bash python faq_forge.py stats # Shows "needs_attention" count ``` ### 📊 Analytics & Stats ```bash python faq_forge.py stats ``` See: - Total entries - Breakdown by category and priority - Most viewed questions - Questions needing attention Track popular questions to prioritize improvements! --- ## Publishing ### 🌐 HTML Output Generate a professional, searchable FAQ page: ```bash python faq_publish.py html faq.html \ --title "Customer Support FAQ" \ --product "default" ``` **Features:** - ✅ Client-side search (no server needed) - ✅ Collapsible Q&A sections - ✅ Category navigation - ✅ Priority badges (critical, high) - ✅ Related questions links - ✅ Mobile-responsive design - ✅ Professional gradient theme **Customization options:** - `--no-search` - Disable search box - `--no-toc` - Disable table of contents - `--no-collapse` - Show all answers expanded - `--title "Custom Title"` - Custom page title The HTML is **100% static**—upload to any web server, no backend required! ### 📄 Markdown Output ```bash python faq_publish.py markdown faq.md ``` Perfect for: - GitHub wikis - Documentation sites (GitBook, MkDocs) - README files - Internal docs ### 📝 Plain Text Output ```bash python faq_publish.py text faq.txt ``` Great for: - Email support templates - Print documentation - Plain text knowledge bases --- ## Importing Existing Documentation ### From Markdown Files FAQ Forge can parse existing markdown files and extract Q&A pairs: ```bash # Import single file python faq_import.py markdown existing_faq.md \ --category "Getting Started" \ --product "default" # Import all markdown files from a directory python faq_import.py directory ./docs \ --pattern "*.md" \ --category "Documentation" ``` **Supported formats:** 1. **Q: / A: format** ``` Q: What is your return policy? A: We accept returns within 30 days... ``` 2. **Heading format** ```markdown ### How do I contact support? Send an email to [REDACTED_SECRET]... ``` 3. **Bold question format** ```markdown **Can I use this commercially?** Yes, commercial use is permitted... ``` ### From JSON ```bash # Import from JSON python faq_import.py json faq_backup.json # Export to JSON (for backup or migration) python faq_import.py export backup.json ``` JSON format: ```json [ { "question": "How do I get started?", "answer": "Sign up on our website...", "category": "Getting Started", "tags": ["signup", "account"], "priority": "high", "product": "default" } ] ``` --- ## Templates Start with pre-built FAQs for common business types: ### Available Templates 1. **digital-products** - Digital downloads, courses, ebooks, software 2. **saas** - Software as a Service, web applications 3. **freelance** - Freelance services, consulting, agencies 4. **ecommerce** - Physical products, online stores ### Using Templates ```bash # See all templates python faq_templates.py list # Apply a template python faq_templates.py apply saas ``` Each template includes 6-8 industry-standard questions covering: - Getting started - Pricing and billing - Common issues - Policies - Support **Customize after applying!** Templates give you a head start—edit answers to match your specific business. --- ## Multi-Product Support Manage FAQs for multiple products or plans: ```bash # Add questions for specific products python faq_forge.py add \ "What's included in Pro?" \ "Pro includes unlimited users, priority support..." \ --product "pro-plan" python faq_forge.py add \ "What's included in Enterprise?" \ "Enterprise includes dedicated support..." \ --product "enterprise" # Search by product python faq_forge.py search --product "pro-plan" # Publish product-specific FAQs python faq_publish.py html pro_faq.html --product "pro-plan" python faq_publish.py html enterprise_faq.html --product "enterprise" ``` Use the "default" product for questions common to all products. --- ## Configuration See `config_example.json` for reference constants and example values ```python # Categories for your business DEFAULT_CATEGORIES = [ "Getting Started", "Billing", "Troubleshooting", "Features" ] # Products PRODUCTS = { "default": {"name": "General FAQ"}, "pro": {"name": "Pro Plan"}, "enterprise": {"name": "Enterprise"} } # HTML theme color HTML_CONFIG = { "theme_color": "#667eea" # Your brand color } # Business info for footer BUSINESS_INFO = { "name": "Your Business", "support_email": "[REDACTED_SECRET]" } ``` See `config_example.json` for all available options. --- ## Workflows ### New Business Setup 1. Apply a template matching your business type 2. Review and customize each answer 3. Add your specific questions 4. Publish HTML to your website ```bash python faq_templates.py apply saas python faq_forge.py list # Review and update answers... python faq_forge.py update question-id --answer "Custom answer" python faq_publish.py html public/faq.html ``` ### Ongoing Maintenance 1. Track view counts to see popular questions 2. Mark questions needing improvement 3. Update answers regularly 4. Re-publish when ready ```bash python faq_forge.py stats # See what's popular python faq_forge.py search --category "Troubleshooting" python faq_forge.py feedback question-id --needs-improvement python faq_forge.py update question-id --answer "Updated answer" python faq_publish.py html faq.html ``` ### Migrating Existing Docs 1. Import from existing markdown files 2. Review imported questions 3. Organize into categories 4. Publish unified FAQ ```bash python faq_import.py directory ./old-docs --category "Support" python faq_forge.py list # Review, categorize, link related questions... python faq_publish.py html new_faq.html ``` --- ## File Structure ``` faq-forge/ ├── faq_forge.py # Core FAQ management engine ├── faq_publish.py # HTML/Markdown/text publisher ├── faq_import.py # Import from markdown/JSON ├── faq_templates.py # Pre-built FAQ templates ├── config_example.json # Configuration template ├── faq_data.json # Your FAQ database (auto-created) ├── README.md # This file ├── LIMITATIONS.md # Known limitations └── LICENSE # MIT License ``` --- ## Use Cases ### For Product Businesses - Reduce support tickets by 40-60% - Onboard customers faster - Improve SEO (FAQ pages rank well) - Build customer confidence ### For SaaS Companies - Self-serve knowledge base - Reduce churn (answer questions before cancellation) - Support multiple product tiers - Track which features confuse users ### For Freelancers - Answer client questions upfront - Professional presentation - Save time on repetitive questions - Set clear expectations ### For E-commerce - Reduce returns and complaints - Build trust with clear policies - Improve conversion rates - Handle international customers --- ## Best Practices ### Writing Great FAQ Answers 1. **Be specific** - Don't be vague. Give concrete steps. 2. **Be concise** - Get to the point quickly. 3. **Use examples** - Show, don't just tell. 4. **Link related questions** - Help users find what they need. 5. **Update regularly** - Mark outdated answers and refresh them. ### Organizing Your FAQ 1. **Start with critical questions** - Use priority levels. 2. **Group logically** - Use clear categories. 3. **Tag everything** - Makes search more powerful. 4. **Link related questions** - Create a knowledge web. 5. **Track analytics** - See what's working. ### Reducing Support Load 1. **Add high-priority badge** to most-asked questions 2. **Link to FAQ** from support emails 3. **Update based on real support tickets** 4. **Make it searchable** (HTML output includes search) 5. **Keep it current** - Outdated FAQs hurt more than they help --- ## Technical Details - **Language:** Python 3.6+ - **Dependencies:** None (stdlib only) - **Database:** JSON file (human-readable, version-control friendly) - **HTML:** Static, no server required (pure CSS/JS) - **Encoding:** UTF-8 - **Cross-platform:** Works on Windows, macOS, Linux --- ## Roadmap Possible future enhancements (not currently included): - Web-based editor (currently CLI only) - Automatic sync from support tickets - Multi-language support - PDF export - API for dynamic FAQ delivery - Analytics integration (Google Analytics events) This is a complete, production-ready tool as-is. Future versions may add these features based on demand. --- ## Support & Contributing This is a standalone product developed by Shadow Rose. **Questions?** Check `LIMITATIONS.md` for known limitations and workarounds. **Found a bug?** FAQ Forge is provided as-is under the MIT License. **Want to customize?** The code is clean, commented, and designed for modification. All Python stdlib—no dependency hell. --- ## License MIT License - see LICENSE file for details. Copyright (c) 2026 Shadow Rose --- ## Quick Reference ```bash # Add question faq_forge.py add "Question?" "Answer" --category CAT --tags TAG1,TAG2 # Search faq_forge.py search [query] [--category CAT] [--tags TAG1,TAG2] # Update faq_forge.py update ID --question "New Q" --answer "New A" # Delete faq_forge.py delete ID # Link related faq_forge.py relate ID1 ID2 # Mark for review faq_forge.py feedback ID --needs-improvement --notes "Fix this" # Stats faq_forge.py stats # Publish faq_publish.py html output.html faq_publish.py markdown output.md faq_publish.py text output.txt # Import faq_import.py markdown file.md --category CAT faq_import.py directory ./docs faq_import.py json backup.json # Templates faq_templates.py list faq_templates.py apply [digital-products|saas|freelance|ecommerce] ``` --- **Built with FAQ Forge** 🔨 --- ## ⚠️ Disclaimer This software is provided "AS IS", without warranty of any kind, express or implied. **USE AT YOUR OWN RISK.** - The author(s) are NOT liable for any damages, losses, or consequences arising from the use or misuse of this software — including but not limited to financial loss, data loss, security breaches, business interruption, or any indirect/consequential damages. - This software does NOT constitute financial, legal, trading, or professional advice. - Users are solely responsible for evaluating whether this software is suitable for their use case, environment, and risk tolerance. - No guarantee is made regarding accuracy, reliability, completeness, or fitness for any particular purpose. - The author(s) are not responsible for how third parties use, modify, or distribute this software after purchase. By downloading, installing, or using this software, you acknowledge that you have read this disclaimer and agree to use the software entirely at your own risk. **DATA DISCLAIMER:** This software processes and stores data locally on your system. The author(s) are not responsible for data loss, corruption, or unauthorized access resulting from software bugs, system failures, or user error. Always maintain independent backups of important data. This software does not transmit data externally unless explicitly configured by the user. --- ## Support & Links | | | |---|---| | 🐛 **Bug Reports** | [REDACTED_SECRET] | | ☕ **Ko-fi** | [ko-fi.com/theshadowrose](https://ko-fi.com/theshadowrose) | | 🛒 **Gumroad** | [shadowyrose.gumroad.com](https://shadowyrose.gumroad.com) | | 🐦 **Twitter** | [@TheShadowyRose](https://twitter.com/TheShadowyRose) | | 🐙 **GitHub** | [github.com/TheShadowRose](https://github.com/TheShadowRose) | | 🧠 **PromptBase** | [promptbase.com/profile/shadowrose](https://promptbase.com/profile/shadowrose) | *Built with [OpenClaw](https://github.com/openclaw/openclaw) — thank you for making this possible.* --- 🛠️ **Need something custom?** Custom OpenClaw agents & skills starting at $500. If you can describe it, I can build it. → [Hire me on Fiverr](https://www.fiverr.com/s/jjmlZ0v)

path: config_example.json
content: {
  "DATABASE_FILE": "faq_data.json",
  "AUTO_BACKUP": true,
  "BACKUP_DIR": "backups",
  "BACKUP_FREQUENCY": "daily",
  "DEFAULT_PRIORITY": "normal",
  "AUTO_TAG_ENABLED": true,
  "TRACK_VIEW_COUNTS": true,
  "MAX_SLUG_LENGTH": 50,
  "FILE_ENCODING": "utf-8",
  "JSON_INDENT": 2,
  "DEFAULT_TEMPLATE": null,
  "APPLY_TEMPLATE_ON_INIT": false,
  "HTML_CONFIG": {
    "title": "Frequently Asked Questions",
    "show_search": true,
    "show_toc": true,
    "collapsible": true,
    "show_priority_badges": true,
    "show_tags": true,
    "show_related": true,
    "theme_color": "#667eea"
  },
  "MARKDOWN_CONFIG": {
    "title": "Frequently Asked Questions",
    "include_toc": true,
    "include_metadata": true,
    "include_related": true
  },
  "BUSINESS_INFO": {
    "name": "Your Business Name",
    "website": "https://yourbusiness.com",
    "support_email": "[REDACTED_SECRET]",
    "support_link": "https://yourbusiness.com/contact"
  }
}


path: faq_forge.py
content: #!/usr/bin/env python3
"""
FAQ Forge - Main FAQ Management Engine
Author: Shadow Rose
License: MIT
quality-verified

Core FAQ management: add, edit, search, categorize, track updates.
"""

import json
import os
import re
from datetime import datetime
from typing import List, Dict, Optional, Set


class FAQEntry:
    """Single FAQ entry with metadata."""
    
    def __init__(self, question: str, answer: str, category: str = "General",
                 tags: Optional[List[str]] = None, priority: str = "normal",
                 product: str = "default", entry_id: Optional[str] = None):
        self.id = entry_id or self._generate_id(question)
        self.question = question
        self.answer = answer
        self.category = category
        self.tags = tags or []
        self.priority = priority  # low, normal, high, critical
        self.product = product
        self.created = datetime.now().isoformat()
        self.updated = datetime.now().isoformat()
        self.related = []  # List of related question IDs
        self.feedback = {"needs_improvement": False, "outdated": False, "notes": ""}
        self.view_count = 0
        
    def _generate_id(self, question: str) -> str:
        """Generate unique ID from question text."""
        # Simple slug generation
        slug = re.sub(r'[^\w\s-]', '', question.lower())
        slug = re.sub(r'[-\s]+', '-', slug)
        return slug[:50]
    
    def to_dict(self) -> Dict:
        """Convert to dictionary for JSON storage."""
        return {
            "id": self.id,
            "question": self.question,
            "answer": self.answer,
            "category": self.category,
            "tags": self.tags,
            "priority": self.priority,
            "product": self.product,
            "created": self.created,
            "updated": self.updated,
            "related": self.related,
            "feedback": self.feedback,
            "view_count": self.view_count
        }
    
    @classmethod
    def from_dict(cls, data: Dict) -> 'FAQEntry':
        """Create FAQEntry from dictionary."""
        entry = cls(
            question=data["question"],
            answer=data["answer"],
            category=data.get("category", "General"),
            tags=data.get("tags", []),
            priority=data.get("priority", "normal"),
            product=data.get("product", "default"),
            entry_id=data.get("id")
        )
        entry.created = data.get("created", entry.created)
        entry.updated = data.get("updated", entry.updated)
        entry.related = data.get("related", [])
        entry.feedback = data.get("feedback", entry.feedback)
        entry.view_count = data.get("view_count", 0)
        return entry
    
    def update(self, question: Optional[str] = None, answer: Optional[str] = None,
               category: Optional[str] = None, tags: Optional[List[str]] = None,
               priority: Optional[str] = None):
        """Update entry fields and timestamp."""
        if question:
            self.question = question
        if answer:
            self.answer = answer
        if category:
            self.category = category
        if tags is not None:
            self.tags = tags
        if priority:
            self.priority = priority
        self.updated = datetime.now().isoformat()


class FAQDatabase:
    """FAQ database manager."""
    
    def __init__(self, data_file: str = "faq_data.json"):
        self.data_file = data_file
        self.entries: Dict[str, FAQEntry] = {}
        self.load()
    
    def load(self):
        """Load FAQ database from JSON."""
        if os.path.exists(self.data_file):
            try:
                with open(self.data_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.entries = {
                        entry_id: FAQEntry.from_dict(entry_data)
                        for entry_id, entry_data in data.items()
                    }
            except json.JSONDecodeError as e:
                print(f"ERROR: Database file is corrupted (invalid JSON): {e}")
                print(f"Backup your {self.data_file} file and fix the JSON syntax,")
                print(f"or delete it to start fresh.")
                raise
            except Exception as e:
                print(f"Warning: Could not load {self.data_file}: {e}")
                self.entries = {}
    
    def save(self):
        """Save FAQ database to JSON."""
        data = {entry_id: entry.to_dict() for entry_id, entry in self.entries.items()}
        
        # Create backup before overwriting (simple safety net)
        if os.path.exists(self.data_file):
            backup_file = self.data_file + ".backup"
            try:
                import shutil
                shutil.copy2(self.data_file, backup_file)
            except Exception:
                pass  # Backup failure shouldn't block save
        
        # Write with atomic rename for safety
        temp_file = self.data_file + ".tmp"
        with open(temp_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        # Atomic rename (safer than direct overwrite)
        import shutil
        shutil.move(temp_file, self.data_file)
    
    def add(self, entry: FAQEntry) -> str:
        """Add new FAQ entry. Returns entry ID."""
        # Ensure unique ID
        base_id = entry.id
        counter = 1
        while entry.id in self.entries:
            entry.id = f"{base_id}-{counter}"
            counter += 1
        
        self.entries[entry.id] = entry
        self.save()
        return entry.id
    
    def get(self, entry_id: str) -> Optional[FAQEntry]:
        """Get FAQ entry by ID."""
        return self.entries.get(entry_id)
    
    def update(self, entry_id: str, **kwargs) -> bool:
        """Update existing FAQ entry."""
        entry = self.entries.get(entry_id)
        if not entry:
            return False
        entry.update(**kwargs)
        self.save()
        return True
    
    def delete(self, entry_id: str) -> bool:
        """Delete FAQ entry."""
        if entry_id in self.entries:
            del self.entries[entry_id]
            self.save()
            return True
        return False
    
    def search(self, query: str = "", category: Optional[str] = None,
               tags: Optional[List[str]] = None, product: Optional[str] = None,
               priority: Optional[str] = None) -> List[FAQEntry]:
        """Search FAQ entries by multiple criteria."""
        results = []
        query_lower = query.lower()
        
        for entry in self.entries.values():
            # Filter by product
            if product and entry.product != product:
                continue
            
            # Filter by category
            if category and entry.category != category:
                continue
            
            # Filter by priority
            if priority and entry.priority != priority:
                continue
            
            # Filter by tags (entry must have ALL specified tags)
            if tags and not all(tag in entry.tags for tag in tags):
                continue
            
            # Text search in question and answer
            if query:
                if query_lower not in entry.question.lower() and \
                   query_lower not in entry.answer.lower():
                    continue
            
            results.append(entry)
        
        # Sort by priority (critical > high > normal > low), then by updated date
        priority_order = {"critical": 0, "high": 1, "normal": 2, "low": 3}
        results.sort(key=lambda e: (priority_order.get(e.priority, 2), e.updated), reverse=True)
        
        return results
    
    def get_categories(self, product: Optional[str] = None) -> Set[str]:
        """Get all unique categories."""
        categories = set()
        for entry in self.entries.values():
            if product is None or entry.product == product:
                categories.add(entry.category)
        return categories
    
    def get_tags(self, product: Optional[str] = None) -> Set[str]:
        """Get all unique tags."""
        tags = set()
        for entry in self.entries.values():
            if product is None or entry.product == product:
                tags.update(entry.tags)
        return tags
    
    def get_products(self) -> Set[str]:
        """Get all unique products."""
        return {entry.product for entry in self.entries.values()}
    
    def add_related(self, entry_id: str, related_id: str) -> bool:
        """Link two questions as related."""
        entry = self.entries.get(entry_id)
        related_entry = self.entries.get(related_id)
        
        if not entry or not related_entry:
            return False
        
        if related_id not in entry.related:
            entry.related.append(related_id)
        if entry_id not in related_entry.related:
            related_entry.related.append(entry_id)
        
        self.save()
        return True
    
    def set_feedback(self, entry_id: str, needs_improvement: bool = False,
                     outdated: bool = False, notes: str = "") -> bool:
        """Set customer feedback flags on an entry."""
        entry = self.entries.get(entry_id)
        if not entry:
            return False
        
        entry.feedback = {
            "needs_improvement": needs_improvement,
            "outdated": outdated,
            "notes": notes
        }
        entry.updated = datetime.now().isoformat()
        self.save()
        return True
    
    def increment_view(self, entry_id: str) -> bool:
        """Increment view count for analytics."""
        entry = self.entries.get(entry_id)
        if not entry:
            return False
        entry.view_count += 1
        self.save()
        return True
    
    def get_stats(self, product: Optional[str] = None) -> Dict:
        """Get database statistics."""
        entries = [e for e in self.entries.values() 
                   if product is None or e.product == product]
        
        if not entries:
            return {
                "total_entries": 0,
                "by_category": {},
                "by_priority": {},
                "needs_attention": 0,
                "total_views": 0
            }
        
        stats = {
            "total_entries": len(entries),
            "by_category": {},
            "by_priority": {},
            "needs_attention": 0,
            "total_views": sum(e.view_count for e in entries),
            "most_viewed": sorted(entries, key=lambda e: e.view_count, reverse=True)[:5]
        }
        
        for entry in entries:
            # Count by category
            stats["by_category"][entry.category] = \
                stats["by_category"].get(entry.category, 0) + 1
            
            # Count by priority
            stats["by_priority"][entry.priority] = \
                stats["by_priority"].get(entry.priority, 0) + 1
            
            # Count entries needing attention
            if entry.feedback.get("needs_improvement") or entry.feedback.get("outdated"):
                stats["needs_attention"] += 1
        
        return stats


def main():
    """Command-line interface for FAQ management."""
    import sys
    
    if len(sys.argv) < 2:
        print("FAQ Forge - FAQ Management Engine")
        print("\nUsage:")
        print("  faq_forge.py add <question> <answer> [--category CAT] [--tags TAG1,TAG2] [--priority LEVEL] [--product PROD]")
        print("  faq_forge.py update <id> [--question Q] [--answer A] [--category CAT] [--tags TAG1,TAG2] [--priority LEVEL]")
        print("  faq_forge.py delete <id>")
        print("  faq_forge.py search [query] [--category CAT] [--tags TAG1,TAG2] [--product PROD] [--priority LEVEL]")
        print("  faq_forge.py list [--product PROD]")
        print("  faq_forge.py stats [--product PROD]")
        print("  faq_forge.py relate <id1> <id2>")
        print("  faq_forge.py feedback <id> [--needs-improvement] [--outdated] [--notes TEXT]")
        print("\nPriority levels: low, normal, high, critical")
        return
    
    db = FAQDatabase()
    command = sys.argv[1]
    
    if command == "add":
        if len(sys.argv) < 4:
            print("Error: add requires <question> <answer>")
            return
        
        question = sys.argv[2]
        answer = sys.argv[3]
        
        # Parse optional arguments
        category = "General"
        tags = []
        priority = "normal"
        product = "default"
        
        for i in range(4, len(sys.argv)):
            if sys.argv[i] == "--category" and i + 1 < len(sys.argv):
                category = sys.argv[i + 1]
            elif sys.argv[i] == "--tags" and i + 1 < len(sys.argv):
                tags = sys.argv[i + 1].split(",")
            elif sys.argv[i] == "--priority" and i + 1 < len(sys.argv):
                priority = sys.argv[i + 1]
            elif sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
        
        entry = FAQEntry(question, answer, category, tags, priority, product)
        entry_id = db.add(entry)
        print(f"✓ Added entry: {entry_id}")
    
    elif command == "update":
        if len(sys.argv) < 3:
            print("Error: update requires <id>")
            return
        
        entry_id = sys.argv[2]
        kwargs = {}
        
        for i in range(3, len(sys.argv)):
            if sys.argv[i] == "--question" and i + 1 < len(sys.argv):
                kwargs["question"] = sys.argv[i + 1]
            elif sys.argv[i] == "--answer" and i + 1 < len(sys.argv):
                kwargs["answer"] = sys.argv[i + 1]
            elif sys.argv[i] == "--category" and i + 1 < len(sys.argv):
                kwargs["category"] = sys.argv[i + 1]
            elif sys.argv[i] == "--tags" and i + 1 < len(sys.argv):
                kwargs["tags"] = sys.argv[i + 1].split(",")
            elif sys.argv[i] == "--priority" and i + 1 < len(sys.argv):
                kwargs["priority"] = sys.argv[i + 1]
        
        if db.update(entry_id, **kwargs):
            print(f"✓ Updated entry: {entry_id}")
        else:
            print(f"✗ Entry not found: {entry_id}")
    
    elif command == "delete":
        if len(sys.argv) < 3:
            print("Error: delete requires <id>")
            return
        
        entry_id = sys.argv[2]
        if db.delete(entry_id):
            print(f"✓ Deleted entry: {entry_id}")
        else:
            print(f"✗ Entry not found: {entry_id}")
    
    elif command == "search":
        query = sys.argv[2] if len(sys.argv) > 2 and not sys.argv[2].startswith("--") else ""
        
        category = None
        tags = None
        product = None
        priority = None
        
        for i in range(2, len(sys.argv)):
            if sys.argv[i] == "--category" and i + 1 < len(sys.argv):
                category = sys.argv[i + 1]
            elif sys.argv[i] == "--tags" and i + 1 < len(sys.argv):
                tags = sys.argv[i + 1].split(",")
            elif sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
            elif sys.argv[i] == "--priority" and i + 1 < len(sys.argv):
                priority = sys.argv[i + 1]
        
        results = db.search(query, category, tags, product, priority)
        print(f"\nFound {len(results)} results:\n")
        
        for entry in results:
            print(f"[{entry.id}] {entry.question}")
            print(f"  Category: {entry.category} | Priority: {entry.priority} | Product: {entry.product}")
            if entry.tags:
                print(f"  Tags: {', '.join(entry.tags)}")
            print(f"  Answer: {entry.answer[:100]}{'...' if len(entry.answer) > 100 else ''}")
            print()
    
    elif command == "list":
        product = None
        for i in range(2, len(sys.argv)):
            if sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
        
        entries = db.search(product=product)
        print(f"\nAll FAQ entries ({len(entries)}):\n")
        
        for entry in entries:
            print(f"[{entry.id}] {entry.question}")
            print(f"  Category: {entry.category} | Priority: {entry.priority}")
            print()
    
    elif command == "stats":
        product = None
        for i in range(2, len(sys.argv)):
            if sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
        
        stats = db.get_stats(product)
        print("\n=== FAQ Statistics ===\n")
        print(f"Total entries: {stats['total_entries']}")
        print(f"Total views: {stats['total_views']}")
        print(f"Needs attention: {stats['needs_attention']}")
        
        print("\nBy category:")
        for cat, count in sorted(stats['by_category'].items()):
            print(f"  {cat}: {count}")
        
        print("\nBy priority:")
        for pri, count in sorted(stats['by_priority'].items()):
            print(f"  {pri}: {count}")
        
        if stats['most_viewed']:
            print("\nMost viewed:")
            for entry in stats['most_viewed']:
                print(f"  {entry.view_count} views - {entry.question}")
    
    elif command == "relate":
        if len(sys.argv) < 4:
            print("Error: relate requires <id1> <id2>")
            return
        
        id1 = sys.argv[2]
        id2 = sys.argv[3]
        
        if db.add_related(id1, id2):
            print(f"✓ Linked {id1} ↔ {id2}")
        else:
            print("✗ One or both entries not found")
    
    elif command == "feedback":
        if len(sys.argv) < 3:
            print("Error: feedback requires <id>")
            return
        
        entry_id = sys.argv[2]
        needs_improvement = "--needs-improvement" in sys.argv
        outdated = "--outdated" in sys.argv
        
        notes = ""
        for i in range(3, len(sys.argv)):
            if sys.argv[i] == "--notes" and i + 1 < len(sys.argv):
                notes = sys.argv[i + 1]
        
        if db.set_feedback(entry_id, needs_improvement, outdated, notes):
            print(f"✓ Feedback updated for {entry_id}")
        else:
            print(f"✗ Entry not found: {entry_id}")
    
    else:
        print(f"Unknown command: {command}")


if __name__ == "__main__":
    main()


path: faq_import.py
content: #!/usr/bin/env python3
"""
FAQ Forge - Import Tool
Author: Shadow Rose
License: MIT
quality-verified

Import Q&A pairs from existing markdown documentation.
Supports common FAQ markdown patterns and structured formats.
"""

import re
import os
from typing import List, Tuple, Optional
from faq_forge import FAQDatabase, FAQEntry


class FAQImporter:
    """Import FAQ entries from markdown files."""
    
    def __init__(self, db: FAQDatabase):
        self.db = db
    
    def import_from_markdown(self, file_path: str, category: str = "General",
                            product: str = "default", auto_tag: bool = True) -> int:
        """
        Import Q&A pairs from markdown file.
        Supports multiple markdown FAQ formats.
        Returns number of entries imported.
        """
        if not os.path.exists(file_path):
            print(f"Error: File not found: {file_path}")
            return 0
        
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        # Try different parsing strategies
        entries = []
        
        # Strategy 1: Q: / A: format
        qa_pairs = self._parse_qa_format(content)
        if qa_pairs:
            entries.extend(qa_pairs)
        
        # Strategy 2: Heading + paragraph format (e.g., ### Question\nAnswer paragraph)
        if not entries:
            entries = self._parse_heading_format(content)
        
        # Strategy 3: Bold question + answer format (**Question**\nAnswer)
        if not entries:
            entries = self._parse_bold_format(content)
        
        if not entries:
            print(f"Warning: No Q&A pairs found in {file_path}")
            return 0
        
        # Import entries
        imported = 0
        for question, answer in entries:
            # Auto-tag based on content
            tags = []
            if auto_tag:
                tags = self._auto_generate_tags(question, answer)
            
            entry = FAQEntry(
                question=question.strip(),
                answer=answer.strip(),
                category=category,
                tags=tags,
                priority="normal",
                product=product
            )
            
            self.db.add(entry)
            imported += 1
        
        print(f"✓ Imported {imported} FAQ entries from {file_path}")
        return imported
    
    def _parse_qa_format(self, content: str) -> List[Tuple[str, str]]:
        """
        Parse Q: / A: format:
        Q: What is your return policy?
        A: We accept returns within 30 days...
        """
        entries = []
        
        # Find Q:/A: pairs (case insensitive)
        pattern = r'(?:^|\n)Q:\s*(.+?)(?:\n|\r\n)A:\s*(.+?)(?=\n\s*Q:|$)'
        matches = re.findall(pattern, content, re.DOTALL | re.IGNORECASE)
        
        for question, answer in matches:
            entries.append((question.strip(), answer.strip()))
        
        return entries
    
    def _parse_heading_format(self, content: str) -> List[Tuple[str, str]]:
        """
        Parse heading + paragraph format:
        ### How do I reset my password?
        Click on "Forgot Password" on the login page...
        """
        entries = []
        
        # Split by headings (### or ##)
        sections = re.split(r'\n#{2,3}\s+', content)
        
        for section in sections[1:]:  # Skip first section (before any heading)
            # Split heading from content
            lines = section.split('\n', 1)
            if len(lines) < 2:
                continue
            
            question = lines[0].strip()
            answer = lines[1].strip()
            
            # Filter out headings that are clearly section titles, not questions
            if len(question) > 10 and len(answer) > 10:
                # Clean up answer (remove extra whitespace, keep paragraphs)
                answer = re.sub(r'\n{3,}', '\n\n', answer)
                entries.append((question, answer))
        
        return entries
    
    def _parse_bold_format(self, content: str) -> List[Tuple[str, str]]:
        """
        Parse bold question format:
        **What payment methods do you accept?**
        We accept Visa, Mastercard, PayPal...
        """
        entries = []
        
        # Find bold text followed by content
        pattern = r'\*\*(.+?)\*\*\s*\n\s*(.+?)(?=\n\s*\*\*|$)'
        matches = re.findall(pattern, content, re.DOTALL)
        
        for question, answer in matches:
            question = question.strip()
            answer = answer.strip()
            
            # Only accept if it looks like a question-answer pair
            if len(question) > 10 and len(answer) > 10:
                entries.append((question, answer))
        
        return entries
    
    def _auto_generate_tags(self, question: str, answer: str) -> List[str]:
        """Auto-generate tags based on content keywords."""
        tags = []
        text = (question + " " + answer).lower()
        
        # Common business keywords
        keyword_map = {
            "payment": ["payment", "pay", "credit card", "paypal", "billing"],
            "shipping": ["ship", "deliver", "freight", "tracking"],
            "refund": ["refund", "return", "money back"],
            "account": ["account", "login", "password", "profile"],
            "pricing": ["price", "cost", "discount", "coupon"],
            "support": ["support", "help", "contact", "assistance"],
            "product": ["product", "item", "feature"],
            "subscription": ["subscription", "subscribe", "plan", "membership"],
            "security": ["security", "safe", "encrypt", "privacy"],
            "technical": ["technical", "bug", "error", "issue"]
        }
        
        for tag, keywords in keyword_map.items():
            if any(keyword in text for keyword in keywords):
                tags.append(tag)
        
        return tags[:5]  # Limit to 5 tags
    
    def import_from_directory(self, dir_path: str, pattern: str = "*.md",
                             category: str = "General", product: str = "default") -> int:
        """Import all markdown files from a directory."""
        import glob
        
        if not os.path.isdir(dir_path):
            print(f"Error: Directory not found: {dir_path}")
            return 0
        
        # Find all markdown files
        search_pattern = os.path.join(dir_path, pattern)
        files = glob.glob(search_pattern)
        
        if not files:
            print(f"No files matching {pattern} found in {dir_path}")
            return 0
        
        total_imported = 0
        for file_path in files:
            print(f"\nImporting from {os.path.basename(file_path)}...")
            imported = self.import_from_markdown(file_path, category, product)
            total_imported += imported
        
        print(f"\n✓ Total: {total_imported} entries imported from {len(files)} files")
        return total_imported
    
    def import_from_json(self, file_path: str) -> int:
        """
        Import from JSON format:
        [
          {
            "question": "...",
            "answer": "...",
            "category": "...",
            "tags": ["..."],
            "priority": "normal",
            "product": "default"
          }
        ]
        """
        import json
        
        if not os.path.exists(file_path):
            print(f"Error: File not found: {file_path}")
            return 0
        
        with open(file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        if not isinstance(data, list):
            print("Error: JSON must be an array of FAQ objects")
            return 0
        
        imported = 0
        for item in data:
            if not isinstance(item, dict) or "question" not in item or "answer" not in item:
                print(f"Warning: Skipping invalid entry: {item}")
                continue
            
            entry = FAQEntry(
                question=item["question"],
                answer=item["answer"],
                category=item.get("category", "General"),
                tags=item.get("tags", []),
                priority=item.get("priority", "normal"),
                product=item.get("product", "default")
            )
            
            self.db.add(entry)
            imported += 1
        
        print(f"✓ Imported {imported} FAQ entries from {file_path}")
        return imported
    
    def export_to_json(self, file_path: str, product: Optional[str] = None):
        """Export FAQ database to JSON format for backup/migration."""
        import json
        
        entries = self.db.search(product=product)
        
        data = []
        for entry in entries:
            data.append({
                "question": entry.question,
                "answer": entry.answer,
                "category": entry.category,
                "tags": entry.tags,
                "priority": entry.priority,
                "product": entry.product
            })
        
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        print(f"✓ Exported {len(data)} entries to {file_path}")


def main():
    """Command-line interface for FAQ import."""
    import sys
    
    if len(sys.argv) < 2:
        print("FAQ Forge Importer")
        print("\nUsage:")
        print("  faq_import.py markdown <file.md> [--category CAT] [--product PROD] [--no-auto-tag]")
        print("  faq_import.py directory <dir> [--pattern *.md] [--category CAT] [--product PROD]")
        print("  faq_import.py json <file.json>")
        print("  faq_import.py export <output.json> [--product PROD]")
        print("\nSupported markdown formats:")
        print("  - Q: / A: format")
        print("  - Heading format (### Question)")
        print("  - Bold question format (**Question**)")
        return
    
    db = FAQDatabase()
    importer = FAQImporter(db)
    
    command = sys.argv[1]
    
    if command == "markdown":
        if len(sys.argv) < 3:
            print("Error: markdown requires <file.md>")
            return
        
        file_path = sys.argv[2]
        category = "General"
        product = "default"
        auto_tag = True
        
        for i in range(3, len(sys.argv)):
            if sys.argv[i] == "--category" and i + 1 < len(sys.argv):
                category = sys.argv[i + 1]
            elif sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
            elif sys.argv[i] == "--no-auto-tag":
                auto_tag = False
        
        importer.import_from_markdown(file_path, category, product, auto_tag)
    
    elif command == "directory":
        if len(sys.argv) < 3:
            print("Error: directory requires <dir>")
            return
        
        dir_path = sys.argv[2]
        pattern = "*.md"
        category = "General"
        product = "default"
        
        for i in range(3, len(sys.argv)):
            if sys.argv[i] == "--pattern" and i + 1 < len(sys.argv):
                pattern = sys.argv[i + 1]
            elif sys.argv[i] == "--category" and i + 1 < len(sys.argv):
                category = sys.argv[i + 1]
            elif sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
        
        importer.import_from_directory(dir_path, pattern, category, product)
    
    elif command == "json":
        if len(sys.argv) < 3:
            print("Error: json requires <file.json>")
            return
        
        file_path = sys.argv[2]
        importer.import_from_json(file_path)
    
    elif command == "export":
        if len(sys.argv) < 3:
            print("Error: export requires <output.json>")
            return
        
        output_file = sys.argv[2]
        product = None
        
        for i in range(3, len(sys.argv)):
            if sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
        
        importer.export_to_json(output_file, product)
    
    else:
        print(f"Unknown command: {command}")


if __name__ == "__main__":
    main()


path: faq_publish.py
content: #!/usr/bin/env python3
"""
FAQ Forge - Publisher
Author: Shadow Rose
License: MIT
quality-verified

Generate publishable FAQ documentation in HTML, Markdown, and plain text.
Professional templates with search, navigation, and collapsible sections.
"""

import json
import os
from datetime import datetime
from typing import List, Optional
from faq_forge import FAQDatabase, FAQEntry


class FAQPublisher:
    """Generate FAQ documentation in multiple formats."""
    
    def __init__(self, db: FAQDatabase):
        self.db = db
    
    def publish_html(self, output_file: str, product: Optional[str] = None,
                     title: str = "Frequently Asked Questions",
                     show_search: bool = True, collapsible: bool = True,
                     show_toc: bool = True):
        """
        Generate professional HTML FAQ page with search and navigation.
        Static page - no server required.
        """
        entries = self.db.search(product=product)
        
        if not entries:
            print("Warning: No FAQ entries found")
            return
        
        # Group by category
        by_category = {}
        for entry in entries:
            if entry.category not in by_category:
                by_category[entry.category] = []
            by_category[entry.category].append(entry)
        
        # Generate HTML
        html = f"""<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{title}</title>
    <style>
        * {{
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }}
        
        body {{
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            background: #f5f5f5;
            padding: 20px;
        }}
        
        .container {{
            max-width: 1000px;
            margin: 0 auto;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            overflow: hidden;
        }}
        
        header {{
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 40px 30px;
            text-align: center;
        }}
        
        header h1 {{
            font-size: 2.5em;
            margin-bottom: 10px;
        }}
        
        header p {{
            font-size: 1.1em;
            opacity: 0.9;
        }}
        
        .search-box {{
            padding: 20px 30px;
            background: #f9f9f9;
            border-bottom: 1px solid #e0e0e0;
        }}
        
        .search-box input {{
            width: 100%;
            padding: 12px 20px;
            font-size: 16px;
            border: 2px solid #ddd;
            border-radius: 25px;
            outline: none;
            transition: border-color 0.3s;
        }}
        
        .search-box input:focus {{
            border-color: #667eea;
        }}
        
        .toc {{
            padding: 20px 30px;
            background: #fafafa;
            border-bottom: 1px solid #e0e0e0;
        }}
        
        .toc h2 {{
            font-size: 1.3em;
            margin-bottom: 15px;
            color: #667eea;
        }}
        
        .toc ul {{
            list-style: none;
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }}
        
        .toc li a {{
            display: inline-block;
            padding: 8px 16px;
            background: white;
            border: 1px solid #ddd;
            border-radius: 20px;
            color: #667eea;
            text-decoration: none;
            transition: all 0.3s;
        }}
        
        .toc li a:hover {{
            background: #667eea;
            color: white;
            border-color: #667eea;
        }}
        
        .content {{
            padding: 30px;
        }}
        
        .category {{
            margin-bottom: 40px;
        }}
        
        .category-title {{
            font-size: 1.8em;
            color: #667eea;
            margin-bottom: 20px;
            padding-bottom: 10px;
            border-bottom: 3px solid #667eea;
        }}
        
        .faq-item {{
            margin-bottom: 20px;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            overflow: hidden;
            transition: box-shadow 0.3s;
        }}
        
        .faq-item:hover {{
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }}
        
        .faq-question {{
            padding: 18px 20px;
            background: #f9f9f9;
            cursor: pointer;
            font-weight: 600;
            font-size: 1.1em;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: background 0.3s;
        }}
        
        .faq-question:hover {{
            background: #f0f0f0;
        }}
        
        .faq-question .icon {{
            font-size: 1.2em;
            transition: transform 0.3s;
        }}
        
        .faq-item.active .faq-question .icon {{
            transform: rotate(180deg);
        }}
        
        .faq-answer {{
            padding: 20px;
            background: white;
            display: none;
            line-height: 1.8;
        }}
        
        .faq-item.active .faq-answer {{
            display: block;
        }}
        
        .faq-meta {{
            margin-top: 15px;
            padding-top: 15px;
            border-top: 1px solid #f0f0f0;
            font-size: 0.9em;
            color: #666;
        }}
        
        .tag {{
            display: inline-block;
            padding: 4px 10px;
            background: #e8eaf6;
            color: #5c6bc0;
            border-radius: 12px;
            font-size: 0.85em;
            margin-right: 5px;
            margin-top: 5px;
        }}
        
        .priority-badge {{
            display: inline-block;
            padding: 4px 10px;
            border-radius: 12px;
            font-size: 0.85em;
            font-weight: 600;
            margin-left: 10px;
        }}
        
        .priority-critical {{
            background: #ffebee;
            color: #c62828;
        }}
        
        .priority-high {{
            background: #fff3e0;
            color: #e65100;
        }}
        
        .priority-normal {{
            background: #e8f5e9;
            color: #2e7d32;
        }}
        
        .priority-low {{
            background: #f5f5f5;
            color: #757575;
        }}
        
        .related-questions {{
            margin-top: 15px;
            padding: 15px;
            background: #f5f7fa;
            border-radius: 6px;
        }}
        
        .related-questions h4 {{
            margin-bottom: 10px;
            color: #667eea;
            font-size: 0.95em;
        }}
        
        .related-questions ul {{
            list-style: none;
            padding-left: 0;
        }}
        
        .related-questions li {{
            margin: 5px 0;
        }}
        
        .related-questions a {{
            color: #667eea;
            text-decoration: none;
        }}
        
        .related-questions a:hover {{
            text-decoration: underline;
        }}
        
        footer {{
            padding: 20px 30px;
            text-align: center;
            color: #666;
            font-size: 0.9em;
            background: #f9f9f9;
            border-top: 1px solid #e0e0e0;
        }}
        
        .no-results {{
            text-align: center;
            padding: 40px;
            color: #999;
            font-size: 1.1em;
        }}
        
        @media (max-width: 768px) {{
            header h1 {{
                font-size: 1.8em;
            }}
            
            .category-title {{
                font-size: 1.4em;
            }}
            
            .toc ul {{
                flex-direction: column;
            }}
        }}
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>{title}</h1>
            <p>Find answers to common questions</p>
        </header>
"""
        
        if show_search:
            html += """
        <div class="search-box">
            <input type="text" id="searchInput" placeholder="Search questions and answers..." />
        </div>
"""
        
        if show_toc:
            html += """
        <div class="toc">
            <h2>Categories</h2>
            <ul>
"""
            for category in sorted(by_category.keys()):
                cat_id = category.lower().replace(" ", "-")
                html += f'                <li><a href="#{cat_id}">{category}</a></li>\n'
            
            html += """            </ul>
        </div>
"""
        
        html += """
        <div class="content">
"""
        
        for category in sorted(by_category.keys()):
            cat_id = category.lower().replace(" ", "-")
            html += f'            <div class="category" id="{cat_id}">\n'
            html += f'                <h2 class="category-title">{category}</h2>\n'
            
            for entry in by_category[category]:
                active_class = "" if collapsible else "active"
                html += f'                <div class="faq-item {active_class}" data-id="{entry.id}">\n'
                html += '                    <div class="faq-question">\n'
                html += f'                        <span>{entry.question}</span>\n'
                
                if entry.priority in ["critical", "high"]:
                    html += f'                        <span class="priority-badge priority-{entry.priority}">{entry.priority.upper()}</span>\n'
                
                if collapsible:
                    html += '                        <span class="icon">▼</span>\n'
                
                html += '                    </div>\n'
                html += '                    <div class="faq-answer">\n'
                html += f'                        <p>{entry.answer}</p>\n'
                
                if entry.tags or entry.related:
                    html += '                        <div class="faq-meta">\n'
                    
                    if entry.tags:
                        for tag in entry.tags:
                            html += f'                            <span class="tag">{tag}</span>\n'
                    
                    if entry.related:
                        html += '                            <div class="related-questions">\n'
                        html += '                                <h4>Related Questions:</h4>\n'
                        html += '                                <ul>\n'
                        for related_id in entry.related:
                            related_entry = self.db.get(related_id)
                            if related_entry:
                                html += f'                                    <li><a href="#" data-related="{related_id}">{related_entry.question}</a></li>\n'
                        html += '                                </ul>\n'
                        html += '                            </div>\n'
                    
                    html += '                        </div>\n'
                
                html += '                    </div>\n'
                html += '                </div>\n'
            
            html += '            </div>\n'
        
        html += """        </div>
        
        <div class="no-results" id="noResults" style="display: none;">
            No questions found matching your search.
        </div>
        
        <footer>
            <p>Generated by FAQ Forge &bull; Last updated: """ + datetime.now().strftime("%Y-%m-%d %H:%M") + """</p>
        </footer>
    </div>
    
    <script>
        // Toggle FAQ items
        document.querySelectorAll('.faq-question').forEach(question => {
            question.addEventListener('click', function() {
                const item = this.closest('.faq-item');
                item.classList.toggle('active');
            });
        });
        
        // Search functionality
        const searchInput = document.getElementById('searchInput');
        if (searchInput) {
            searchInput.addEventListener('input', function() {
                const query = this.value.toLowerCase();
                const items = document.querySelectorAll('.faq-item');
                let visibleCount = 0;
                
                items.forEach(item => {
                    const question = item.querySelector('.faq-question span').textContent.toLowerCase();
                    const answer = item.querySelector('.faq-answer p').textContent.toLowerCase();
                    
                    if (question.includes(query) || answer.includes(query)) {
                        item.style.display = '';
                        visibleCount++;
                        if (query.length > 0) {
                            item.classList.add('active');
                        }
                    } else {
                        item.style.display = 'none';
                    }
                });
                
                // Show/hide categories based on visible items
                document.querySelectorAll('.category').forEach(category => {
                    const visibleItems = category.querySelectorAll('.faq-item[style=""]').length;
                    category.style.display = visibleItems > 0 ? '' : 'none';
                });
                
                // Show "no results" message
                document.getElementById('noResults').style.display = visibleCount === 0 ? 'block' : 'none';
                document.querySelector('.content').style.display = visibleCount === 0 ? 'none' : 'block';
            });
        }
        
        // Related question links
        document.querySelectorAll('a[data-related]').forEach(link => {
            link.addEventListener('click', function(e) {
                e.preventDefault();
                const targetId = this.getAttribute('data-related');
                const targetItem = document.querySelector(`.faq-item[data-id="${targetId}"]`);
                
                if (targetItem) {
                    targetItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
                    targetItem.classList.add('active');
                    
                    // Highlight effect
                    targetItem.style.backgroundColor = '#fff9c4';
                    setTimeout(() => {
                        targetItem.style.backgroundColor = '';
                    }, 2000);
                }
            });
        });
    </script>
</body>
</html>
"""
        
        # Create output directory if it doesn't exist
        output_dir = os.path.dirname(output_file)
        if output_dir and not os.path.exists(output_dir):
            os.makedirs(output_dir)
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(html)
        
        print(f"✓ HTML FAQ published to {output_file}")
    
    def publish_markdown(self, output_file: str, product: Optional[str] = None,
                         title: str = "Frequently Asked Questions"):
        """Generate Markdown FAQ documentation."""
        entries = self.db.search(product=product)
        
        if not entries:
            print("Warning: No FAQ entries found")
            return
        
        # Group by category
        by_category = {}
        for entry in entries:
            if entry.category not in by_category:
                by_category[entry.category] = []
            by_category[entry.category].append(entry)
        
        # Generate Markdown
        md = f"# {title}\n\n"
        md += f"*Last updated: {datetime.now().strftime('%Y-%m-%d')}*\n\n"
        
        # Table of contents
        md += "## Table of Contents\n\n"
        for category in sorted(by_category.keys()):
            cat_anchor = category.lower().replace(" ", "-")
            md += f"- [{category}](#{cat_anchor})\n"
        md += "\n---\n\n"
        
        # FAQ entries
        for category in sorted(by_category.keys()):
            cat_anchor = category.lower().replace(" ", "-")
            md += f"## {category}\n\n"
            
            for entry in by_category[category]:
                md += f"### {entry.question}\n\n"
                
                if entry.priority in ["critical", "high"]:
                    md += f"**Priority: {entry.priority.upper()}**\n\n"
                
                md += f"{entry.answer}\n\n"
                
                if entry.tags:
                    md += f"*Tags: {', '.join(entry.tags)}*\n\n"
                
                if entry.related:
                    md += "**Related questions:**\n"
                    for related_id in entry.related:
                        related_entry = self.db.get(related_id)
                        if related_entry:
                            md += f"- {related_entry.question}\n"
                    md += "\n"
                
                md += "---\n\n"
        
        # Create output directory if it doesn't exist
        output_dir = os.path.dirname(output_file)
        if output_dir and not os.path.exists(output_dir):
            os.makedirs(output_dir)
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(md)
        
        print(f"✓ Markdown FAQ published to {output_file}")
    
    def publish_text(self, output_file: str, product: Optional[str] = None,
                     title: str = "Frequently Asked Questions"):
        """Generate plain text FAQ documentation."""
        entries = self.db.search(product=product)
        
        if not entries:
            print("Warning: No FAQ entries found")
            return
        
        # Group by category
        by_category = {}
        for entry in entries:
            if entry.category not in by_category:
                by_category[entry.category] = []
            by_category[entry.category].append(entry)
        
        # Generate text
        txt = f"{title}\n"
        txt += "=" * len(title) + "\n\n"
        txt += f"Last updated: {datetime.now().strftime('%Y-%m-%d')}\n\n"
        
        for category in sorted(by_category.keys()):
            txt += f"\n{category}\n"
            txt += "-" * len(category) + "\n\n"
            
            for i, entry in enumerate(by_category[category], 1):
                txt += f"{i}. {entry.question}\n"
                
                if entry.priority in ["critical", "high"]:
                    txt += f"   [PRIORITY: {entry.priority.upper()}]\n"
                
                txt += f"\n   {entry.answer}\n"
                
                if entry.tags:
                    txt += f"\n   Tags: {', '.join(entry.tags)}\n"
                
                if entry.related:
                    txt += "\n   Related questions:\n"
                    for related_id in entry.related:
                        related_entry = self.db.get(related_id)
                        if related_entry:
                            txt += f"   - {related_entry.question}\n"
                
                txt += "\n"
        
        # Create output directory if it doesn't exist
        output_dir = os.path.dirname(output_file)
        if output_dir and not os.path.exists(output_dir):
            os.makedirs(output_dir)
        
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(txt)
        
        print(f"✓ Text FAQ published to {output_file}")


def main():
    """Command-line interface for FAQ publishing."""
    import sys
    
    if len(sys.argv) < 3:
        print("FAQ Forge Publisher")
        print("\nUsage:")
        print("  faq_publish.py html <output.html> [--product PROD] [--title TITLE]")
        print("  faq_publish.py markdown <output.md> [--product PROD] [--title TITLE]")
        print("  faq_publish.py text <output.txt> [--product PROD] [--title TITLE]")
        print("\nOptions:")
        print("  --product PROD    Filter by product")
        print("  --title TITLE     Custom page title")
        print("  --no-search       Disable search box (HTML only)")
        print("  --no-toc          Disable table of contents")
        print("  --no-collapse     Show all answers expanded (HTML only)")
        return
    
    db = FAQDatabase()
    format_type = sys.argv[1]
    output_file = sys.argv[2]
    
    # Parse options
    product = None
    title = "Frequently Asked Questions"
    show_search = True
    show_toc = True
    collapsible = True
    
    for i in range(3, len(sys.argv)):
        if sys.argv[i] == "--product" and i + 1 < len(sys.argv):
            product = sys.argv[i + 1]
        elif sys.argv[i] == "--title" and i + 1 < len(sys.argv):
            title = sys.argv[i + 1]
        elif sys.argv[i] == "--no-search":
            show_search = False
        elif sys.argv[i] == "--no-toc":
            show_toc = False
        elif sys.argv[i] == "--no-collapse":
            collapsible = False
    
    publisher = FAQPublisher(db)
    
    if format_type == "html":
        publisher.publish_html(output_file, product, title, show_search, collapsible, show_toc)
    elif format_type == "markdown":
        publisher.publish_markdown(output_file, product, title)
    elif format_type == "text":
        publisher.publish_text(output_file, product, title)
    else:
        print(f"Unknown format: {format_type}")
        print("Supported formats: html, markdown, text")


if __name__ == "__main__":
    main()


path: faq_templates.py
content: #!/usr/bin/env python3
"""
FAQ Forge - Templates
Author: Shadow Rose
License: MIT
quality-verified

Pre-built FAQ templates for common business types.
Get started quickly with industry-standard questions.
"""

from typing import List, Dict
from faq_forge import FAQDatabase, FAQEntry


class FAQTemplates:
    """Pre-built FAQ templates for common business types."""
    
    TEMPLATES = {
        "digital-products": {
            "name": "Digital Products / Downloads",
            "description": "For selling ebooks, courses, software, templates, etc.",
            "categories": ["Getting Started", "Purchasing", "Downloads", "Technical Support", "Licensing"],
            "faqs": [
                {
                    "question": "How do I access my purchase after buying?",
                    "answer": "After completing your purchase, you'll receive an email with download links and access instructions. Check your spam folder if you don't see it within 5 minutes. You can also log into your account on our website to access your downloads anytime.",
                    "category": "Getting Started",
                    "tags": ["access", "download", "purchase"],
                    "priority": "high"
                },
                {
                    "question": "What payment methods do you accept?",
                    "answer": "We accept all major credit cards (Visa, Mastercard, American Express, Discover), PayPal, and Apple Pay. All transactions are securely processed through our payment provider.",
                    "category": "Purchasing",
                    "tags": ["payment", "security"],
                    "priority": "normal"
                },
                {
                    "question": "What is your refund policy?",
                    "answer": "We offer a 30-day money-back guarantee. If you're not satisfied with your purchase, contact us within 30 days for a full refund—no questions asked. Digital products cannot be returned after download, but we stand behind our quality.",
                    "category": "Purchasing",
                    "tags": ["refund", "guarantee"],
                    "priority": "high"
                },
                {
                    "question": "Can I download my purchase on multiple devices?",
                    "answer": "Yes! Your purchase is tied to your account, not your device. You can download and use your products on as many devices as you own for personal use. Commercial redistribution is not permitted.",
                    "category": "Downloads",
                    "tags": ["devices", "licensing"],
                    "priority": "normal"
                },
                {
                    "question": "I lost my download link. How do I get it again?",
                    "answer": "Log into your account and visit the Downloads section to access all your purchases. If you don't have an account or forgot your password, use the password reset option. Still having trouble? Contact support with your purchase email.",
                    "category": "Downloads",
                    "tags": ["download", "account", "support"],
                    "priority": "normal"
                },
                {
                    "question": "Do you offer updates for your products?",
                    "answer": "Yes! All updates and improvements are free for existing customers. When we release an update, you'll receive an email notification, and the latest version will be available in your account dashboard.",
                    "category": "Technical Support",
                    "tags": ["updates", "versions"],
                    "priority": "normal"
                },
                {
                    "question": "Can I use this product commercially?",
                    "answer": "Most of our products include commercial use rights, but licenses vary by product. Check the specific product page or license documentation included with your download. If you need extended licensing, contact us for options.",
                    "category": "Licensing",
                    "tags": ["commercial", "licensing", "rights"],
                    "priority": "high"
                }
            ]
        },
        
        "saas": {
            "name": "SaaS / Software as a Service",
            "description": "For subscription-based software and web applications.",
            "categories": ["Getting Started", "Account & Billing", "Features", "Technical Support", "Plans & Pricing"],
            "faqs": [
                {
                    "question": "How do I get started?",
                    "answer": "Sign up for a free trial on our homepage—no credit card required. After creating your account, you'll be guided through a quick onboarding process. Our getting started guide will walk you through the essential features.",
                    "category": "Getting Started",
                    "tags": ["signup", "trial", "onboarding"],
                    "priority": "critical"
                },
                {
                    "question": "Is there a free trial?",
                    "answer": "Yes! We offer a 14-day free trial with full access to all features. No credit card required to start. You can upgrade to a paid plan anytime, and your data will be preserved.",
                    "category": "Plans & Pricing",
                    "tags": ["trial", "pricing", "free"],
                    "priority": "high"
                },
                {
                    "question": "Can I cancel my subscription anytime?",
                    "answer": "Absolutely. You can cancel your subscription at any time from your account settings. Your access will continue until the end of your current billing period, and you won't be charged again. No cancellation fees.",
                    "category": "Account & Billing",
                    "tags": ["cancel", "subscription", "billing"],
                    "priority": "high"
                },
                {
                    "question": "How do I upgrade or downgrade my plan?",
                    "answer": "Go to Settings > Billing and select a new plan. Upgrades take effect immediately. Downgrades take effect at the end of your current billing cycle. You'll only pay the difference when upgrading mid-cycle.",
                    "category": "Plans & Pricing",
                    "tags": ["upgrade", "downgrade", "plans"],
                    "priority": "normal"
                },
                {
                    "question": "What happens to my data if I cancel?",
                    "answer": "Your data is safe! After cancellation, you have 60 days to export your data or reactivate your account. After 60 days, data is permanently deleted for security reasons. You can export your data anytime from your account.",
                    "category": "Account & Billing",
                    "tags": ["data", "cancel", "export"],
                    "priority": "high"
                },
                {
                    "question": "Do you offer team/multi-user accounts?",
                    "answer": "Yes! Our Team and Business plans support multiple users with role-based permissions. You can add or remove team members anytime, and billing is automatically adjusted. Contact us for custom enterprise pricing.",
                    "category": "Features",
                    "tags": ["team", "users", "collaboration"],
                    "priority": "normal"
                },
                {
                    "question": "Is my data secure?",
                    "answer": "Security is our top priority. All data is encrypted in transit (SSL/TLS) and at rest. We use industry-standard security practices, regular backups, and comply with GDPR and SOC 2 standards. Read our security page for full details.",
                    "category": "Technical Support",
                    "tags": ["security", "encryption", "privacy"],
                    "priority": "high"
                }
            ]
        },
        
        "freelance": {
            "name": "Freelance Services",
            "description": "For freelancers and service providers.",
            "categories": ["Getting Started", "Services & Pricing", "Process", "Payments", "Policies"],
            "faqs": [
                {
                    "question": "How do I get started working with you?",
                    "answer": "Reach out through the contact form with details about your project. I'll respond within 24 hours to discuss your needs, timeline, and budget. If we're a good fit, I'll send a proposal and contract to get started.",
                    "category": "Getting Started",
                    "tags": ["inquiry", "contact", "process"],
                    "priority": "critical"
                },
                {
                    "question": "What services do you offer?",
                    "answer": "I specialize in [your services]. Each project is unique, so I tailor my approach to your specific needs. Check out my portfolio for examples of past work and the results I've delivered for clients.",
                    "category": "Services & Pricing",
                    "tags": ["services", "offerings"],
                    "priority": "high"
                },
                {
                    "question": "How much do you charge?",
                    "answer": "Rates vary based on project scope, complexity, and timeline. I offer both hourly and project-based pricing. Contact me with your project details for a custom quote. My typical range is [your range], but I'm happy to work with various budgets.",
                    "category": "Services & Pricing",
                    "tags": ["pricing", "rates", "budget"],
                    "priority": "high"
                },
                {
                    "question": "What is your typical timeline?",
                    "answer": "Project timelines vary based on scope. Smaller projects typically take 1-2 weeks, while larger projects may take 4-8 weeks. I'll provide a detailed timeline in my proposal. Rush projects may be available for an additional fee.",
                    "category": "Process",
                    "tags": ["timeline", "delivery", "schedule"],
                    "priority": "normal"
                },
                {
                    "question": "Do you require a deposit?",
                    "answer": "Yes, I require a 50% deposit to begin work, with the remainder due upon completion. For larger projects, I offer milestone-based payment schedules. All payment terms are outlined in the contract before work begins.",
                    "category": "Payments",
                    "tags": ["deposit", "payment", "terms"],
                    "priority": "normal"
                },
                {
                    "question": "What payment methods do you accept?",
                    "answer": "I accept bank transfers, PayPal, and major credit cards. Payment details and invoicing will be handled through [your platform]. International payments are welcome.",
                    "category": "Payments",
                    "tags": ["payment", "methods"],
                    "priority": "normal"
                },
                {
                    "question": "What if I'm not happy with the work?",
                    "answer": "Client satisfaction is my top priority. I include revision rounds in all projects (typically 2-3 rounds depending on scope). If you're still not satisfied after revisions, we can discuss options. My goal is always to deliver work you're excited about.",
                    "category": "Policies",
                    "tags": ["revisions", "satisfaction", "guarantee"],
                    "priority": "high"
                },
                {
                    "question": "Do you sign NDAs or work under confidentiality?",
                    "answer": "Absolutely. I'm happy to sign NDAs and confidentiality agreements. I respect client privacy and never share project details without permission. All work is covered by a standard contract protecting both parties.",
                    "category": "Policies",
                    "tags": ["nda", "confidentiality", "contract"],
                    "priority": "normal"
                }
            ]
        },
        
        "ecommerce": {
            "name": "E-commerce / Physical Products",
            "description": "For online stores selling physical goods.",
            "categories": ["Ordering", "Shipping", "Returns & Refunds", "Products", "Support"],
            "faqs": [
                {
                    "question": "How long does shipping take?",
                    "answer": "Standard shipping takes 5-7 business days within the US. Express shipping (2-3 days) is available at checkout. International shipping typically takes 10-15 business days. You'll receive tracking information once your order ships.",
                    "category": "Shipping",
                    "tags": ["shipping", "delivery", "tracking"],
                    "priority": "critical"
                },
                {
                    "question": "What is your return policy?",
                    "answer": "We accept returns within 30 days of delivery for a full refund. Items must be unused and in original packaging. Return shipping is free for defective items; customer pays return shipping for other returns. Contact us to initiate a return.",
                    "category": "Returns & Refunds",
                    "tags": ["returns", "refund", "policy"],
                    "priority": "high"
                },
                {
                    "question": "Do you ship internationally?",
                    "answer": "Yes! We ship to most countries worldwide. Shipping costs and delivery times vary by location. International customers are responsible for any customs fees or import taxes. Check available countries at checkout.",
                    "category": "Shipping",
                    "tags": ["international", "shipping", "customs"],
                    "priority": "normal"
                },
                {
                    "question": "How can I track my order?",
                    "answer": "You'll receive a tracking number via email once your order ships. Click the tracking link to see real-time updates. You can also track your order by logging into your account and viewing order history.",
                    "category": "Ordering",
                    "tags": ["tracking", "order", "status"],
                    "priority": "high"
                },
                {
                    "question": "What if my item arrives damaged?",
                    "answer": "We're sorry if your item arrived damaged! Contact us within 48 hours with photos of the damage. We'll send a replacement immediately at no charge or issue a full refund—your choice. Keep the damaged item until we confirm receipt of your claim.",
                    "category": "Returns & Refunds",
                    "tags": ["damage", "replacement", "refund"],
                    "priority": "high"
                },
                {
                    "question": "Are your products authentic?",
                    "answer": "100% authentic guaranteed. We source directly from authorized distributors and manufacturers. Every product comes with authenticity verification. If you ever receive a counterfeit item, we'll refund you double your purchase price.",
                    "category": "Products",
                    "tags": ["authentic", "genuine", "quality"],
                    "priority": "high"
                }
            ]
        }
    }
    
    def __init__(self, db: FAQDatabase):
        self.db = db
    
    def list_templates(self):
        """Display available templates."""
        print("\nAvailable FAQ Templates:\n")
        for key, template in self.TEMPLATES.items():
            print(f"  {key}")
            print(f"    {template['name']}")
            print(f"    {template['description']}")
            print(f"    {len(template['faqs'])} questions\n")
    
    def apply_template(self, template_key: str, product: str = "default",
                       customize: bool = True) -> int:
        """
        Apply a template to the FAQ database.
        Returns number of entries added.
        """
        if template_key not in self.TEMPLATES:
            print(f"Error: Template '{template_key}' not found")
            return 0
        
        template = self.TEMPLATES[template_key]
        
        if customize:
            print(f"\nApplying template: {template['name']}")
            print(f"{template['description']}\n")
            print("These questions are templates. You'll want to customize them with your specific:")
            print("  - Business details")
            print("  - Policies and timelines")
            print("  - Pricing and terms")
            print("  - Contact information\n")
        
        added = 0
        for faq_data in template['faqs']:
            entry = FAQEntry(
                question=faq_data['question'],
                answer=faq_data['answer'],
                category=faq_data['category'],
                tags=faq_data['tags'],
                priority=faq_data['priority'],
                product=product
            )
            
            # Mark template entries for review
            entry.feedback = {
                "needs_improvement": customize,
                "outdated": False,
                "notes": "Template entry - customize with your specific business details"
            }
            
            self.db.add(entry)
            added += 1
        
        print(f"✓ Added {added} FAQ entries from '{template['name']}' template")
        
        if customize:
            print("\nNext steps:")
            print("  1. Review each question: faq_forge.py search --category [category]")
            print("  2. Customize answers: faq_forge.py update <id> --answer 'Your specific answer'")
            print("  3. Add your own questions: faq_forge.py add <question> <answer>")
            print("  4. Publish: faq_publish.py html output.html")
        
        return added


def main():
    """Command-line interface for FAQ templates."""
    import sys
    
    if len(sys.argv) < 2:
        print("FAQ Forge Templates")
        print("\nUsage:")
        print("  faq_templates.py list")
        print("  faq_templates.py apply <template-key> [--product PROD] [--no-customize-prompt]")
        print("\nAvailable templates:")
        print("  digital-products    - Digital downloads, courses, ebooks")
        print("  saas               - Software as a Service applications")
        print("  freelance          - Freelance services and consulting")
        print("  ecommerce          - Physical products and online stores")
        return
    
    db = FAQDatabase()
    templates = FAQTemplates(db)
    
    command = sys.argv[1]
    
    if command == "list":
        templates.list_templates()
    
    elif command == "apply":
        if len(sys.argv) < 3:
            print("Error: apply requires <template-key>")
            return
        
        template_key = sys.argv[2]
        product = "default"
        customize = True
        
        for i in range(3, len(sys.argv)):
            if sys.argv[i] == "--product" and i + 1 < len(sys.argv):
                product = sys.argv[i + 1]
            elif sys.argv[i] == "--no-customize-prompt":
                customize = False
        
        templates.apply_template(template_key, product, customize)
    
    else:
        print(f"Unknown command: {command}")


if __name__ == "__main__":
    main()


path: LICENSE.md
content: MIT License

Copyright (c) 2026 Shadow Rose

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


path: LIMITATIONS.md
content: # Limitations & Known Issues

**FAQ Forge** is designed to be simple, reliable, and dependency-free. This document outlines known limitations and design constraints.

---

## By Design (Not Bugs)

### 1. Command-Line Interface Only

**Limitation:** No graphical interface or web dashboard.

**Why:** Keeps the tool simple, fast, and portable. No web server required.

**Workaround:** 
- Use your favorite text editor to edit `faq_data.json` directly
- Build a simple web UI on top if needed (JSON is easy to integrate)
- Command-line is scriptable and automation-friendly

### 2. Single JSON Database File

**Limitation:** All FAQs stored in one JSON file (`faq_data.json`).

**Why:** Simple, portable, version-control friendly, no database server needed.

**Implications:**
- Not ideal for >10,000 entries (but who has 10,000 FAQs?)
- All data loaded into memory (fast, but uses RAM)
- Concurrent writes from multiple processes may conflict

**Workaround:**
- For most businesses, 50-500 FAQs is typical—works perfectly
- Use file locking for concurrent access if needed
- Export/import for migration or splitting large databases

### 3. No Built-in Version Control

**Limitation:** Changes overwrite previous versions. No built-in history tracking.

**Why:** Keeps the tool simple and the JSON file clean.

**Workaround:**
- Store `faq_data.json` in Git for automatic version history
- Export to JSON periodically for backups
- Track the `updated` timestamp to see when answers changed

### 4. Static HTML Output Only

**Limitation:** Published HTML is static. No dynamic search backend.

**Why:** Simplicity and portability. Host anywhere, no server required.

**Implications:**
- Search runs client-side in JavaScript (works fine for <1000 entries)
- No server-side analytics tracking (unless you add Google Analytics)
- No real-time updates (need to republish)

**Workaround:**
- Client-side search is fast for typical FAQ sizes
- Add Google Analytics to published HTML for tracking
- Re-publish when content changes (takes seconds)

### 5. Manual Publishing Required

**Limitation:** After updating FAQs, you must run `faq_publish.py` again.

**Why:** Separation of content management and publishing.

**Workaround:**
- Automate with a simple script: `python faq_forge.py add ... && python faq_publish.py html faq.html`
- Set up a cron job or webhook to auto-publish on changes
- Use file watching tools (like `watchdog`) to auto-regenerate HTML

---

## Technical Constraints

### 6. Python 3.6+ Required

**Limitation:** Requires Python 3.6 or newer.

**Why:** Uses f-strings and modern type hints.

**Workaround:**
- Python 3.6 is from 2016—widely available
- If stuck on older Python, minor code changes can backport

### 7. No Fuzzy Search

**Limitation:** Search is exact substring matching, not fuzzy/typo-tolerant.

**Why:** Python stdlib doesn't include fuzzy search. Keeping it dependency-free.

**Workaround:**
- Use clear, descriptive questions
- Add multiple tags to improve searchability
- HTML client-side search works well for exact matches

### 8. No Natural Language Understanding

**Limitation:** Can't automatically categorize or understand question intent.

**Why:** No AI/ML libraries (staying dependency-free).

**Workaround:**
- Auto-tagging uses keyword matching (works well)
- Templates provide good starting structure
- Manual categorization is fast and accurate

---

## Import Limitations

### 9. Markdown Import Heuristics

**Limitation:** Markdown import uses pattern matching, not semantic understanding.

**Why:** No natural language processing (NLP) libraries.

**Implications:**
- May miss unusually formatted Q&A pairs
- May incorrectly parse section headings as questions
- Needs manual review after import

**Workaround:**
- Review imported questions: `faq_forge.py list`
- Delete incorrect imports: `faq_forge.py delete ID`
- Format your source markdown consistently for best results

### 10. No Automatic Merging

**Limitation:** Importing can create duplicate questions if similar ones exist.

**Why:** No sophisticated duplicate detection.

**Workaround:**
- Review imports before applying: Use `--category` to organize
- Search existing questions first: `faq_forge.py search "keyword"`
- Manually merge duplicates after import

---

## Publishing Limitations

### 11. Basic HTML Theme

**Limitation:** Single professional theme. No theme marketplace.

**Why:** Keeping it simple and focused.

**Workaround:**
- Edit `faq_publish.py` to customize the HTML template
- The HTML/CSS is clean and well-commented
- Change colors via `theme_color` in config
- Or export to Markdown and use your own static site generator

### 12. No PDF Export

**Limitation:** Can't directly export to PDF.

**Why:** Python stdlib doesn't include PDF generation.

**Workaround:**
- Export to HTML, then print to PDF from browser
- Export to Markdown, then use Pandoc or similar tool
- Use plain text output and convert with external tools

### 13. Limited Markdown Tables in HTML

**Limitation:** Markdown inside answers isn't rendered in HTML.

**Why:** No markdown parser (staying dependency-free).

**Workaround:**
- Use plain HTML in answers (supported)
- Keep formatting simple
- Use line breaks and basic text formatting

---

## Multi-User / Team Limitations

### 14. No User Permissions

**Limitation:** No built-in user authentication or role-based access.

**Why:** Command-line tool, not a web application.

**Workaround:**
- Use file system permissions to control access
- Store FAQ database in a shared Git repo for team collaboration
- Build a simple web wrapper if multi-user editing is critical

### 15. No Conflict Resolution

**Limitation:** Concurrent edits may conflict if multiple users edit simultaneously.

**Why:** Simple file-based storage.

**Workaround:**
- Use Git for collaboration (automatic conflict resolution)
- Coordinate who edits when (small teams)
- Export/import for merging changes from different sources

---

## Scaling Limitations

### 16. Not Optimized for Huge FAQs

**Limitation:** Performance degrades with >5,000 entries.

**Why:** In-memory JSON loading, linear search.

**Reality:** Most businesses have 50-500 FAQs. If you have 5,000, you need a knowledge base platform, not a FAQ.

**Workaround:**
- Split into multiple products/databases
- Archive old or irrelevant questions
- Use multi-product feature to organize large sets

### 17. Client-Side Search Limits

**Limitation:** HTML search loads entire FAQ in browser. Slow for very large FAQs.

**Why:** Static site, no backend.

**Reality:** Works fine up to ~1,000 entries. Beyond that, use Markdown + static site generator.

**Workaround:**
- Split large FAQs by product or category
- Publish separate HTML pages per category
- Use Markdown export + static site generator for very large FAQs

---

## Analytics Limitations

### 18. View Counts Are Local

**Limitation:** View counts track database access, not actual customer views.

**Why:** Static HTML can't report back to database.

**Workaround:**
- Add Google Analytics to published HTML
- Use server logs if hosting on your own server
- View counts show which questions you look at (useful for maintenance)

### 19. No Built-in A/B Testing

**Limitation:** Can't test different answer variations.

**Why:** Out of scope for a simple FAQ tool.

**Workaround:**
- Use external A/B testing tools on your website
- Track metrics via Google Analytics
- Manually update answers based on support ticket trends

---

## Integration Limitations

### 20. No API

**Limitation:** No REST API for dynamic FAQ delivery.

**Why:** Static tool, not a service.

**Workaround:**
- JSON database is easy to parse from any language
- Build a simple API on top if needed (Flask, FastAPI, etc.)
- Use HTML output with iframe embedding

### 21. No Zapier/Webhook Integration

**Limitation:** Can't automatically create FAQs from support tickets.

**Why:** Command-line tool, not a web service.

**Workaround:**
- Build a simple script to parse support tickets and call `faq_forge.py`
- Use cron + API polling to import from external sources
- Export/import JSON for integration with other systems

---

## What FAQ Forge Does Well

Despite these limitations, FAQ Forge excels at:

✅ **Quick setup** - No dependencies, no complex config  
✅ **Professional output** - Beautiful HTML, clean Markdown  
✅ **Easy maintenance** - Simple commands, clear structure  
✅ **Portability** - Works anywhere Python runs  
✅ **Version control friendly** - JSON format, git-compatible  
✅ **Small to medium businesses** - Perfect for 50-500 FAQs  
✅ **Zero hosting costs** - Static HTML, host anywhere  
✅ **Privacy** - Your data stays local, no cloud service  

---

## When to Use Something Else

Consider a different solution if you need:

- ❌ Multi-user collaboration with permissions (use a wiki or CMS)
- ❌ Dynamic, real-time FAQ delivery (use a knowledge base SaaS)
- ❌ Advanced AI/ML categorization (use dedicated KB platforms)
- ❌ Thousands of articles with complex relationships (use a proper CMS)
- ❌ Multi-language with translation workflows (use localization platforms)
- ❌ Advanced analytics and A/B testing (use enterprise KB software)

---

## Known Bugs (None Currently!)


If you discover a bug:
1. Check if it's a documented limitation above
2. Verify you're using Python 3.6+
3. Check `faq_data.json` for corruption (JSON syntax)
4. Try exporting and reimporting to fix database issues

---

## Future Enhancements (Maybe)

These are NOT currently implemented but could be added:

- Web-based editor
- PDF export
- Advanced search (fuzzy, semantic)
- Multi-language support
- Automatic duplicate detection
- Revision history UI
- Zapier integration
- REST API
- Theme marketplace

FAQ Forge is intentionally **simple and complete**. These features may come in future versions if there's demand, but the current version is production-ready as-is.

---

**Questions about limitations?** Most are by design to keep the tool simple and dependency-free. For 95% of businesses, these trade-offs are worth it for the simplicity gained.

**Need something more complex?** FAQ Forge is MIT licensed—fork it and extend it! Or consider enterprise knowledge base platforms if you need advanced features.

**Bottom line:** FAQ Forge is perfect for small to medium businesses who want a professional FAQ without the complexity and cost of enterprise solutions.


path: QUICKSTART.md
content: # FAQ Forge - Quick Start Guide

**Get your FAQ up and running in 5 minutes!**

---

## Prerequisites

- Python 3.6+ installed
- That's it! No dependencies needed.

---

## Installation

```bash
# Download/clone FAQ Forge
cd faq-forge

# Verify installation
python3 test_demo.py
```

You should see "ALL TESTS PASSED!" 🎉

---

## Option 1: Start with a Template (Recommended)

### Step 1: Choose Your Template

```bash
python3 faq_templates.py list
```

Available templates:
- `digital-products` - Ebooks, courses, software
- `saas` - Web apps, subscription services
- `freelance` - Services, consulting
- `ecommerce` - Physical products, online stores

### Step 2: Apply Template

```bash
# Example: digital products business
python3 faq_templates.py apply digital-products
```

### Step 3: Customize Answers

```bash
# See what was created
python3 faq_forge.py list

# Update an answer
python3 faq_forge.py update how-do-i-access-my-purchase-after-buying \
  --answer "Your customized answer here"
```

### Step 4: Publish

```bash
python3 faq_publish.py html faq.html
```

Open `faq.html` in your browser! ✨

---

## Option 2: Start from Scratch

### Step 1: Add Your First Question

```bash
python3 faq_forge.py add \
  "How do I contact support?" \
  "Email us at [REDACTED_SECRET] or use the contact form on our website." \
  --category "Support" \
  --tags "contact,help" \
  --priority "high"
```

### Step 2: Add More Questions

```bash
python3 faq_forge.py add \
  "What payment methods do you accept?" \
  "We accept Visa, Mastercard, PayPal, and Apple Pay." \
  --category "Billing" \
  --tags "payment,billing"

python3 faq_forge.py add \
  "What is your refund policy?" \
  "We offer a 30-day money-back guarantee. Contact support for refunds." \
  --category "Billing" \
  --tags "refund,policy" \
  --priority "high"
```

### Step 3: Publish

```bash
python3 faq_publish.py html faq.html
```

---

## Option 3: Import from Existing Docs

### If You Have Markdown Files

```bash
python3 faq_import.py markdown your_existing_faq.md \
  --category "General"
```

### If You Have a Folder of Docs

```bash
python3 faq_import.py directory ./docs \
  --category "Documentation"
```

Then review and publish:

```bash
python3 faq_forge.py list
python3 faq_publish.py html faq.html
```

---

## Essential Commands

### Add a Question
```bash
python3 faq_forge.py add "Question?" "Answer" \
  --category "Category Name" \
  --tags "tag1,tag2" \
  --priority "high"
```

### Search Questions
```bash
python3 faq_forge.py search "keyword"
python3 faq_forge.py search --category "Billing"
```

### Update an Answer
```bash
python3 faq_forge.py update question-id \
  --answer "New improved answer"
```

### Publish HTML
```bash
python3 faq_publish.py html output.html
```

### See All Questions
```bash
python3 faq_forge.py list
```

### Get Statistics
```bash
python3 faq_forge.py stats
```

---

## Publishing Options

### HTML (Recommended)
```bash
python3 faq_publish.py html faq.html \
  --title "Customer Support FAQ"
```
✅ Searchable, collapsible, professional design  
✅ Works on any web server (no backend needed)  
✅ Mobile-responsive

### Markdown
```bash
python3 faq_publish.py markdown faq.md
```
✅ Perfect for GitHub wikis  
✅ Documentation sites (GitBook, MkDocs)

### Plain Text
```bash
python3 faq_publish.py text faq.txt
```
✅ Email templates  
✅ Print documentation

---

## Tips for Great FAQs

1. **Start with templates** - Customize from a solid base
2. **Use priorities** - Mark common questions as "high"
3. **Add tags** - Makes searching easier
4. **Link related questions** - Help users find answers
5. **Keep updating** - FAQs should evolve with your business

---

## Example Workflow

```bash
# Day 1: Setup
python3 faq_templates.py apply saas
python3 faq_forge.py list

# Day 2: Customize
python3 faq_forge.py update entry-id --answer "Custom answer"
python3 faq_forge.py add "New question?" "New answer"

# Day 3: Publish
python3 faq_publish.py html public/faq.html
# Upload faq.html to your website

# Ongoing: Maintain
python3 faq_forge.py search --category "Billing"
python3 faq_forge.py update entry-id --answer "Updated answer"
python3 faq_publish.py html public/faq.html
```

---

## Configuration (Optional)

Want to customize categories, colors, or business info?

```bash
cp config_example.json config.json
# Edit config.json with your preferences
```

See `config_example.json` for all options.

---

## Getting Help

- **Full docs:** `README.md`
- **Known issues:** `LIMITATIONS.md`
- **Test installation:** `python3 test_demo.py`

---

## Common Questions

**Q: Do I need to install anything?**  
A: Just Python 3.6+. No pip packages needed!

**Q: Where is my data stored?**  
A: In `faq_data.json` in the current directory.

**Q: Can I use this commercially?**  
A: Yes! MIT License. Use it however you want.

**Q: Can I customize the HTML design?**  
A: Yes! Edit `faq_publish.py` or change colors in `config.json`.

**Q: How do I backup my FAQs?**  
A: Store `faq_data.json` in Git, or export: `python3 faq_import.py export backup.json`

---

## What's Next?

After you're comfortable with the basics:

- Link related questions: `python3 faq_forge.py relate id1 id2`
- Track feedback: `python3 faq_forge.py feedback id --needs-improvement`
- Multi-product FAQs: Use `--product` flag
- Automation: Script it! All commands are scriptable

---

**Ready to build your FAQ?**

```bash
# Choose one:
python3 faq_templates.py apply digital-products
# OR
python3 faq_forge.py add "Your first question?" "Your first answer"

# Then publish:
python3 faq_publish.py html faq.html
```

🚀 **You're all set!**

For complete documentation, see `README.md`.


path: README.md
content: # FAQ Forge

**Build, maintain, and publish professional customer FAQ documentation**

FAQ Forge is a complete FAQ management system that helps you create, organize, and publish customer-facing knowledge bases. Reduce support burden by answering common questions before they're asked.

**Author:** Shadow Rose  
**License:** MIT  
**Quality:** quality-verified

---

## What It Does

- ✅ **Manage Q&A pairs** with categories, tags, and priorities
- 🔍 **Search** across all questions and answers
- 📊 **Track** which questions are viewed most (analytics-ready)
- 🌐 **Publish** to professional HTML, Markdown, or plain text
- 📥 **Import** from existing markdown documentation
- 🚀 **Templates** for common business types (SaaS, e-commerce, freelance, digital products)
- 🔗 **Link** related questions together
- 💬 **Feedback** system to mark questions needing improvement
- 🏷️ **Multi-product** support (separate FAQ sections per product)

---

## Quick Start

### 1. Install (No Dependencies!)

```bash
# Clone or download FAQ Forge
cd faq-forge

# Make scripts executable (optional)
chmod +x *.py
```

**That's it!** FAQ Forge uses only Python standard library—no pip installs required.

### 2. Create Your First FAQ

**Option A: Start from a template**

```bash
# See available templates
python faq_templates.py list

# Apply a template (digital products, SaaS, freelance, or e-commerce)
python faq_templates.py apply digital-products
```

**Option B: Add questions manually**

```bash
# Add a question
python faq_forge.py add \
  "How do I reset my password?" \
  "Click 'Forgot Password' on the login page and follow the email instructions." \
  --category "Account Management" \
  --tags "password,account,login" \
  --priority "high"
```

### 3. Publish Your FAQ

```bash
# Generate a professional HTML page
python faq_publish.py html faq.html

# Or markdown
python faq_publish.py markdown faq.md

# Or plain text
python faq_publish.py text faq.txt
```

Open `faq.html` in your browser—you have a complete, searchable FAQ ready to publish!

---

## Core Features

### 📝 FAQ Management

**Add questions:**
```bash
python faq_forge.py add \
  "What payment methods do you accept?" \
  "We accept Visa, Mastercard, PayPal, and Apple Pay." \
  --category "Billing" \
  --tags "payment,billing" \
  --priority "high"
```

**Update answers:**
```bash
python faq_forge.py update how-do-i-reset-my-password \
  --answer "New updated answer with more detail."
```

**Search:**
```bash
# Search all questions
python faq_forge.py search "password"

# Filter by category
python faq_forge.py search --category "Billing"

# Filter by tags
python faq_forge.py search --tags "payment,refund"

# Filter by priority
python faq_forge.py search --priority "high"

# Combine filters
python faq_forge.py search "refund" --category "Billing" --priority "high"
```

**List all questions:**
```bash
python faq_forge.py list
python faq_forge.py list --product "pro-plan"
```

**Delete questions:**
```bash
python faq_forge.py delete question-id
```

### 🔗 Related Questions

Link related questions together:

```bash
python faq_forge.py relate \
  how-do-i-reset-my-password \
  i-forgot-my-username
```

Related questions appear as links in the published HTML.

### 💬 Feedback & Improvement Tracking

Mark questions that need attention:

```bash
# Mark as needing improvement
python faq_forge.py feedback question-id --needs-improvement \
  --notes "Add more detail about mobile app"

# Mark as outdated
python faq_forge.py feedback question-id --outdated \
  --notes "Update for new payment system"
```

Track which questions need review:

```bash
python faq_forge.py stats
# Shows "needs_attention" count
```

### 📊 Analytics & Stats

```bash
python faq_forge.py stats
```

See:
- Total entries
- Breakdown by category and priority
- Most viewed questions
- Questions needing attention

Track popular questions to prioritize improvements!

---

## Publishing

### 🌐 HTML Output

Generate a professional, searchable FAQ page:

```bash
python faq_publish.py html faq.html \
  --title "Customer Support FAQ" \
  --product "default"
```

**Features:**
- ✅ Client-side search (no server needed)
- ✅ Collapsible Q&A sections
- ✅ Category navigation
- ✅ Priority badges (critical, high)
- ✅ Related questions links
- ✅ Mobile-responsive design
- ✅ Professional gradient theme

**Customization options:**
- `--no-search` - Disable search box
- `--no-toc` - Disable table of contents
- `--no-collapse` - Show all answers expanded
- `--title "Custom Title"` - Custom page title

The HTML is **100% static**—upload to any web server, no backend required!

### 📄 Markdown Output

```bash
python faq_publish.py markdown faq.md
```

Perfect for:
- GitHub wikis
- Documentation sites (GitBook, MkDocs)
- README files
- Internal docs

### 📝 Plain Text Output

```bash
python faq_publish.py text faq.txt
```

Great for:
- Email support templates
- Print documentation
- Plain text knowledge bases

---

## Importing Existing Documentation

### From Markdown Files

FAQ Forge can parse existing markdown files and extract Q&A pairs:

```bash
# Import single file
python faq_import.py markdown existing_faq.md \
  --category "Getting Started" \
  --product "default"

# Import all markdown files from a directory
python faq_import.py directory ./docs \
  --pattern "*.md" \
  --category "Documentation"
```

**Supported formats:**
1. **Q: / A: format**
   ```
   Q: What is your return policy?
   A: We accept returns within 30 days...
   ```

2. **Heading format**
   ```markdown
   ### How do I contact support?
   Send an email to [REDACTED_SECRET]...
   ```

3. **Bold question format**
   ```markdown
   **Can I use this commercially?**
   Yes, commercial use is permitted...
   ```

### From JSON

```bash
# Import from JSON
python faq_import.py json faq_backup.json

# Export to JSON (for backup or migration)
python faq_import.py export backup.json
```

JSON format:
```json
[
  {
    "question": "How do I get started?",
    "answer": "Sign up on our website...",
    "category": "Getting Started",
    "tags": ["signup", "account"],
    "priority": "high",
    "product": "default"
  }
]
```

---

## Templates

Start with pre-built FAQs for common business types:

### Available Templates

1. **digital-products** - Digital downloads, courses, ebooks, software
2. **saas** - Software as a Service, web applications
3. **freelance** - Freelance services, consulting, agencies
4. **ecommerce** - Physical products, online stores

### Using Templates

```bash
# See all templates
python faq_templates.py list

# Apply a template
python faq_templates.py apply saas
```

Each template includes 6-8 industry-standard questions covering:
- Getting started
- Pricing and billing
- Common issues
- Policies
- Support

**Customize after applying!** Templates give you a head start—edit answers to match your specific business.

---

## Multi-Product Support

Manage FAQs for multiple products or plans:

```bash
# Add questions for specific products
python faq_forge.py add \
  "What's included in Pro?" \
  "Pro includes unlimited users, priority support..." \
  --product "pro-plan"

python faq_forge.py add \
  "What's included in Enterprise?" \
  "Enterprise includes dedicated support..." \
  --product "enterprise"

# Search by product
python faq_forge.py search --product "pro-plan"

# Publish product-specific FAQs
python faq_publish.py html pro_faq.html --product "pro-plan"
python faq_publish.py html enterprise_faq.html --product "enterprise"
```

Use the "default" product for questions common to all products.

---

## Configuration

See `config_example.json` for reference constants and example values

```python
# Categories for your business
DEFAULT_CATEGORIES = [
    "Getting Started",
    "Billing",
    "Troubleshooting",
    "Features"
]

# Products
PRODUCTS = {
    "default": {"name": "General FAQ"},
    "pro": {"name": "Pro Plan"},
    "enterprise": {"name": "Enterprise"}
}

# HTML theme color
HTML_CONFIG = {
    "theme_color": "#667eea"  # Your brand color
}

# Business info for footer
BUSINESS_INFO = {
    "name": "Your Business",
    "support_email": "[REDACTED_SECRET]"
}
```

See `config_example.json` for all available options.

---

## Workflows

### New Business Setup

1. Apply a template matching your business type
2. Review and customize each answer
3. Add your specific questions
4. Publish HTML to your website

```bash
python faq_templates.py apply saas
python faq_forge.py list
# Review and update answers...
python faq_forge.py update question-id --answer "Custom answer"
python faq_publish.py html public/faq.html
```

### Ongoing Maintenance

1. Track view counts to see popular questions
2. Mark questions needing improvement
3. Update answers regularly
4. Re-publish when ready

```bash
python faq_forge.py stats  # See what's popular
python faq_forge.py search --category "Troubleshooting"
python faq_forge.py feedback question-id --needs-improvement
python faq_forge.py update question-id --answer "Updated answer"
python faq_publish.py html faq.html
```

### Migrating Existing Docs

1. Import from existing markdown files
2. Review imported questions
3. Organize into categories
4. Publish unified FAQ

```bash
python faq_import.py directory ./old-docs --category "Support"
python faq_forge.py list
# Review, categorize, link related questions...
python faq_publish.py html new_faq.html
```

---

## File Structure

```
faq-forge/
├── faq_forge.py          # Core FAQ management engine
├── faq_publish.py        # HTML/Markdown/text publisher
├── faq_import.py         # Import from markdown/JSON
├── faq_templates.py      # Pre-built FAQ templates
├── config_example.json     # Configuration template
├── faq_data.json         # Your FAQ database (auto-created)
├── README.md             # This file
├── LIMITATIONS.md        # Known limitations
└── LICENSE               # MIT License
```

---

## Use Cases

### For Product Businesses
- Reduce support tickets by 40-60%
- Onboard customers faster
- Improve SEO (FAQ pages rank well)
- Build customer confidence

### For SaaS Companies
- Self-serve knowledge base
- Reduce churn (answer questions before cancellation)
- Support multiple product tiers
- Track which features confuse users

### For Freelancers
- Answer client questions upfront
- Professional presentation
- Save time on repetitive questions
- Set clear expectations

### For E-commerce
- Reduce returns and complaints
- Build trust with clear policies
- Improve conversion rates
- Handle international customers

---

## Best Practices

### Writing Great FAQ Answers

1. **Be specific** - Don't be vague. Give concrete steps.
2. **Be concise** - Get to the point quickly.
3. **Use examples** - Show, don't just tell.
4. **Link related questions** - Help users find what they need.
5. **Update regularly** - Mark outdated answers and refresh them.

### Organizing Your FAQ

1. **Start with critical questions** - Use priority levels.
2. **Group logically** - Use clear categories.
3. **Tag everything** - Makes search more powerful.
4. **Link related questions** - Create a knowledge web.
5. **Track analytics** - See what's working.

### Reducing Support Load

1. **Add high-priority badge** to most-asked questions
2. **Link to FAQ** from support emails
3. **Update based on real support tickets**
4. **Make it searchable** (HTML output includes search)
5. **Keep it current** - Outdated FAQs hurt more than they help

---

## Technical Details

- **Language:** Python 3.6+
- **Dependencies:** None (stdlib only)
- **Database:** JSON file (human-readable, version-control friendly)
- **HTML:** Static, no server required (pure CSS/JS)
- **Encoding:** UTF-8
- **Cross-platform:** Works on Windows, macOS, Linux

---

## Roadmap

Possible future enhancements (not currently included):

- Web-based editor (currently CLI only)
- Automatic sync from support tickets
- Multi-language support
- PDF export
- API for dynamic FAQ delivery
- Analytics integration (Google Analytics events)

This is a complete, production-ready tool as-is. Future versions may add these features based on demand.

---

## Support & Contributing

This is a standalone product developed by Shadow Rose.

**Questions?** Check `LIMITATIONS.md` for known limitations and workarounds.

**Found a bug?** FAQ Forge is provided as-is under the MIT License.

**Want to customize?** The code is clean, commented, and designed for modification. All Python stdlib—no dependency hell.

---

## License

MIT License - see LICENSE file for details.

Copyright (c) 2026 Shadow Rose

---

## Quick Reference

```bash
# Add question
faq_forge.py add "Question?" "Answer" --category CAT --tags TAG1,TAG2

# Search
faq_forge.py search [query] [--category CAT] [--tags TAG1,TAG2]

# Update
faq_forge.py update ID --question "New Q" --answer "New A"

# Delete
faq_forge.py delete ID

# Link related
faq_forge.py relate ID1 ID2

# Mark for review
faq_forge.py feedback ID --needs-improvement --notes "Fix this"

# Stats
faq_forge.py stats

# Publish
faq_publish.py html output.html
faq_publish.py markdown output.md
faq_publish.py text output.txt

# Import
faq_import.py markdown file.md --category CAT
faq_import.py directory ./docs
faq_import.py json backup.json

# Templates
faq_templates.py list
faq_templates.py apply [digital-products|saas|freelance|ecommerce]
```

---

**Built with FAQ Forge** 🔨


---

## ⚠️ Disclaimer

This software is provided "AS IS", without warranty of any kind, express or implied.

**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for 
  their use case, environment, and risk tolerance.
- No guarantee is made regarding accuracy, reliability, completeness, or fitness 
  for any particular purpose.
- The author(s) are not responsible for how third parties use, modify, or distribute 
  this software after purchase.

By downloading, installing, or using this software, you acknowledge that you have read 
this disclaimer and agree to use the software entirely at your own risk.


**DATA DISCLAIMER:** This software processes and stores data locally on your system. 
The author(s) are not responsible for data loss, corruption, or unauthorized access 
resulting from software bugs, system failures, or user error. Always maintain 
independent backups of important data. This software does not transmit data externally 
unless explicitly configured by the user.

---

## Support & Links

| | |
|---|---|
| 🐛 **Bug Reports** | [REDACTED_SECRET] |
| ☕ **Ko-fi** | [ko-fi.com/theshadowrose](https://ko-fi.com/theshadowrose) |
| 🛒 **Gumroad** | [shadowyrose.gumroad.com](https://shadowyrose.gumroad.com) |
| 🐦 **Twitter** | [@TheShadowyRose](https://twitter.com/TheShadowyRose) |
| 🐙 **GitHub** | [github.com/TheShadowRose](https://github.com/TheShadowRose) |
| 🧠 **PromptBase** | [promptbase.com/profile/shadowrose](https://promptbase.com/profile/shadowrose) |

*Built with [OpenClaw](https://github.com/openclaw/openclaw) — thank you for making this possible.*

---

🛠️ **Need something custom?** Custom OpenClaw agents & skills starting at $500. If you can describe it, I can build it. → [Hire me on Fiverr](https://www.fiverr.com/s/jjmlZ0v)


path: test_demo.py
content: #!/usr/bin/env python3
"""
FAQ Forge Demo & Test Script
Author: Shadow Rose

Quick demonstration and verification that FAQ Forge works correctly.
Run this to test the installation and see features in action.
"""

import os
import sys
import tempfile
import shutil

# Add current directory to path
sys.path.insert(0, os.path.dirname(__file__))

from faq_forge import FAQDatabase, FAQEntry
from faq_publish import FAQPublisher
from faq_import import FAQImporter
from faq_templates import FAQTemplates


def test_basic_operations():
    """Test basic FAQ operations."""
    print("\n=== Testing Basic Operations ===\n")
    
    # Create temporary database
    temp_dir = tempfile.mkdtemp()
    db_file = os.path.join(temp_dir, "test_faq.json")
    db = FAQDatabase(db_file)
    
    print("✓ Database created")
    
    # Add entries
    entry1 = FAQEntry(
        "How do I get started?",
        "Visit our website and sign up for a free account.",
        category="Getting Started",
        tags=["signup", "account"],
        priority="high"
    )
    
    entry2 = FAQEntry(
        "What payment methods do you accept?",
        "We accept Visa, Mastercard, and PayPal.",
        category="Billing",
        tags=["payment", "billing"],
        priority="normal"
    )
    
    id1 = db.add(entry1)
    id2 = db.add(entry2)
    print(f"✓ Added 2 entries: {id1}, {id2}")
    
    # Search
    results = db.search("payment")
    assert len(results) == 1, "Search failed"
    print(f"✓ Search works: found {len(results)} result(s)")
    
    # Update
    db.update(id1, answer="Updated answer with more detail.")
    updated_entry = db.get(id1)
    assert "Updated" in updated_entry.answer, "Update failed"
    print("✓ Update works")
    
    # Related questions
    db.add_related(id1, id2)
    assert id2 in db.get(id1).related, "Related link failed"
    print("✓ Related questions work")
    
    # Stats
    stats = db.get_stats()
    assert stats['total_entries'] == 2, "Stats failed"
    print(f"✓ Stats: {stats['total_entries']} entries")
    
    # Delete
    db.delete(id2)
    assert db.get(id2) is None, "Delete failed"
    print("✓ Delete works")
    
    # Clean up
    shutil.rmtree(temp_dir)
    print("\n✅ All basic operations passed!\n")


def test_publishing():
    """Test publishing to different formats."""
    print("\n=== Testing Publishing ===\n")
    
    # Create temporary database with sample data
    temp_dir = tempfile.mkdtemp()
    db_file = os.path.join(temp_dir, "test_faq.json")
    db = FAQDatabase(db_file)
    
    # Add sample entries
    categories = ["Getting Started", "Billing", "Support"]
    for i, cat in enumerate(categories):
        entry = FAQEntry(
            f"Question {i+1} in {cat}?",
            f"This is the answer to question {i+1}.",
            category=cat,
            tags=[cat.lower()],
            priority="normal"
        )
        db.add(entry)
    
    print(f"✓ Created test database with {len(db.entries)} entries")
    
    publisher = FAQPublisher(db)
    
    # Test HTML output
    html_file = os.path.join(temp_dir, "test.html")
    publisher.publish_html(html_file)
    assert os.path.exists(html_file), "HTML output failed"
    
    with open(html_file, 'r') as f:
        html_content = f.read()
        assert "Question 1" in html_content, "HTML content missing"
        assert "searchInput" in html_content, "HTML search missing"
    
    print("✓ HTML publishing works")
    
    # Test Markdown output
    md_file = os.path.join(temp_dir, "test.md")
    publisher.publish_markdown(md_file)
    assert os.path.exists(md_file), "Markdown output failed"
    print("✓ Markdown publishing works")
    
    # Test text output
    txt_file = os.path.join(temp_dir, "test.txt")
    publisher.publish_text(txt_file)
    assert os.path.exists(txt_file), "Text output failed"
    print("✓ Text publishing works")
    
    # Test nested directory creation
    nested_file = os.path.join(temp_dir, "output", "nested", "test.html")
    publisher.publish_html(nested_file)
    assert os.path.exists(nested_file), "Nested directory creation failed"
    print("✓ Nested directory creation works")
    
    # Clean up
    shutil.rmtree(temp_dir)
    print("\n✅ All publishing tests passed!\n")


def test_import():
    """Test import functionality."""
    print("\n=== Testing Import ===\n")
    
    # Create temporary files
    temp_dir = tempfile.mkdtemp()
    db_file = os.path.join(temp_dir, "test_faq.json")
    db = FAQDatabase(db_file)
    
    # Create sample markdown file
    md_content = """# FAQ

## Getting Started

### How do I sign up?
Visit our website and click "Sign Up". Fill out the form and verify your email.

### What do I need to get started?
Just an email address and a password.

**Can I try before buying?**
Yes! We offer a 14-day free trial with no credit card required.

Q: How long does setup take?
A: Most users complete setup in under 5 minutes.
"""
    
    md_file = os.path.join(temp_dir, "sample.md")
    with open(md_file, 'w', encoding='utf-8') as f:
        f.write(md_content)
    
    # Test import
    importer = FAQImporter(db)
    imported = importer.import_from_markdown(md_file, category="Getting Started")
    
    assert imported >= 1, f"Import failed: only {imported} entries"
    print(f"✓ Imported {imported} entries from markdown (heuristic parsing)")
    
    # Test JSON export
    json_file = os.path.join(temp_dir, "export.json")
    importer.export_to_json(json_file)
    assert os.path.exists(json_file), "JSON export failed"
    print("✓ JSON export works")
    
    # Test JSON import
    db2 = FAQDatabase(os.path.join(temp_dir, "test_faq2.json"))
    importer2 = FAQImporter(db2)
    imported2 = importer2.import_from_json(json_file)
    assert imported2 == imported, "JSON import failed"
    print("✓ JSON import works")
    
    # Clean up
    shutil.rmtree(temp_dir)
    print("\n✅ All import tests passed!\n")


def test_templates():
    """Test template functionality."""
    print("\n=== Testing Templates ===\n")
    
    # Create temporary database
    temp_dir = tempfile.mkdtemp()
    db_file = os.path.join(temp_dir, "test_faq.json")
    db = FAQDatabase(db_file)
    
    templates = FAQTemplates(db)
    
    # Test applying template
    added = templates.apply_template("digital-products", customize=False)
    assert added > 5, f"Template failed: only {added} entries"
    print(f"✓ Applied template: {added} entries added")
    
    # Verify categories
    categories = db.get_categories()
    assert len(categories) > 0, "No categories created"
    print(f"✓ Categories created: {', '.join(sorted(categories))}")
    
    # Verify priorities
    high_priority = db.search(priority="high")
    assert len(high_priority) > 0, "No high-priority questions"
    print(f"✓ Priority system works: {len(high_priority)} high-priority questions")
    
    # Clean up
    shutil.rmtree(temp_dir)
    print("\n✅ All template tests passed!\n")


def test_edge_cases():
    """Test edge cases and error handling."""
    print("\n=== Testing Edge Cases ===\n")
    
    temp_dir = tempfile.mkdtemp()
    db_file = os.path.join(temp_dir, "test_faq.json")
    db = FAQDatabase(db_file)
    
    # Test unicode and special characters
    entry = FAQEntry(
        "How do I use émojis 🎉?",
        "Just type them! We support unicode: 中文, العربية, 日本語",
        category="Technical Support",
        tags=["unicode", "emoji"]
    )
    entry_id = db.add(entry)
    
    # Reload and verify
    db2 = FAQDatabase(db_file)
    loaded_entry = db2.get(entry_id)
    assert loaded_entry is not None, "Unicode entry failed to load"
    assert "🎉" in loaded_entry.question, "Emoji not preserved"
    print("✓ Unicode and emoji support works")
    
    # Test ID collision handling
    entry1 = FAQEntry("How do I reset my password?", "Answer 1")
    entry2 = FAQEntry("How do I reset my password?", "Answer 2")
    id1 = db.add(entry1)
    id2 = db.add(entry2)
    assert id1 != id2, "ID collision not handled"
    print("✓ ID collision handling works")
    
    # Test empty search
    all_entries = db.search()
    assert len(all_entries) > 0, "Empty search failed"
    print("✓ Empty search returns all entries")
    
    # Test non-existent entry
    result = db.get("non-existent-id")
    assert result is None, "Non-existent entry should return None"
    print("✓ Non-existent entry handling works")
    
    # Clean up
    shutil.rmtree(temp_dir)
    print("\n✅ All edge case tests passed!\n")


def run_all_tests():
    """Run all tests."""
    print("\n" + "="*60)
    print("FAQ Forge - Comprehensive Test Suite")
    print("="*60)
    
    try:
        test_basic_operations()
        test_publishing()
        test_import()
        test_templates()
        test_edge_cases()
        
        print("\n" + "="*60)
        print("🎉 ALL TESTS PASSED! FAQ Forge is working correctly.")
        print("="*60)
        print("\nFAQ Forge is ready to use!")
        print("\nNext steps:")
        print("  1. Try a template: python faq_templates.py apply digital-products")
        print("  2. Add a question: python faq_forge.py add 'Question?' 'Answer'")
        print("  3. Publish: python faq_publish.py html faq.html")
        print("\nSee README.md for complete documentation.\n")
        
        return True
        
    except Exception as e:
        print(f"\n❌ TEST FAILED: {e}")
        import traceback
        traceback.print_exc()
        return False


if __name__ == "__main__":
    success = run_all_tests()
    sys.exit(0 if success else 1)


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)