caveat-cli 0.20.2 → 0.20.3

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
@@ -146,6 +146,14 @@ are `ack <cursor>`, `resolve <fingerprint>`, `reopen <fingerprint>`, and
146
146
  boolean `true` keeps collection disabled. Runtime state lives under Caveat's
147
147
  own state directory, independently of the knowledge index and host hook files.
148
148
 
149
+ Handled sync cancellation and remote failures retain local input and show a
150
+ non-zero result without automatic app repair registration. Failed recovery is
151
+ still reported. Severity uses observed impact (`warn` for a failed operation,
152
+ `high` for an unavailable feature or failed recovery, `fatal` for confirmed data
153
+ loss); occurrence counts do not raise it. Existing serious unresolved records
154
+ remain unchanged until reviewed. Cause and recovery need diagnostic evidence.
155
+ See the [reporting contract](https://github.com/kitepon/Caveat/blob/main/docs/01_plan.md#通信失敗の報告と重大度).
156
+
149
157
  Nothing is sent anywhere by default and the package contains no destination.
150
158
  To report to a collector you operate, also set
151
159
  `"runtimeErrorReportCredentialFile"` to the absolute path of the credential
@@ -15313,6 +15313,34 @@ var SyncError = class extends Error {
15313
15313
  }
15314
15314
  code;
15315
15315
  };
15316
+ var SyncRemoteError = class extends Error {
15317
+ constructor(phase, cause) {
15318
+ super(`sync ${phase} did not complete; local entries are retained; remote result may be unknown: ${errorMessage2(cause)}`, { cause });
15319
+ this.phase = phase;
15320
+ this.name = "SyncRemoteError";
15321
+ }
15322
+ phase;
15323
+ };
15324
+ var SyncRecoveryError = class extends Error {
15325
+ constructor(cause) {
15326
+ super("sync recovery failed; inspect the local rebase before retrying", { cause });
15327
+ this.name = "SyncRecoveryError";
15328
+ }
15329
+ };
15330
+ async function remoteOperation(phase, run) {
15331
+ try {
15332
+ return await run();
15333
+ } catch (cause) {
15334
+ throw new SyncRemoteError(phase, cause);
15335
+ }
15336
+ }
15337
+ async function activeRebase(git, ownDir) {
15338
+ for (const name of ["rebase-merge", "rebase-apply"]) {
15339
+ const path = (await git.raw(["rev-parse", "--git-path", name])).trim();
15340
+ if (existsSync8(resolve(ownDir, path))) return true;
15341
+ }
15342
+ return false;
15343
+ }
15316
15344
  function normalizePath(path) {
15317
15345
  return normalizePathCase(resolve(path).replace(/\\/g, "/"));
15318
15346
  }
