caveat-cli 0.10.0 → 0.11.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.
- package/dist/index.js +119 -4
- package/dist/index.js.map +1 -1
- package/dist/migrations/002_last_hit_at.sql +3 -0
- package/dist/schema.sql +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21339,6 +21339,10 @@ function stopReminderText(signals, related) {
|
|
|
21339
21339
|
if (signals.durationMinutes > 0) {
|
|
21340
21340
|
lines.push(`- \u7D4C\u904E\u6642\u9593: ${signals.durationMinutes} \u5206`);
|
|
21341
21341
|
}
|
|
21342
|
+
const externalLookup = signals.webSearchCount + signals.webFetchCount > 0;
|
|
21343
|
+
lines.push(
|
|
21344
|
+
`- \u5206\u985E\u30D2\u30F3\u30C8: ${externalLookup ? "\u5916\u90E8\u4ED5\u69D8\u8ABF\u67FB\u3042\u308A \u2192 public \u5BC4\u308A" : "\u5916\u90E8\u8ABF\u67FB\u306A\u3057 \u2192 private \u5BC4\u308A"}`
|
|
21345
|
+
);
|
|
21342
21346
|
lines.push("");
|
|
21343
21347
|
if (related.length > 0) {
|
|
21344
21348
|
lines.push(
|
|
@@ -21356,6 +21360,9 @@ function stopReminderText(signals, related) {
|
|
|
21356
21360
|
"\u65E2\u5B58\u7F60\u306B\u8A72\u5F53\u306A\u3057\u3002\u5916\u90E8\u4ED5\u69D8\u306E\u7F60\u306B\u82E6\u6226\u3057\u3066\u3044\u305F\u306A\u3089 mcp__caveat__caveat_record \u3067\u767B\u9332\u3057\u3066\u304F\u3060\u3055\u3044\u3002outcome: impossible \u3082\u8A18\u9332\u5BFE\u8C61\u3002"
|
|
21357
21361
|
);
|
|
21358
21362
|
}
|
|
21363
|
+
lines.push(
|
|
21364
|
+
"\u8A18\u9332\u6642\u306F tool \u8AAC\u660E\u306E\u4E8C\u9805\u57FA\u6E96\u3067 visibility \u3092\u9078\u3076\uFF08public = \u7B2C\u4E09\u8005\u518D\u73FE\u53EF\u80FD / private = repo \u56FA\u6709\uFF09\u3002\u8FF7\u3063\u305F\u3089 private\u3002"
|
|
21365
|
+
);
|
|
21359
21366
|
return lines.join("\n");
|
|
21360
21367
|
}
|
|
21361
21368
|
|
|
@@ -21513,6 +21520,48 @@ function drainPendingReminders(caveatHome, sessionId) {
|
|
|
21513
21520
|
return out;
|
|
21514
21521
|
}
|
|
21515
21522
|
|
|
21523
|
+
// ../../packages/core/dist/markHit.js
|
|
21524
|
+
function markHit(db, keys, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
21525
|
+
if (keys.length === 0) return;
|
|
21526
|
+
const ts = now();
|
|
21527
|
+
const stmt = db.prepare(
|
|
21528
|
+
"UPDATE entries SET last_hit_at = ? WHERE source = ? AND id = ?"
|
|
21529
|
+
);
|
|
21530
|
+
for (const k2 of keys) {
|
|
21531
|
+
stmt.run(ts, k2.source, k2.id);
|
|
21532
|
+
}
|
|
21533
|
+
}
|
|
21534
|
+
|
|
21535
|
+
// ../../packages/core/dist/stale.js
|
|
21536
|
+
function listStale(db, opts = {}) {
|
|
21537
|
+
const days = opts.days ?? 90;
|
|
21538
|
+
const limit = opts.limit ?? 50;
|
|
21539
|
+
const now = (opts.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
21540
|
+
const cutoff = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3).toISOString();
|
|
21541
|
+
const conditions = ["(last_hit_at IS NULL OR last_hit_at < ?)"];
|
|
21542
|
+
const params = [cutoff];
|
|
21543
|
+
if (opts.visibility === "public" || opts.visibility === "private") {
|
|
21544
|
+
conditions.push("visibility = ?");
|
|
21545
|
+
params.push(opts.visibility);
|
|
21546
|
+
}
|
|
21547
|
+
const sql = `
|
|
21548
|
+
SELECT id, source, title, visibility, last_hit_at
|
|
21549
|
+
FROM entries
|
|
21550
|
+
WHERE ${conditions.join(" AND ")}
|
|
21551
|
+
ORDER BY last_hit_at IS NULL DESC, last_hit_at ASC
|
|
21552
|
+
LIMIT ?
|
|
21553
|
+
`;
|
|
21554
|
+
params.push(limit);
|
|
21555
|
+
const rows = db.prepare(sql).all(...params);
|
|
21556
|
+
return rows.map((r2) => ({
|
|
21557
|
+
id: r2.id,
|
|
21558
|
+
source: r2.source,
|
|
21559
|
+
title: r2.title,
|
|
21560
|
+
visibility: r2.visibility ?? "public",
|
|
21561
|
+
last_hit_at: r2.last_hit_at
|
|
21562
|
+
}));
|
|
21563
|
+
}
|
|
21564
|
+
|
|
21516
21565
|
// src/context.ts
|
|
21517
21566
|
function buildContext(logger, overrides = {}) {
|
|
21518
21567
|
const userHome = overrides.userHome ?? homedir();
|
|
@@ -21906,6 +21955,29 @@ function runList(ctx, opts) {
|
|
|
21906
21955
|
}
|
|
21907
21956
|
}
|
|
21908
21957
|
|
|
21958
|
+
// src/commands/stale.ts
|
|
21959
|
+
function runStale(ctx, opts) {
|
|
21960
|
+
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21961
|
+
try {
|
|
21962
|
+
const rows = listStale(db, {
|
|
21963
|
+
days: opts.days,
|
|
21964
|
+
visibility: opts.visibility,
|
|
21965
|
+
limit: opts.limit
|
|
21966
|
+
});
|
|
21967
|
+
if (rows.length === 0) {
|
|
21968
|
+
process.stdout.write("(no stale entries)\n");
|
|
21969
|
+
return;
|
|
21970
|
+
}
|
|
21971
|
+
for (const r2 of rows) {
|
|
21972
|
+
const age = r2.last_hit_at ?? "never";
|
|
21973
|
+
process.stdout.write(`${r2.id} [${r2.source}] (${r2.visibility}) ${age} \u2014 ${r2.title}
|
|
21974
|
+
`);
|
|
21975
|
+
}
|
|
21976
|
+
} finally {
|
|
21977
|
+
db.close();
|
|
21978
|
+
}
|
|
21979
|
+
}
|
|
21980
|
+
|
|
21909
21981
|
// src/commands/show.ts
|
|
21910
21982
|
function runShow(ctx, opts) {
|
|
21911
21983
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
@@ -44694,12 +44766,24 @@ function buildMcpContext(overrides = {}) {
|
|
|
44694
44766
|
// ../mcp/dist/tools/search.js
|
|
44695
44767
|
var sourceFilter = external_exports.enum(["own", "community", "all"]);
|
|
44696
44768
|
var confidenceSchema = external_exports.enum(["confirmed", "reproduced", "tentative"]);
|
|
44769
|
+
var visibilityFilter = external_exports.enum(["public", "private", "all"]);
|
|
44697
44770
|
var searchInputShape = {
|
|
44698
44771
|
query: external_exports.string().describe("FTS query (3+ chars for trigram). Empty string lists without text filter."),
|
|
44699
44772
|
filters: external_exports.object({
|
|
44700
44773
|
tags: external_exports.array(external_exports.string()).optional(),
|
|
44701
44774
|
confidence: external_exports.array(confidenceSchema).optional(),
|
|
44702
|
-
source: sourceFilter.optional()
|
|
44775
|
+
source: sourceFilter.optional(),
|
|
44776
|
+
visibility: visibilityFilter.optional().describe(
|
|
44777
|
+
[
|
|
44778
|
+
"Narrow by publish tier.",
|
|
44779
|
+
"'public' = external-spec gotchas reproducible by any third party (PyInstaller, Stripe, Claude Code hook behavior, etc).",
|
|
44780
|
+
"'private' = your own cross-project notes (repo-specific, your workflow, intentional non-standard design).",
|
|
44781
|
+
"'all' (or omit) = both tiers.",
|
|
44782
|
+
"Use 'public' when drafting externally-visible output (PR descriptions, public docs, answers to third parties) so private notes do not bleed into external content.",
|
|
44783
|
+
"Use 'private' when specifically recalling your own past decisions.",
|
|
44784
|
+
"Default to omitting this filter \u2014 narrowing too aggressively hides relevant entries."
|
|
44785
|
+
].join(" ")
|
|
44786
|
+
)
|
|
44703
44787
|
}).optional(),
|
|
44704
44788
|
limit: external_exports.number().int().min(1).max(200).optional()
|
|
44705
44789
|
};
|
|
@@ -44709,6 +44793,7 @@ function handleSearch(ctx, args) {
|
|
|
44709
44793
|
filters: args.filters,
|
|
44710
44794
|
limit: args.limit
|
|
44711
44795
|
});
|
|
44796
|
+
if (results.length > 0) markHit(ctx.db, results);
|
|
44712
44797
|
return results;
|
|
44713
44798
|
}
|
|
44714
44799
|
|
|
@@ -44740,7 +44825,14 @@ var recordInputShape = {
|
|
|
44740
44825
|
confidence: confidenceSchema2.optional(),
|
|
44741
44826
|
outcome: outcomeSchema.optional(),
|
|
44742
44827
|
visibility: visibilitySchema.describe(
|
|
44743
|
-
|
|
44828
|
+
[
|
|
44829
|
+
"REQUIRED. Classify using this binary criterion:",
|
|
44830
|
+
"- 'public' if a third party running the same external tool/spec could reproduce this gotcha (external-spec trap, e.g. PyInstaller/Stripe/Podman/Claude Code hook behavior).",
|
|
44831
|
+
"- 'private' if it is specific to your repo, your workflow, an intentional non-standard design, or context that only exists in this project.",
|
|
44832
|
+
"- When unclear, prefer 'private' (leak-safety).",
|
|
44833
|
+
`Exception: if the user explicitly asks to record it as private/public (e.g. "save this as private", "\u3053\u308C\u306F\u81EA\u5206\u7528\u306B\u30E1\u30E2\u3057\u3066"), follow the user's instruction regardless of the criterion \u2014 explicit user intent overrides auto-classification.`,
|
|
44834
|
+
"When recording with visibility: 'private', always include repo-specific identifiers (function names, file paths, class names, custom terminology) in the body so the entry can be retrieved later by co-occurrence FTS when you touch that area again."
|
|
44835
|
+
].join(" ")
|
|
44744
44836
|
),
|
|
44745
44837
|
tags: external_exports.array(external_exports.string()).optional(),
|
|
44746
44838
|
environment: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
@@ -44761,7 +44853,13 @@ var patchFrontmatterSchema = external_exports.object({
|
|
|
44761
44853
|
title: external_exports.string().optional(),
|
|
44762
44854
|
confidence: confidenceSchema3.optional(),
|
|
44763
44855
|
outcome: outcomeSchema2.optional(),
|
|
44764
|
-
visibility: visibilitySchema2.optional()
|
|
44856
|
+
visibility: visibilitySchema2.optional().describe(
|
|
44857
|
+
[
|
|
44858
|
+
"Change the publish tier. Use the same binary criterion as caveat_record:",
|
|
44859
|
+
"'public' if third-party reproducible, 'private' if repo-specific/your-workflow-specific.",
|
|
44860
|
+
"When unclear, prefer 'private'. Explicit user instruction overrides auto-classification."
|
|
44861
|
+
].join(" ")
|
|
44862
|
+
),
|
|
44765
44863
|
tags: external_exports.array(external_exports.string()).optional(),
|
|
44766
44864
|
environment: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
44767
44865
|
last_verified: external_exports.string().optional()
|
|
@@ -44982,7 +45080,17 @@ function searchCaveatsFromTextSafely(text2) {
|
|
|
44982
45080
|
const ctx = buildContextSafely();
|
|
44983
45081
|
if (!ctx || !existsSync14(ctx.paths.dbPath)) return [];
|
|
44984
45082
|
db = openDb({ path: ctx.paths.dbPath });
|
|
44985
|
-
|
|
45083
|
+
const hits = findCaveatsForPrompt(db, text2);
|
|
45084
|
+
if (hits.length > 0) {
|
|
45085
|
+
try {
|
|
45086
|
+
markHit(db, hits);
|
|
45087
|
+
} catch (err) {
|
|
45088
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
45089
|
+
process.stderr.write(`[caveat:hook] markHit error: ${msg}
|
|
45090
|
+
`);
|
|
45091
|
+
}
|
|
45092
|
+
}
|
|
45093
|
+
return hits;
|
|
44986
45094
|
} catch (err) {
|
|
44987
45095
|
const msg = err instanceof Error ? err.message : String(err);
|
|
44988
45096
|
process.stderr.write(`[caveat:hook] search error: ${msg}
|
|
@@ -45312,6 +45420,13 @@ program.command("list").description("List caveats by updated_at DESC").option("-
|
|
|
45312
45420
|
const ctx = buildContext(stdoutLogger);
|
|
45313
45421
|
runList(ctx, { limit: opts.recent });
|
|
45314
45422
|
});
|
|
45423
|
+
program.command("stale").description(
|
|
45424
|
+
"List entries not surfaced by retrieval for N days (default 90). Use this to find private caveats that may be buried \u2014 if a 3-month-old private entry never surfaces, rewrite its body to include repo-specific identifiers, or delete it."
|
|
45425
|
+
).option("--days <n>", "age threshold in days", (v) => Number(v), 90).option("--visibility <v>", "public | private").option("--limit <n>", "max rows", (v) => Number(v), 50).action((opts) => {
|
|
45426
|
+
const ctx = buildContext(stdoutLogger);
|
|
45427
|
+
const vis = opts.visibility === "public" || opts.visibility === "private" ? opts.visibility : void 0;
|
|
45428
|
+
runStale(ctx, { days: opts.days, visibility: vis, limit: opts.limit });
|
|
45429
|
+
});
|
|
45315
45430
|
program.command("show").description("Show full caveat by id").argument("<id>", "entry id").option("--source <source>", "own or community/<handle>", "own").action((id, opts) => {
|
|
45316
45431
|
const ctx = buildContext(stdoutLogger);
|
|
45317
45432
|
runShow(ctx, { id, source: opts.source });
|