@ghostpaw/questlog 0.9.1 → 0.10.1

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.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  **Agent tasks as quests that finish only with a written outcome.**
8
8
 
9
9
  ```bash
10
- npm install @ghostpaw/questlog
10
+ curl -fsSL https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.sh | sh
11
11
  ```
12
12
 
13
13
  A unified task and calendar model for Node.js, built on SQLite.
@@ -16,14 +16,15 @@ Questlog treats tasks, events, deadlines, recurring commitments, and loose intak
16
16
 
17
17
  ## MCP server
18
18
 
19
- Every client below starts the same stdio server: `npx -y @ghostpaw/questlog mcp`.
19
+ Every client below starts the same stdio server: `ghostpaw-questlog mcp`.
20
+ For npm-based environments, use `npx -y @ghostpaw/questlog mcp` (Node 24+).
20
21
  Data lives in `~/.ghostpaw/questlog.db` unless the `GHOSTPAW_QUESTLOG_DB`
21
22
  environment variable (absolute path) or `--db <path>` says otherwise.
22
23
 
23
24
  **Claude Code**
24
25
 
25
26
  ```bash
26
- claude mcp add ghostpaw-questlog -- npx -y @ghostpaw/questlog mcp
27
+ claude mcp add ghostpaw-questlog -- ghostpaw-questlog mcp
27
28
  ```
28
29
 
29
30
  or as a plugin, which also installs the skill:
@@ -37,8 +38,8 @@ or as a plugin, which also installs the skill:
37
38
 
38
39
  ```toml
39
40
  [mcp_servers.ghostpaw-questlog]
40
- command = "npx"
41
- args = ["-y", "@ghostpaw/questlog", "mcp"]
41
+ command = "ghostpaw-questlog"
42
+ args = ["mcp"]
42
43
  ```
43
44
 
44
45
  **Cursor** (`.cursor/mcp.json`), **Claude Desktop** (`claude_desktop_config.json`)
@@ -48,8 +49,8 @@ or a project `.mcp.json`:
48
49
  {
49
50
  "mcpServers": {
50
51
  "ghostpaw-questlog": {
51
- "command": "npx",
52
- "args": ["-y", "@ghostpaw/questlog", "mcp"]
52
+ "command": "ghostpaw-questlog",
53
+ "args": ["mcp"]
53
54
  }
54
55
  }
55
56
  }
@@ -65,10 +66,10 @@ initialize-based revisions 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.
65
66
 
66
67
  ## CLI
67
68
 
