codealmanac 0.2.3 → 0.2.5

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.
Files changed (58) hide show
  1. package/README.md +27 -14
  2. package/dist/agents-RVTQYE6A.js +25 -0
  3. package/dist/chunk-6BJUYZ43.js +195 -0
  4. package/dist/chunk-6BJUYZ43.js.map +1 -0
  5. package/dist/chunk-BGUID5BS.js +766 -0
  6. package/dist/chunk-BGUID5BS.js.map +1 -0
  7. package/dist/{chunk-NBVIEZZQ.js → chunk-DL5BXZCX.js} +53 -3
  8. package/dist/chunk-DL5BXZCX.js.map +1 -0
  9. package/dist/{chunk-XNTNXEWY.js → chunk-GFUB57IT.js} +243 -83
  10. package/dist/chunk-GFUB57IT.js.map +1 -0
  11. package/dist/{chunk-P3LDTCLB.js → chunk-H37GKBWI.js} +13 -1
  12. package/dist/chunk-H37GKBWI.js.map +1 -0
  13. package/dist/{chunk-QQHIVTXT.js → chunk-MRRX4UQB.js} +4 -4
  14. package/dist/{chunk-QQHIVTXT.js.map → chunk-MRRX4UQB.js.map} +1 -1
  15. package/dist/chunk-P5WGG4FJ.js +359 -0
  16. package/dist/chunk-P5WGG4FJ.js.map +1 -0
  17. package/dist/{chunk-HNVOYWC2.js → chunk-SMIK2YLU.js} +165 -76
  18. package/dist/chunk-SMIK2YLU.js.map +1 -0
  19. package/dist/{chunk-V3QOQSXI.js → chunk-TILAKDN6.js} +14 -8
  20. package/dist/chunk-TILAKDN6.js.map +1 -0
  21. package/dist/chunk-TT6ZP4GS.js +282 -0
  22. package/dist/chunk-TT6ZP4GS.js.map +1 -0
  23. package/dist/{cli-6BOB6KAN.js → cli-CL4ID7EO.js} +123 -33
  24. package/dist/cli-CL4ID7EO.js.map +1 -0
  25. package/dist/codealmanac.js +1 -1
  26. package/dist/config-ML2RCR7J.js +16 -0
  27. package/dist/doctor-DOLJRGS4.js +17 -0
  28. package/dist/{register-commands-IXYE5CNZ.js → register-commands-FBJ6XQ3L.js} +296 -398
  29. package/dist/register-commands-FBJ6XQ3L.js.map +1 -0
  30. package/dist/uninstall-DX6LFKMX.js +15 -0
  31. package/dist/{update-RAF7QRYF.js → update-P2IPG7RO.js} +3 -3
  32. package/guides/mini.md +4 -4
  33. package/guides/reference.md +75 -16
  34. package/package.json +1 -1
  35. package/dist/agents-RVYQ44DB.js +0 -16
  36. package/dist/auth-S5DVUIUJ.js +0 -18
  37. package/dist/chunk-HNVOYWC2.js.map +0 -1
  38. package/dist/chunk-NBVIEZZQ.js.map +0 -1
  39. package/dist/chunk-P3LDTCLB.js.map +0 -1
  40. package/dist/chunk-PIYJQE4Z.js +0 -102
  41. package/dist/chunk-PIYJQE4Z.js.map +0 -1
  42. package/dist/chunk-SSYMRT4I.js +0 -126
  43. package/dist/chunk-SSYMRT4I.js.map +0 -1
  44. package/dist/chunk-TWM7I2LU.js +0 -116
  45. package/dist/chunk-TWM7I2LU.js.map +0 -1
  46. package/dist/chunk-V3QOQSXI.js.map +0 -1
  47. package/dist/chunk-WRUSDYYE.js +0 -97
  48. package/dist/chunk-WRUSDYYE.js.map +0 -1
  49. package/dist/chunk-XNTNXEWY.js.map +0 -1
  50. package/dist/cli-6BOB6KAN.js.map +0 -1
  51. package/dist/doctor-DD7EQGCA.js +0 -18
  52. package/dist/register-commands-IXYE5CNZ.js.map +0 -1
  53. package/dist/uninstall-OBV4Z3JE.js +0 -16
  54. /package/dist/{agents-RVYQ44DB.js.map → agents-RVTQYE6A.js.map} +0 -0
  55. /package/dist/{auth-S5DVUIUJ.js.map → config-ML2RCR7J.js.map} +0 -0
  56. /package/dist/{doctor-DD7EQGCA.js.map → doctor-DOLJRGS4.js.map} +0 -0
  57. /package/dist/{uninstall-OBV4Z3JE.js.map → uninstall-DX6LFKMX.js.map} +0 -0
  58. /package/dist/{update-RAF7QRYF.js.map → update-P2IPG7RO.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readStateForDoctor
4
- } from "./chunk-V3QOQSXI.js";
4
+ } from "./chunk-TILAKDN6.js";
5
5
  import {
6
6
  formatDuration
7
7
  } from "./chunk-4CODZRHH.js";
@@ -15,16 +15,17 @@ import {
15
15
  } from "./chunk-FM3VRDK7.js";
16
16
  import {
17
17
  IMPORT_LINE
18
- } from "./chunk-XNTNXEWY.js";
18
+ } from "./chunk-GFUB57IT.js";
19
19
  import {
20
+ buildProviderSetupView,
20
21
  checkClaudeAuth
21
- } from "./chunk-SSYMRT4I.js";
22
+ } from "./chunk-BGUID5BS.js";
22
23
  import {
23
24
  isNewer
24
25
  } from "./chunk-F53U6JQG.js";
25
26
  import {
26
27
  readConfig
27
- } from "./chunk-WRUSDYYE.js";
28
+ } from "./chunk-P5WGG4FJ.js";
28
29
 
29
30
  // src/commands/doctor-checks/format.ts
30
31
  function formatReport(report, options) {
@@ -39,6 +40,13 @@ function formatReport(report, options) {
39
40
  }
40
41
  lines.push("");
41
42
  }
