prdigest 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +183 -94
- data/SECURITY.md +4 -5
- data/configs/config.example.yml +3 -12
- data/lib/prdigest/cli.rb +24 -74
- data/lib/prdigest/collector.rb +4 -2
- data/lib/prdigest/config.rb +4 -29
- data/lib/prdigest/digest.rb +9 -3
- data/lib/prdigest/document.rb +131 -0
- data/lib/prdigest/facts.rb +5 -1
- data/lib/prdigest/facts_runner.rb +4 -2
- data/lib/prdigest/github.rb +61 -9
- data/lib/prdigest/openai_compatible.rb +2 -7
- data/lib/prdigest/prose_renderer.rb +3 -6
- data/lib/prdigest/prose_runner.rb +1 -1
- data/lib/prdigest/telegram.rb +0 -1
- data/lib/prdigest/version.rb +1 -1
- data/lib/prdigest.rb +1 -5
- data/scripts/systemd/prdigest.service +1 -1
- metadata +5 -9
- data/lib/prdigest/renderer.rb +0 -134
- data/lib/prdigest/result.rb +0 -83
- data/lib/prdigest/runner.rb +0 -168
- data/lib/prdigest/schedule.rb +0 -30
- data/lib/prdigest/state.rb +0 -147
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85eefac373cdb54b93c372913fcb97cdcd4f1c003aa7cae487355ffd4da2e5e6
|
|
4
|
+
data.tar.gz: cb91e2f60e8ae566485dbdb7f56053a93b01df8fe357a9b0b6442c616c1498ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c5009123d2b0df8c39612e47c89ea794a98383808cc709ddbdd1b415d32b71881733007a26f5c3dff69b13027a6c0c9e1ab59e79ee63832c363584b3ef32dc6
|
|
7
|
+
data.tar.gz: ab5fd25f659459f3dbd9a787747756bfd9f73c7cc7a56d237529eea18792a15515ac27c9f8a59e954db557338e967c3f639f7c77e318d697255e89be51307d18
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 - 2026-09-16
|
|
4
|
+
|
|
5
|
+
- Add bounded PR descriptions and relevant patches to facts, plus the reusable
|
|
6
|
+
provider-free `Prdigest::Document` generation boundary for embedders.
|
|
7
|
+
- Generate one concise editorial Markdown digest with dated and grouped
|
|
8
|
+
headings, user-facing outcomes, and clickable source PR links.
|
|
9
|
+
- Canonicalize renamed repository aliases before collection and deduplicate
|
|
10
|
+
repeated repository configuration.
|
|
11
|
+
|
|
12
|
+
## 0.3.0 - 2026-07-27
|
|
13
|
+
|
|
14
|
+
- Remove `prdigest run`, deterministic Telegram HTML, schedule/cursor state,
|
|
15
|
+
catch-up processing, the `prdigest-result` envelope, and the `serve` stub.
|
|
16
|
+
- Keep `prdigest facts` for agents and `prdigest prose` for stdout or
|
|
17
|
+
checkpointed plain-text Telegram delivery.
|
|
18
|
+
- Make `prdigest prose --deliver` the systemd and container default.
|
|
19
|
+
|
|
3
20
|
## 0.2.0 - 2026-07-26
|
|
4
21
|
|
|
5
22
|
- Add a deterministic `prdigest facts` JSON contract over the canonical
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<h1>PRDigest</h1>
|
|
4
4
|
|
|
5
|
-
<p><strong>One
|
|
5
|
+
<p><strong>One merged-PR facts contract. Prose for people and JSON for agents.</strong></p>
|
|
6
6
|
|
|
7
7
|
<p>
|
|
8
8
|
<a href="https://github.com/ivankuznetsov/prdigest/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/ivankuznetsov/prdigest/actions/workflows/ci.yml/badge.svg"></a>
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
OpenAI-compatible Chat Completions endpoint.
|
|
15
|
+
Give OpenClaw and other agents stable merged-PR facts, or generate concise<br>
|
|
16
|
+
prose through any OpenAI-compatible Chat Completions endpoint.
|
|
18
17
|
</p>
|
|
19
18
|
|
|
20
19
|
<p>
|
|
21
20
|
<a href="#quick-start">Quick start</a> ·
|
|
22
21
|
<a href="#choose-a-mode">Choose a mode</a> ·
|
|
22
|
+
<a href="#standalone-telegram-bot">Telegram bot</a> ·
|
|
23
23
|
<a href="#configuration">Configuration</a> ·
|
|
24
24
|
<a href="#deployment">Deploy</a> ·
|
|
25
25
|
<a href="#openclaw">OpenClaw</a>
|
|
@@ -35,20 +35,28 @@ PRDigest keeps collection separate from presentation. Every mode starts from
|
|
|
35
35
|
the same ordered, immutable facts, so adding an agent or prose model never
|
|
36
36
|
changes what was fetched from GitHub.
|
|
37
37
|
|
|
38
|
+
Facts include bounded PR descriptions and relevant file patches for prose
|
|
39
|
+
generation. `description_truncated`, each patch's `truncated` or `omitted`,
|
|
40
|
+
and `patches_omitted` explicitly mark incomplete evidence; consumers must not
|
|
41
|
+
present it as a full diff. Embedders can call
|
|
42
|
+
`Prdigest::Document.generate(facts:, generator:)` with precollected facts and
|
|
43
|
+
their own generator. It makes no provider, GitHub, or Telegram call itself.
|
|
44
|
+
Prompt construction keeps every PR reference, title, and description, trimming
|
|
45
|
+
patches first with explicit metadata and failing if non-patch metadata cannot
|
|
46
|
+
fit.
|
|
47
|
+
|
|
38
48
|
```mermaid
|
|
39
49
|
flowchart LR
|
|
40
50
|
GH[GitHub repositories] --> C[Canonical collector]
|
|
41
|
-
C --> RUN["prdigest run<br/>deterministic Telegram"]
|
|
42
51
|
C --> FACTS["prdigest facts<br/>versioned JSON"]
|
|
43
52
|
C --> PROSE["prdigest prose<br/>provider-written text"]
|
|
44
53
|
FACTS --> OC[OpenClaw or another client]
|
|
45
54
|
PROSE --> OUT[stdout or explicit Telegram delivery]
|
|
46
55
|
```
|
|
47
56
|
|
|
48
|
-
- **
|
|
49
|
-
shape are
|
|
50
|
-
- **AI stays
|
|
51
|
-
prose provider.
|
|
57
|
+
- **Stable facts for agents** — repository order, pull-request order, and JSON
|
|
58
|
+
shape are deterministic.
|
|
59
|
+
- **AI stays explicit** — `facts` never configures or contacts a prose provider.
|
|
52
60
|
- **Safe to resume** — delivery checkpoints prevent already accepted chunks
|
|
53
61
|
from being sent twice.
|
|
54
62
|
- **Secrets stay out of config** — YAML names environment variables; it never
|
|
@@ -58,10 +66,9 @@ flowchart LR
|
|
|
58
66
|
|
|
59
67
|
| What you need | Command | Result | Side effects |
|
|
60
68
|
|---|---|---|---|
|
|
61
|
-
|
|
|
62
|
-
| Facts for OpenClaw or another client | `prdigest facts` | `prdigest-facts` JSON on stdout | No schedule state, Telegram, or AI provider |
|
|
69
|
+
| Facts for OpenClaw or another agent | `prdigest facts` | `prdigest-facts` JSON on stdout | No Telegram, provider, or delivery state |
|
|
63
70
|
| Provider-written text | `prdigest prose` | Plain text on stdout | Fresh run calls GitHub, then the provider; no Telegram or checkpoint |
|
|
64
|
-
|
|
|
71
|
+
| Scheduled or one-off Telegram prose | `prdigest prose --deliver` | Checkpointed plain-text Telegram delivery | Fresh run calls GitHub and provider, persists, then sends; resume reuses the checkpoint |
|
|
65
72
|
|
|
66
73
|
All modes accept `--date YYYY-MM-DD` and repeatable
|
|
67
74
|
`--repo OWNER/NAME` overrides. Repository order is always preserved.
|
|
@@ -84,7 +91,6 @@ config to state paths writable by your user:
|
|
|
84
91
|
|
|
85
92
|
```yaml
|
|
86
93
|
state:
|
|
87
|
-
path: tmp/prdigest/state.json
|
|
88
94
|
delivery_path: tmp/prdigest/deliveries
|
|
89
95
|
```
|
|
90
96
|
|
|
@@ -96,7 +102,7 @@ mode you plan to run:
|
|
|
96
102
|
|
|
97
103
|
```sh
|
|
98
104
|
export GITHUB_TOKEN=github_pat_...
|
|
99
|
-
export TELEGRAM_BOT_TOKEN=... #
|
|
105
|
+
export TELEGRAM_BOT_TOKEN=... # prose --deliver only
|
|
100
106
|
export OPENROUTER_API_KEY=... # prose only; use your configured env name
|
|
101
107
|
```
|
|
102
108
|
|
|
@@ -107,12 +113,9 @@ allowlisted destination chat.
|
|
|
107
113
|
### 2. Try the read-only paths
|
|
108
114
|
|
|
109
115
|
```sh
|
|
110
|
-
# Stable JSON: no
|
|
116
|
+
# Stable JSON: no Telegram, delivery state, or prose provider
|
|
111
117
|
bundle exec prdigest facts --config prdigest.yml
|
|
112
118
|
|
|
113
|
-
# Deterministic Telegram preview: fetches GitHub but does not send or save state
|
|
114
|
-
bundle exec prdigest run --config prdigest.yml --dry-run
|
|
115
|
-
|
|
116
119
|
# Provider-written text on stdout: no Telegram or delivery checkpoint
|
|
117
120
|
bundle exec prdigest prose --config prdigest.yml
|
|
118
121
|
```
|
|
@@ -120,9 +123,6 @@ bundle exec prdigest prose --config prdigest.yml
|
|
|
120
123
|
### 3. Deliver intentionally
|
|
121
124
|
|
|
122
125
|
```sh
|
|
123
|
-
# Scheduled deterministic delivery
|
|
124
|
-
bundle exec prdigest run --config prdigest.yml
|
|
125
|
-
|
|
126
126
|
# Provider-written delivery, checkpointed before the first Telegram request
|
|
127
127
|
bundle exec prdigest prose --config prdigest.yml --deliver
|
|
128
128
|
```
|
|
@@ -131,7 +131,7 @@ To build and install the current checkout as a gem without publishing it:
|
|
|
131
131
|
|
|
132
132
|
```sh
|
|
133
133
|
gem build prdigest.gemspec
|
|
134
|
-
gem install prdigest-0.
|
|
134
|
+
gem install prdigest-0.4.0.gem
|
|
135
135
|
prdigest version
|
|
136
136
|
```
|
|
137
137
|
|
|
@@ -167,13 +167,8 @@ github:
|
|
|
167
167
|
- owner/api
|
|
168
168
|
- owner/web
|
|
169
169
|
|
|
170
|
-
schedule:
|
|
171
|
-
max_catchup_days: 7
|
|
172
|
-
|
|
173
170
|
digest:
|
|
174
171
|
line_stats: true
|
|
175
|
-
send_empty: true
|
|
176
|
-
empty_message: "Merged PR digest — {date}\nTotal: 0 PRs"
|
|
177
172
|
|
|
178
173
|
telegram:
|
|
179
174
|
token_env: TELEGRAM_BOT_TOKEN
|
|
@@ -181,7 +176,6 @@ telegram:
|
|
|
181
176
|
chat_id: -1001234567890
|
|
182
177
|
|
|
183
178
|
state:
|
|
184
|
-
path: /var/lib/prdigest/state.json
|
|
185
179
|
delivery_path: /var/lib/prdigest/deliveries
|
|
186
180
|
|
|
187
181
|
prose:
|
|
@@ -198,12 +192,10 @@ annotated configuration.
|
|
|
198
192
|
<summary><strong>Configuration rules</strong></summary>
|
|
199
193
|
|
|
200
194
|
- Repository order controls digest order.
|
|
201
|
-
- `max_catchup_days` must be between `1` and `30`.
|
|
202
195
|
- `chat_id` must appear in the non-empty allowlist. Extra IDs are accepted for
|
|
203
196
|
schema compatibility, but delivery sends only to `chat_id`.
|
|
204
197
|
- Token values belong in environment variables, never YAML.
|
|
205
|
-
- The prose block is validated only for `prdigest prose`;
|
|
206
|
-
enable provider access for deterministic commands.
|
|
198
|
+
- The prose block is validated only for `prdigest prose`; `facts` ignores it.
|
|
207
199
|
- Remote provider URLs require HTTPS. Plaintext HTTP is accepted only for exact
|
|
208
200
|
loopback hosts.
|
|
209
201
|
|
|
@@ -212,24 +204,11 @@ annotated configuration.
|
|
|
212
204
|
## Command reference
|
|
213
205
|
|
|
214
206
|
```text
|
|
215
|
-
prdigest run [--config PATH] [--date YYYY-MM-DD] [--dry-run] [--json] [--repo OWNER/NAME ...]
|
|
216
207
|
prdigest facts [--config PATH] [--date YYYY-MM-DD] [--repo OWNER/NAME ...]
|
|
217
208
|
prdigest prose [--config PATH] [--date YYYY-MM-DD] [--repo OWNER/NAME ...] [--deliver]
|
|
218
|
-
prdigest serve
|
|
219
209
|
prdigest version
|
|
220
210
|
```
|
|
221
211
|
|
|
222
|
-
### `run`
|
|
223
|
-
|
|
224
|
-
An ordinary run processes owed dates oldest-first and advances state after each
|
|
225
|
-
settled day. `--date` replays exactly one local date without reading or writing
|
|
226
|
-
the schedule cursor. `--dry-run` previews an explicit date or yesterday and
|
|
227
|
-
constructs neither state nor Telegram delivery.
|
|
228
|
-
|
|
229
|
-
`--json` emits a versioned `prdigest-result` document with requested, settled,
|
|
230
|
-
skipped, failed, and remaining dates plus delivery progress. `serve` is a
|
|
231
|
-
compatibility stub; use the supplied systemd timer for scheduling.
|
|
232
|
-
|
|
233
212
|
### `facts`
|
|
234
213
|
|
|
235
214
|
Fetches one explicit date or yesterday and prints exactly one JSON document. It
|
|
@@ -275,17 +254,17 @@ Sends the same facts document as untrusted data to
|
|
|
275
254
|
facts without adding to or changing them.
|
|
276
255
|
|
|
277
256
|
Without `--deliver`, prose is printed to stdout. With `--deliver`, the final
|
|
278
|
-
|
|
279
|
-
|
|
257
|
+
plain-text chunks are stored under `state.delivery_path/prose` before the first
|
|
258
|
+
Telegram request. Provider output containing terminal control
|
|
280
259
|
characters is rejected before it can reach stdout, a checkpoint, or Telegram.
|
|
281
260
|
|
|
282
|
-
There is no silent fallback to
|
|
261
|
+
There is no silent fallback to another provider.
|
|
283
262
|
|
|
284
263
|
## Delivery guarantees
|
|
285
264
|
|
|
286
265
|
PRDigest treats sending as a durable protocol, not a best-effort loop:
|
|
287
266
|
|
|
288
|
-
1. Fetch and
|
|
267
|
+
1. Fetch facts and generate the complete prose digest.
|
|
289
268
|
2. Persist the exact final chunk list.
|
|
290
269
|
3. Mark a chunk in flight before sending.
|
|
291
270
|
4. Advance only after Telegram definitely accepts it.
|
|
@@ -295,49 +274,134 @@ A definite Telegram 429/5xx response receives at most three attempts. Transport
|
|
|
295
274
|
failures are ambiguous because Telegram may have accepted the request before
|
|
296
275
|
the connection failed; PRDigest parks them instead of risking a duplicate.
|
|
297
276
|
|
|
298
|
-
|
|
277
|
+
Once a prose payload exists, retry
|
|
299
278
|
loads those exact chunks before checking GitHub or provider credentials, so a
|
|
300
279
|
resume never regenerates different prose. A failure before the payload becomes
|
|
301
280
|
durable can incur another provider request on retry.
|
|
302
281
|
|
|
303
282
|
<details>
|
|
304
|
-
<summary><strong>
|
|
283
|
+
<summary><strong>Date and checkpoint details</strong></summary>
|
|
305
284
|
|
|
306
285
|
Each local day is converted to independent UTC midnight boundaries, including
|
|
307
|
-
DST gaps and repeats.
|
|
308
|
-
|
|
286
|
+
DST gaps and repeats. Both commands use yesterday in the configured timezone
|
|
287
|
+
unless `--date YYYY-MM-DD` is supplied.
|
|
309
288
|
|
|
310
|
-
|
|
289
|
+
Checkpoint directories are mode `0700`; files and locks are mode `0600`.
|
|
290
|
+
A per-date lock prevents concurrent sends for the same repository scope and
|
|
291
|
+
chat. While a completed checkpoint exists, repeating `prose --deliver` for that
|
|
292
|
+
date is a no-op. Moving that checkpoint aside intentionally allows complete
|
|
293
|
+
regeneration and redelivery.
|
|
311
294
|
|
|
312
|
-
|
|
313
|
-
|
|
295
|
+
The supplied systemd timer invokes `prose --deliver` once each day. PRDigest
|
|
296
|
+
does not maintain a catch-up cursor: explicitly run missed dates with `--date`.
|
|
297
|
+
|
|
298
|
+
</details>
|
|
299
|
+
|
|
300
|
+
## Standalone Telegram bot
|
|
301
|
+
|
|
302
|
+
This path creates an outbound bot that posts one provider-written digest per
|
|
303
|
+
day. It does not listen for Telegram commands; scheduling is owned by systemd,
|
|
304
|
+
and every send uses `prdigest prose --deliver`.
|
|
305
|
+
|
|
306
|
+
### 1. Create the bot and destination
|
|
307
|
+
|
|
308
|
+
1. Open Telegram's official `@BotFather`, run `/newbot`, and keep the returned
|
|
309
|
+
token private.
|
|
310
|
+
2. For a direct message, open the new bot and send `/start`. For a group, add
|
|
311
|
+
the bot and send a command such as `/start@your_bot_name` in that group.
|
|
312
|
+
3. Read the destination chat ID from Telegram without putting the token in the
|
|
313
|
+
command line or browser history:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
read -rsp "Telegram bot token: " TELEGRAM_BOT_TOKEN
|
|
317
|
+
echo
|
|
318
|
+
export TELEGRAM_BOT_TOKEN
|
|
319
|
+
ruby -rjson -rnet/http -e '
|
|
320
|
+
uri = URI("https://api.telegram.org/bot#{ENV.fetch("TELEGRAM_BOT_TOKEN")}/getUpdates")
|
|
321
|
+
updates = JSON.parse(Net::HTTP.get(uri)).fetch("result", [])
|
|
322
|
+
keys = %w[message edited_message channel_post edited_channel_post my_chat_member chat_member]
|
|
323
|
+
chats = updates.filter_map { |update| keys.filter_map { |key| update.dig(key, "chat") }.first }
|
|
324
|
+
chats.uniq { |chat| chat.fetch("id") }.each do |chat|
|
|
325
|
+
puts [chat.fetch("id"), chat["title"] || chat["username"] || chat["first_name"]].compact.join("\t")
|
|
326
|
+
end
|
|
327
|
+
'
|
|
328
|
+
unset TELEGRAM_BOT_TOKEN
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
If no ID appears, send the bot another command and repeat the lookup. Direct
|
|
332
|
+
chat IDs are normally positive; groups and channels normally use negative IDs.
|
|
333
|
+
|
|
334
|
+
### 2. Install and configure PRDigest
|
|
335
|
+
|
|
336
|
+
Install the released gem:
|
|
337
|
+
|
|
338
|
+
```sh
|
|
339
|
+
gem install prdigest -v 0.4.0
|
|
340
|
+
prdigest version
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Create a private `prdigest.yml` with the repositories, destination chat, and
|
|
344
|
+
provider you want. This example uses OpenRouter with DeepSeek V4 Flash:
|
|
345
|
+
|
|
346
|
+
```yaml
|
|
347
|
+
timezone: Europe/London
|
|
348
|
+
|
|
349
|
+
github:
|
|
350
|
+
token_env: GITHUB_TOKEN
|
|
351
|
+
repos:
|
|
352
|
+
- owner/api
|
|
353
|
+
- owner/web
|
|
354
|
+
|
|
355
|
+
digest:
|
|
356
|
+
line_stats: true
|
|
357
|
+
|
|
358
|
+
telegram:
|
|
359
|
+
token_env: TELEGRAM_BOT_TOKEN
|
|
360
|
+
chat_id_allowlist: [-1001234567890]
|
|
361
|
+
chat_id: -1001234567890
|
|
362
|
+
|
|
363
|
+
prose:
|
|
364
|
+
provider: openai_compatible
|
|
365
|
+
base_url: https://openrouter.ai/api/v1
|
|
366
|
+
model: deepseek/deepseek-v4-flash
|
|
367
|
+
api_key_env: OPENROUTER_API_KEY
|
|
314
368
|
```
|
|
315
369
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
prevents concurrent sends for the same checkpoint.
|
|
370
|
+
The provider and model above are only an example. Replace them with any
|
|
371
|
+
OpenAI-compatible endpoint and model you prefer.
|
|
319
372
|
|
|
320
|
-
|
|
321
|
-
|
|
373
|
+
Replace both chat IDs with the value from step 1. Keep the GitHub, Telegram,
|
|
374
|
+
and provider tokens in environment variables, never in YAML. Use a dedicated
|
|
375
|
+
bot token, a read-only fine-grained GitHub token, and a provider key with a
|
|
376
|
+
small spend limit.
|
|
322
377
|
|
|
323
|
-
|
|
324
|
-
prefix and processes only the newest window. For a timezone migration, stop the
|
|
325
|
-
timer, preserve and move aside the old state, change timezone, explicitly
|
|
326
|
-
replay any required dates, then restart the timer.
|
|
378
|
+
### 3. Preview, send once, then schedule
|
|
327
379
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
by a later audit. Explicit replay uses the same delivery ledger: while a
|
|
331
|
-
completed checkpoint for that date exists, delivery is a no-op and newly
|
|
332
|
-
rendered content is ignored. Intentionally archiving the checkpoint makes the
|
|
333
|
-
next replay regenerate and send the complete date, not only the delayed pull
|
|
334
|
-
request.
|
|
380
|
+
Read the three credentials without saving them in shell history, preview the
|
|
381
|
+
prose without Telegram, then perform the first intentional delivery:
|
|
335
382
|
|
|
336
|
-
|
|
383
|
+
```bash
|
|
384
|
+
read -rsp "GitHub token: " GITHUB_TOKEN; echo
|
|
385
|
+
read -rsp "Telegram bot token: " TELEGRAM_BOT_TOKEN; echo
|
|
386
|
+
read -rsp "OpenRouter API key: " OPENROUTER_API_KEY; echo
|
|
387
|
+
export GITHUB_TOKEN TELEGRAM_BOT_TOKEN OPENROUTER_API_KEY
|
|
388
|
+
|
|
389
|
+
prdigest prose --config ./prdigest.yml
|
|
390
|
+
prdigest prose --config ./prdigest.yml --deliver
|
|
391
|
+
unset GITHUB_TOKEN TELEGRAM_BOT_TOKEN OPENROUTER_API_KEY
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Once the one-off send succeeds, use the tested [systemd deployment](#deployment)
|
|
395
|
+
below. Its timer runs at `09:05` in the host timezone and sends the previous
|
|
396
|
+
day's digest. Change `OnCalendar` before enabling the timer if you want another
|
|
397
|
+
time. Re-running a completed date is a no-op; use `--date YYYY-MM-DD` for an
|
|
398
|
+
explicit missed day.
|
|
337
399
|
|
|
338
400
|
## OpenClaw
|
|
339
401
|
|
|
340
|
-
The repository includes
|
|
402
|
+
The repository includes the published ClawHub skill
|
|
403
|
+
[`@ivankuznetsov/prdigest`](https://clawhub.ai/ivankuznetsov/skills/prdigest)
|
|
404
|
+
under **Development**, with its source at
|
|
341
405
|
[`openclaw/skills/prdigest/SKILL.md`](openclaw/skills/prdigest/SKILL.md). It:
|
|
342
406
|
|
|
343
407
|
- invokes only `prdigest facts`;
|
|
@@ -346,31 +410,53 @@ The repository includes a ClawHub-ready skill at
|
|
|
346
410
|
- makes no second GitHub request; and
|
|
347
411
|
- never invokes delivery or configures a prose provider.
|
|
348
412
|
|
|
349
|
-
The
|
|
350
|
-
|
|
413
|
+
The Ruby CLI and ClawHub skill are separate installs with independent version
|
|
414
|
+
histories. Pin the Ruby gem release you want, but let ClawHub resolve the
|
|
415
|
+
current skill. To let OpenClaw install both, copy and paste this prompt into an
|
|
416
|
+
OpenClaw chat:
|
|
351
417
|
|
|
352
|
-
```
|
|
353
|
-
|
|
418
|
+
```text
|
|
419
|
+
Install PRDigest 0.4.0 in the same user/runtime context as OpenClaw with
|
|
420
|
+
`gem install prdigest -v 0.4.0`, then install the ClawHub skill with
|
|
421
|
+
`openclaw skills install @ivankuznetsov/prdigest`. This message
|
|
422
|
+
explicitly authorizes those two installs and only the PATH adjustment needed to
|
|
423
|
+
make the installed `prdigest` executable visible to the OpenClaw runtime. Do
|
|
424
|
+
not create PRDigest configuration files, store credentials, enable Telegram
|
|
425
|
+
delivery, or install a scheduler. First verify Ruby 3.2 or newer is available;
|
|
426
|
+
if it is not, stop and report the exact blocker instead of changing system
|
|
427
|
+
packages. After installation, run `prdigest version`, confirm that OpenClaw can
|
|
428
|
+
discover the installed PRDigest skill, and report the installed paths and
|
|
429
|
+
versions without exposing environment variables or tokens.
|
|
354
430
|
```
|
|
355
431
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
432
|
+
For a manual install, run `gem install prdigest -v 0.4.0` and
|
|
433
|
+
`openclaw skills install @ivankuznetsov/prdigest`. The skill gives an agent
|
|
434
|
+
facts-to-prose behavior only; use the
|
|
435
|
+
[standalone Telegram bot](#standalone-telegram-bot) when PRDigest itself should
|
|
436
|
+
generate and deliver scheduled prose.
|
|
437
|
+
|
|
438
|
+
See [`openclaw/README.md`](openclaw/README.md) for local development and
|
|
439
|
+
publication details.
|
|
359
440
|
|
|
360
441
|
## Deployment
|
|
361
442
|
|
|
362
443
|
<details open>
|
|
363
|
-
<summary><strong>systemd on
|
|
444
|
+
<summary><strong>systemd on Linux</strong></summary>
|
|
364
445
|
|
|
365
|
-
Install Ruby
|
|
446
|
+
Install Ruby 3.2 or newer and `tzdata`, then install the gem system-wide. The
|
|
447
|
+
gem contains the example configuration and tested service units, so a source
|
|
448
|
+
checkout is not required:
|
|
366
449
|
|
|
367
450
|
```sh
|
|
368
|
-
sudo
|
|
451
|
+
sudo gem install prdigest -v 0.4.0 --no-document --bindir /usr/local/bin
|
|
452
|
+
gem_root=$(ruby -e 'print Gem::Specification.find_by_name("prdigest", "0.4.0").full_gem_path')
|
|
453
|
+
|
|
454
|
+
sudo useradd --system --home /nonexistent --shell "$(command -v nologin)" prdigest
|
|
369
455
|
sudo install -d -o root -g prdigest -m 0750 /etc/prdigest
|
|
370
|
-
sudo install -o root -g prdigest -m 0640 configs/config.example.yml /etc/prdigest/config.yml
|
|
371
|
-
sudo install -o root -g root -m 0600
|
|
372
|
-
sudo install -o root -g root -m 0644 scripts/systemd/prdigest.service /etc/systemd/system/
|
|
373
|
-
sudo install -o root -g root -m 0644 scripts/systemd/prdigest.timer /etc/systemd/system/
|
|
456
|
+
sudo install -o root -g prdigest -m 0640 "$gem_root/configs/config.example.yml" /etc/prdigest/config.yml
|
|
457
|
+
sudo install -o root -g root -m 0600 /dev/null /etc/prdigest/.env
|
|
458
|
+
sudo install -o root -g root -m 0644 "$gem_root/scripts/systemd/prdigest.service" /etc/systemd/system/
|
|
459
|
+
sudo install -o root -g root -m 0644 "$gem_root/scripts/systemd/prdigest.timer" /etc/systemd/system/
|
|
374
460
|
sudoedit /etc/prdigest/config.yml
|
|
375
461
|
sudoedit /etc/prdigest/.env
|
|
376
462
|
sudo systemctl daemon-reload
|
|
@@ -378,6 +464,10 @@ sudo systemctl start prdigest.service
|
|
|
378
464
|
sudo systemctl enable --now prdigest.timer
|
|
379
465
|
```
|
|
380
466
|
|
|
467
|
+
Put `GITHUB_TOKEN=...`, `TELEGRAM_BOT_TOKEN=...`, and the configured provider
|
|
468
|
+
key such as `OPENROUTER_API_KEY=...` in `/etc/prdigest/.env`. Do not prefix
|
|
469
|
+
systemd environment-file entries with `export`.
|
|
470
|
+
|
|
381
471
|
systemd creates `/var/lib/prdigest` as `prdigest:prdigest` mode `0700`.
|
|
382
472
|
|
|
383
473
|
```sh
|
|
@@ -416,25 +506,24 @@ docker run --rm --env-file /etc/prdigest/.env \
|
|
|
416
506
|
### Rollback
|
|
417
507
|
|
|
418
508
|
Stop the timer, install the prior gem or image, restore its matching config and
|
|
419
|
-
|
|
420
|
-
move a checkpoint forward by hand.
|
|
509
|
+
checkpoint backup, then restart. Never move a checkpoint forward by hand.
|
|
421
510
|
|
|
422
511
|
## Exit codes and troubleshooting
|
|
423
512
|
|
|
424
513
|
| Exit | Meaning | First check |
|
|
425
514
|
|---:|---|---|
|
|
426
|
-
| `0` | Completed
|
|
515
|
+
| `0` | Completed | — |
|
|
427
516
|
| `1` | Unexpected or render failure | Logs and input shape |
|
|
428
517
|
| `2` | CLI/configuration refusal | Config path, YAML, timezone, allowlist, env |
|
|
429
518
|
| `3` | GitHub failure | Token scope, repository access, rate/search limits |
|
|
430
519
|
| `4` | Telegram failure | `error.kind` and delivery checkpoint |
|
|
431
|
-
| `5` |
|
|
432
|
-
| `6` | Failure after durable progress | Settled/skipped dates before retry |
|
|
520
|
+
| `5` | Checkpoint state failure | Path, owner, mode, checkpoint JSON |
|
|
433
521
|
| `7` | Provider failure or ambiguous outcome | Endpoint, model, key env, retry cost |
|
|
434
522
|
|
|
435
523
|
Reconcile `telegram_ambiguous`, `telegram_permanent`, and
|
|
436
|
-
`delivery_checkpoint_permanent` before moving a checkpoint.
|
|
437
|
-
|
|
524
|
+
`delivery_checkpoint_permanent` before moving a checkpoint. Overlapping delivery
|
|
525
|
+
for the same date is refused by the checkpoint lock; the systemd oneshot is the
|
|
526
|
+
normal scheduler.
|
|
438
527
|
|
|
439
528
|
See [`SECURITY.md`](SECURITY.md) for token scope, rotation, and private-data
|
|
440
529
|
flow.
|
data/SECURITY.md
CHANGED
|
@@ -35,8 +35,8 @@ and model boundary so OpenClaw can write prose. Standalone `prdigest prose`
|
|
|
35
35
|
sends the same complete document plus the configured model name to the
|
|
36
36
|
OpenAI-compatible endpoint. Private repositories therefore require an OpenClaw
|
|
37
37
|
deployment or provider whose data handling, retention, and access controls are
|
|
38
|
-
acceptable to the operator.
|
|
39
|
-
|
|
38
|
+
acceptable to the operator. Provider access is never ambient: `prdigest facts`
|
|
39
|
+
does not contact the standalone prose provider.
|
|
40
40
|
|
|
41
41
|
Pull-request fields are untrusted input. The built-in provider prompt and the
|
|
42
42
|
OpenClaw skill explicitly classify the facts JSON as data, never instructions,
|
|
@@ -45,9 +45,8 @@ processing private repository content. The OpenClaw skill must not make a second
|
|
|
45
45
|
GitHub query, deliver messages, install software silently, or print credentials.
|
|
46
46
|
|
|
47
47
|
Prose Telegram checkpoints contain generated text derived from repository facts.
|
|
48
|
-
They use
|
|
49
|
-
|
|
50
|
-
delete them as private repository metadata.
|
|
48
|
+
They use secret-free mode-`0600` files inside mode-`0700` directories. Protect,
|
|
49
|
+
retain, and delete them as private repository metadata.
|
|
51
50
|
|
|
52
51
|
## Reporting
|
|
53
52
|
|
data/configs/config.example.yml
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
# prdigest —
|
|
1
|
+
# prdigest — merged-PR facts for agents and provider-written prose
|
|
2
2
|
|
|
3
3
|
timezone: Europe/London
|
|
4
|
-
schedule:
|
|
5
|
-
# Prefer systemd timer + `prdigest run` for v1.
|
|
6
|
-
# serve mode may honor this later.
|
|
7
|
-
cron: "5 9 * * *"
|
|
8
|
-
# Newest retained catch-up window; valid range is 1..30.
|
|
9
|
-
max_catchup_days: 7
|
|
10
4
|
|
|
11
5
|
github:
|
|
12
6
|
token_env: GITHUB_TOKEN
|
|
@@ -23,11 +17,9 @@ telegram:
|
|
|
23
17
|
|
|
24
18
|
digest:
|
|
25
19
|
line_stats: true
|
|
26
|
-
send_empty: true
|
|
27
|
-
empty_message: "Merged PR digest — {date}\nTotal: 0 PRs"
|
|
28
20
|
|
|
29
21
|
prose:
|
|
30
|
-
# Used only by `prdigest prose`; `
|
|
22
|
+
# Used only by `prdigest prose`; `facts` never contacts this endpoint.
|
|
31
23
|
provider: openai_compatible
|
|
32
24
|
base_url: https://openrouter.ai/api/v1
|
|
33
25
|
# Replace with a model identifier accepted by the configured endpoint.
|
|
@@ -36,6 +28,5 @@ prose:
|
|
|
36
28
|
api_key_env: OPENROUTER_API_KEY
|
|
37
29
|
|
|
38
30
|
state:
|
|
39
|
-
|
|
40
|
-
# Stable rendered chunks and next-unsent position, separate from the date cursor.
|
|
31
|
+
# Stable prose chunks and the next-unsent position.
|
|
41
32
|
delivery_path: /var/lib/prdigest/deliveries
|