diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f7405d5c..87ed1cee 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -4,7 +4,7 @@ - AugmentedQuill is a web-based GUI for AI-assisted prose writing, with a FastAPI backend and a React + TypeScript SPA frontend (Vite). - Backend lives in src/augmentedquill/, frontend in src/frontend/, tests in tests/. -- Python 3.11+ required (CI uses 3.11; local validation on 3.12.3). Node.js 18+ required (CI uses 18). +- Python 3.12+ required (CI uses 3.12). Node.js 24+ required (CI uses 24). - The backend serves the built frontend from static/dist; frontend build output must be present for production-like runs. ## High-level structure and key files @@ -112,6 +112,13 @@ Top-level directories (one level down): - Prefer frontend changes inside features// and keep API calls in services/. - Trust these instructions and only search the repo if something is missing or contradicts these notes. +## Test Data Safety (Mandatory) + +- Never read from or write to real user runtime files under `data/config/`, `data/projects/`, or `data/logs/` when running tests. +- For all automated tests, force the app to use a temporary user data root by setting `AUGQ_USER_DATA_DIR` to a temp directory (for example under `/tmp`). +- Tests and AI-generated test code must isolate runtime paths via environment variables before importing app modules so default config constants resolve into temp paths. +- When tests need projects/registry overrides, use temp values for `AUGQ_PROJECTS_ROOT` and `AUGQ_PROJECTS_REGISTRY` inside that same temp root. + ## Branching and Release Policy The repository uses the following branch layout by default: diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..aadfee01 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,40 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "UTC" + target-branch: "develop" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "python" + + - package-ecosystem: "npm" + directory: "/src/frontend" + schedule: + interval: "weekly" + day: "monday" + time: "06:15" + timezone: "UTC" + target-branch: "develop" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "frontend" + + - package-ecosystem: "npm" + directory: "/electron" + schedule: + interval: "weekly" + day: "monday" + time: "06:30" + timezone: "UTC" + target-branch: "develop" + open-pull-requests-limit: 10 + labels: + - "dependencies" + - "electron" diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 5b35aa7b..a8818fda 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -2,10 +2,11 @@ name: Build Docker Image on: release: - # Run this workflow only when a release is published - types: [published] + # Run this workflow when a release or pre-release is published + types: [published, released, prereleased] env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} @@ -18,7 +19,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Log in to the Container registry uses: docker/login-action@v3 @@ -34,7 +35,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: ${{ github.event_name != 'pull_request' }} diff --git a/.github/workflows/build-electron.yml b/.github/workflows/build-electron.yml deleted file mode 100644 index 73013454..00000000 --- a/.github/workflows/build-electron.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Build Standalone App (Electron) - -on: - release: - # Run this workflow only when a release is published - types: [published] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.11"] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '18' - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - pip install pyinstaller uvicorn - - - name: Set Electron package version from release tag - uses: actions/github-script@v6 - with: - script: | - const fs = require('fs'); - const tag = context.payload.release && context.payload.release.tag_name; - if (!tag) throw new Error('Release tag not available on context.payload.release.tag_name'); - const version = tag.startsWith('v') ? tag.slice(1) : tag; - core.info(`Setting electron/package.json version to ${version}`); - const pkgPath = 'electron/package.json'; - const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); - pkg.version = version; - fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); - - - name: Build Frontend - run: | - cd src/frontend - npm install - npm run build - - - name: Build Backend with PyInstaller (Directory Mode) - run: | - python build_backend.py onedir - - - name: Build Electron App - run: | - cd electron - npm install - npm run dist - env: - GH_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }} - - - name: Upload Artifact (Windows) - if: runner.os == 'Windows' - uses: actions/upload-artifact@v4 - with: - name: AugmentedQuill-Electron-Windows - path: electron/dist/*.exe - if-no-files-found: ignore - - - name: Upload Artifact (Mac) - if: runner.os == 'macOS' - uses: actions/upload-artifact@v4 - with: - name: AugmentedQuill-Electron-Mac - path: electron/dist/*.dmg - if-no-files-found: ignore - - - name: Upload Artifact (Linux) - if: runner.os == 'Linux' - uses: actions/upload-artifact@v4 - with: - name: AugmentedQuill-Electron-Linux - path: electron/dist/*.AppImage - if-no-files-found: ignore diff --git a/.github/workflows/build-pyinstaller.yml b/.github/workflows/build-pyinstaller.yml deleted file mode 100644 index 7097e973..00000000 --- a/.github/workflows/build-pyinstaller.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build Portable Executable (PyInstaller) - -on: - release: - # Run this workflow only when a release is published - types: [published] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.11"] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '18' - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - pip install pyinstaller uvicorn - - - name: Build Frontend - run: | - cd src/frontend - npm install - npm run build - - - name: Build with PyInstaller - run: | - python build_backend.py onefile - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: AugmentedQuill-${{ runner.os }} - path: dist/AugmentedQuill* diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 3dc30665..abf7b146 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -10,15 +10,16 @@ jobs: backend-checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e .[dev] + pip install pip-audit - name: Run ruff run: ruff check . - name: Run black @@ -29,20 +30,23 @@ jobs: - name: Run tests run: pytest + - name: Run pip-audit + run: pip-audit --ignore-vuln CVE-2026-4539 + frontend-checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '24' cache: 'npm' cache-dependency-path: src/frontend/package-lock.json - name: Install dependencies run: | cd src/frontend - npm install + npm ci - name: Run ESLint run: | cd src/frontend @@ -59,3 +63,8 @@ jobs: run: | cd src/frontend npm run build + + - name: Run npm audit + run: | + cd src/frontend + npm audit --audit-level=high diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..724bf604 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,38 @@ +name: CodeQL Analysis + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + schedule: + - cron: "0 4 * * 1" + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: ["python", "javascript-typescript"] + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 41eda173..78ad06a4 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -2,12 +2,16 @@ name: Create Release (manual) on: workflow_dispatch: - branches: [ "main" ] inputs: version: description: 'New release version (tag), e.g. v1.2.3' required: true type: string + prerelease: + description: 'Set as a pre-release' + required: false + default: false + type: boolean body: description: 'Release notes / body' required: false @@ -16,35 +20,150 @@ on: permissions: contents: write +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + RELEASE_VERSION: ${{ github.event.inputs.version }} + jobs: - create-release: + pre-check: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - - name: Ensure workflow is running on `main` + - name: Ensure workflow is running on allowed branches run: | - if [ "${GITHUB_REF#refs/heads/}" != "main" ]; then - echo "This workflow can only be run on the main branch."; + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + if [ "${BRANCH_NAME}" != "main" ] && [ "${BRANCH_NAME}" != "develop" ]; then + echo "This workflow can only be run on main or develop."; + echo "Current branch: ${BRANCH_NAME}"; exit 1; fi - - name: Ensure RELEASE_TOKEN secret is provided + build-backend-onedir: + needs: pre-check + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v5 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + - name: Install dependencies run: | - if [ -z "${{ secrets.RELEASE_TOKEN }}" ]; then - echo "Secret RELEASE_TOKEN is not set. This workflow requires a personal access token (repo scope) in the repo secrets named RELEASE_TOKEN."; - echo "Create a PAT with 'repo' scope and add it at Settings → Secrets → Actions → New repository secret."; - exit 1; - fi + python -m pip install --upgrade pip + pip install -e . + pip install pyinstaller uvicorn + cd src/frontend + npm ci + npm run build + - name: Build Backend (onedir) + run: python build_backend.py onedir + - name: Upload Backend Artifact + uses: actions/upload-artifact@v4 + with: + name: backend-onedir-${{ runner.os }} + path: dist/run_app/ + + build-electron: + needs: build-backend-onedir + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v5 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + - name: Download Backend Artifact + uses: actions/download-artifact@v4 + with: + name: backend-onedir-${{ runner.os }} + path: dist/run_app + - name: Set version in electron/package.json + run: | + VERSION="${{ env.RELEASE_VERSION }}" + VERSION_CLEAN="${VERSION#v}" + # Use python for cross-platform JSON editing to avoid sed incompatibilities + python3 -c "import json; p='electron/package.json'; d=json.load(open(p)); d['version']='${VERSION_CLEAN}'; json.dump(d, open(p, 'w'), indent=2)" + shell: bash + - name: Build Electron App + run: | + cd electron + npm install + npm run dist + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Electron Artifact + uses: actions/upload-artifact@v4 + with: + name: electron-assets-${{ runner.os }} + path: electron/dist/* + + build-backend-onefile: + needs: pre-check + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v5 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + pip install pyinstaller uvicorn + cd src/frontend + npm ci + npm run build + - name: Build Backend (onefile) + run: python build_backend.py onefile + - name: Upload Onefile Artifact + uses: actions/upload-artifact@v4 + with: + name: backend-onefile-${{ runner.os }} + path: dist/AugmentedQuill* + + create-release: + needs: [build-electron, build-backend-onefile] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: all-assets + merge-multiple: true - - name: Create GitHub Release (action) + - name: Create GitHub Release uses: ncipollo/release-action@v1 with: tag: ${{ github.event.inputs.version }} name: ${{ github.event.inputs.version }} body: ${{ github.event.inputs.body }} draft: false - prerelease: false - commit: main - token: ${{ secrets.RELEASE_TOKEN }} + prerelease: ${{ github.event.inputs.prerelease }} + commit: ${{ github.ref_name }} + token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }} + artifacts: "all-assets/*" + allowUpdates: true + replacesArtifacts: true diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000..4f80c9d5 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,23 @@ +name: Dependency Review + +on: + pull_request: + branches: [main, develop] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Dependency review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high diff --git a/.gitignore b/.gitignore index 8d483225..b1c47710 100644 --- a/.gitignore +++ b/.gitignore @@ -215,11 +215,10 @@ marimo/_static/ marimo/_lsp/ __marimo__/ -# User configuration and data -resources/config/*.json -!resources/config/examples/*.json +# User configuration at data data/ # Build artifacts static/dist/ +tools.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d189740b..da4681b3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: language_version: python3 - repo: https://github.com/psf/black - rev: 25.12.0 + rev: 26.1.0 hooks: - id: black # Run black in check-only mode so it does not modify files automatically diff --git a/.vscode/launch.json b/.vscode/launch.json index edc7058b..293430ba 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,15 +8,22 @@ "module": "augmentedquill.main", "args": ["--host", "127.0.0.1", "--port", "28000", "--reload", "--llm-dump"], "console": "integratedTerminal", - "justMyCode": true + "justMyCode": true, + "preLaunchTask": "backend: clear-llm-log", + "env": { + "AUGQ_LLM_DUMP_LEVEL": "normal" + } }, { "name": "Frontend (Vite 28001)", "type": "node-terminal", "request": "launch", "preLaunchTask": "frontend: install", - "command": "npm run dev -- --host 127.0.0.1 --port 28001 --strictPort", - "cwd": "${workspaceFolder}/src/frontend" + "command": "npm run dev -- --port 28001 --strictPort", + "cwd": "${workspaceFolder}/src/frontend", + "env": { + "VITE_BACKEND_PORT": "28000" + } } ], "compounds": [ diff --git a/.vscode/settings.json b/.vscode/settings.json index 768507eb..8131eb21 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "python-envs.pythonProjects": [], "python.terminal.activateEnvironment": true, - "python.defaultInterpreterPath": "./venv/bin/python" + "python.defaultInterpreterPath": "./venv/bin/python", + "python.testing.pytestArgs": ["tests"], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4464e2ba..fee4f68c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -13,6 +13,15 @@ "presentation": { "reveal": "silent" } + }, + { + "label": "backend: clear-llm-log", + "type": "shell", + "command": "rm -f ${workspaceFolder}/data/logs/llm_raw.log", + "presentation": { + "reveal": "silent", + "close": true + } } ] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b5b995e9..fecc74b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,15 +7,15 @@ Thank you for your interest in contributing to AugmentedQuill! We welcome contri 1. **Search for existing issues**: Before starting work, check if there's already an issue or pull request for what you're planning. 2. **Open an issue**: If you find a bug or have a feature request, please open an issue first to discuss it. 3. **Fork the repository**: Create your own fork and work on a feature branch. -4. **Follow code hygiene**: Ensure your code follows the project's hygiene standards (see `doc/ORGANIZATION.md`). +4. **Follow code hygiene**: Ensure your code follows the project's hygiene standards (see `docs/ORGANIZATION.md`). ```bash python tools/enforce_code_hygiene.py . - python tools/check_copyright.py . + pre-commit run --all-files ``` 5. **Run tests**: Make sure all tests pass before submitting. ```bash pytest - cd frontend && npm run test + cd src/frontend && npm run test ``` 6. **Submit a Pull Request**: Provide a clear description of your changes. diff --git a/Dockerfile b/Dockerfile index c7d52f57..a3d548af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM node:18-bullseye AS frontend-builder +FROM node:24-bookworm AS frontend-builder WORKDIR /app/src/frontend COPY src/frontend/package*.json ./ RUN npm ci --no-audit --prefer-offline COPY src/frontend/ ./ RUN npm run build -FROM python:3.11-slim AS runtime +FROM python:3.12-slim AS runtime WORKDIR /app # Install system dependencies diff --git a/INSTALL.md b/INSTALL.md index 7ad73131..2a24f9a8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -19,11 +19,13 @@ If you just want to double-click an app and start writing in your own web browse ## 2. Standalone Desktop App (Electron) +> **Note:** The Electron desktop wrapper is currently **experimental / work in progress**. No official installer is published yet. See `docs/ARCHITECTURE.md` § 8 for development notes. + If you prefer a complete, isolated desktop application experience rather than using your web browser, you can use the Electron version. **How it works:** This bundles the PyInstaller backend with an Electron frontend, giving you a native-feeling windowed application. -**Installation:** +**Installation (once an official release is available):** 1. Go to the [Releases](../../releases) page on GitHub. 2. Download the installer for your operating system (e.g., `AugmentedQuill-Setup.exe` for Windows, `.dmg` for macOS, `.AppImage` for Linux). @@ -41,12 +43,26 @@ If you run a home server, NAS, or just prefer keeping your applications containe **Installation:** -1. Download the `docker-compose.yml` file from this repository. +1. Download the `docker-compose.yml` file from this repository (or create your own using the example below). 2. Open a terminal in the directory containing the file and run: + ```bash docker compose up -d ``` -3. Open your browser and navigate to `http://localhost:8000`. + + This uses image `ghcr.io/stablellamaai/augmentedquill:latest` and maps host port `8000` to container port `8000`. + + If you don’t want to use Docker Compose, you can run directly with Docker: + + ```bash + docker run -d --name augmentedquill \ + -p 8000:8000 \ + -v "$PWD/data:/app/data" \ + -v "$PWD/resources/config:/app/resources/config" \ + ghcr.io/stablellamaai/augmentedquill:latest + ``` + +3. Open your browser and point to `http://localhost:8000/` (or `http://:8000/` when running on a remote machine). _Note: Your stories and configuration will be saved in the `./data` and `./resources/config` directories next to your `docker-compose.yml` file._ @@ -58,8 +74,8 @@ If you want to modify the code, contribute to the project, or just prefer runnin **Prerequisites:** -- Python 3.11+ -- Node.js 18+ +- Python 3.12+ +- Node.js 24+ - Git **Installation:** diff --git a/README.md b/README.md index 42f882ef..e9a377ed 100644 --- a/README.md +++ b/README.md @@ -1,165 +1,147 @@ # AugmentedQuill -**NOTE:** This project is under heavy development. Do not try to use it for any productive work. +[![Build Status](https://img.shields.io/github/actions/workflow/status/StableLlamaAI/AugmentedQuill/ci.yml?branch=develop)](https://github.com/StableLlamaAI/AugmentedQuill/actions) +[![License: GPLv3](https://img.shields.io/badge/license-GPLv3-blue.svg)](LICENSE) -> **Important:** The main base for this code has moved to the GitHub project [StableLlamaAI/AugmentedQuill](https://github.com/StableLlamaAI/AugmentedQuill). The repository `StableLlama/AugmentedQuill` is now a fork of it. +![AugmentedQuill logo](static/images/logo_2048.png) -Augmented Quill - Your Words, Amplified +**Local-first AI writing assistant with story structure + chatbot + image prompt support.** -**AugmentedQuill** is a modern, web-based GUI for AI-assisted prose writing. It leverages Large Language Models (LLMs) via the OpenAI-compatible API to act as a writing partner, editor, and continuation engine. +- You are the author in the driver seat: your story is your story, and the AI is a creative partner (from brainstorm buddy to ghostwriter-style assistant) that supports your voice and choices. +- Join the community: [r/AugmentedQuill](https://www.reddit.com/r/AugmentedQuill/) -It is designed for writers who want to maintain creative control while using AI to overcome writer's block, brainstorm ideas, or edit text. +> **Screenshot placeholder:** Insert your app screenshot here (e.g., `docs/assets/screenshot.png`). -## Features +--- -- **Project Management**: Organize your work into projects. -- **Chapter-Based Writing**: Structure your story with multiple chapters. -- **Dual View Modes**: - - **Raw**: Distraction-free text editing. - - **Visual**: Markdown-rendered reading view. -- **AI Writing Partner**: A dedicated chat interface ("Writing Partner") to brainstorm, ask questions about your story, or get feedback. -- **Smart Editing Tools**: - - **Text Generation**: Continue writing from where you left off. - - **Prompt Overrides**: Customize system prompts for different AI personas (Editor, Writer, Chat). -- **Sourcebook Management**: Maintain a knowledge base of characters, locations, lore, items, organizations, and events with rich metadata, synonyms, and image associations. -- **Chat Session Management**: Save, load, and organize your conversations with the AI. Includes incognito mode for private chats that aren't saved to disk. -- **Web Search Integration**: Enable the AI to search the web for real-time information during conversations to enhance research and world-building. -- **Enhanced Metadata**: Track chapter conflicts, detailed notes, private notes, and comprehensive project information with advanced editing tools. -- **Visual Helpers**: Toggle whitespace characters to spot layout issues. -- **LLM Request Logging**: Debug and monitor AI interactions with detailed request/response logging (optional, for development). -- **Dark/Light Mode**: Fully themable UI. +## 🚀 Quick start (for users) -## Project Types +1. Clone repository and create Python environment + - `git clone https://github.com/StableLlamaAI/AugmentedQuill.git` + - `cd AugmentedQuill` + - `python -m venv venv && source venv/bin/activate` +2. Install dependencies + - `python -m pip install -e ".[dev]"` +3. Build frontend + - `cd src/frontend && npm install && npm run build` +4. Run backend + - Default: `augmentedquill --reload --host 127.0.0.1 --port 8000` +5. Run frontend dev server + - `cd src/frontend && npm run dev` + - Default proxy target: backend on 8000 + - Override with environment variable (if you use a different backend port): + - `VITE_BACKEND_URL=http://127.0.0.1: npm run dev` +6. Open + - `http://127.0.0.1:5173` (vite dev) + - `http://127.0.0.1:8000/` (production mode) -AugmentedQuill supports three project types to accommodate different writing needs: +### ✅ First actions in the app -- **Short Story**: No chapters - perfect for short stories, poems, or notes. Content is stored in a single file. -- **Novel**: Multiple chapters - standard novel structure with sequential chapters. -- **Series**: Multiple books - epic sagas organized into multiple books, each containing chapters. +- Ensure your OpenAI-compatible API provider endpoint is running and reachable (local `llama.cpp`/Ollama endpoint or cloud OpenAI-compatible endpoint), and enter the key/URL in Settings before creating your first project. +- Talk to Writing Partner (AI chat) +- Create a project - or let the Writing Partner do it for you +- Add sourcebook entries - or let the Writing Partner do it for you +- Add chapters / short story content - or let the Writing Partner do it for you +- (Optional) Open Images panel and use prompt generator to create images in external tools -You can convert between project types in the Settings panel, with validation to prevent data loss (e.g., you cannot convert a multi-chapter novel to a short story). +--- -## Architecture +## 📘 User documentation (most important) -The application follows a modular FastAPI architecture: +The complete user guide is in `docs/user_manual/`: -- **Backend**: FastAPI with modular routers for different API endpoints (Python 3.11+). -- **Frontend**: React SPA served by FastAPI (built with Vite, TypeScript). -- **Configuration**: JSON-based config files with environment variable support, versioning, and schema validation. -- **LLM Integration**: Client-side integration with OpenAI-compatible APIs (OpenAI, local models like Ollama/vLLM). +- [Getting started](docs/user_manual/01_getting_started.md) +- [Projects and settings](docs/user_manual/02_projects_and_settings.md) +- [Writing interface](docs/user_manual/03_writing_interface.md) +- [Chapters and books](docs/user_manual/04_chapters_and_books.md) +- [Sourcebook](docs/user_manual/05_sourcebook.md) +- [Project images](docs/user_manual/06_project_images.md) +- [AI chat assistant](docs/user_manual/07_ai_chat_assistant.md) +- [Appearance and display](docs/user_manual/08_appearance_and_display.md) +- [First story tutorial](docs/user_manual/09_tutorial_first_story.md) +- [Writing a story](docs/user_manual/10_writing_a_story.md) +- [Troubleshooting](docs/user_manual/11_troubleshooting.md) -## Quickstart +> Tip: Start with `01_getting_started.md`, then `03_writing_interface.md`. -We offer multiple ways to install and run AugmentedQuill, depending on your technical background and how you prefer to use it: +--- -1. **Portable Executable (PyInstaller)**: For artists and authors who want to double-click an app and start writing in their own web browser. -2. **Standalone Desktop App (Electron)**: For those who prefer a complete, isolated desktop application experience. -3. **Docker**: For self-hosters and home servers. -4. **Developer Setup**: For those who want to modify the code or run from source. +## ✨ What AugmentedQuill does -**Please see our [Installation Guide](INSTALL.md) for detailed instructions on each option.** +- Project-based story authoring (short story, novel, series) +- Multi-chapter and multi-book structure +- Live AI writing assistant and chat (local API key / OpenAI-compatible endpoints) +- Custom prompt pipelines (editor, writer, chat voices) +- Sourcebook (characters, scenes, lore, items, etc.) +- Image metadata + optimized image prompt generation +- Config-driven with JSON templates and env overrides +- Auto-captured project artifacts in `data/projects` -### Development Workflow +--- -If you want to modify the frontend and see changes on the fly: +## ⚠️ Important (security and deployment) -1. **Install Dev Dependencies**: +- Local-first app. No built-in auth. Do not expose to public internet without reverse proxy + access control. +- Security model: single-user local use. +- Browser-based LLM calls may require CORS-friendly endpoints or use internal proxy route `/api/v1/openai/models`. +- AugmentedQuill does not include an LLM server; you must point it at an OpenAI-compatible API endpoint (self-hosted or cloud). For local use, set up a compatible host such as `llama.cpp` endpoints, `Ollama`, or another OpenAI API compliant server. +- For easier setup and releases, try the official Electron or Docker builds provided with each release instead of building from source. - ```bash - pip install -e ".[dev]" - cd src/frontend && npm install - ``` +--- -2. **Run in Development Mode**: - - **Option A (VS Code)**: Use the "Full Stack Dev" launch configuration. This starts the backend and the frontend dev server automatically. - - **Option B (Terminal)**: - - Terminal 1 (Backend): `augmentedquill --reload` - - Terminal 2 (Frontend): `cd src/frontend && npm run dev` - - Open http://127.0.0.1:28001 (Vite Dev Server) for hot-reloading. API requests are proxied to port 28000. +## 🛠️ Developer section (find all dev info here) -## Configuration +### Repo layout -Configuration is JSON-based with environment variable precedence and interpolation. +- Backend: `src/augmentedquill/` +- Frontend: `src/frontend/` +- Integration artifacts: `static/` and `data/` +- Tests: `tests/unit/` +- Config schemas: `resources/schemas/` -- Machine-specific config (API credentials/endpoints): config/machine.json -- Story-specific config (active project): config/story.json -- Environment variables always override JSON values. JSON may include placeholders like ${OPENAI_API_KEY}. +### Development commands -Sample files can be found under config/examples/: +- Backend lint/test + - `ruff check .` + - `black --check .` + - `python -m pytest` +- Frontend: `cd src/frontend && npm run lint && npm run test && npm run build` +- Quick run: `augmentedquill --reload --host 127.0.0.1 --port 28000` -- config/examples/machine.json -- config/examples/story.json +### Configuration paths -Machine config supports multiple OpenAI model endpoints: +Runtime config: -- openai.models: array of endpoints with fields {name, base_url, api_key, model, timeout_s} -- openai.selected: the name of the active endpoint +- `data/config/machine.json` +- `data/config/story.json` +- `data/config/projects.json` -In the Settings UI, you can add multiple endpoints, test availability, and load the list of remote models from an endpoint, then select which to use. All calls to the OpenAI API are done from the browser, not the backend. +Model endpoint variables: -Environment variables recognized for OpenAI: +- `OPENAI_API_KEY` +- `OPENAI_BASE_URL` +- `OPENAI_MODEL` +- `OPENAI_TIMEOUT_S` -- OPENAI_API_KEY -- OPENAI_BASE_URL -- OPENAI_MODEL -- OPENAI_TIMEOUT_S +### QA requirements -Note: Direct browser access to third-party APIs requires proper CORS headers from the API. If your endpoint does not allow browser calls from your origin, you will need to configure an allowed origin or use a CORS-enabled proxy under your control. +- Run `tools/enforce_code_hygiene.py .` after code changes. +- Run `tools/check_copyright.py .`. +- Keep `data/projects/` and `data/logs/` names safe by setting `AUGQ_USER_DATA_DIR` in test runs. -### CORS and model loading +--- -The Settings UI tries to load models directly from your OpenAI-compatible endpoint in the browser. If that direct call is blocked by CORS, the UI will automatically fall back to a same-origin proxy endpoint provided by this app: +## 📄 Links -- POST /api/v1/openai/models with JSON body: {"base_url":"...","api_key":"...","timeout_s":60} -- The server fetches `${base_url}/models` and relays the JSON back to the browser. +- `docs/ARCHITECTURE.md` +- `docs/ORGANIZATION.md` +- `CONTRIBUTING.md` +- `LICENSE` (GPLv3) -This keeps your API key client-provided for development purposes while avoiding cross-origin limitations. For production, prefer configuring your endpoint to allow your app's origin or place a controlled proxy in front of it. +--- -## Tests +## 🧩 Known limitations -After installing with dev dependencies, run tests: - -- `source venv/bin/activate && pytest` - -Notes: - -- Current tests focus on configuration parsing and do not call external services. -- Avoid committing real secrets. Use environment variables or placeholders. - -## License - -This project is licensed under the GNU General Public License v3.0 (GPLv3). See the [LICENSE](LICENSE) file for details. - -Copyright (C) 2026 StableLlama - -## Project Image Settings - -AugmentedQuill allows you to manage reference images for your story. You can upload images, generate descriptions for them using Vision models, and create highly optimized art prompts for generation. - -To maintain visual consistency across your project, you can configure **Project Image Settings**: - -1. Open the **Images** panel (Image icon in the sidebar). -2. Expand the **Project Image Settings** section at the top. -3. **Global Style**: Define a consistent art style (e.g., "Cyberpunk", "Oil Painting", "Watercolor"). This style will be prioritized in all generated prompts. -4. **Additional Information**: Add specific technical parameters or LoRA triggers (e.g., ", dark lighting, no humans"). These details are appended to the generation prompt to ensure your specific generation pipeline rules are followed. - -When you click "Create Prompt" for an image, the AI will intelligently fuse your image's specific description with your global style and additional parameters into a single, optimized prompt line. - -## Branching Strategy - -This repository uses a simple Git Flow variant to keep releases stable while allowing ongoing development: - -- `main` — the stable branch that always reflects the last tagged release. Protected; merges to `main` should be done only via reviewed PRs and after passing CI. -- `develop` — the integration branch for ongoing development. Feature branches are branched from and merged into `develop`. -- `release/vX.Y` — short-lived release candidate branches created from `develop` when preparing a release. -- `hotfix/vX.Y.Z` — branch from `main` for urgent fixes; merge back into both `main` and `develop`. -- `feature/` — feature branches off `develop`. - -Workflow summary: - -1. Develop features on `feature/*` branches and open PRs against `develop`. -2. When ready, create `release/vX.Y` from `develop`, finalize tests and fixes, then merge into `main` and tag `vX.Y`. -3. Merge the release back into `develop` if any release-specific changes were made. -4. For urgent fixes, create `hotfix/*` from `main`, merge into `main` and `develop`, and tag as appropriate. - -See CONTRIBUTING.md for PR and review requirements. +- No multi-user access controls. +- Limited accessibility support. +- No real-time external editor sync. diff --git a/build_backend.py b/build_backend.py index 8ae19877..ef449ece 100644 --- a/build_backend.py +++ b/build_backend.py @@ -4,7 +4,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# Purpose: Defines the build backend unit so this responsibility stays isolated, testable, and easy to evolve. + +"""Defines the build backend unit so this responsibility stays isolated, testable, and easy to evolve.""" import os import sys @@ -20,6 +21,11 @@ def main(): if len(sys.argv) > 1 and sys.argv[1] == "onedir": mode = "--onedir" name = "run_app" + # We need an entry point that calls main() + # In the original it was run_app.py, but we can use a wrapper or -m + # Let's create a minimal run_app.py as well or use --entry-point if pyinstaller supports it via hook + + # We will create run_app.py to be the entry point for PyInstaller cmd = [ "pyinstaller", @@ -30,6 +36,10 @@ def main(): f"static/dist{sep}static/dist", "--add-data", f"static/images{sep}static/images", + "--add-data", + f"resources{sep}resources", + "--collect-all", + "augmentedquill", "run_app.py", ] diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c5edd6ef..39ec37a3 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -124,3 +124,19 @@ The architecture treats `resources/` as reference/config contracts and `data/` a - Use typed API contracts to avoid shape drift between frontend and backend. - Enforce code hygiene headers with: - `python tools/enforce_code_hygiene.py .` + +## 8) Electron Desktop Wrapper (Experimental) + +The `electron/` directory contains an **experimental** desktop wrapper that embeds the PyInstaller backend and the built frontend into a native windowed application using [Electron](https://www.electronjs.org/). + +**Current status**: Work in progress. There is no CI pipeline, no automated tests, and no official release artifact produced from this wrapper yet. + +**Requirements**: Node.js ≥ 24 (enforced via `electron/package.json` `engines` field). + +**How it works**: + +1. The Python backend is packaged with PyInstaller into `dist/run_app`. +2. Electron starts that binary as a child process and opens a `BrowserWindow` pointed at the local FastAPI server. +3. Building a distributable: `cd electron && npm install && npm run dist` + +Do not rely on the Electron build in production workflows until CI coverage and release automation are in place. diff --git a/docs/user_manual/01_getting_started.md b/docs/user_manual/01_getting_started.md index d15d0610..010dfd5c 100644 --- a/docs/user_manual/01_getting_started.md +++ b/docs/user_manual/01_getting_started.md @@ -2,15 +2,24 @@ AugmentedQuill is designed to be a seamless extension of your creative process. It combines a traditional writing environment with powerful AI tools that understand your story's context. +> Your story belongs to you. AugmentedQuill keeps you in the driver seat: every beat, character voice, and plot decision is always yours. AI is here as a collaborator—to brainstorm, refine, and ghostwrite in ways that match your intent, not replace it. + +## Important Limits (As of 2026) + +- AugmentedQuill is local-first and not designed for public internet deployment without adding your own security layer. +- No built-in user authentication or per-project access control exists. Treat the running instance as trusted local software. +- No external editor sync is provided; project data is kept in local folders (e.g., `data/projects/`). +- Accessibility support is currently not implemented (no dedicated keyboard shortcuts, ARIA, screen-reader enhancements). + ## Core Concepts Before diving in, it's helpful to understand how AugmentedQuill organizes your work: -- **Projects**: A project is the container for your entire book or story. It holds everything related to that specific work. +- **Projects**: A project is the container for your entire short story, novel, or series. It holds everything related to that specific work. - **Story Metadata**: The overarching information about your project — the title, synopsis, style tags, and notes that guide the AI. -- **Chapters**: The actual prose of your story, broken down into manageable sections. In a series, chapters live inside books. +- **Chapters**: The actual prose of your story. A short story exposes one chapter-like writing unit, a novel uses a flat chapter list, and a series stores chapters inside books. - **Sourcebook**: Your story's encyclopedia. This is where you keep track of characters, locations, lore, items, and other important details. The AI uses this to stay consistent. -- **Chat Assistant**: Your AI co-writer. You can brainstorm, ask for suggestions, or have it generate text based on your instructions and the context of your story. +- **Chat Assistant**: Your AI coordinator. You can brainstorm, ask for suggestions, let it maintain metadata, or let it delegate prose writing to WRITING and prose refinement to EDITING. ## The Main Interface @@ -20,7 +29,7 @@ When you open AugmentedQuill, you'll be greeted by the main writing environment. 1. **Left Sidebar** — Your project's control center. Scroll through it to find: - **Story Metadata**: The story title, summary, style tags, and LLM-visible notes at a glance. Click the Edit icon pencil icon to open the full Metadata Editor. - - **Chapters** (or Books & Chapters in a series): The navigation list for your prose. Click any entry to open it in the editor; drag entries to reorder them. + - **Chapters** (or Books & Chapters in a series): The navigation list for your prose. In a short story this shows the single writing unit; in other project types it shows the active chapter structure. Click any entry to open it in the editor; drag entries to reorder them where supported. - **Sourcebook**: A searchable list of every character, location, and lore entry in your world. 2. **Main Area (The Editor)** — The central writing canvas. It shows the active chapter title and body, along with the AI suggestion footer at the bottom. @@ -61,13 +70,18 @@ On mobile a single **View** dropdown (showing the current mode and a chevron) co - **B** (Bold): Wraps the selected text in `**bold**` markers. - **I** (Italic): Wraps the selection in `_italic_` markers. -- **Link** (Link icon): Inserts a `[text](url)` link skeleton. +- **Image**: Opens the project image picker and inserts an `![alt](url)` tag at the cursor. - **H1**, **H2**, **H3**: Prepend the appropriate heading level to the selected line. - **Quote** (blockquote icon): Inserts a `> ` blockquote prefix. - **List** (bullet list icon): Starts an unordered `- ` list. - **Numbered List** (numbered list icon): Starts an ordered `1. ` list. +- **Link** (Link icon): Inserts a `[text](url)` link skeleton. +- **Footnote** (hash icon): Inserts a numbered footnote reference and a matching definition block. +- **Code Block** (code icon): Wraps the selection in a fenced code block ` ``` … ``` `. +- **Subscript** / **Superscript**: Wraps the selection in `~…~` or `^…^`. +- **Strikethrough**: Wraps the selection in `~~…~~`. -On medium screens the heading and list buttons collapse into a **Format** dropdown (the Type icon Type icon with chevron). +On medium screens the less common buttons collapse into a **Format** dropdown (the Type icon Type icon with chevron). See [The Writing Interface](03_writing_interface.md#supported-markdown-elements) for the full list of all supported markdown elements, including tables and inline code. **Chapter AI** — Two quick actions that call the Book Open icon Violet swatch [WRITING model](02_projects_and_settings.md#the-three-ai-models) on the current chapter: diff --git a/docs/user_manual/02_projects_and_settings.md b/docs/user_manual/02_projects_and_settings.md index 6b24f879..6e1bde8f 100644 --- a/docs/user_manual/02_projects_and_settings.md +++ b/docs/user_manual/02_projects_and_settings.md @@ -1,6 +1,21 @@ # Projects and Settings -To access your projects and configure the application, click the Settings icon **Settings** icon (or the logo/title area) in the top navigation bar. This opens the Settings Dialog, which has two main tabs: **Projects** and **Machine Settings**. +To access your projects and configure the application, click the Settings icon **Settings** icon (or the logo/title area) in the top navigation bar. This opens the Settings Dialog, which has three main tabs: **Projects**, **Machine Settings**, and **About**. + +## The About Tab + +The About tab provides version and runtime information about AugmentedQuill and the environment it is running in: + +- **Version** (from `src/frontend/package.json`) +- **Git revision** (short commit hash) +- **Built** timestamp +- **Python version** (build environment) +- **Node version** (build environment) +- **Browser user agent** (runtime client browser) +- **License** and **copyright** notice +- **GitHub project** link + +This tab is useful for troubleshooting, bug reports, and confirming exactly which code and dependencies are in use. ## The Projects Tab @@ -22,10 +37,11 @@ Three buttons sit above the project list: Each project in the list shows: -- **Project name** — click the Edit icon pencil icon next to the name to edit it inline. A text field appears; press **Enter** or click the **Save** icon to confirm. The name change takes effect on disk immediately. +- **Project name** — click the Edit icon pencil icon next to the name to edit it inline. A text field appears; press **Enter** or click the **Save** icon to confirm. The name change takes effect on disk immediately. While editing you can also change the **Project Language** using the adjacent dropdown. - **Active badge** — the currently loaded project has an "Active" label in place of the Open button. - **Project type selector** — a `