GhostPaw

@ghostpaw/questlog 0.6.2

Your agent said it was done. Was it?

An agent that ticks its own box gets to grade its own homework, so questlog makes it write the homework down: no quest is done without an outcome, and work that waits on other work stays blocked until that work is done.

As a library

As a local MCP server

Try to finish a quest Read the source on GitHub

Free and MIT licensed for Node.js 24 or newer, with zero runtime dependencies and the tables in a SQLite database you open.

A ghost paw drawn in cyan light pressing a glowing hexagonal exclamation mark, violet sparks around it.

0runtime dependencies

Three quests from the README

Try to finish one with the outcome left blank.

This is the Vendor Renewal example from the README, replayed in your browser from a real run: we made the same calls against questlog 0.6.2 on 2026-10-24 with a fixed clock, and every error and every list you see here is what came back, so press Start, leave the outcome empty, press Finish, try to start #3 the way an agent would, then finish #1 for real and watch #3, and jump a week to see the due date go overdue. The live demo runs the library itself.

Today: Sat 24 Oct

! available #1

Collect redlines

Obtain the latest legal markup.

! available #2

Review pricing delta

Compare current vs. proposed cost.

? incomplete #3

Obtain finance approval

Sign-off on the new terms. Due Thu 29 Oct.

blocked by #1

What your code sees

read.listAvailableQuests(db);
// [2, 1]
read.listBlockedQuests(db);
// [3]
read.listDueSoonQuests(db, 7 * DAY);
// [3]

A done quest stays done, and a second finishQuest throws "Resolved quests cannot be resolved again."

Since 0.6.0 blocked holds where your agent acts. The run_quest tool, which the MCP server and the CLI also call, refuses to start #3 with invalid_state and names the open blocker. With force: true it starts the quest anyway and returns the warning "Forced start with unmet prerequisites: Collect redlines."

The library's own startQuest does not check unlocks, so your code can still start #3 directly. That is on purpose.

Where your TODO.md headings go

Hexagon nodes joined by lines of light: two lit cyan, one locked in violet, the rest faint.
## Todo
- Collect redlines
- Obtain finance approval (after redlines)

## Doing
- Review pricing delta

## Done
- Sent the renewal notice
Todo
Open quests. The ones nothing waits on carry "!" and show up in listAvailableQuests.
(after …)
An unlock. The quest sits in listBlockedQuests until its prerequisite finishes with success.
Doing
startQuest sets a start time. The marker turns to "?".
Done
finishQuest with an outcome string. abandonQuest records a failure, also with a reason.
The note in the margin
A rumor. captureRumor holds it until you know what the work is, then settleRumor turns it into a questline and quests.

None of these states is stored.

questlog works them out from the timestamps and the unlocks every time you read. A list cannot go stale, because there is no list.

Deadlines, appointments, repeats

Same table, same quest, and the fields decide what it is.

A deadline
A quest with dueAt. It moves from due soon to overdue without anyone touching it.
An appointment
A quest with scheduledStartAt and scheduledEndAt. Missed windows have their own list.
Not before
notBeforeAt keeps a quest off the available list until that date.
Every Monday
A repeatable quest with a standard RRULE string spawns a fresh quest each time it comes due.

If agent task trackers have burned you

Some agent issue trackers run to hundreds of thousands of lines, and people went back to a markdown file, which is fair enough.

  • One package, one bundled file, no runtime dependencies. The numbers are below.
  • An outcome is text. It records what the agent claims; it does not run your tests.
  • No scheduler and no hosted service. Your code or your agent harness decides when to read.
  • Twelve tools with JSON Schemas, served over MCP stdio by npx -y @ghostpaw/questlog mcp or called from your own harness.
  • Node.js 24 or newer, because it uses the built-in node:sqlite.

What it costs to add

Runtime dependencies
0
Node.js
24 or newer (node:sqlite)
Size of dist/index.js, gzip
42.1 KB
Import time
about 4.6 ms
Tests
352 passing
Line coverage
96.0%
Agent tools
12

Measured 2026-10-24 on the maintainer's machine with Node.js 24.14.0 against questlog 0.6.1: size of the published dist/index.js at gzip -9, median of 11 cold imports, tests and coverage from the v0.6.1 tag. 0.6.2 only changes the version string and homepage, so the numbers carry over.

Start from the README

import { DatabaseSync } from 'node:sqlite';
import { initQuestlogTables, read, write } from '@ghostpaw/questlog';

const db = new DatabaseSync(':memory:');
initQuestlogTables(db);

const rumor = write.captureRumor(db, {
  title: 'Legal says the vendor terms changed',
  details: 'Need to understand pricing delta and termination clause.',
});

write.settleRumor(db, rumor.id, {
  settledAt: Date.now(),
  questline: {
    title: 'Vendor Renewal',
    description: 'Commercial, legal, and approval work.',
  },
  quests: [
    { title: 'Collect redlines', objective: 'Obtain the latest legal markup.' },
    { title: 'Review pricing delta', objective: 'Compare current vs. proposed cost.' },
  ],
});

const available = read.listAvailableQuests(db);
const dueSoon = read.listDueSoonQuests(db, 7 * 24 * 60 * 60 * 1000);

Agents get twelve tools, among them review_questlog, capture_rumor, shape_work, run_quest and reward_work, and each one returns success, no_op, needs_clarification or error with a hint for the next step.