Skip to main content
Glama

stylesync

A small local model that rewrites a neutral Vietnamese draft in your own voice: your abbreviations, pronouns, code-mixing and sentence length. Coding agents (Claude Code, omp) call it through an MCP server before they write a commit message, a PR/issue comment or a chat message, so what they write sounds like you, not like an LLM.

  • Model: a LoRA (r16) on Gemma 4 E2B, served as GGUF (base Q4_K_M + a 48MB LoRA) by llama-server. One server on 127.0.0.1 is shared by every agent session.

  • Safety net: the model only changes the voice. The MCP server keeps the conventional-commit prefix, trailers and code spans out of the model's reach and checks the result. If anything fails it returns the neutral draft.

  • Data: your own Messenger and Discord exports. A large model (OpenAI Batch here) back-translates each of your messages into a neutral version, which gives (neutral → yours) training pairs.

This repository holds the code, configs, tests and the aggregate reports of one run. It ships no data and no model weights: an adapter trained on private chats memorises them, so the owner's adapter stays private, and yours should too.

Results of the reference run

From reports/ (owner's data, aggregates only):

Training data

8,769 own messages → 17,038 neutral/own pairs; train 12,707 / val 1,649 / test 1,708 (time-based split, 24h embargo)

Training

31 min on an RTX 3060 12GB, 1 epoch

Style on the test set

100% classified as the owner's (base model zero-shot: 6%)

Meaning kept (LLM judge, 1–5)

4.81, against 4.90 for GPT few-shot with 10 retrieved examples

Owner-written commit/comment set (40)

guard pass 100% with code-span masking, meaning 4.97

Blind test (owner, 50 pairs)

68% told apart (goal ≈ 50%: not reached)

Latency

0.4–0.9s per call on an M4 (Metal); ~1.3s on Windows/Linux after the first call

API cost

≈ $1.62 of OpenAI Batch for neutralizing and judging

Full write-up (Vietnamese): reports/project_report.md. Plan and design notes: IMPLEMENTATION_PLAN.md.

Related MCP server: Natural Voice MCP

Layout

Path

What

src/stylesync/ingest/, clean/

Phase 1: parse exports, burst merge, PII scrub, filters, style drift

src/stylesync/neutralize/

Phase 2: back-translation with the OpenAI Batch API, validator

src/stylesync/dataset/

Phase 3: time-based split, TRL prompt-completion files

src/stylesync/train/

Phase 4: LoRA SFT with Unsloth + TRL (CUDA, Linux/WSL2)

src/stylesync/eval/

Phase 5: style classifier, stylometry, LLM judge, gate

src/stylesync/export/

Phase 6: LoRA → GGUF, parity against PyTorch, install

src/stylesync/mcp_server.py, llama.py

Phase 7: MCP tools rewrite_style, rewrite_feedback and health, shared llama-server backend

src/stylesync/history.py, feedback.py

Usage history and the owner's corrections, exported as training pairs

configs/

One YAML per phase; threads.example.yaml shows the shape of the generated thread list

integrations/omp/

omp skill and AGENTS.md rule

spike/phase0/

Model/runtime benchmark with 40 synthetic sentences

reports/

Aggregate reports of the reference run

Train your own voice

Only train on messages you wrote, keep data/, runs/ and models/ private (they are gitignored), and do not publish the adapter. Python 3.11–3.13 and uv.

  1. Exports. Put your Facebook export (your_facebook_activity/, JSON) and your Discord data package (package/) in the repo root, or edit the paths and cutoffs in configs/filter.yaml.

  2. Clean (Phase 1). uv run stylesync-data writes data/processed/mine.jsonl, configs/threads.yaml (set include: false to drop a thread, then run again) and reports/data.md.

  3. Neutralize (Phase 2). Needs OPENAI_API_KEY in the environment; only PII-scrubbed text is sent, with store: false. Try uv run stylesync-neutralize run --pilot and review --n 100 first, then uv run stylesync-neutralize run. Batches can take hours.

  4. Dataset (Phase 3). uv run stylesync-dataset.

  5. Train (Phase 4), on a CUDA GPU under Linux/WSL2 (8–12GB VRAM). uv sync --group train, then uv run --group train stylesync-train --exp gemma4-e2b-r16-e1. Experiments live in configs/train.yaml; add --max-steps 20 --limit 400 for a smoke test and --resume to continue from a checkpoint.

  6. Eval (Phase 5, optional). uv run stylesync-eval gen --system <name> (GPU), then fewshot, judge and report (OpenAI Batch). Settings in configs/eval.yaml.

  7. Export (Phase 6). Put the base GGUF (gemma-4-E2B-it-Q4_K_M.gguf, Unsloth's Q4_K_M of google/gemma-4-E2B-it in the reference run) in models/gguf/, copy runs/<exp>/adapter to this machine, and set up the converter that configs/export.yaml points at: a llama.cpp checkout in tools/llama.cpp and a venv in tools/venv-convert with requirements/requirements-convert_lora_to_gguf.txt and transformers>=5.17. Then uv run stylesync-export convert and, optionally, parity and report.

Tests need no model and no data: uv run pytest -q.

Use with Claude Code

Needs llama-server (llama.cpp) on PATH and the GGUF build from step 7.

uv run stylesync-export install
claude mcp add --scope user stylesync -- uv run --directory /path/to/fine-tune-chat stylesync-mcp

If Claude Code runs from an app whose PATH lacks Homebrew, give absolute paths: /opt/homebrew/bin/uv as the command and -e STYLESYNC_LLAMA_SERVER=/opt/homebrew/bin/llama-server (after the server name).

install copies the base and LoRA GGUF to platformdirs.user_cache_dir("stylesync") (macOS: ~/Library/Caches/stylesync). The first rewrite_style call starts one llama-server on 127.0.0.1:18080, detached, and later sessions reuse it; its log is llama-server.log next to the models. Overrides: STYLESYNC_LLAMA_URL, STYLESYNC_MODEL_DIR, STYLESYNC_BASE_GGUF, STYLESYNC_LORA_GGUF, STYLESYNC_LLAMA_SERVER.

The tool description alone is not enough for Claude Code to call a deferred MCP tool (Phase 0), so add a rule to ~/.claude/CLAUDE.md:

## stylesync
- Trước `git commit`, `gh pr comment`, `gh issue comment` hoặc gửi tin thay t: viết bản trung tính tiếng Việt
  (xưng anh/em như t, kh dùng tôi/bạn; code, path, identifier để trong backtick), gọi
  `mcp__stylesync__rewrite_style` (task: commit | comment | message) và dùng nguyên văn `text` trả về.
- `guard_passed: false` → dùng `text` (bản trung tính) và báo t lý do trong `warnings`.
- T chê bản viết lại ("viết lại", "kh giống t", "bỏ chữ X"…) hoặc tự gõ bản khác: sửa tay theo ý t (kh gọi
  lại `rewrite_style`, ra y chang), dùng bản mới, rồi gọi ngay `mcp__stylesync__rewrite_feedback` (`id` của lần
  rewrite, `final_text` = nguyên văn bản mới đầy đủ, `by`: `owner` nếu t gõ nguyên câu, `agent` nếu m sửa theo
  lời t). Mỗi lần sửa tiếp lại gọi với bản mới nhất. T nói ok luôn → gọi với `final_text` rỗng; t kh nói gì thì thôi.
- Tin gửi người thật: đưa t xem trước, kh tự gửi.

Change the pronouns in the rule to the ones you use.

Collect corrections for the next training run

Each rewrite_style call is logged to history.jsonl in platformdirs.user_data_dir("stylesync") (macOS: ~/Library/Application Support/stylesync), owner-only and outside the repo; the tool returns its id. When you ask the agent to change a rewrite and then approve a version, the rule above has it call rewrite_feedback with that id and the approved text. A rewrite with no feedback counts as used as is after 24 hours. STYLESYNC_HISTORY moves the file; STYLESYNC_HISTORY=off turns logging off.

uv run stylesync-feedback stats    # counts, correction rate, progress toward 500 corrections
uv run stylesync-feedback review   # go through your corrections, drop the bad ones
uv run stylesync-feedback export   # PII-scrubbed pairs -> data/processed/feedback.jsonl

Adding these pairs to the training set is not built yet (plan section 10.6).

Use with omp

omp (oh-my-pi) reads its own config, not Claude Code's. Add the server to ~/.omp/agent/mcp.json under mcpServers (the tool is then mcp__stylesync_rewrite_style, called through xd://):

"stylesync": {
  "type": "stdio",
  "command": "/opt/homebrew/bin/uv",
  "args": ["run", "--directory", "/path/to/fine-tune-chat", "stylesync-mcp"],
  "env": {"STYLESYNC_LLAMA_SERVER": "/opt/homebrew/bin/llama-server"}
}

Then link the skill and append the rule to the user-level context file:

ln -s "$PWD/integrations/omp/stylesync" ~/.agents/skills/stylesync
cat integrations/omp/AGENTS.md >> ~/.omp/agent/AGENTS.md

Stop the backend: pkill -f "llama-server.*stylesync-lora" (Windows: end llama-server.exe in Task Manager).

Limits

  • Vietnamese only; one voice per adapter (no tone switch).

  • The blind test did not reach chance level, and the judge shares a model with the strongest baseline.

  • CI (.github/workflows/ci.yml) runs the tests on Linux, macOS and Windows; the model itself was run by hand on macOS (Metal), Windows and WSL2.

License

MIT, see LICENSE. The license covers this code only. Base models keep their own licenses (Gemma 4: Apache 2.0), and a model trained on chat exports carries the privacy of everyone in those chats.

Available Tools

2 tools
healthA

Report the local llama-server backend: reachable, stylesync adapter loaded, model files, last latency.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries the full burden, and it does disclose the report contents (reachable, adapter loaded, model files, last latency), which implies a non-mutating diagnostic. It does not say what happens when the backend is unreachable — a returned status versus an error — nor whether the call can block on a network probe, which matters for a health endpoint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One compact sentence, front-loaded with the verb and target, then a colon-delimited list of reported facets. Every token carries information; there is no preamble or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no parameters, the description effectively serves as the return-shape documentation by enumerating the reported fields, which is what an agent needs to interpret the result. The remaining gap is failure semantics — whether an unreachable backend produces a report value or throws.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has zero parameters, so there is nothing for the description to disambiguate; the baseline of 4 applies. The description correctly adds no parameter detail it does not need.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb (Report) and a specific resource (the local llama-server backend), then enumerates the exact facets reported: reachability, stylesync adapter load state, model files, last latency. That is clearly distinct from the only sibling, rewrite_style, but the differentiation is implicit rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the name and the diagnostic verb: call this to verify the backend before or after invoking rewrite_style. However, the description never says when to call it, when not to, or names rewrite_style as the downstream consumer, so the agent must infer the routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rewrite_styleA

Rewrite a neutral Vietnamese draft into the owner's own voice (casual, abbreviations, Gen Z).

Call this before writing a commit message, a PR/issue comment, or a chat message on the
owner's behalf. `text` is a complete neutral draft in Vietnamese that addresses people with
anh/em, as the owner does (not tôi/bạn); keep code, paths and identifiers in backticks.
`task` is message | commit | comment; for a commit pass the whole message: the
`type(scope):` prefix and trailers are kept by code. Use the returned `text` VERBATIM: do
not fix spelling, abbreviations or tone. If `guard_passed` is false, `text` is the neutral
draft; use it and tell the owner why (`warnings`). Never send a message to another person
without the owner seeing it first.
NameRequiredDescriptionDefault
taskNomessage
textYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses that code preserves the type(scope): prefix and trailers for commits, defines the guard_passed=false fallback (text reverts to the neutral draft, warnings explains why), and mandates verbatim use of the returned text. These are non-obvious behaviors an agent could not infer from the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, then the when-to-use, then parameter details and the verbatim/fallback rules. Dense but nearly every clause adds operative information; the closing safety instruction is policy-adjacent rather than tool behavior, a minor surplus.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description explains the return contract (text, guard_passed, warnings) and the exact obligation to reuse text verbatim. For a two-parameter transformation tool this leaves nothing an agent needs in order to call and consume it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the task param has no enum in the schema, yet the description compensates fully: text is a complete neutral Vietnamese draft addressing people as anh/em (not tôi/bạn) with code/paths/identifiers in backticks, and task is enumerated as message | commit | comment with commit-specific guidance to pass the whole message.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource with a concrete transformation target: rewrite a neutral Vietnamese draft into the owner's voice (casual, abbreviations, Gen Z). The input and output artifacts are both named, so an agent knows exactly what goes in and what comes out.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to call it (before writing a commit message, PR/issue comment, or chat message on the owner's behalf) and adds a hard constraint: never send a message to another person without the owner seeing it first. There is only one sibling (health), so no alternative routing is needed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.0.1
    • First observedhealth
    • First observedrewrite_style

TDQS

A4.1/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: rewrite_style transforms text into the owner's voice, while health reports backend status. There is no realistic overlap or risk of misselection between them.

Naming Consistency4/5

rewrite_style follows a clear verb_noun snake_case pattern, while health is a standalone noun. This is a minor deviation, but the meaning remains obvious and predictable.

Tool Count3/5

The surface is very narrow: one core transformation tool plus a health check. Two tools is borderline thin, though it may be appropriate for a tightly scoped style-rewriting backend.

Completeness4/5

The core rewrite operation covers message, commit, and comment use cases with guard and warning feedback, and health covers backend visibility. Minor gaps remain around style configuration or inspecting the owner profile, but core workflows are covered.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    C
    maintenance
    Enables LLMs to access a user's personal writing context—voice, style, opinions, expertise, projects, and communication patterns—via curated markdown files, helping the LLM match the user's voice when generating written content.
    2
    2
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides writing guidance and mechanical lint checks to help AI clients draft or rewrite content in a specific natural voice.
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables agents to rewrite drafts in your own voice, adapted to the occasion and channel, by learning your style from samples you wrote and from feedback on what you actually send. Supports multiple "Hands" per account with tools for creating, updating, and inspecting learned rules, style cards, and usage.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to learn a user's Korean writing style from their own posts, build style guides, and check drafts against that style, including per-kind conventions for blogs, notices, emails, and other writing types.
    12
    MIT