@ghostpaw/questlog 0.6.0 → 0.6.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 +18 -0
- package/dist/cli.js +3 -5
- package/dist/index.cjs +2 -4
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -4
- package/dist/mcp-server.js +3 -5
- package/dist/mcp.cjs +3 -5
- package/dist/mcp.js +3 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -260,6 +260,24 @@ The `run_quest` tool refuses to start a quest with unmet unlock prerequisites an
|
|
|
260
260
|
- **WoW-style markers.** Semantic marker IDs (`attention.available`, `progress.complete`, ...) computed on every read, renderable to any channel.
|
|
261
261
|
- **Colocated tests.** Every non-type module has a colocated `.test.ts` file. The behavior described in docs is backed by executable coverage.
|
|
262
262
|
|
|
263
|
+
## Performance
|
|
264
|
+
|
|
265
|
+
Measured with `npm run bench` on Node 24.14.0, Linux, Intel Xeon (shared
|
|
266
|
+
cloud vCPU), 10,000 quests in a file-backed database:
|
|
267
|
+
|
|
268
|
+
| Operation | p50 | p95 |
|
|
269
|
+
|---|---:|---:|
|
|
270
|
+
| `search_questlog` (tool) | 0.06 ms | 0.07 ms |
|
|
271
|
+
| MCP `tools/call` search, stdio framing included | 0.07 ms | 0.08 ms |
|
|
272
|
+
| `read.searchQuestlog`, one matching quest | 0.07 ms | 0.08 ms |
|
|
273
|
+
| `read.searchQuestlog`, all 10,000 quests matching | 26.6 ms | 41.9 ms |
|
|
274
|
+
| `write.createQuest` | 0.22 ms | 0.69 ms |
|
|
275
|
+
|
|
276
|
+
The library search returns every match with a highlighted snippet, so a query
|
|
277
|
+
that matches the whole log pays for building all of those rows. Cold start
|
|
278
|
+
(process spawn to first response, median) is about 30 ms for `--version` and
|
|
279
|
+
the MCP server.
|
|
280
|
+
|
|
263
281
|
## Package Surface
|
|
264
282
|
|
|
265
283
|
```ts
|
package/dist/cli.js
CHANGED
|
@@ -4194,8 +4194,7 @@ function searchQuestlog(db, query) {
|
|
|
4194
4194
|
AND blocker.deleted_at IS NULL
|
|
4195
4195
|
AND NOT (blocker.resolved_at IS NOT NULL AND blocker.success = 1)
|
|
4196
4196
|
) AS has_blockers
|
|
4197
|
-
FROM quests_fts
|
|
4198
|
-
JOIN quests q ON q.id = quests_fts.rowid
|
|
4197
|
+
FROM quests_fts CROSS JOIN quests q ON q.id = quests_fts.rowid
|
|
4199
4198
|
WHERE quests_fts MATCH ? AND q.deleted_at IS NULL`
|
|
4200
4199
|
).all(trimmed).map((row) => {
|
|
4201
4200
|
const state = deriveQuestState({
|
|
@@ -4227,8 +4226,7 @@ function searchQuestlog(db, query) {
|
|
|
4227
4226
|
snippet(rumors_fts, 1, '[', ']', '\u2026', 10) AS snippet,
|
|
4228
4227
|
r.settled_at,
|
|
4229
4228
|
r.dismissed_at
|
|
4230
|
-
FROM rumors_fts
|
|
4231
|
-
JOIN rumors r ON r.id = rumors_fts.rowid
|
|
4229
|
+
FROM rumors_fts CROSS JOIN rumors r ON r.id = rumors_fts.rowid
|
|
4232
4230
|
WHERE rumors_fts MATCH ? AND r.deleted_at IS NULL`
|
|
4233
4231
|
).all(trimmed).map((row) => ({
|
|
4234
4232
|
entityKind: "rumor",
|
|
@@ -5233,7 +5231,7 @@ var init_validate_input = __esm({
|
|
|
5233
5231
|
var questlogVersion;
|
|
5234
5232
|
var init_version = __esm({
|
|
5235
5233
|
"src/version.ts"() {
|
|
5236
|
-
questlogVersion = "0.6.
|
|
5234
|
+
questlogVersion = "0.6.1";
|
|
5237
5235
|
}
|
|
5238
5236
|
});
|
|
5239
5237
|
|
package/dist/index.cjs
CHANGED
|
@@ -2056,8 +2056,7 @@ function searchQuestlog(db, query) {
|
|
|
2056
2056
|
AND blocker.deleted_at IS NULL
|
|
2057
2057
|
AND NOT (blocker.resolved_at IS NOT NULL AND blocker.success = 1)
|
|
2058
2058
|
) AS has_blockers
|
|
2059
|
-
FROM quests_fts
|
|
2060
|
-
JOIN quests q ON q.id = quests_fts.rowid
|
|
2059
|
+
FROM quests_fts CROSS JOIN quests q ON q.id = quests_fts.rowid
|
|
2061
2060
|
WHERE quests_fts MATCH ? AND q.deleted_at IS NULL`
|
|
2062
2061
|
).all(trimmed).map((row) => {
|
|
2063
2062
|
const state = deriveQuestState({
|
|
@@ -2089,8 +2088,7 @@ function searchQuestlog(db, query) {
|
|
|
2089
2088
|
snippet(rumors_fts, 1, '[', ']', '\u2026', 10) AS snippet,
|
|
2090
2089
|
r.settled_at,
|
|
2091
2090
|
r.dismissed_at
|
|
2092
|
-
FROM rumors_fts
|
|
2093
|
-
JOIN rumors r ON r.id = rumors_fts.rowid
|
|
2091
|
+
FROM rumors_fts CROSS JOIN rumors r ON r.id = rumors_fts.rowid
|
|
2094
2092
|
WHERE rumors_fts MATCH ? AND r.deleted_at IS NULL`
|
|
2095
2093
|
).all(trimmed).map((row) => ({
|
|
2096
2094
|
entityKind: "rumor",
|
package/dist/index.d.cts
CHANGED
|
@@ -665,6 +665,8 @@ interface QuestlogSearchResult {
|
|
|
665
665
|
|
|
666
666
|
/**
|
|
667
667
|
* Full-text search across quest and rumor content.
|
|
668
|
+
* CROSS JOIN keeps the FTS table as the outer loop; with a plain JOIN SQLite
|
|
669
|
+
* may walk idx_quests_deleted_at and probe the FTS index once per quest.
|
|
668
670
|
*/
|
|
669
671
|
declare function searchQuestlog(db: QuestlogDb, query: string): QuestlogSearchResult[];
|
|
670
672
|
|
package/dist/index.d.ts
CHANGED
|
@@ -665,6 +665,8 @@ interface QuestlogSearchResult {
|
|
|
665
665
|
|
|
666
666
|
/**
|
|
667
667
|
* Full-text search across quest and rumor content.
|
|
668
|
+
* CROSS JOIN keeps the FTS table as the outer loop; with a plain JOIN SQLite
|
|
669
|
+
* may walk idx_quests_deleted_at and probe the FTS index once per quest.
|
|
668
670
|
*/
|
|
669
671
|
declare function searchQuestlog(db: QuestlogDb, query: string): QuestlogSearchResult[];
|
|
670
672
|
|
package/dist/index.js
CHANGED
|
@@ -2054,8 +2054,7 @@ function searchQuestlog(db, query) {
|
|
|
2054
2054
|
AND blocker.deleted_at IS NULL
|
|
2055
2055
|
AND NOT (blocker.resolved_at IS NOT NULL AND blocker.success = 1)
|
|
2056
2056
|
) AS has_blockers
|
|
2057
|
-
FROM quests_fts
|
|
2058
|
-
JOIN quests q ON q.id = quests_fts.rowid
|
|
2057
|
+
FROM quests_fts CROSS JOIN quests q ON q.id = quests_fts.rowid
|
|
2059
2058
|
WHERE quests_fts MATCH ? AND q.deleted_at IS NULL`
|
|
2060
2059
|
).all(trimmed).map((row) => {
|
|
2061
2060
|
const state = deriveQuestState({
|
|
@@ -2087,8 +2086,7 @@ function searchQuestlog(db, query) {
|
|
|
2087
2086
|
snippet(rumors_fts, 1, '[', ']', '\u2026', 10) AS snippet,
|
|
2088
2087
|
r.settled_at,
|
|
2089
2088
|
r.dismissed_at
|
|
2090
|
-
FROM rumors_fts
|
|
2091
|
-
JOIN rumors r ON r.id = rumors_fts.rowid
|
|
2089
|
+
FROM rumors_fts CROSS JOIN rumors r ON r.id = rumors_fts.rowid
|
|
2092
2090
|
WHERE rumors_fts MATCH ? AND r.deleted_at IS NULL`
|
|
2093
2091
|
).all(trimmed).map((row) => ({
|
|
2094
2092
|
entityKind: "rumor",
|
package/dist/mcp-server.js
CHANGED
|
@@ -677,7 +677,7 @@ var init_open_questlog_db = __esm({
|
|
|
677
677
|
var questlogVersion;
|
|
678
678
|
var init_version = __esm({
|
|
679
679
|
"src/version.ts"() {
|
|
680
|
-
questlogVersion = "0.6.
|
|
680
|
+
questlogVersion = "0.6.1";
|
|
681
681
|
}
|
|
682
682
|
});
|
|
683
683
|
|
|
@@ -4870,8 +4870,7 @@ function searchQuestlog(db, query) {
|
|
|
4870
4870
|
AND blocker.deleted_at IS NULL
|
|
4871
4871
|
AND NOT (blocker.resolved_at IS NOT NULL AND blocker.success = 1)
|
|
4872
4872
|
) AS has_blockers
|
|
4873
|
-
FROM quests_fts
|
|
4874
|
-
JOIN quests q ON q.id = quests_fts.rowid
|
|
4873
|
+
FROM quests_fts CROSS JOIN quests q ON q.id = quests_fts.rowid
|
|
4875
4874
|
WHERE quests_fts MATCH ? AND q.deleted_at IS NULL`
|
|
4876
4875
|
).all(trimmed).map((row) => {
|
|
4877
4876
|
const state = deriveQuestState({
|
|
@@ -4903,8 +4902,7 @@ function searchQuestlog(db, query) {
|
|
|
4903
4902
|
snippet(rumors_fts, 1, '[', ']', '\u2026', 10) AS snippet,
|
|
4904
4903
|
r.settled_at,
|
|
4905
4904
|
r.dismissed_at
|
|
4906
|
-
FROM rumors_fts
|
|
4907
|
-
JOIN rumors r ON r.id = rumors_fts.rowid
|
|
4905
|
+
FROM rumors_fts CROSS JOIN rumors r ON r.id = rumors_fts.rowid
|
|
4908
4906
|
WHERE rumors_fts MATCH ? AND r.deleted_at IS NULL`
|
|
4909
4907
|
).all(trimmed).map((row) => ({
|
|
4910
4908
|
entityKind: "rumor",
|
package/dist/mcp.cjs
CHANGED
|
@@ -386,7 +386,7 @@ async function openQuestlogDb(path) {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
// src/version.ts
|
|
389
|
-
var questlogVersion = "0.6.
|
|
389
|
+
var questlogVersion = "0.6.1";
|
|
390
390
|
|
|
391
391
|
// src/surface/questlog_mcp_instructions.ts
|
|
392
392
|
var questlogMcpInstructions = "Questlog manages local tasks, calendar commitments, and recurring work. Search before creating work, inspect before changing it, and keep uncertain work as rumors. Use unlock prerequisites to sequence work. run_quest refuses blocked starts unless force is true. Review commitments and resolve work with an honest outcome.";
|
|
@@ -3846,8 +3846,7 @@ function searchQuestlog(db, query) {
|
|
|
3846
3846
|
AND blocker.deleted_at IS NULL
|
|
3847
3847
|
AND NOT (blocker.resolved_at IS NOT NULL AND blocker.success = 1)
|
|
3848
3848
|
) AS has_blockers
|
|
3849
|
-
FROM quests_fts
|
|
3850
|
-
JOIN quests q ON q.id = quests_fts.rowid
|
|
3849
|
+
FROM quests_fts CROSS JOIN quests q ON q.id = quests_fts.rowid
|
|
3851
3850
|
WHERE quests_fts MATCH ? AND q.deleted_at IS NULL`
|
|
3852
3851
|
).all(trimmed).map((row) => {
|
|
3853
3852
|
const state = deriveQuestState({
|
|
@@ -3879,8 +3878,7 @@ function searchQuestlog(db, query) {
|
|
|
3879
3878
|
snippet(rumors_fts, 1, '[', ']', '\u2026', 10) AS snippet,
|
|
3880
3879
|
r.settled_at,
|
|
3881
3880
|
r.dismissed_at
|
|
3882
|
-
FROM rumors_fts
|
|
3883
|
-
JOIN rumors r ON r.id = rumors_fts.rowid
|
|
3881
|
+
FROM rumors_fts CROSS JOIN rumors r ON r.id = rumors_fts.rowid
|
|
3884
3882
|
WHERE rumors_fts MATCH ? AND r.deleted_at IS NULL`
|
|
3885
3883
|
).all(trimmed).map((row) => ({
|
|
3886
3884
|
entityKind: "rumor",
|
package/dist/mcp.js
CHANGED
|
@@ -384,7 +384,7 @@ async function openQuestlogDb(path) {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
// src/version.ts
|
|
387
|
-
var questlogVersion = "0.6.
|
|
387
|
+
var questlogVersion = "0.6.1";
|
|
388
388
|
|
|
389
389
|
// src/surface/questlog_mcp_instructions.ts
|
|
390
390
|
var questlogMcpInstructions = "Questlog manages local tasks, calendar commitments, and recurring work. Search before creating work, inspect before changing it, and keep uncertain work as rumors. Use unlock prerequisites to sequence work. run_quest refuses blocked starts unless force is true. Review commitments and resolve work with an honest outcome.";
|
|
@@ -3844,8 +3844,7 @@ function searchQuestlog(db, query) {
|
|
|
3844
3844
|
AND blocker.deleted_at IS NULL
|
|
3845
3845
|
AND NOT (blocker.resolved_at IS NOT NULL AND blocker.success = 1)
|
|
3846
3846
|
) AS has_blockers
|
|
3847
|
-
FROM quests_fts
|
|
3848
|
-
JOIN quests q ON q.id = quests_fts.rowid
|
|
3847
|
+
FROM quests_fts CROSS JOIN quests q ON q.id = quests_fts.rowid
|
|
3849
3848
|
WHERE quests_fts MATCH ? AND q.deleted_at IS NULL`
|
|
3850
3849
|
).all(trimmed).map((row) => {
|
|
3851
3850
|
const state = deriveQuestState({
|
|
@@ -3877,8 +3876,7 @@ function searchQuestlog(db, query) {
|
|
|
3877
3876
|
snippet(rumors_fts, 1, '[', ']', '\u2026', 10) AS snippet,
|
|
3878
3877
|
r.settled_at,
|
|
3879
3878
|
r.dismissed_at
|
|
3880
|
-
FROM rumors_fts
|
|
3881
|
-
JOIN rumors r ON r.id = rumors_fts.rowid
|
|
3879
|
+
FROM rumors_fts CROSS JOIN rumors r ON r.id = rumors_fts.rowid
|
|
3882
3880
|
WHERE rumors_fts MATCH ? AND r.deleted_at IS NULL`
|
|
3883
3881
|
).all(trimmed).map((row) => ({
|
|
3884
3882
|
entityKind: "rumor",
|