@@ -15359,6 +15387,9 @@ async function preflightSync(ownDir, opts = {}) {
15359
15387
  if (normalizePath(root) !== normalizePath(requested)) {
15360
15388
  throw new SyncError("EXTERNAL_TOPLEVEL", `EXTERNAL_TOPLEVEL: own directory must be the repository root: ${requested} (root: ${root})`);
15361
15389
  }
15390
+ if (await activeRebase(git, requested)) {
15391
+ throw new SyncError("REBASE_IN_PROGRESS", "a rebase is already active; inspect and finish or abort it before syncing");
15392
+ }
15362
15393
  let branch;
15363
15394
  try {
15364
15395
  branch = (await git.raw(["symbolic-ref", "--short", "HEAD"])).trim();
@@ -15405,23 +15436,32 @@ async function syncOwn(opts) {
15405
15436
  await git.commit(`caveat sync: ${changed} changed file${changed === 1 ? "" : "s"}`);
15406
15437
  committed = true;
15407
15438
  }
15408
- const remoteBranch = (await git.raw(["ls-remote", "--heads", "origin", preflight.branch])).trim();
15439
+ const remoteBranch = (await remoteOperation("inspect", () => git.raw(["ls-remote", "--heads", "origin", preflight.branch]))).trim();
15409
15440
  let pulled = false;
15410
15441
  if (remoteBranch) {
15411
15442
  try {
15412
15443
  await git.pull("origin", preflight.branch, ["--rebase"]);
15413
15444
  pulled = true;
15414
15445
  } catch (err) {
15446
+ let rebasing;
15447
+ try {
15448
+ rebasing = await activeRebase(git, preflight.ownDir);
15449
+ } catch (cause) {
15450
+ throw new SyncRecoveryError(cause);
15451
+ }
15452
+ if (!rebasing) throw new SyncRemoteError("pull", err);
15415
15453
  try {
15416
15454
  await git.raw(["rebase", "--abort"]);
15417
- } catch {
15455
+ if (await activeRebase(git, preflight.ownDir)) throw Error("rebase still active");
15456
+ } catch (cause) {
15457
+ throw new SyncRecoveryError(cause);
15418
15458
  }
15419
15459
  const detail = err instanceof Error ? err.message : String(err);
15420
15460
  throw new SyncError("SYNC_CONFLICT", `sync rebase failed and was aborted: ${detail}`);
15421
15461
  }
15422
15462
  }
15423
15463
  await reindexAndMark(opts);
15424
- await git.push("origin", preflight.branch, ["-u"]);
15464
+ await remoteOperation("push", () => git.push("origin", preflight.branch, ["-u"]));
15425
15465
  return {
15426
15466
  ...preflight,
15427
15467
  committed,
@@ -15471,10 +15511,10 @@ function removeCanonicalScaffoldBeforeCheckout(ownDir) {
15471
15511
  }
15472
15512
  }
15473
15513
  async function defaultRemoteBranch(git, url) {
15474
- const symref = await git.raw(["ls-remote", "--symref", url, "HEAD"]);
15514
+ const symref = await remoteOperation("inspect", () => git.raw(["ls-remote", "--symref", url, "HEAD"]));
15475
15515
  const match = /^ref: refs\/heads\/([^\s]+)\s+HEAD$/m.exec(symref);
15476
15516
  if (match) return match[1];
15477
- const heads = await git.raw(["ls-remote", "--heads", url]);
15517
+ const heads = await remoteOperation("inspect", () => git.raw(["ls-remote", "--heads", url]));
15478
15518
  const first2 = /refs\/heads\/([^\s]+)\s*$/m.exec(heads);
15479
15519
  if (!first2) throw new Error(`remote has refs but no branch heads: ${url}`);
15480
15520
  return first2[1];
@@ -15487,7 +15527,7 @@ async function initOwnSync(opts) {
15487
15527
  throw new SyncError("OWN_REPO_EXISTS", `own knowledge directory is already a git repository: ${ownDir}`);
15488
15528
  }
15489
15529
  const inspector = createGit(ownDir);
15490
- const refs = (await inspector.raw(["ls-remote", "--heads", opts.url])).trim();
15530
+ const refs = (await remoteOperation("inspect", () => inspector.raw(["ls-remote", "--heads", opts.url]))).trim();
15491
15531
  const entryCount = countMarkdownEntries(ownDir);
15492
15532
  if (refs && entryCount > 0) {
15493
15533
  throw new SyncError("BOTH_HAVE_ENTRIES", "local and remote both contain entries; resolve the ownership conflict before initializing sync");
@@ -15503,12 +15543,12 @@ async function initOwnSync(opts) {
15503
15543
  await git.add("-A");
15504
15544
  await git.commit(`caveat sync: initial import (${entryCount} entries)`);
15505
15545
  const branch2 = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
15506
- await git.push("origin", branch2, ["-u"]);
15546
+ await remoteOperation("push", () => git.push("origin", branch2, ["-u"]));
15507
15547
  await reindexAndMark(opts);
15508
15548
  return { ownDir, branch: branch2, remoteWasEmpty: true };
15509
15549
  }
15510
15550
  const branch = await defaultRemoteBranch(git, opts.url);
15511
- await git.fetch("origin", branch);
15551
+ await remoteOperation("fetch", () => git.fetch("origin", branch));
15512
15552
  removeCanonicalScaffoldBeforeCheckout(ownDir);
15513
15553
  await git.checkout(["--track", "-B", branch, `origin/${branch}`]);
15514
15554
  await reindexAndMark(opts);
@@ -15516,7 +15556,8 @@ async function initOwnSync(opts) {
15516
15556
  } catch (err) {
15517
15557
  try {
15518
15558
  rmSync4(createdGitDir, { recursive: true, force: true });
15519
- } catch {
15559
+ } catch (cause) {
15560
+ throw new SyncRecoveryError(cause);
15520
15561
  }
15521
15562
  throw err;
15522
15563
  }
@@ -15600,13 +15641,20 @@ function classifyOwnSyncOutcome(err, lastProbe) {
15600
15641
  return "skip";
15601
15642
  case "REMOTE_PUBLIC":
15602
15643
  case "SYNC_CONFLICT":
15644
+ case "REBASE_IN_PROGRESS":
15603
15645
  return "fail";
15604
15646
  case "REMOTE_VISIBILITY_INDETERMINATE":
15605
15647
  return lastProbe?.kind === "indeterminate" && lastProbe.reason === PROBE_REQUEST_FAILED_REASON ? "network-skip" : "fail";
15606
15648
  }
15607
15649
  }
15650
+ function syncFailureCode(err) {
15651
+ if (err instanceof SyncError) return err.code;
15652
+ if (err instanceof SyncRemoteError) return `REMOTE_${err.phase.toUpperCase()}_FAILED`;
15653
+ if (err instanceof SyncRecoveryError) return "RECOVERY_FAILED";
15654
+ return "UNKNOWN";
15655
+ }
15608
15656
  function ownSyncFailureSignature(err) {
15609
- const code = err instanceof SyncError ? err.code : "UNKNOWN";
15657
+ const code = syncFailureCode(err);
15610
15658
  return sha256(code);
15611
15659
  }
15612
15660
  function autoSyncNotification(outcome) {
@@ -15679,8 +15727,8 @@ async function runAutoSync(opts) {
15679
15727
  ownSyncState = { consecutiveFailureSignature: null, consecutiveFailureCount: 0 };
15680
15728
  } catch (err) {
15681
15729
  const disposition = classifyOwnSyncOutcome(err, lastProbe);
15682
- const code = err instanceof SyncError ? err.code : void 0;
15683
- own = { disposition, code };
15730
+ const code = syncFailureCode(err);
15731
+ own = { disposition, code: code === "UNKNOWN" ? void 0 : code };
15684
15732
  if (disposition === "fail") {
15685
15733
  const failureSignature = ownSyncFailureSignature(err);
15686
15734
  const consecutiveFailureCount = ownSyncState.consecutiveFailureSignature === failureSignature ? ownSyncState.consecutiveFailureCount + 1 : 1;
@@ -17111,15 +17159,25 @@ var STATE_VERSION = "1.0";
17111
17159
  var MAX_RECORDS = 256;
17112
17160
  var RETENTION_MS = 30 * 864e5;
17113
17161
  var definitions = {
17114
- "CAVEAT.DATABASE_OPEN_FAILED": { component: "database", severity: "high", template: "Caveat database open failed" },
17115
- "CAVEAT.INDEX_FAILED": { component: "index", severity: "high", template: "Caveat index operation failed" },
17116
- "CAVEAT.SYNC_FAILED": { component: "sync", severity: "high", template: "Caveat own sync failed" },
17117
- "CAVEAT.MCP_SERVER_FAILED": { component: "mcp", severity: "high", template: "Caveat MCP server failed" },
17118
- "CAVEAT.MCP_TOOL_FAILED": { component: "mcp_tool", severity: "high", template: "Caveat MCP tool handler failed" },
17119
- "CAVEAT.CLAUDE_HOOK_FAILED": { component: "claude_hook", severity: "high", template: "Caveat Claude hook failed" },
17120
- "CAVEAT.CODEX_HOOK_FAILED": { component: "codex_hook", severity: "high", template: "Caveat Codex hook failed" },
17121
- "CAVEAT.CURSOR_HOOK_FAILED": { component: "cursor_hook", severity: "high", template: "Caveat Cursor hook failed" }
17162
+ "CAVEAT.DATABASE_OPEN_FAILED": { component: "database", template: "Caveat database open failed" },
17163
+ "CAVEAT.INDEX_FAILED": { component: "index", template: "Caveat index operation failed" },
17164
+ "CAVEAT.SYNC_FAILED": { component: "sync", template: "Caveat own sync failed" },
17165
+ "CAVEAT.MCP_SERVER_FAILED": { component: "mcp", template: "Caveat MCP server failed" },
17166
+ "CAVEAT.MCP_TOOL_FAILED": { component: "mcp_tool", template: "Caveat MCP tool handler failed" },
17167
+ "CAVEAT.CLAUDE_HOOK_FAILED": { component: "claude_hook", template: "Caveat Claude hook failed" },
17168
+ "CAVEAT.CODEX_HOOK_FAILED": { component: "codex_hook", template: "Caveat Codex hook failed" },
17169
+ "CAVEAT.CURSOR_HOOK_FAILED": { component: "cursor_hook", template: "Caveat Cursor hook failed" }
17122
17170
  };
17171
+ function runtimeErrorSeverity(impact) {
17172
+ if (impact === "data_lost") return "fatal";
17173
+ if (impact === "operation_failed") return "warn";
17174
+ if (impact === void 0 || impact === "feature_unavailable" || impact === "recovery_failed" || impact === "mutation_outcome_unverified") return "high";
17175
+ throw Error("invalid_runtime_impact");
17176
+ }
17177
+ function highestSeverity(previous, current) {
17178
+ const rank = ["info", "warn", "high", "fatal"];
17179
+ return rank.indexOf(previous) >= rank.indexOf(current) ? previous : current;
17180
+ }
17123
17181
  function normalizeOptions(options2 = {}) {
17124
17182
  return "env" in options2 || "configPath" in options2 || "storePath" in options2 || "version" in options2 || "isWindows" in options2 ? options2 : { env: options2 };
17125
17183
  }
@@ -17194,7 +17252,7 @@ function validate(store) {
17194
17252
  if (!plain(record) || !exact(record, ["product", "product_version", "component", "error_code", "message_template", "severity", "fingerprint", "count", "first_seen", "last_seen", "state_schema_version", "os", "arch", "status", "resolved_at", "reason_code", "sequence"])) throw Error("state_invalid");
17195
17253
  const code = typeof record.error_code === "string" ? record.error_code : void 0;
17196
17254
  const d = code && definitions[code];
17197
- if (!d || record.product !== "caveat" || !validVersion(record.product_version) || record.component !== d.component || record.message_template !== d.template || record.severity !== d.severity || record.fingerprint !== fingerprint(code) || seen.has(record.fingerprint) || !Number.isSafeInteger(record.count) || record.count < 1 || !validTime(record.first_seen) || !validTime(record.last_seen) || Date.parse(record.first_seen) > Date.parse(record.last_seen) || record.state_schema_version !== STATE_VERSION || !validOs(record.os) || !validArch(record.arch) || !Number.isSafeInteger(record.sequence) || record.sequence <= previous || record.sequence >= checked.next_sequence || !["open", "resolved"].includes(record.status) || record.status === "open" && (record.resolved_at !== null || record.reason_code !== null) || record.status === "resolved" && (typeof record.resolved_at !== "string" || !validTime(record.resolved_at) || Date.parse(record.resolved_at) < Date.parse(record.last_seen) || record.reason_code !== "operator_resolved")) throw Error("state_invalid");
17255
+ if (!d || record.product !== "caveat" || !validVersion(record.product_version) || record.component !== d.component || record.message_template !== d.template || !["fatal", "high", "warn", "info"].includes(record.severity) || record.fingerprint !== fingerprint(code) || seen.has(record.fingerprint) || !Number.isSafeInteger(record.count) || record.count < 1 || !validTime(record.first_seen) || !validTime(record.last_seen) || Date.parse(record.first_seen) > Date.parse(record.last_seen) || record.state_schema_version !== STATE_VERSION || !validOs(record.os) || !validArch(record.arch) || !Number.isSafeInteger(record.sequence) || record.sequence <= previous || record.sequence >= checked.next_sequence || !["open", "resolved"].includes(record.status) || record.status === "open" && (record.resolved_at !== null || record.reason_code !== null) || record.status === "resolved" && (typeof record.resolved_at !== "string" || !validTime(record.resolved_at) || Date.parse(record.resolved_at) < Date.parse(record.last_seen) || record.reason_code !== "operator_resolved")) throw Error("state_invalid");
17198
17256
  seen.add(record.fingerprint);
17199
17257
  previous = record.sequence;
17200
17258
  }
@@ -17302,6 +17360,7 @@ function recordRuntimeError(code, options2 = {}) {
17302
17360
  const definition = definitions[code];
17303
17361
  if (!definition) throw Error("unknown_runtime_code");
17304
17362
  const key = fingerprint(code);
17363
+ const severity = runtimeErrorSeverity(options2.impact);
17305
17364
  const sequence = store.next_sequence++;
17306
17365
  const time = now(options2);
17307
17366
  const existing = store.records.find((r2) => r2.fingerprint === key);
@@ -17314,6 +17373,7 @@ function recordRuntimeError(code, options2 = {}) {
17314
17373
  existing.os = os;
17315
17374
  existing.arch = arch;
17316
17375
  existing.count += 1;
17376
+ existing.severity = existing.status === "resolved" ? severity : highestSeverity(existing.severity, severity);
17317
17377
  existing.last_seen = time;
17318
17378
  existing.sequence = sequence;
17319
17379
  existing.status = "open";
@@ -17321,7 +17381,7 @@ function recordRuntimeError(code, options2 = {}) {
17321
17381
  existing.reason_code = null;
17322
17382
  } else {
17323
17383
  if (store.records.length >= MAX_RECORDS) throw Error("store_overflow");
17324
- store.records.push({ product: "caveat", product_version: version, component: definition.component, error_code: code, message_template: definition.template, severity: definition.severity, fingerprint: key, count: 1, first_seen: time, last_seen: time, state_schema_version: STATE_VERSION, os, arch, status: "open", resolved_at: null, reason_code: null, sequence });
17384
+ store.records.push({ product: "caveat", product_version: version, component: definition.component, error_code: code, message_template: definition.template, severity, fingerprint: key, count: 1, first_seen: time, last_seen: time, state_schema_version: STATE_VERSION, os, arch, status: "open", resolved_at: null, reason_code: null, sequence });
17325
17385
  }
17326
17386
  store.records.sort((a, b2) => a.sequence - b2.sequence);
17327
17387
  writeStore(path, store, isWin, options2);
@@ -18147,6 +18207,9 @@ export {
18147
18207
  peekPendingRemindersDetailed,
18148
18208
  acknowledgePendingReminders,
18149
18209
  createKeyserverKeyProvider,
18210
+ SyncError,
18211
+ SyncRemoteError,
18212
+ SyncRecoveryError,
18150
18213
  syncOwn,
18151
18214
  KNOWLEDGE_GITIGNORE,
18152
18215
  initOwnSync,
@@ -18220,4 +18283,4 @@ strip-bom-string/index.js:
18220
18283
  js-yaml/dist/js-yaml.mjs:
18221
18284
  (*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT *)
18222
18285
  */
18223
- //# sourceMappingURL=chunk-JYXVT46B.js.map
18286
+ //# sourceMappingURL=chunk-TGIVITP4.js.map