Releases: Lancetnik/slop-writer
Release list
v0.4.3
A query may contain a semicolon again. The multi-statement guard scanned the whole body for a bare ;, so WHERE text LIKE '%;%', SELECT 'a;b' and any query ending in a -- comment; with one came back as multi-statement — refused before the engine ever saw them.
Nothing was protected by that. The write barrier is ?mode=ro, and the guard exists to give a clear early error; a false refusal closed no hole and cost a valid question — on a real channel, the only query that finds the posts containing a semicolon. sqlite3.complete_statement now decides what terminates a statement: SQLite's own quote- and comment-aware answer, stdlib, so the query path stays importable without a client and grows no SQL parser of its own.
Still refused, unchanged: SELECT 1; DROP TABLE posts, and SELECT 'a;b'; DROP TABLE posts — a semicolon inside a literal hides nothing about the one after it.
slop-writer --version also answers now, reading the same string the MCP handshake reports and the skill's frontmatter copies.
A patch release: no tool argument, schema or skill file changed.
Fixes #57.
v0.4.2
run_query now takes queries: list[Query] rather than a single sql. Every question that does not need a value from another's answer goes in the same call, at one round trip instead of one each.
A statement SQLite refuses comes back as that question's section rather than as a failed call: a query's verdict is content, and isError is the call refusing. One bad statement beside three good answers no longer discards them; only a condition with no per-question answer to give — no database for the channel — raises.
The tool's description and schema also stopped paying for what they already said. run_query is the only always-loaded tool, so its text is the roster's whole per-turn cost: ~645 tokens down to ~470.
A patch release: nothing downstream pins the tool's argument, an agent reads the schema fresh each session, and the skill's files are unchanged.
Rationale, rejected alternatives and the measurements — including the ones that do not support the change at small context — are in adr/0007.
v0.4.1
Four fixes from a field A/B: the same 11-step scenario driven through the 0.2.0 CLI and the 0.4.0 MCP server. The data agreed — an FTS query byte-identical, forwards and reactions equal to the unit — so every fix here is about what a summary says, which is the only channel an agent reads.
The heading says which channel again
# Group summary: fastnewsdev is @fastnewsdev once more. The @ is stripped for identity — @chan and chan are one channel, and the database filename comes off the stripped form — but a heading is display, so it gets the sigil back. Five headings had lost it.
Only username-shaped labels are touched: a group title, an invite link and a numeric id are left as they are.
A join count now says where it came from
- Joins: 20 (added 20) | Leaves: 2 (self 2) used to render identically whether or not the admin log could be read, and the two mean different things. Without admin rights the numbers come from service messages alone — which Telegram suppresses during a join burst, i.e. exactly when a CTA post is working — so they are a floor, not a total.
The summary now states its source on the line carrying the numbers, and says outright when the counts are a floor.
Ranking by a rate has a rule
Two runs of one question can pick different second places, because each row was measured whenever its post was last scraped and a staler row is the smaller one. references/analysis.md now says to refresh a window before ranking it by a rate.
Server startup is quiet
pydantic-settings warned about the MCP SDK's own Settings model on every start, into the stderr the server's own logs share. Resolved at the boundary; startup writes nothing before its first log line.
Also: the agent-facing docs lost the half a lookup already answers — CLAUDE.md 461 lines → 175, README.md 157 → 115 — keeping the rules, pins and gotchas that no tool confesses.
Upgrading from 0.4.0 needs nothing beyond the install: uv tool install --force slop-writer, then slop-writer install to refresh the skill.
v0.4.0
The agent-facing surface is now eleven MCP tools, not three CLIs driven
through Bash. slop-writer is a console script you install once; the scripts
that used to ship inside the skill are gone from it.
uv tool install slop-writer
slop-writer install # wires Claude Code: MCP server, permissions, the skill
slop-writer init # Telegram credentials and the one-time loginRestart your MCP client afterwards — .mcp.json is read at session start only.
What's new
- The MCP server.
slop-writer serve --mcpover stdio. Eight read tools
(scrape_posts,refresh_posts,scan_linked_group,
scan_standalone_group,fetch_subscribers,fetch_views_by_hour,
list_scheduled,run_query) and three write tools (publish_schedule,
publish_reschedule,publish_edit). Results are text — structure travels
inside it, never asstructuredContent. Failures answer with
{code, message, hint}over a closed 16-code vocabulary. install/init/uninstall.installwrites the path-free
.mcp.jsonentry (safe to commit), the permission block, the skill, and a
CLAUDE.mdaddress block, and prints everything it touches.inithandles
credentials and the TTY login.uninstalltakes back exactly whatinstall
wrote and never touches.tg-analytic/.- Publishing is gated by the distribution, not by your config. The three
publish_*tools ship withaskrules thatinstallwrites from the same
module that registers them. - The skill is five files, routing question → tool, plus what the numbers
mean and the invariants that break an answer silently. - A test suite: 341 tests over the package, green on 3.11 and 3.13.
Upgrading from 0.2.0
Your .tg-analytic/ needs nothing: same directory, same .env keys, and the
per-channel DBs self-heal on open. install deletes a
.claude/skills/tg-analytic-skill/ left by a pre-0.4 install — it would
otherwise load alongside the current skill and describe a surface that no
longer exists.
0.3.0 was never published; this release carries it.
Breaking
- The PEP-723 scripts are no longer part of the skill. They survive in
tools/in the repository as dev-only and undocumented. render.summarize_*return a string instead of printing.- The
setup-tg-analyticskill is deleted;slop-writer initis its whole job.
Why the move, what was rejected, and what it cost: docs/adr/0006.
v0.2.0
Stage (1b) of the MCP migration (#22): the ingest pipeline, publish paths and SQL execution move out of the skill's scripts and into slop_writer.
- New modules:
errors,messages,scrape,stats,scheduled,publish,query;groupgains the scan. - The domain raises
SlopWriterError/UsageErrorinstead of printing and exiting, sotyperis no longer a dependency of this package. - Domain functions return their summary shape; rendering is the caller's job.
- The write surface stays isolated:
slop_writer.publishis the only module that can post.
Breaking for anyone importing the 0.1.0 modules directly: tg._require_session/tg._resolve_peer are now require_session/resolve_peer.
v0.1.0
The shared modules move out of the skill's scripts/utils/ and into this package: slop_writer.db, .tg, .render, .markdown, .group. The skill CLIs now resolve them from PyPI instead of sys.path.
db no longer reads the current working directory at import time — data_dir() / env_path() take the project root explicitly, and the CLI layer decides what that root is. _require_session takes the login command from its caller rather than deriving a script path from __file__, which inside an installed package pointed into site-packages.
db and group import no third-party package, so the schema, the SQL helpers and the group classification are usable without a Telegram client.