68
- Install globally for `questlog` (`ghostpaw-questlog` is the same binary): `npm i -g @ghostpaw/questlog`.
69
+ Install the [release binary](#install) for `ghostpaw-questlog` (`questlog` is an alias).
69
70
 
70
71
  ```bash
71
- questlog add --title "Review release notes" --tag release --due 2030-01-02
72
+ questlog add "Review release notes" --tag release --due 2030-01-02
72
73
  questlog list --limit 10
73
74
  questlog search release notes --limit 5
74
75
  questlog show 1
@@ -114,32 +115,74 @@ an installation hint on older Node versions, including for help and version.
114
115
  Copy `skills/questlog/` into your agent's skills folder, or print it:
115
116
 
116
117
  ```bash
117
- npx -y @ghostpaw/questlog skill > SKILL.md
118
+ ghostpaw-questlog skill > SKILL.md
118
119
  ```
119
120
 
120
121
  ## Install
121
122
 
122
- The [npm package](https://www.npmjs.com/package/@ghostpaw/questlog) requires Node 24+:
123
+ ### Release binary (default)
124
+
125
+ Each line downloads the latest release asset, checks it against the release’s SHA256SUMS and installs it on the PATH (~/.local/bin on Linux/macOS; the Windows script adds its folder to the user PATH).
126
+
127
+ These lines never edit ~/.profile or other shell startup files: `export PATH` applies to the current shell only. To keep it, add `export PATH="$HOME/.local/bin:$PATH"` to your shell profile yourself.
128
+
129
+ linux-x64:
123
130
 
124
131
  ```sh
125
- npm install -g @ghostpaw/questlog
126
- npx -y @ghostpaw/questlog --help
132
+ curl -fsSL https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.sh | sh -s -- --target linux-x64 && export PATH="$HOME/.local/bin:$PATH"
133
+ ```
134
+
135
+ linux-arm64:
136
+
137
+ ```sh
138
+ curl -fsSL https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.sh | sh -s -- --target linux-arm64 && export PATH="$HOME/.local/bin:$PATH"
127
139
  ```
128
140
 
129
- As a library dependency: `npm install @ghostpaw/questlog`. It uses the built-in
130
- `node:sqlite` module, so there are no runtime dependencies.
141
+ darwin-arm64:
131
142
 
132
- Standalone binaries are available from [GitHub Releases](https://github.com/GhostPawJS/questlog/releases)
133
- for linux-x64, linux-arm64, darwin-arm64, darwin-x64 and windows-x64.
143
+ ```sh
144
+ curl -fsSL https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.sh | sh -s -- --target darwin-arm64 && export PATH="$HOME/.local/bin:$PATH"
145
+ ```
146
+
147
+ darwin-x64:
134
148
 
135
149
  ```sh
136
- curl -fsSL https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.sh | sh
150
+ curl -fsSL https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.sh | sh -s -- --target darwin-x64 && export PATH="$HOME/.local/bin:$PATH"
137
151
  ```
138
152
 
153
+ windows-x64 (PowerShell; persistently adds its folder to the user PATH):
154
+
139
155
  ```powershell
140
156
  irm https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.ps1 | iex
141
157
  ```
142
158
 
159
+ Pinned to a release:
160
+
161
+ ```sh
162
+ curl -fsSL https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.sh | sh -s -- --target linux-x64 --version 0.10.1 && export PATH="$HOME/.local/bin:$PATH"
163
+ ```
164
+
165
+ To skip the persistent Windows user PATH edit (the installer still installs and prints the folder to add):
166
+
167
+ ```powershell
168
+ $env:GHOSTPAW_NO_MODIFY_PATH=1; irm https://raw.githubusercontent.com/GhostPawJS/questlog/main/install.ps1 | iex
169
+ ```
170
+
171
+ ```text
172
+ $ ghostpaw-questlog --version
173
+ questlog 0.10.1 (binary, linux-x64)
174
+ ```
175
+
176
+ Scripts that compare versions should use `--version --short` or `--version --json`.
177
+
178
+ Standalone binaries are available from [GitHub Releases](https://github.com/GhostPawJS/questlog/releases).
179
+
180
+ A session with the 0.9.1 linux-x64 binary against a fresh database:
181
+
182
+ ![questlog 0.9.1: --version, three add calls, list, search, done with an outcome, and show](https://raw.githubusercontent.com/GhostPawJS/questlog/main/docs/assets/cli.png)
183
+
184
+ The same session as an [animated GIF](https://raw.githubusercontent.com/GhostPawJS/questlog/main/docs/assets/cli.gif).
185
+
143
186
  Other release installers:
144
187
 
145
188
  ```sh
@@ -193,6 +236,27 @@ download on macOS, remove quarantine with
193
236
  Linux binaries require glibc 2.28+; use npx on Alpine.
194
237
  See the [binary build workflow](.github/workflows/release-binaries.yml).
195
238
 
239
+ ### npx / npm (for npm-based environments)
240
+
241
+ The [npm package](https://www.npmjs.com/package/@ghostpaw/questlog) requires Node 24+:
242
+
243
+ ```sh
244
+ npx -y @ghostpaw/questlog --help
245
+ npm install -g @ghostpaw/questlog
246
+ ```
247
+
248
+ ### Library (for harnesses embedding it in code)
249
+
250
+ ```sh
251
+ npm install @ghostpaw/questlog
252
+ ```
253
+
254
+ ```ts
255
+ import { initQuestlogTables, read, write } from '@ghostpaw/questlog';
256
+ ```
257
+
258
+ See [library usage](#quick-start). The library uses built-in `node:sqlite` with zero runtime dependencies.
259
+
196
260
  ## Quick Start
197
261
 
198
262
  ```ts
@@ -450,3 +514,101 @@ If this package helps your project, consider sponsoring its maintenance:
450
514
  ---
451
515
 
452
516
  **[Anonyfox](https://anonyfox.com) • [MIT License](LICENSE)**
517
+
518
+ ## Webhooks
519
+
520
+ Configure opt-in outbound wakes with the release binary. No server or scheduler is started.
521
+ With no targets, tick makes no network calls. Targets and retry state live in the database.
522
+
523
+ ```sh
524
+ questlog hook add https://example.org/wake --min-gap 60
525
+ questlog hook add https://example.org/team --tag work --events quest.due,quest.overdue --secret-stdin
526
+ questlog hook label work
527
+ questlog hook list
528
+ questlog tick --dry-run
529
+ questlog tick
530
+ questlog watch --interval 60
531
+ questlog hook test 1
532
+ questlog review deliveries
533
+ questlog show 12
534
+ questlog hook remove 1
535
+ ```
536
+
537
+ Save the signing secret printed once by `hook add` (including its `--json` result).
538
+ `--secret-stdin` reads a supplied secret from standard input; there is no secret flag.
539
+ List output redacts URL userinfo and query strings. Removal deletes the target and its deliveries.
540
+ A target covers the database unless `--tag` restricts it to quests with that tag;
541
+ rumors have no tags and reach database targets only. `--events` defaults to all five events.
542
+ The label defaults to the database filename without directory or extension (`questlog` for memory).
543
+ Only use labels suitable for disclosure to the receiver.
544
+
545
+ For cron:
546
+
547
+ ```cron
548
+ * * * * * questlog tick
549
+ ```
550
+
551
+ Cron's PATH may need `$HOME/.local/bin/questlog tick --db /path/to/quests.db`.
552
+ `watch` repeats tick (default 60 seconds, minimum 5), finishing its current tick on SIGINT/SIGTERM.
553
+ Tick exits 0 after running even when deliveries need retrying. Dry runs roll back all changes,
554
+ including repeatable materialization, and make no network calls.
555
+
556
+ Events are detected from the model at tick time:
557
+
558
+ - `quest.due`: an unresolved, undeleted quest reaches its effective due time (own `due_at`, otherwise its undeleted questline's due time).
559
+ - `quest.overdue`: that effective deadline is strictly earlier than now and the quest remains unresolved.
560
+ - `quest.unblocked`: an unresolved quest is available, all active prerequisites have succeeded or been deleted, and at least one prerequisite succeeded after its unlock was installed. Occurrence time is the latest active prerequisite success or not-before time, whichever is later.
561
+ - `repeatable.spawned`: a concrete repeatable instance was created; occurrence time is its creation time. Tick first materializes due anchors through the existing spawn operation.
562
+ - `rumor.captured`: an undeleted rumor was created; occurrence time is its creation time, even if subsequently settled or dismissed.
563
+
564
+ Each type, entity and occurrence timestamp has a persistent unique event key. Repeated ticks
565
+ and restarts do not create another occurrence. Changes removed before any tick cannot be
566
+ reconstructed. Occurrences earlier than target creation are excluded, preventing backlog floods.
567
+ Tags are evaluated when assigning an event. Already assigned events retain their batch membership.
568
+
569
+ One POST batch per target per tick contains only identifiers, types, timestamps, and the DB label:
570
+
571
+ ```json
572
+ {"type":"questlog.events","db":"work","deliveryId":"d_…","sentAt":"2026-10-28T10:00:00.000Z","events":[{"id":"e_…","type":"quest.due","questId":12,"occurredAt":"2026-10-28T09:59:00.000Z"}]}
573
+ ```
574
+
575
+ Headers are `content-type: application/json`, `user-agent: questlog/<version>`,
576
+ `x-questlog-delivery: <deliveryId>`, `x-questlog-timestamp: <unix seconds>`, and
577
+ `x-questlog-signature: sha256=<hex HMAC-SHA256(secret, timestamp + "." + rawBody)>`.
578
+ No titles, objectives, details, or tag text are sent. `hook test` sends a logged `hook.test`
579
+ event immediately, ignoring the min gap. Add and test are CLI-only in the tool facade;
580
+ `manage_hooks` supports list, remove, tick (with `dry_run`), and deliveries.
581
+
582
+ 2xx delivers the batch. Requests time out after 5 seconds. Other responses, timeouts, and
583
+ network errors retry after 30 seconds, 1 minute, 2 minutes, 4 minutes, and so on, capped
584
+ at 1 hour, plus less than 1 second jitter. The target's min gap also applies to retries.
585
+ Eight unsuccessful attempts mark a delivery failed. Retries retain the delivery id and events.
586
+ Pending batches wait behind retries. Only the last 500 delivered entries per target are kept;
587
+ pending and failed entries are retained. Transactions and two-minute leases prevent overlapping
588
+ ticks from claiming the same delivery. A crash after receipt but before recording success can
589
+ cause another request: receivers must dedupe by delivery id.
590
+
591
+ ### Verifying a delivery
592
+
593
+ Compute HMAC-SHA256 over `timestamp + "." + rawBody` with the secret and compare in constant
594
+ time. Reject timestamps older than 5 minutes (and timestamps too far in the future), then
595
+ dedupe on `x-questlog-delivery`. Use durable deduplication storage in a production receiver.
596
+ This 15-line Node example uses an in-memory set:
597
+
598
+ ```js
599
+ import { createHmac, timingSafeEqual } from 'node:crypto';
600
+ import { createServer } from 'node:http';
601
+ const secret = process.env.WEBHOOK_SECRET, seen = new Set();
602
+ createServer(async (req, res) => {
603
+ const chunks = []; for await (const chunk of req) chunks.push(chunk);
604
+ const rawBody = Buffer.concat(chunks), h = req.headers;
605
+ const timestamp = h['x-questlog-timestamp'], id = h['x-questlog-delivery'];
606
+ const age = Date.now() / 1000 - Number(timestamp);
607
+ const expected = createHmac('sha256', secret).update(timestamp + '.').update(rawBody).digest();
608
+ const signature = String(h['x-questlog-signature'] || '');
609
+ const actual = Buffer.from(signature.slice(7), 'hex');
610
+ if (!id || !/^\d+$/.test(timestamp) || age > 300 || age < -300 || !/^sha256=[a-f0-9]{64}$/.test(signature) || actual.length !== expected.length || !timingSafeEqual(actual, expected)) { res.writeHead(401).end(); return; }
611
+ if (!seen.has(id)) { seen.add(id); console.log(JSON.parse(rawBody)); }
612
+ res.writeHead(204).end();
613
+ }).listen(8080, '127.0.0.1');
614
+ ```