refactor: rename Fabled Assistant to Fabled Scribe
Updates user-visible branding across frontend (PWA manifest, page title, Settings, push fallback), backend (email templates and subjects, LLM system prompt, CalDAV displayname, SMTP from-name default), README, quickstart compose, and MCP server description. Also updates the CI image path and quickstart image reference to git.fabledsword.com/bvandeusen/fabledscribe in preparation for the Forgejo repo rename. Internal Python package, env vars, and database schema unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -57,7 +57,7 @@ permissions:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: git.fabledsword.com
|
REGISTRY: git.fabledsword.com
|
||||||
IMAGE: git.fabledsword.com/bvandeusen/fabledassistant
|
IMAGE: git.fabledsword.com/bvandeusen/fabledscribe
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
typecheck:
|
typecheck:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Fabled Assistant
|
# Fabled Scribe
|
||||||
|
|
||||||
A self-hosted second brain and project management application with integrated LLM capabilities. Write, organise, and act on your notes and tasks with the help of a local AI assistant — all running on your own hardware.
|
A self-hosted second brain and project management application with integrated LLM capabilities. Write, organise, and act on your notes and tasks with the help of a local AI assistant — all running on your own hardware.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Fabled Assistant — Quick Start
|
# Fabled Scribe — Quick Start
|
||||||
#
|
#
|
||||||
# No build required. Pulls the latest pre-built image from the registry.
|
# No build required. Pulls the latest pre-built image from the registry.
|
||||||
#
|
#
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: git.fabledsword.com/bvandeusen/fabledassistant:latest
|
image: git.fabledsword.com/bvandeusen/fabledscribe:latest
|
||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "5000:5000"
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|||||||
[project]
|
[project]
|
||||||
name = "fable-mcp"
|
name = "fable-mcp"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
description = "MCP server for Fabled Assistant"
|
description = "MCP server for Fabled Scribe"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mcp[cli]>=1.0",
|
"mcp[cli]>=1.0",
|
||||||
|
|||||||
+2
-2
@@ -9,8 +9,8 @@
|
|||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<meta name="apple-mobile-web-app-title" content="Fabled">
|
<meta name="apple-mobile-web-app-title" content="Scribe">
|
||||||
<title>Fabled Assistant</title>
|
<title>Fabled Scribe</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Fabled Assistant",
|
"name": "Fabled Scribe",
|
||||||
"short_name": "Fabled",
|
"short_name": "Scribe",
|
||||||
"description": "Your self-hosted second brain with AI assistance",
|
"description": "Your self-hosted second brain with AI assistance",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ self.addEventListener('push', event => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.registration.showNotification(data.title || 'Fabled Assistant', {
|
return self.registration.showNotification(data.title || 'Scribe', {
|
||||||
body: data.body || '',
|
body: data.body || '',
|
||||||
icon: '/favicon.ico',
|
icon: '/favicon.ico',
|
||||||
badge: '/favicon.ico',
|
badge: '/favicon.ico',
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ const smtp = ref({
|
|||||||
smtp_username: "",
|
smtp_username: "",
|
||||||
smtp_password: "",
|
smtp_password: "",
|
||||||
smtp_from_address: "",
|
smtp_from_address: "",
|
||||||
smtp_from_name: "Fabled Assistant",
|
smtp_from_name: "Fabled Scribe",
|
||||||
smtp_use_tls: "true",
|
smtp_use_tls: "true",
|
||||||
});
|
});
|
||||||
const savingSmtp = ref(false);
|
const savingSmtp = ref(false);
|
||||||
@@ -1981,7 +1981,7 @@ function formatUserDate(iso: string): string {
|
|||||||
|
|
||||||
<section class="settings-section full-width">
|
<section class="settings-section full-width">
|
||||||
<h2>About</h2>
|
<h2>About</h2>
|
||||||
<p class="version-line">Fabled Assistant <span class="version-badge">{{ appVersion }}</span></p>
|
<p class="version-line">Fabled Scribe <span class="version-badge">{{ appVersion }}</span></p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -2741,7 +2741,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="smtp-from-name">From Name</label>
|
<label for="smtp-from-name">From Name</label>
|
||||||
<input id="smtp-from-name" v-model="smtp.smtp_from_name" type="text" placeholder="Fabled Assistant" class="input" />
|
<input id="smtp-from-name" v-model="smtp.smtp_from_name" type="text" placeholder="Fabled Scribe" class="input" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox-field">
|
<div class="checkbox-field">
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Config:
|
|||||||
SMTP_USERNAME: str = os.environ.get("SMTP_USERNAME", "")
|
SMTP_USERNAME: str = os.environ.get("SMTP_USERNAME", "")
|
||||||
SMTP_PASSWORD: str = _read_secret("SMTP_PASSWORD", "SMTP_PASSWORD_FILE", "")
|
SMTP_PASSWORD: str = _read_secret("SMTP_PASSWORD", "SMTP_PASSWORD_FILE", "")
|
||||||
SMTP_FROM_ADDRESS: str = os.environ.get("SMTP_FROM_ADDRESS", "")
|
SMTP_FROM_ADDRESS: str = os.environ.get("SMTP_FROM_ADDRESS", "")
|
||||||
SMTP_FROM_NAME: str = os.environ.get("SMTP_FROM_NAME", "Fabled Assistant")
|
SMTP_FROM_NAME: str = os.environ.get("SMTP_FROM_NAME", "Fabled Scribe")
|
||||||
SMTP_USE_TLS: bool = os.environ.get("SMTP_USE_TLS", "true").lower() in ("1", "true", "yes")
|
SMTP_USE_TLS: bool = os.environ.get("SMTP_USE_TLS", "true").lower() in ("1", "true", "yes")
|
||||||
BASE_URL: str = os.environ.get("BASE_URL", "http://localhost:5000").rstrip("/")
|
BASE_URL: str = os.environ.get("BASE_URL", "http://localhost:5000").rstrip("/")
|
||||||
TRUST_PROXY_HEADERS: bool = os.environ.get("TRUST_PROXY_HEADERS", "").lower() in ("1", "true", "yes")
|
TRUST_PROXY_HEADERS: bool = os.environ.get("TRUST_PROXY_HEADERS", "").lower() in ("1", "true", "yes")
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ async def setup_user_calendar(user_id: int) -> bool:
|
|||||||
<set>
|
<set>
|
||||||
<prop>
|
<prop>
|
||||||
<resourcetype><collection/><C:calendar/></resourcetype>
|
<resourcetype><collection/><C:calendar/></resourcetype>
|
||||||
<displayname>Fabled Assistant</displayname>
|
<displayname>Fabled Scribe</displayname>
|
||||||
</prop>
|
</prop>
|
||||||
</set>
|
</set>
|
||||||
</mkcol>""",
|
</mkcol>""",
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ _EMAIL_LOGO_SVG = (
|
|||||||
|
|
||||||
|
|
||||||
def _email_html(title: str, body: str) -> str:
|
def _email_html(title: str, body: str) -> str:
|
||||||
"""Wrap email body content in the standard Fabled Assistant template."""
|
"""Wrap email body content in the standard Fabled Scribe template."""
|
||||||
return f"""<!DOCTYPE html>
|
return f"""<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -56,7 +56,7 @@ def _email_html(title: str, body: str) -> str:
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div style="background:linear-gradient(135deg,#7c3aed 0%,#6d28d9 100%);padding:24px 28px;text-align:center;">
|
<div style="background:linear-gradient(135deg,#7c3aed 0%,#6d28d9 100%);padding:24px 28px;text-align:center;">
|
||||||
<div style="margin-bottom:8px;">
|
<div style="margin-bottom:8px;">
|
||||||
{_EMAIL_LOGO_SVG}<span style="display:inline-block;vertical-align:middle;color:#ffffff;font-size:18px;font-weight:700;letter-spacing:0.01em;">Fabled Assistant</span>
|
{_EMAIL_LOGO_SVG}<span style="display:inline-block;vertical-align:middle;color:#ffffff;font-size:18px;font-weight:700;letter-spacing:0.01em;">Fabled Scribe</span>
|
||||||
</div>
|
</div>
|
||||||
<p style="margin:0;color:#ede9fe;font-size:13px;letter-spacing:0.03em;">{title}</p>
|
<p style="margin:0;color:#ede9fe;font-size:13px;letter-spacing:0.03em;">{title}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +68,7 @@ def _email_html(title: str, body: str) -> str:
|
|||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<div style="border-top:1px solid #ede9fe;padding:16px 28px;text-align:center;background:#faf5ff;">
|
<div style="border-top:1px solid #ede9fe;padding:16px 28px;text-align:center;background:#faf5ff;">
|
||||||
<p style="margin:0;color:#a78bfa;font-size:12px;">Sent by your Fabled Assistant instance.</p>
|
<p style="margin:0;color:#a78bfa;font-size:12px;">Sent by your Fabled Scribe instance.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -146,7 +146,7 @@ async def send_email(to: str, subject: str, html_body: str) -> None:
|
|||||||
username = config.get("smtp_username", "")
|
username = config.get("smtp_username", "")
|
||||||
password = config.get("smtp_password", "")
|
password = config.get("smtp_password", "")
|
||||||
from_address = config.get("smtp_from_address", "")
|
from_address = config.get("smtp_from_address", "")
|
||||||
from_name = config.get("smtp_from_name", "Fabled Assistant")
|
from_name = config.get("smtp_from_name", "Fabled Scribe")
|
||||||
use_tls = config.get("smtp_use_tls", "true") == "true"
|
use_tls = config.get("smtp_use_tls", "true") == "true"
|
||||||
|
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
@@ -176,6 +176,6 @@ async def send_test_email(to: str) -> None:
|
|||||||
"""Send a branded test email."""
|
"""Send a branded test email."""
|
||||||
body = """
|
body = """
|
||||||
<p style="margin:0 0 12px;color:#1e1b4b;font-size:15px;font-weight:600;">SMTP is configured correctly</p>
|
<p style="margin:0 0 12px;color:#1e1b4b;font-size:15px;font-weight:600;">SMTP is configured correctly</p>
|
||||||
<p style="margin:0;color:#6b7280;font-size:14px;">Your Fabled Assistant instance can send email notifications.</p>
|
<p style="margin:0;color:#6b7280;font-size:14px;">Your Fabled Scribe instance can send email notifications.</p>
|
||||||
"""
|
"""
|
||||||
await send_email(to, "Fabled Assistant - Test Email", _email_html("Test Email", body))
|
await send_email(to, "Fabled Scribe - Test Email", _email_html("Test Email", body))
|
||||||
|
|||||||
@@ -632,7 +632,7 @@ async def build_context(
|
|||||||
tool_guidance = "\n".join(tool_lines)
|
tool_guidance = "\n".join(tool_lines)
|
||||||
|
|
||||||
static_block = (
|
static_block = (
|
||||||
f"You are a helpful assistant named {assistant_name}, integrated into a note-taking and task-tracking app called Fabled Assistant. "
|
f"You are a helpful assistant named {assistant_name}, integrated into a note-taking and task-tracking app called Fabled Scribe. "
|
||||||
"Help users with their notes, tasks, and general questions. "
|
"Help users with their notes, tasks, and general questions. "
|
||||||
"When note context is provided, use it to give relevant answers.\n\n"
|
"When note context is provided, use it to give relevant answers.\n\n"
|
||||||
f"{tool_guidance}"
|
f"{tool_guidance}"
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ async def notify_security_event(
|
|||||||
</table>
|
</table>
|
||||||
<p style="margin:16px 0 0;color:#9ca3af;font-size:12px;">If this wasn't you, change your password immediately.</p>
|
<p style="margin:16px 0 0;color:#9ca3af;font-size:12px;">If this wasn't you, change your password immediately.</p>
|
||||||
"""
|
"""
|
||||||
await send_email(email, f"Fabled Assistant - {label}", _email_html("Security Alert", body))
|
await send_email(email, f"Fabled Scribe - {label}", _email_html("Security Alert", body))
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to send security notification for user %d", user_id)
|
logger.exception("Failed to send security notification for user %d", user_id)
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ async def send_password_reset_email(email: str, reset_url: str) -> None:
|
|||||||
<p style="margin:0;color:#9ca3af;font-size:12px;">If the button doesn't work, copy and paste this link:</p>
|
<p style="margin:0;color:#9ca3af;font-size:12px;">If the button doesn't work, copy and paste this link:</p>
|
||||||
<p style="margin:4px 0 0;color:#9ca3af;font-size:12px;word-break:break-all;">{reset_url}</p>
|
<p style="margin:4px 0 0;color:#9ca3af;font-size:12px;word-break:break-all;">{reset_url}</p>
|
||||||
"""
|
"""
|
||||||
await send_email(email, "Fabled Assistant - Password Reset", _email_html("Password Reset", body))
|
await send_email(email, "Fabled Scribe - Password Reset", _email_html("Password Reset", body))
|
||||||
|
|
||||||
|
|
||||||
async def send_password_reset_success_email(email: str) -> None:
|
async def send_password_reset_success_email(email: str) -> None:
|
||||||
@@ -125,14 +125,14 @@ async def send_password_reset_success_email(email: str) -> None:
|
|||||||
</p>
|
</p>
|
||||||
<p style="margin:0;color:#6b7280;font-size:13px;">If you didn't make this change, please contact your administrator immediately.</p>
|
<p style="margin:0;color:#6b7280;font-size:13px;">If you didn't make this change, please contact your administrator immediately.</p>
|
||||||
"""
|
"""
|
||||||
await send_email(email, "Fabled Assistant - Password Changed", _email_html("Password Changed", body))
|
await send_email(email, "Fabled Scribe - Password Changed", _email_html("Password Changed", body))
|
||||||
|
|
||||||
|
|
||||||
async def send_invitation_email(email: str, invite_url: str, invited_by_username: str) -> None:
|
async def send_invitation_email(email: str, invite_url: str, invited_by_username: str) -> None:
|
||||||
"""Send a branded invitation email with a registration link."""
|
"""Send a branded invitation email with a registration link."""
|
||||||
body = f"""
|
body = f"""
|
||||||
<p style="margin:0 0 16px;color:#374151;font-size:15px;">
|
<p style="margin:0 0 16px;color:#374151;font-size:15px;">
|
||||||
<strong>{invited_by_username}</strong> has invited you to join Fabled Assistant.
|
<strong>{invited_by_username}</strong> has invited you to join Fabled Scribe.
|
||||||
Click the button below to create your account.
|
Click the button below to create your account.
|
||||||
</p>
|
</p>
|
||||||
<div style="text-align:center;margin:24px 0;">
|
<div style="text-align:center;margin:24px 0;">
|
||||||
@@ -146,7 +146,7 @@ async def send_invitation_email(email: str, invite_url: str, invited_by_username
|
|||||||
<p style="margin:0;color:#9ca3af;font-size:12px;">If the button doesn't work, copy and paste this link:</p>
|
<p style="margin:0;color:#9ca3af;font-size:12px;">If the button doesn't work, copy and paste this link:</p>
|
||||||
<p style="margin:4px 0 0;color:#9ca3af;font-size:12px;word-break:break-all;">{invite_url}</p>
|
<p style="margin:4px 0 0;color:#9ca3af;font-size:12px;word-break:break-all;">{invite_url}</p>
|
||||||
"""
|
"""
|
||||||
await send_email(email, "Fabled Assistant - You're Invited!", _email_html("You're Invited!", body))
|
await send_email(email, "Fabled Scribe - You're Invited!", _email_html("You're Invited!", body))
|
||||||
|
|
||||||
|
|
||||||
async def check_due_tasks() -> None:
|
async def check_due_tasks() -> None:
|
||||||
@@ -225,7 +225,7 @@ async def check_due_tasks() -> None:
|
|||||||
{task_rows}
|
{task_rows}
|
||||||
</table>
|
</table>
|
||||||
"""
|
"""
|
||||||
await send_email(email, f"Fabled Assistant - {len(user_tasks)} Task(s) Due", _email_html("Task Reminders", body))
|
await send_email(email, f"Fabled Scribe - {len(user_tasks)} Task(s) Due", _email_html("Task Reminders", body))
|
||||||
await log_audit(
|
await log_audit(
|
||||||
"task_reminder",
|
"task_reminder",
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
|
|||||||
Reference in New Issue
Block a user