43
+ if (report.agents.length > 0) {
44
+ lines.push(color ? `${BOLD}## Agents${RST}` : "## Agents");
45
+ for (const c of report.agents.map(agentToCheck)) {
46
+ lines.push(formatCheck(c, color));
47
+ }
48
+ lines.push("");
49
+ }
42
50
  if (report.updates.length > 0) {
43
51
  lines.push(color ? `${BOLD}## Updates${RST}` : "## Updates");
44
52
  for (const c of report.updates) {
@@ -56,6 +64,27 @@ function formatReport(report, options) {
56
64
  return `${lines.join("\n")}
57
65
  `;
58
66
  }
67
+ function agentToCheck(agent) {
68
+ const message = [
69
+ agent.label,
70
+ agent.selected ? "(default)" : null,
71
+ agent.recommended ? "(recommended)" : null,
72
+ agent.status === "ok" ? "ready" : readinessMessage(agent),
73
+ `model: ${agent.model ?? "provider default"}`,
74
+ agent.account
75
+ ].filter((part) => part !== null).join(" ");
76
+ return {
77
+ status: agent.status,
78
+ key: `agents.${agent.id}`,
79
+ message,
80
+ fix: agent.fix
81
+ };
82
+ }
83
+ function readinessMessage(agent) {
84
+ if (!agent.installed) return "missing";
85
+ if (!agent.authenticated) return `not ready: ${agent.detail}`;
86
+ return agent.detail;
87
+ }
59
88
  function formatCheck(c, color) {
60
89
  const { icon, tint } = iconFor(c.status, color);
61
90
  const head = ` ${tint}${icon}${color ? RST : ""} ${c.message}`;
@@ -135,13 +164,6 @@ function probeBetterSqlite3() {
135
164
  return { ok: false, summary: firstLine };
136
165
  }
137
166
  }
138
- async function safeCheckAuth(spawnCli) {
139
- try {
140
- return await checkClaudeAuth(spawnCli);
141
- } catch {
142
- return { loggedIn: false };
143
- }
144
- }
145
167
  function readPackageVersion() {
146
168
  const candidates = [
147
169
  "../../../package.json",
@@ -174,13 +196,12 @@ async function gatherInstallChecks(options) {
174
196
  message: sqlite.ok ? `better-sqlite3 native binding OK (Node ${nodeVersion})` : `better-sqlite3 native binding failed: ${sqlite.summary}`,
175
197
  fix: sqlite.ok ? void 0 : "run: npm rebuild better-sqlite3 (in the install directory)"
176
198
  });
177
- const auth = await safeCheckAuth(options.spawnCli);
178
- checks.push(describeAuth(auth));
199
+ checks.push(describeProviderAuthSurface());
179
200
  const settingsPath = options.settingsPath ?? path2.join(homedir2(), ".claude", "settings.json");
180
201
  checks.push(await describeHook(settingsPath));
181
202
  const claudeDir = options.claudeDir ?? path2.join(homedir2(), ".claude");
182
- checks.push(describeGuides(claudeDir));
183
- checks.push(await describeImportLine(claudeDir));
203
+ checks.push(describeGuides(claudeDir, options.cwd));
204
+ checks.push(await describeGuideActivation(claudeDir, options.cwd));
184
205
  return checks;
185
206
  }
186
207
  function describeInstallPath(installPath, isEphemeral) {
@@ -199,35 +220,12 @@ function describeInstallPath(installPath, isEphemeral) {
199
220
  fix: isEphemeral ? "run: npm install -g codealmanac (to make the install permanent)" : void 0
200
221
  };
201
222
  }
202
- function describeAuth(auth) {
203
- if (auth.loggedIn) {
204
- if (auth.authMethod === "apiKey") {
205
- return {
206
- status: "ok",
207
- key: "install.auth",
208
- message: "claude auth: ANTHROPIC_API_KEY set"
209
- };
210
- }
211
- const who = auth.email ?? "Claude account";
212
- const plan = auth.subscriptionType !== void 0 ? ` (${auth.subscriptionType} subscription)` : "";
213
- return {
214
- status: "ok",
215
- key: "install.auth",
216
- message: `claude auth: ${who}${plan}`
217
- };
218
- }
219
- if (process.env.ANTHROPIC_API_KEY !== void 0 && process.env.ANTHROPIC_API_KEY.length > 0) {
220
- return {
221
- status: "ok",
222
- key: "install.auth",
223
- message: "claude auth: ANTHROPIC_API_KEY set"
224
- };
225
- }
223
+ function describeProviderAuthSurface() {
226
224
  return {
227
- status: "problem",
225
+ status: "info",
228
226
  key: "install.auth",
229
- message: "claude auth: not signed in",
230
- fix: "run: claude auth login --claudeai (or export ANTHROPIC_API_KEY)"
227
+ message: "provider auth is reported under Agents",
228
+ fix: "run: almanac agents list"
231
229
  };
232
230
  }
233
231
  async function describeHook(settingsPath) {
@@ -277,7 +275,7 @@ async function describeHook(settingsPath) {
277
275
  };
278
276
  }
279
277
  }
280
- function describeGuides(claudeDir) {
278
+ function describeGuides(claudeDir, cwd) {
281
279
  const mini = path2.join(claudeDir, "codealmanac.md");
282
280
  const ref = path2.join(claudeDir, "codealmanac-reference.md");
283
281
  const haveMini = existsSync2(mini);
@@ -286,7 +284,24 @@ function describeGuides(claudeDir) {
286
284
  return {
287
285
  status: "ok",
288
286
  key: "install.guides",
289
- message: `Agent guides installed (${path2.basename(mini)}, ${path2.basename(ref)})`
287
+ message: `Agent guides installed for Claude (${path2.basename(mini)}, ${path2.basename(ref)})`
288
+ };
289
+ }
290
+ const codexMini = path2.join(homedir2(), ".codex", "codealmanac.md");
291
+ const codexRef = path2.join(homedir2(), ".codex", "codealmanac-reference.md");
292
+ if (existsSync2(codexMini) && existsSync2(codexRef)) {
293
+ return {
294
+ status: "ok",
295
+ key: "install.guides",
296
+ message: "Agent guides installed for Codex (codealmanac.md, codealmanac-reference.md)"
297
+ };
298
+ }
299
+ const cursorRule = path2.join(cwd, ".cursor", "rules", "codealmanac.mdc");
300
+ if (existsSync2(cursorRule)) {
301
+ return {
302
+ status: "ok",
303
+ key: "install.guides",
304
+ message: "Agent guide installed for Cursor (.cursor/rules/codealmanac.mdc)"
290
305
  };
291
306
  }
292
307
  const missing = [
@@ -300,46 +315,119 @@ function describeGuides(claudeDir) {
300
315
  fix: "run: almanac setup --yes"
301
316
  };
302
317
  }
303
- async function describeImportLine(claudeDir) {
318
+ async function describeGuideActivation(claudeDir, cwd) {
304
319
  const claudeMd = path2.join(claudeDir, "CLAUDE.md");
305
- if (!existsSync2(claudeMd)) {
306
- return {
307
- status: "problem",
308
- key: "install.import",
309
- message: "CLAUDE.md import not present (no ~/.claude/CLAUDE.md)",
310
- fix: "run: almanac setup --yes"
311
- };
320
+ if (existsSync2(claudeMd)) {
321
+ try {
322
+ const contents = await readFile(claudeMd, "utf8");
323
+ const lines = contents.split(/\r?\n/).map((l) => l.trim());
324
+ const present = lines.some((line) => {
325
+ if (line === IMPORT_LINE) return true;
326
+ if (!line.startsWith(IMPORT_LINE)) return false;
327
+ const next = line[IMPORT_LINE.length];
328
+ return next === " " || next === " ";
329
+ });
330
+ if (present) {
331
+ return {
332
+ status: "ok",
333
+ key: "install.import",
334
+ message: "Claude guide import present"
335
+ };
336
+ }
337
+ } catch (err) {
338
+ const msg = err instanceof Error ? err.message : String(err);
339
+ return {
340
+ status: "problem",
341
+ key: "install.import",
342
+ message: `could not read ${claudeMd}: ${msg}`
343
+ };
344
+ }
312
345
  }
313
- try {
314
- const contents = await readFile(claudeMd, "utf8");
315
- const lines = contents.split(/\r?\n/).map((l) => l.trim());
316
- const present = lines.some((line) => {
317
- if (line === IMPORT_LINE) return true;
318
- if (!line.startsWith(IMPORT_LINE)) return false;
319
- const next = line[IMPORT_LINE.length];
320
- return next === " " || next === " ";
321
- });
322
- if (present) {
346
+ const codexAgents = path2.join(homedir2(), ".codex", "AGENTS.md");
347
+ if (existsSync2(codexAgents)) {
348
+ try {
349
+ const contents = await readFile(codexAgents, "utf8");
350
+ if (contents.includes("<!-- codealmanac:start -->") && contents.includes("<!-- codealmanac:end -->")) {
351
+ return {
352
+ status: "ok",
353
+ key: "install.import",
354
+ message: "Codex guide block present"
355
+ };
356
+ }
357
+ } catch (err) {
358
+ const msg = err instanceof Error ? err.message : String(err);
323
359
  return {
324
- status: "ok",
360
+ status: "problem",
325
361
  key: "install.import",
326
- message: "CLAUDE.md import present"
362
+ message: `could not read ${codexAgents}: ${msg}`
327
363
  };
328
364
  }
365
+ }
366
+ const cursorRule = path2.join(cwd, ".cursor", "rules", "codealmanac.mdc");
367
+ if (existsSync2(cursorRule)) {
329
368
  return {
330
- status: "problem",
369
+ status: "ok",
331
370
  key: "install.import",
332
- message: "CLAUDE.md import line missing",
333
- fix: "run: almanac setup --yes"
371
+ message: "Cursor guide rule present"
334
372
  };
335
- } catch (err) {
336
- const msg = err instanceof Error ? err.message : String(err);
373
+ }
374
+ return {
375
+ status: "problem",
376
+ key: "install.import",
377
+ message: "provider guide activation missing",
378
+ fix: "run: almanac setup --yes"
379
+ };
380
+ }
381
+
382
+ // src/commands/doctor-checks/agents.ts
383
+ async function gatherAgentChecks(options) {
384
+ const view = await buildProviderSetupView({
385
+ spawnCli: options.spawnCli,
386
+ statuses: options.providerStatuses ?? (options.spawnCli === void 0 ? void 0 : await injectedProviderStatuses(options))
387
+ });
388
+ return view.choices.map((choice) => {
337
389
  return {
338
- status: "problem",
339
- key: "install.import",
340
- message: `could not read ${claudeMd}: ${msg}`
390
+ status: choice.ready ? "ok" : "problem",
391
+ id: choice.id,
392
+ label: choice.label,
393
+ readiness: choice.readiness,
394
+ selected: choice.selected,
395
+ recommended: choice.recommended,
396
+ installed: choice.installed,
397
+ authenticated: choice.authenticated,
398
+ model: choice.effectiveModel,
399
+ providerDefaultModel: choice.providerDefaultModel,
400
+ configuredModel: choice.configuredModel,
401
+ account: choice.account,
402
+ detail: choice.detail,
403
+ fix: choice.fixCommand ?? void 0
341
404
  };
342
- }
405
+ });
406
+ }
407
+ async function injectedProviderStatuses(options) {
408
+ const auth = await checkClaudeAuth(options.spawnCli);
409
+ const hasApiKey = process.env.ANTHROPIC_API_KEY !== void 0 && process.env.ANTHROPIC_API_KEY.length > 0;
410
+ const claudeReady = auth.loggedIn || hasApiKey;
411
+ return [
412
+ {
413
+ id: "claude",
414
+ installed: true,
415
+ authenticated: claudeReady,
416
+ detail: claudeReady ? auth.email ?? (hasApiKey ? "ANTHROPIC_API_KEY set" : "logged in") : "not logged in"
417
+ },
418
+ {
419
+ id: "codex",
420
+ installed: false,
421
+ authenticated: false,
422
+ detail: "codex status not injected"
423
+ },
424
+ {
425
+ id: "cursor",
426
+ installed: false,
427
+ authenticated: false,
428
+ detail: "cursor-agent status not injected"
429
+ }
430
+ ];
343
431
  }
344
432
 
345
433
  // src/commands/doctor-checks/updates.ts
@@ -389,7 +477,7 @@ async function gatherUpdateChecks(options, installedVersion) {
389
477
  status: "info",
390
478
  key: "update.notifier",
391
479
  message: `update notifier: ${config.update_notifier ? "enabled" : "disabled"}`,
392
- fix: config.update_notifier ? void 0 : "run: almanac update --enable-notifier"
480
+ fix: config.update_notifier ? void 0 : "run: almanac config set update_notifier true"
393
481
  });
394
482
  if (state !== null && state.dismissed_versions.length > 0) {
395
483
  checks.push({
@@ -405,9 +493,10 @@ async function gatherUpdateChecks(options, installedVersion) {
405
493
  async function runDoctor(options) {
406
494
  const version = options.versionOverride ?? readPackageVersion() ?? "unknown";
407
495
  const install = options.wikiOnly === true ? [] : await gatherInstallChecks(options);
496
+ const agents = options.wikiOnly === true ? [] : await gatherAgentChecks(options);
408
497
  const updates = options.wikiOnly === true ? [] : await gatherUpdateChecks(options, version);
409
498
  const wiki = options.installOnly === true ? [] : await safeGatherWikiChecks(options);
410
- const report = { version, install, updates, wiki };
499
+ const report = { version, install, agents, updates, wiki };
411
500
  if (options.json === true) {
412
501
  return {
413
502
  stdout: `${JSON.stringify(report, null, 2)}
@@ -442,4 +531,4 @@ async function safeGatherWikiChecks(options) {
442
531
  export {
443
532
  runDoctor
444
533
  };
445
- //# sourceMappingURL=chunk-HNVOYWC2.js.map
534
+ //# sourceMappingURL=chunk-SMIK2YLU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/doctor-checks/format.ts","../src/commands/doctor-checks/install.ts","../src/commands/doctor-checks/probes.ts","../src/commands/doctor-checks/agents.ts","../src/commands/doctor-checks/updates.ts","../src/commands/doctor.ts"],"sourcesContent":["import { BLUE, BOLD, DIM, GREEN, RED, RST } from \"../../ansi.js\";\nimport type {\n AgentDoctorCheck,\n Check,\n CheckStatus,\n DoctorOptions,\n DoctorReport,\n} from \"./types.js\";\n\nexport function formatReport(\n report: DoctorReport,\n options: DoctorOptions,\n): string {\n const color = options.stdout === undefined && process.stdout.isTTY === true;\n const lines: string[] = [];\n lines.push(`codealmanac v${report.version}`);\n lines.push(\"\");\n if (report.install.length > 0) {\n lines.push(color ? `${BOLD}## Install${RST}` : \"## Install\");\n for (const c of report.install) {\n lines.push(formatCheck(c, color));\n }\n lines.push(\"\");\n }\n if (report.agents.length > 0) {\n lines.push(color ? `${BOLD}## Agents${RST}` : \"## Agents\");\n for (const c of report.agents.map(agentToCheck)) {\n lines.push(formatCheck(c, color));\n }\n lines.push(\"\");\n }\n if (report.updates.length > 0) {\n lines.push(color ? `${BOLD}## Updates${RST}` : \"## Updates\");\n for (const c of report.updates) {\n lines.push(formatCheck(c, color));\n }\n lines.push(\"\");\n }\n if (report.wiki.length > 0) {\n lines.push(color ? `${BOLD}## Current wiki${RST}` : \"## Current wiki\");\n for (const c of report.wiki) {\n lines.push(formatCheck(c, color));\n }\n lines.push(\"\");\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction agentToCheck(agent: AgentDoctorCheck): Check {\n const message = [\n agent.label,\n agent.selected ? \"(default)\" : null,\n agent.recommended ? \"(recommended)\" : null,\n agent.status === \"ok\" ? \"ready\" : readinessMessage(agent),\n `model: ${agent.model ?? \"provider default\"}`,\n agent.account,\n ].filter((part): part is string => part !== null).join(\" \");\n return {\n status: agent.status,\n key: `agents.${agent.id}`,\n message,\n fix: agent.fix,\n };\n}\n\nfunction readinessMessage(agent: AgentDoctorCheck): string {\n if (!agent.installed) return \"missing\";\n if (!agent.authenticated) return `not ready: ${agent.detail}`;\n return agent.detail;\n}\n\nfunction formatCheck(c: Check, color: boolean): string {\n const { icon, tint } = iconFor(c.status, color);\n const head = ` ${tint}${icon}${color ? RST : \"\"} ${c.message}`;\n if (c.fix === undefined) return head;\n const fixLine = color\n ? ` ${DIM}${c.fix}${RST}`\n : ` ${c.fix}`;\n return `${head}\\n${fixLine}`;\n}\n\nfunction iconFor(\n status: CheckStatus,\n color: boolean,\n): { icon: string; tint: string } {\n switch (status) {\n case \"ok\":\n return { icon: \"\\u2713\", tint: color ? GREEN : \"\" };\n case \"problem\":\n return { icon: \"\\u2717\", tint: color ? RED : \"\" };\n case \"info\":\n return { icon: \"\\u25c7\", tint: color ? BLUE : \"\" };\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nimport { IMPORT_LINE } from \"../setup.js\";\nimport {\n classifyInstallPath,\n detectInstallPath,\n probeBetterSqlite3,\n} from \"./probes.js\";\nimport type { Check, DoctorOptions } from \"./types.js\";\n\nexport async function gatherInstallChecks(\n options: DoctorOptions,\n): Promise<Check[]> {\n const checks: Check[] = [];\n\n const rawPath = options.installPath ?? detectInstallPath();\n const { installPath, isEphemeral } = classifyInstallPath(rawPath);\n checks.push(describeInstallPath(installPath, isEphemeral));\n\n const nodeVersion = options.nodeVersion ?? process.version;\n const sqlite = options.sqliteProbe ?? probeBetterSqlite3();\n checks.push({\n status: sqlite.ok ? \"ok\" : \"problem\",\n key: \"install.sqlite\",\n message: sqlite.ok\n ? `better-sqlite3 native binding OK (Node ${nodeVersion})`\n : `better-sqlite3 native binding failed: ${sqlite.summary}`,\n fix: sqlite.ok\n ? undefined\n : \"run: npm rebuild better-sqlite3 (in the install directory)\",\n });\n\n checks.push(describeProviderAuthSurface());\n\n const settingsPath =\n options.settingsPath ?? path.join(homedir(), \".claude\", \"settings.json\");\n checks.push(await describeHook(settingsPath));\n\n const claudeDir = options.claudeDir ?? path.join(homedir(), \".claude\");\n checks.push(describeGuides(claudeDir, options.cwd));\n checks.push(await describeGuideActivation(claudeDir, options.cwd));\n\n return checks;\n}\n\nfunction describeInstallPath(\n installPath: string | null,\n isEphemeral: boolean,\n): Check {\n if (installPath === null) {\n return {\n status: \"problem\",\n key: \"install.path\",\n message: \"could not detect codealmanac install path\",\n fix: \"reinstall with: npm install -g codealmanac\",\n };\n }\n return {\n status: isEphemeral ? \"info\" : \"ok\",\n key: \"install.path\",\n message: isEphemeral\n ? `codealmanac running from ephemeral npx location: ${installPath}`\n : `codealmanac installed at ${installPath}`,\n fix: isEphemeral\n ? \"run: npm install -g codealmanac (to make the install permanent)\"\n : undefined,\n };\n}\n\nfunction describeProviderAuthSurface(): Check {\n return {\n status: \"info\",\n key: \"install.auth\",\n message: \"provider auth is reported under Agents\",\n fix: \"run: almanac agents list\",\n };\n}\n\nasync function describeHook(settingsPath: string): Promise<Check> {\n if (!existsSync(settingsPath)) {\n return {\n status: \"problem\",\n key: \"install.hook\",\n message: \"SessionEnd hook not installed\",\n fix: \"run: almanac setup --yes\",\n };\n }\n try {\n const raw = await readFile(settingsPath, \"utf8\");\n const parsed = JSON.parse(raw) as {\n hooks?: {\n SessionEnd?: {\n command?: string;\n hooks?: { command?: string }[];\n }[];\n };\n };\n const entries = parsed.hooks?.SessionEnd ?? [];\n const found = entries.some((e) => {\n if (\n typeof e?.command === \"string\" &&\n e.command.endsWith(\"almanac-capture.sh\")\n ) {\n return true;\n }\n if (Array.isArray(e?.hooks)) {\n return e.hooks.some(\n (h) =>\n typeof h?.command === \"string\" &&\n h.command.endsWith(\"almanac-capture.sh\"),\n );\n }\n return false;\n });\n if (!found) {\n return {\n status: \"problem\",\n key: \"install.hook\",\n message: \"SessionEnd hook not installed\",\n fix: \"run: almanac setup --yes\",\n };\n }\n return {\n status: \"ok\",\n key: \"install.hook\",\n message: `SessionEnd hook installed at ${settingsPath}`,\n };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n status: \"problem\",\n key: \"install.hook\",\n message: `could not read ${settingsPath}: ${msg}`,\n fix: \"check the file for malformed JSON\",\n };\n }\n}\n\nfunction describeGuides(claudeDir: string, cwd: string): Check {\n const mini = path.join(claudeDir, \"codealmanac.md\");\n const ref = path.join(claudeDir, \"codealmanac-reference.md\");\n const haveMini = existsSync(mini);\n const haveRef = existsSync(ref);\n if (haveMini && haveRef) {\n return {\n status: \"ok\",\n key: \"install.guides\",\n message: `Agent guides installed for Claude (${path.basename(mini)}, ${path.basename(ref)})`,\n };\n }\n const codexMini = path.join(homedir(), \".codex\", \"codealmanac.md\");\n const codexRef = path.join(homedir(), \".codex\", \"codealmanac-reference.md\");\n if (existsSync(codexMini) && existsSync(codexRef)) {\n return {\n status: \"ok\",\n key: \"install.guides\",\n message: \"Agent guides installed for Codex (codealmanac.md, codealmanac-reference.md)\",\n };\n }\n const cursorRule = path.join(cwd, \".cursor\", \"rules\", \"codealmanac.mdc\");\n if (existsSync(cursorRule)) {\n return {\n status: \"ok\",\n key: \"install.guides\",\n message: \"Agent guide installed for Cursor (.cursor/rules/codealmanac.mdc)\",\n };\n }\n const missing = [\n haveMini ? null : \"codealmanac.md\",\n haveRef ? null : \"codealmanac-reference.md\",\n ].filter((s): s is string => s !== null);\n return {\n status: \"problem\",\n key: \"install.guides\",\n message: `Agent guides missing (${missing.join(\", \")})`,\n fix: \"run: almanac setup --yes\",\n };\n}\n\nasync function describeGuideActivation(\n claudeDir: string,\n cwd: string,\n): Promise<Check> {\n const claudeMd = path.join(claudeDir, \"CLAUDE.md\");\n if (existsSync(claudeMd)) {\n try {\n const contents = await readFile(claudeMd, \"utf8\");\n const lines = contents.split(/\\r?\\n/).map((l) => l.trim());\n const present = lines.some((line) => {\n if (line === IMPORT_LINE) return true;\n if (!line.startsWith(IMPORT_LINE)) return false;\n const next = line[IMPORT_LINE.length];\n return next === \" \" || next === \"\\t\";\n });\n if (present) {\n return {\n status: \"ok\",\n key: \"install.import\",\n message: \"Claude guide import present\",\n };\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n status: \"problem\",\n key: \"install.import\",\n message: `could not read ${claudeMd}: ${msg}`,\n };\n }\n }\n\n const codexAgents = path.join(homedir(), \".codex\", \"AGENTS.md\");\n if (existsSync(codexAgents)) {\n try {\n const contents = await readFile(codexAgents, \"utf8\");\n if (\n contents.includes(\"<!-- codealmanac:start -->\") &&\n contents.includes(\"<!-- codealmanac:end -->\")\n ) {\n return {\n status: \"ok\",\n key: \"install.import\",\n message: \"Codex guide block present\",\n };\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n status: \"problem\",\n key: \"install.import\",\n message: `could not read ${codexAgents}: ${msg}`,\n };\n }\n }\n\n const cursorRule = path.join(cwd, \".cursor\", \"rules\", \"codealmanac.mdc\");\n if (existsSync(cursorRule)) {\n return {\n status: \"ok\",\n key: \"install.import\",\n message: \"Cursor guide rule present\",\n };\n }\n\n return {\n status: \"problem\",\n key: \"install.import\",\n message: \"provider guide activation missing\",\n fix: \"run: almanac setup --yes\",\n };\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport {\n checkClaudeAuth,\n type ClaudeAuthStatus,\n type SpawnCliFn,\n} from \"../../agent/providers/claude/index.js\";\nimport type { SqliteProbeResult } from \"./types.js\";\n\n// Single `createRequire` instance — used by package/binding probes.\nconst req = createRequire(import.meta.url);\n\n/**\n * Detect where codealmanac is installed by walking up from the running\n * module until we find a `package.json` whose `name` is `codealmanac`.\n */\nexport function detectInstallPath(): string | null {\n try {\n const here = fileURLToPath(import.meta.url);\n let dir = path.dirname(here);\n for (let i = 0; i < 6; i++) {\n const pkgPath = path.join(dir, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const raw = readFileSync(pkgPath, \"utf-8\");\n const pkg = JSON.parse(raw) as { name?: unknown };\n if (pkg.name === \"codealmanac\") return dir;\n } catch {\n // ignore — keep walking\n }\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Classify the detected install path as permanent or ephemeral.\n * Ephemeral locations (npm npx cache, pnpm dlx cache, /tmp/) are valid\n * installs but will disappear when the cache is evicted or the machine\n * reboots. Doctor reports them as `info` rather than `ok`.\n */\nexport function classifyInstallPath(\n raw: string | null,\n): { installPath: string | null; isEphemeral: boolean } {\n if (raw === null) return { installPath: null, isEphemeral: false };\n const home = homedir();\n const ephemeralPrefixes = [\n path.join(home, \".npm\", \"_npx\"),\n path.join(home, \".local\", \"share\", \"pnpm\", \"dlx\"),\n \"/tmp/\",\n \"/var/folders/\",\n ];\n const isEphemeral = ephemeralPrefixes.some((p) => raw.startsWith(p));\n return { installPath: raw, isEphemeral };\n}\n\n/**\n * Probe the better-sqlite3 native binding by opening an in-memory DB.\n */\nexport function probeBetterSqlite3(): SqliteProbeResult {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const Database = req(\"better-sqlite3\") as typeof import(\"better-sqlite3\");\n const db = new Database(\":memory:\");\n db.close();\n return { ok: true, summary: \"native binding loads cleanly\" };\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n const firstLine = msg.split(\"\\n\")[0] ?? msg;\n return { ok: false, summary: firstLine };\n }\n}\n\nexport async function safeCheckAuth(\n spawnCli?: SpawnCliFn,\n): Promise<ClaudeAuthStatus> {\n try {\n return await checkClaudeAuth(spawnCli);\n } catch {\n return { loggedIn: false };\n }\n}\n\nexport function readPackageVersion(): string | null {\n const candidates = [\n \"../../../package.json\",\n \"../../package.json\",\n \"../package.json\",\n ];\n for (const candidate of candidates) {\n try {\n const pkg = req(candidate) as { version?: unknown };\n if (typeof pkg.version === \"string\" && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // Fall through to the next runtime layout candidate.\n }\n }\n return null;\n}\n","import { buildProviderSetupView } from \"../../agent/provider-view.js\";\nimport { checkClaudeAuth } from \"../../agent/providers/claude/index.js\";\nimport type { ProviderStatus } from \"../../agent/types.js\";\nimport type { AgentDoctorCheck, DoctorOptions } from \"./types.js\";\n\nexport async function gatherAgentChecks(\n options: DoctorOptions,\n): Promise<AgentDoctorCheck[]> {\n const view = await buildProviderSetupView({\n spawnCli: options.spawnCli,\n statuses:\n options.providerStatuses ??\n (options.spawnCli === undefined\n ? undefined\n : await injectedProviderStatuses(options)),\n });\n return view.choices.map((choice) => {\n return {\n status: choice.ready ? \"ok\" : \"problem\",\n id: choice.id,\n label: choice.label,\n readiness: choice.readiness,\n selected: choice.selected,\n recommended: choice.recommended,\n installed: choice.installed,\n authenticated: choice.authenticated,\n model: choice.effectiveModel,\n providerDefaultModel: choice.providerDefaultModel,\n configuredModel: choice.configuredModel,\n account: choice.account,\n detail: choice.detail,\n fix: choice.fixCommand ?? undefined,\n };\n });\n}\n\nasync function injectedProviderStatuses(\n options: DoctorOptions,\n): Promise<ProviderStatus[]> {\n const auth = await checkClaudeAuth(options.spawnCli);\n const hasApiKey =\n process.env.ANTHROPIC_API_KEY !== undefined &&\n process.env.ANTHROPIC_API_KEY.length > 0;\n const claudeReady = auth.loggedIn || hasApiKey;\n return [\n {\n id: \"claude\",\n installed: true,\n authenticated: claudeReady,\n detail: claudeReady\n ? auth.email ?? (hasApiKey ? \"ANTHROPIC_API_KEY set\" : \"logged in\")\n : \"not logged in\",\n },\n {\n id: \"codex\",\n installed: false,\n authenticated: false,\n detail: \"codex status not injected\",\n },\n {\n id: \"cursor\",\n installed: false,\n authenticated: false,\n detail: \"cursor-agent status not injected\",\n },\n ];\n}\n","import { readConfig } from \"../../update/config.js\";\nimport { readStateForDoctor } from \"../../update/schedule.js\";\nimport { isNewer } from \"../../update/semver.js\";\nimport { formatDuration } from \"./duration.js\";\nimport type { Check, DoctorOptions } from \"./types.js\";\n\nexport async function gatherUpdateChecks(\n options: DoctorOptions,\n installedVersion: string,\n): Promise<Check[]> {\n const checks: Check[] = [];\n const state = readStateForDoctor(options.updateStatePath);\n const config = await readConfig(options.updateConfigPath);\n\n if (state === null || state.latest_version.length === 0) {\n checks.push({\n status: \"info\",\n key: \"update.status\",\n message: `on ${installedVersion}; no update check has run yet`,\n fix: \"run: almanac update --check\",\n });\n } else if (isNewer(state.latest_version, installedVersion)) {\n const dismissed = state.dismissed_versions.includes(state.latest_version)\n ? \" (dismissed — run `almanac update` to install anyway)\"\n : \"\";\n checks.push({\n status: \"problem\",\n key: \"update.status\",\n message:\n `${state.latest_version} available (you're on ${installedVersion})${dismissed}`,\n fix: \"run: almanac update\",\n });\n } else {\n checks.push({\n status: \"ok\",\n key: \"update.status\",\n message: `on latest (${installedVersion})`,\n });\n }\n\n if (state !== null && state.last_check_at > 0) {\n const now = (options.now?.() ?? new Date()).getTime();\n const ageMs = now - state.last_check_at * 1000;\n const failedSuffix =\n state.last_fetch_failed_at !== undefined &&\n state.last_fetch_failed_at === state.last_check_at\n ? \" (last attempt failed — will retry next invocation)\"\n : \"\";\n checks.push({\n status: \"info\",\n key: \"update.last_check\",\n message: `last checked: ${formatDuration(ageMs)} ago${failedSuffix}`,\n });\n } else {\n checks.push({\n status: \"info\",\n key: \"update.last_check\",\n message: \"last checked: never\",\n });\n }\n\n checks.push({\n status: \"info\",\n key: \"update.notifier\",\n message: `update notifier: ${config.update_notifier ? \"enabled\" : \"disabled\"}`,\n fix: config.update_notifier\n ? undefined\n : \"run: almanac config set update_notifier true\",\n });\n\n if (state !== null && state.dismissed_versions.length > 0) {\n checks.push({\n status: \"info\",\n key: \"update.dismissed\",\n message: `dismissed versions: ${state.dismissed_versions.join(\", \")}`,\n });\n }\n\n return checks;\n}\n","import { formatReport } from \"./doctor-checks/format.js\";\nimport { gatherInstallChecks } from \"./doctor-checks/install.js\";\nimport { readPackageVersion } from \"./doctor-checks/probes.js\";\nimport type {\n Check,\n CheckStatus,\n AgentDoctorCheck,\n DoctorOptions,\n DoctorReport,\n DoctorResult,\n SqliteProbeResult,\n} from \"./doctor-checks/types.js\";\nimport { gatherAgentChecks } from \"./doctor-checks/agents.js\";\nimport { gatherUpdateChecks } from \"./doctor-checks/updates.js\";\n\nexport type {\n Check,\n CheckStatus,\n DoctorOptions,\n DoctorReport,\n DoctorResult,\n SqliteProbeResult,\n};\n\n/**\n * `almanac doctor` — install + wiki health report.\n *\n * Separate from `almanac health` (which checks graph integrity of a\n * specific wiki). `doctor` answers the \"is this install even set up\n * correctly?\" question that users hit when first trying the tool or when\n * sessions silently stop getting captured.\n *\n * This file is the command composition root. The section-specific probes\n * and formatting live in `doctor-checks/` so each durable fact has one\n * obvious owner.\n */\nexport async function runDoctor(\n options: DoctorOptions,\n): Promise<DoctorResult> {\n const version =\n options.versionOverride ?? readPackageVersion() ?? \"unknown\";\n\n const install: Check[] = options.wikiOnly === true\n ? []\n : await gatherInstallChecks(options);\n\n const agents: AgentDoctorCheck[] = options.wikiOnly === true\n ? []\n : await gatherAgentChecks(options);\n\n const updates: Check[] = options.wikiOnly === true\n ? []\n : await gatherUpdateChecks(options, version);\n\n const wiki: Check[] = options.installOnly === true\n ? []\n : await safeGatherWikiChecks(options);\n\n const report: DoctorReport = { version, install, agents, updates, wiki };\n\n if (options.json === true) {\n return {\n stdout: `${JSON.stringify(report, null, 2)}\\n`,\n stderr: \"\",\n exitCode: 0,\n };\n }\n\n return {\n stdout: formatReport(report, options),\n stderr: \"\",\n exitCode: 0,\n };\n}\n\nasync function safeGatherWikiChecks(\n options: DoctorOptions,\n): Promise<Check[]> {\n try {\n const { gatherWikiChecks } = await import(\"./doctor-checks/wiki.js\");\n return await gatherWikiChecks(options);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n return [\n {\n status: \"problem\",\n key: \"wiki.checks\",\n message: `could not run wiki checks: ${msg.split(\"\\n\")[0] ?? msg}`,\n fix: \"run: npm rebuild better-sqlite3 (in the install directory)\",\n },\n ];\n }\n}\n"],"mappings":"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","names":["existsSync","homedir","path","path","homedir","existsSync"]}
@@ -4,8 +4,10 @@ import {
4
4
  getStatePath
5
5
  } from "./chunk-F53U6JQG.js";
6
6
  import {
7
- getConfigPath
8
- } from "./chunk-WRUSDYYE.js";
7
+ getConfigPath,
8
+ getLegacyConfigPath,
9
+ parseConfigText
10
+ } from "./chunk-P5WGG4FJ.js";
9
11
 
10
12
  // src/update/schedule.ts
11
13
  import { spawn } from "child_process";
@@ -41,13 +43,17 @@ function shouldSchedule(argv) {
41
43
  return true;
42
44
  }
43
45
  function notifierEnabled() {
46
+ const parsed = readConfigSync(getConfigPath());
47
+ if (parsed !== null) return parsed.update_notifier !== false;
48
+ const legacy = readConfigSync(getLegacyConfigPath());
49
+ return legacy?.update_notifier !== false;
50
+ }
51
+ function readConfigSync(path) {
44
52
  try {
45
- const raw = readFileSync(getConfigPath(), "utf8");
46
- const parsed = JSON.parse(raw);
47
- if (parsed.update_notifier === false) return false;
48
- return true;
53
+ const raw = readFileSync(path, "utf8");
54
+ return parseConfigText(raw, path);
49
55
  } catch {
50
- return true;
56
+ return null;
51
57
  }
52
58
  }
53
59
  async function runInternalUpdateCheck() {
@@ -80,4 +86,4 @@ export {
80
86
  runInternalUpdateCheck,
81
87
  readStateForDoctor
82
88
  };
83
- //# sourceMappingURL=chunk-V3QOQSXI.js.map
89
+ //# sourceMappingURL=chunk-TILAKDN6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/update/schedule.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\n\nimport { checkForUpdate } from \"./check.js\";\nimport {\n getConfigPath,\n getLegacyConfigPath,\n parseConfigText,\n} from \"./config.js\";\nimport { getStatePath, type UpdateState } from \"./state.js\";\n\n/**\n * Post-command scheduler for the background update check.\n *\n * After any normal `almanac <command>` exits, we want a fresh check to\n * have happened by the next invocation. We achieve that by spawning a\n * detached copy of ourselves with the hidden `--internal-check-updates`\n * flag; that child does nothing but hit the registry and write\n * `~/.almanac/update-state.json`, then exits.\n *\n * Why detach rather than check inline:\n * - 3s network timeout in the foreground would feel sluggish on every\n * command.\n * - `npm test` and CI scripts shouldn't pay for a registry round-trip\n * (gated below via env).\n * - A detached child with `stdio: \"ignore\"` cannot leak output into\n * the parent's stdout/stderr — critical for pipelines.\n *\n * Hazards we accept:\n * - A Claude Code subprocess whose parent shell exits right after the\n * `almanac` call may kill the child before it finishes. That's\n * fine: a failed check just means we try again next invocation.\n * - Detached child survival on Windows isn't as robust as on Unix.\n * Same fallback: next invocation retries.\n */\n\nexport function scheduleBackgroundUpdateCheck(argv: string[]): void {\n if (!shouldSchedule(argv)) return;\n\n const scriptPath = argv[1];\n const nodeBin = process.execPath;\n if (scriptPath === undefined || scriptPath.length === 0) return;\n\n // Spawn with the current Node and the same script path. `detached:\n // true` + `stdio: \"ignore\"` + `unref()` detaches the child from our\n // event loop so the parent can exit independently.\n try {\n const child = spawn(\n nodeBin,\n [scriptPath, \"--internal-check-updates\"],\n {\n detached: true,\n stdio: \"ignore\",\n // Windows: with `detached: true` and no `stdio`, Node opens a\n // console window — `\"ignore\"` prevents that.\n },\n );\n child.unref();\n // Swallow any synchronous spawn errors (e.g. ENOENT in strange\n // installs) — never propagate to the foreground command.\n child.on(\"error\", () => {});\n } catch {\n // Last-resort swallow: background checks are best-effort.\n }\n}\n\n/**\n * Should we spawn the worker at all?\n *\n * - Respect the `update_notifier` config — no banner means no need\n * for the data that feeds it.\n * - Skip in test environments so `npm test` doesn't fork 300 copies\n * of itself into the background and hammer the registry.\n * - Skip on the worker invocation itself (prevents a fork bomb).\n * - Skip when the user doesn't own the install path (permission\n * weirdness) — detected by `~/.almanac` mkdir failing; simplest\n * to just rely on the worker's own error handling, so we don't\n * gate here.\n * - Skip when the argv contains `--help`/`--version`/nothing — these\n * commands are often run from scripts that care about clean exit;\n * though the inline banner still shows, we don't kick off a check.\n */\nfunction shouldSchedule(argv: string[]): boolean {\n if (process.env.CODEALMANAC_SKIP_UPDATE_CHECK === \"1\") return false;\n if (process.env.NODE_ENV === \"test\") return false;\n if (process.env.VITEST !== undefined) return false;\n\n // Already the worker. argv[2..] contains the internal flag.\n if (argv.slice(2).includes(\"--internal-check-updates\")) return false;\n\n if (!notifierEnabled()) return false;\n\n return true;\n}\n\nfunction notifierEnabled(): boolean {\n const parsed = readConfigSync(getConfigPath());\n if (parsed !== null) return parsed.update_notifier !== false;\n const legacy = readConfigSync(getLegacyConfigPath());\n return legacy?.update_notifier !== false;\n}\n\nfunction readConfigSync(path: string): { update_notifier?: unknown } | null {\n try {\n const raw = readFileSync(path, \"utf8\");\n return parseConfigText(raw, path) as { update_notifier?: unknown };\n } catch {\n return null;\n }\n}\n\n/**\n * The worker body. Invoked when `--internal-check-updates` appears on\n * the argv. Must be fast and must never print: the parent spawned us\n * with `stdio: \"ignore\"` but a stray write could still surprise a\n * downstream debugger.\n *\n * We take a simple file lock at `~/.almanac/.update-check.lock` to\n * prevent two workers running at the same time (which could happen if\n * the user fires several commands in parallel). The lock is just the\n * existence of the file with our PID inside; if an existing lock is\n * stale (older than the 3s + cache-write budget), we steal it.\n */\nexport async function runInternalUpdateCheck(): Promise<void> {\n // The worker is intentionally minimal. Any error (network, fs,\n // JSON) is handled inside `checkForUpdate` and surfaces as a\n // swallowed return; we just need to await it and exit.\n try {\n await checkForUpdate({});\n } catch {\n // Defense-in-depth: nothing must escape the worker.\n }\n}\n\n/**\n * Read the current state snapshot for diagnostic surfaces (doctor, the\n * `update --check` command). Wraps the sync read so callers can grab\n * state without the `async readState` ceremony.\n */\nexport function readStateForDoctor(path?: string): UpdateState | null {\n const file = path ?? getStatePath();\n try {\n const raw = readFileSync(file, \"utf8\");\n const trimmed = raw.trim();\n if (trimmed.length === 0) return null;\n const parsed = JSON.parse(trimmed) as Partial<UpdateState>;\n return {\n last_check_at:\n typeof parsed.last_check_at === \"number\" ? parsed.last_check_at : 0,\n installed_version:\n typeof parsed.installed_version === \"string\"\n ? parsed.installed_version\n : \"\",\n latest_version:\n typeof parsed.latest_version === \"string\" ? parsed.latest_version : \"\",\n dismissed_versions: Array.isArray(parsed.dismissed_versions)\n ? parsed.dismissed_versions.filter((v): v is string => typeof v === \"string\")\n : [],\n last_fetch_failed_at:\n typeof parsed.last_fetch_failed_at === \"number\"\n ? parsed.last_fetch_failed_at\n : undefined,\n };\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,oBAAoB;AAmCtB,SAAS,8BAA8B,MAAsB;AAClE,MAAI,CAAC,eAAe,IAAI,EAAG;AAE3B,QAAM,aAAa,KAAK,CAAC;AACzB,QAAM,UAAU,QAAQ;AACxB,MAAI,eAAe,UAAa,WAAW,WAAW,EAAG;AAKzD,MAAI;AACF,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,CAAC,YAAY,0BAA0B;AAAA,MACvC;AAAA,QACE,UAAU;AAAA,QACV,OAAO;AAAA;AAAA;AAAA,MAGT;AAAA,IACF;AACA,UAAM,MAAM;AAGZ,UAAM,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5B,QAAQ;AAAA,EAER;AACF;AAkBA,SAAS,eAAe,MAAyB;AAC/C,MAAI,QAAQ,IAAI,kCAAkC,IAAK,QAAO;AAC9D,MAAI,QAAQ,IAAI,aAAa,OAAQ,QAAO;AAC5C,MAAI,QAAQ,IAAI,WAAW,OAAW,QAAO;AAG7C,MAAI,KAAK,MAAM,CAAC,EAAE,SAAS,0BAA0B,EAAG,QAAO;AAE/D,MAAI,CAAC,gBAAgB,EAAG,QAAO;AAE/B,SAAO;AACT;AAEA,SAAS,kBAA2B;AAClC,QAAM,SAAS,eAAe,cAAc,CAAC;AAC7C,MAAI,WAAW,KAAM,QAAO,OAAO,oBAAoB;AACvD,QAAM,SAAS,eAAe,oBAAoB,CAAC;AACnD,SAAO,QAAQ,oBAAoB;AACrC;AAEA,SAAS,eAAe,MAAoD;AAC1E,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,MAAM;AACrC,WAAO,gBAAgB,KAAK,IAAI;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcA,eAAsB,yBAAwC;AAI5D,MAAI;AACF,UAAM,eAAe,CAAC,CAAC;AAAA,EACzB,QAAQ;AAAA,EAER;AACF;AAOO,SAAS,mBAAmB,MAAmC;AACpE,QAAM,OAAO,QAAQ,aAAa;AAClC,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,MAAM;AACrC,UAAM,UAAU,IAAI,KAAK;AACzB,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,WAAO;AAAA,MACL,eACE,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB;AAAA,MACpE,mBACE,OAAO,OAAO,sBAAsB,WAChC,OAAO,oBACP;AAAA,MACN,gBACE,OAAO,OAAO,mBAAmB,WAAW,OAAO,iBAAiB;AAAA,MACtE,oBAAoB,MAAM,QAAQ,OAAO,kBAAkB,IACvD,OAAO,mBAAmB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IAC1E,CAAC;AAAA,MACL,sBACE,OAAO,OAAO,yBAAyB,WACnC,OAAO,uBACP;AAAA,IACR;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}