tokentracker-cli 0.96.1 → 0.96.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.
Files changed (145) hide show
  1. package/README.de.md +34 -25
  2. package/README.ja.md +38 -27
  3. package/README.ko.md +39 -28
  4. package/README.md +36 -28
  5. package/README.zh-CN.md +36 -25
  6. package/dashboard/dist/assets/{AchievementBadge-DKyElm0T.js → AchievementBadge-BX4iBBYM.js} +1 -1
  7. package/dashboard/dist/assets/{AchievementsPage-BvCx3wND.js → AchievementsPage-DRYRuCbh.js} +2 -2
  8. package/dashboard/dist/assets/{AchievementsSection-DN69LzXX.js → AchievementsSection-BM_JWWhJ.js} +1 -1
  9. package/dashboard/dist/assets/{BadgeDetailModal-DGiKdZfz.js → BadgeDetailModal-DxoxXst2.js} +1 -1
  10. package/dashboard/dist/assets/{BotAnimated-zN8rH7_K.js → BotAnimated-BVpFxG8u.js} +1 -1
  11. package/dashboard/dist/assets/{Card-COWo-ClV.js → Card-BJJs701t.js} +1 -1
  12. package/dashboard/dist/assets/{ClawdAnimated-BWsB7Eat.js → ClawdAnimated-46wPZ_Tc.js} +2 -2
  13. package/dashboard/dist/assets/{CommandPalette-BfvIwuoJ.js → CommandPalette-B_zkc7IU.js} +1 -1
  14. package/dashboard/dist/assets/{CommunityStatsModal-BQWg7R_y.js → CommunityStatsModal-M2k5rIf1.js} +1 -1
  15. package/dashboard/dist/assets/{ConfirmModal-C7ABSI-O.js → ConfirmModal-DBvOTcAQ.js} +1 -1
  16. package/dashboard/dist/assets/{Controls-vMzEZzej.js → Controls-BCkjPMSH.js} +1 -1
  17. package/dashboard/dist/assets/DashboardPage-3bHhou09.js +1 -0
  18. package/dashboard/dist/assets/{DevicePage-3ejUBLWM.js → DevicePage-DFBUce8C.js} +1 -1
  19. package/dashboard/dist/assets/DialogClose-kvDd8h3V.js +1 -0
  20. package/dashboard/dist/assets/DialogDescription-BMyOicfz.js +1 -0
  21. package/dashboard/dist/assets/DialogTitle-DL5nvD31.js +1 -0
  22. package/dashboard/dist/assets/{FadeIn-D7Jcfz1N.js → FadeIn-B-Bg6SEy.js} +1 -1
  23. package/dashboard/dist/assets/{HeaderGithubStar-D--Rx5BS.js → HeaderGithubStar-BExz0hh4.js} +1 -1
  24. package/dashboard/dist/assets/{HoverTooltip-n1W0z3Wr.js → HoverTooltip-Dj5zemYT.js} +1 -1
  25. package/dashboard/dist/assets/{IpCheckPage-D202tl9B.js → IpCheckPage-DN7FuJ_v.js} +1 -1
  26. package/dashboard/dist/assets/{LandingPage-D6yL2EZo.js → LandingPage-9dKgSBRy.js} +2 -2
  27. package/dashboard/dist/assets/{LeaderboardAvatar-ChFg2mtA.js → LeaderboardAvatar-00V95XIg.js} +1 -1
  28. package/dashboard/dist/assets/{LeaderboardPage-BXHwGs8r.js → LeaderboardPage-DkKfUt0Z.js} +2 -2
  29. package/dashboard/dist/assets/{LeaderboardProfileModal-B5LoOpCk.js → LeaderboardProfileModal-C9sIHBgN.js} +4 -4
  30. package/dashboard/dist/assets/{LeaderboardProfilePage-jsukhRz8.js → LeaderboardProfilePage-BAiLMsEb.js} +1 -1
  31. package/dashboard/dist/assets/LimitsPage-BuenTaHa.js +8 -0
  32. package/dashboard/dist/assets/{LocalOnlyNotice-Du0lz657.js → LocalOnlyNotice-CKhY27Lr.js} +1 -1
  33. package/dashboard/dist/assets/{LoginCard-BFEX9x7-.js → LoginCard-BbvoBx5N.js} +1 -1
  34. package/dashboard/dist/assets/{LoginModal-DqC7_d35.js → LoginModal-P7zIkBhO.js} +1 -1
  35. package/dashboard/dist/assets/{LoginPage-Dzx8N7HE.js → LoginPage-Bo0bR26x.js} +1 -1
  36. package/dashboard/dist/assets/PetPage-C6i9bSSl.js +1 -0
  37. package/dashboard/dist/assets/PopoverPopup-h0gf36vg.js +1 -0
  38. package/dashboard/dist/assets/ProviderIcon-Dm8fwPdj.js +1 -0
  39. package/dashboard/dist/assets/{ResetPasswordPage-x_GR79sR.js → ResetPasswordPage-DvSJ8kg0.js} +1 -1
  40. package/dashboard/dist/assets/{SegmentedControl-BxDiDV4R.js → SegmentedControl-CP7oiGZE.js} +1 -1
  41. package/dashboard/dist/assets/Select-iwP92x9C.js +1 -0
  42. package/dashboard/dist/assets/SelectItemText-DzljA15_.js +1 -0
  43. package/dashboard/dist/assets/{ServiceStatusPage-BVQuVdjg.js → ServiceStatusPage-uD4D0Tid.js} +1 -1
  44. package/dashboard/dist/assets/{SessionsPage-DE39wNIO.js → SessionsPage-Cmb1ssNP.js} +2 -2
  45. package/dashboard/dist/assets/SettingsPage-D8ryg1Pv.js +2 -0
  46. package/dashboard/dist/assets/SkillsPage-eXI09q03.js +1 -0
  47. package/dashboard/dist/assets/{TokenGalaxy-BPM779Wb.js → TokenGalaxy-f2djrlUv.js} +1 -1
  48. package/dashboard/dist/assets/ToolbarRootContext-CDxCPYLv.js +1 -0
  49. package/dashboard/dist/assets/{TrendMonitor-BkNy-o1D.js → TrendMonitor-C419X22G.js} +1 -1
  50. package/dashboard/dist/assets/{WidgetsPage-Bpg6H-ZB.js → WidgetsPage-AYq__pOO.js} +1 -1
  51. package/dashboard/dist/assets/{WrappedPage-C6-NM-RO.js → WrappedPage-Bsm9k5XI.js} +1 -1
  52. package/dashboard/dist/assets/{__vite-browser-external-CvMMIjhi.css → __vite-browser-external-5P7me0Qq.css} +1 -1
  53. package/dashboard/dist/assets/__vite-browser-external-BDkeh90N.js +45 -0
  54. package/dashboard/dist/assets/{agent-logos-DpbkRqXT.js → agent-logos-B6Vg-4_1.js} +1 -1
  55. package/dashboard/dist/assets/{arrow-up-right-BjOtWUVs.js → arrow-up-right-DIQXwaOD.js} +1 -1
  56. package/dashboard/dist/assets/{check-BlxnX5jZ.js → check-CgD_bV39.js} +1 -1
  57. package/dashboard/dist/assets/composite-BIgEqWZD.js +1 -0
  58. package/dashboard/dist/assets/{copy-CIsWjLY5.js → copy-DlM1BHdp.js} +1 -1
  59. package/dashboard/dist/assets/{download-QDaQbQdd.js → download-CQFG-VIY.js} +1 -1
  60. package/dashboard/dist/assets/{external-link-DPV4VU3m.js → external-link-BQyDqGxz.js} +1 -1
  61. package/dashboard/dist/assets/{flame-B_X-7VNp.js → flame-BamzyXzF.js} +1 -1
  62. package/dashboard/dist/assets/{format-tokens-DEx2TJj8.js → format-tokens-C4G-_xYt.js} +1 -1
  63. package/dashboard/dist/assets/{icons-983RyGSl.js → icons-Ntscvl1a.js} +1 -1
  64. package/dashboard/dist/assets/{index-qtonw_x4.js → index-CUrcPqgp.js} +1 -1
  65. package/dashboard/dist/assets/{index-BONYA8HZ.js → index-DGxgsNoQ.js} +1 -1
  66. package/dashboard/dist/assets/{index-Cjs5stC0.js → index-FRIt5mlv.js} +1 -1
  67. package/dashboard/dist/assets/{info-DpVOhXZI.js → info-CXytWcZi.js} +1 -1
  68. package/dashboard/dist/assets/{link-2-CBro-upz.js → link-2-Dexoo_jO.js} +1 -1
  69. package/dashboard/dist/assets/{loader-circle-wYFdSyOd.js → loader-circle-Cn0JHc8t.js} +1 -1
  70. package/dashboard/dist/assets/{maximize-2-DhTyQ4ly.js → maximize-2-DLh7gYuP.js} +1 -1
  71. package/dashboard/dist/assets/{modifiers.esm-CPljwVhN.js → modifiers.esm-DOh_xtyt.js} +1 -1
  72. package/dashboard/dist/assets/module-DUSXqJtA.js +3 -0
  73. package/dashboard/dist/assets/{monitor-smartphone-D_wr-HOR.js → monitor-smartphone-CpqS9Ior.js} +1 -1
  74. package/dashboard/dist/assets/{plus-BNjfkBEF.js → plus-bfQV7nyv.js} +1 -1
  75. package/dashboard/dist/assets/provider-display-Bf9pW06U.js +42 -0
  76. package/dashboard/dist/assets/{react-yHv9MTUA.js → react-BmFZKA_b.js} +1 -1
  77. package/dashboard/dist/assets/{search-Dfak92ZA.js → search-l3jJ2fzC.js} +1 -1
  78. package/dashboard/dist/assets/{skills-api-By2PdNa1.js → skills-api-CWmLnaxn.js} +1 -1
  79. package/dashboard/dist/assets/{sortable.esm-B9xy66_v.js → sortable.esm-DLgyXLHM.js} +1 -1
  80. package/dashboard/dist/assets/store-JM-a8ElZ.js +1 -0
  81. package/dashboard/dist/assets/{terminal-pbXWBmZM.js → terminal-6aY2Sn8P.js} +1 -1
  82. package/dashboard/dist/assets/{trash-2-CboDPwbW.js → trash-D5oon5pT.js} +1 -1
  83. package/dashboard/dist/assets/{use-community-stats-D6HTdnHq.js → use-community-stats-ZWUMFwzc.js} +1 -1
  84. package/dashboard/dist/assets/{use-latest-request-guard-hwSr9Cam.js → use-latest-request-guard-BDaXr4gK.js} +1 -1
  85. package/dashboard/dist/assets/use-limits-display-prefs-yXXToNIc.js +1 -0
  86. package/dashboard/dist/assets/{use-ordered-list-OtfOqrNb.js → use-ordered-list-C6RG8N6c.js} +1 -1
  87. package/dashboard/dist/assets/use-reduced-motion-BqDKim_3.js +1 -0
  88. package/dashboard/dist/assets/{use-session-efficiency-pref-BScvtS8F.js → use-session-efficiency-pref-DpgZO_Lx.js} +1 -1
  89. package/dashboard/dist/assets/{use-transform-Cy1ykjEs.js → use-transform-D42P7jkd.js} +1 -1
  90. package/dashboard/dist/assets/use-usage-limits-CV6puFwq.js +1 -0
  91. package/dashboard/dist/assets/{useCurrency-VqF3cy1C.js → useCurrency-GWBmOrCR.js} +1 -1
  92. package/dashboard/dist/assets/{useTokenFormat-Cgub4MXu.js → useTokenFormat-Beybkb0v.js} +1 -1
  93. package/dashboard/dist/assets/{zap-DStzvQJI.js → zap-CEq2616W.js} +1 -1
  94. package/dashboard/dist/brand-logos/acode-dark.png +0 -0
  95. package/dashboard/dist/brand-logos/acode.png +0 -0
  96. package/dashboard/dist/brand-logos/commandcode.svg +1 -0
  97. package/dashboard/dist/brand-logos/devin.svg +6 -0
  98. package/dashboard/dist/index.html +20 -16
  99. package/dashboard/dist/ip-check.html +2 -2
  100. package/dashboard/dist/leaderboard.html +2 -2
  101. package/dashboard/dist/llms.txt +4 -2
  102. package/dashboard/dist/share.html +2 -2
  103. package/package.json +2 -2
  104. package/src/commands/init.js +114 -4
  105. package/src/commands/status.js +37 -0
  106. package/src/commands/sync.js +92 -10
  107. package/src/commands/uninstall.js +23 -0
  108. package/src/lib/codex-config.js +42 -0
  109. package/src/lib/codex-rollout-parser.js +2 -2
  110. package/src/lib/commandcode-limits.js +317 -0
  111. package/src/lib/devin-limits.js +249 -0
  112. package/src/lib/devin-usage.js +212 -0
  113. package/src/lib/local-api.js +14 -0
  114. package/src/lib/passive-mode.js +13 -1
  115. package/src/lib/pricing/curated-overrides.json +78 -1
  116. package/src/lib/pricing/index.js +11 -9
  117. package/src/lib/pricing/matcher.js +28 -0
  118. package/src/lib/pricing/seed-snapshot.json +1 -1
  119. package/src/lib/rollout.js +1473 -147
  120. package/src/lib/session-analytics.js +2 -2
  121. package/src/lib/skills-manager.js +5 -0
  122. package/src/lib/subscriptions.js +70 -16
  123. package/src/lib/usage-limits.js +240 -49
  124. package/dashboard/dist/assets/DashboardPage-QBhbCMZF.js +0 -1
  125. package/dashboard/dist/assets/DialogClose-D_dvtfGV.js +0 -1
  126. package/dashboard/dist/assets/DialogDescription-ryGYdQjX.js +0 -1
  127. package/dashboard/dist/assets/DialogTitle-_ahf0z8b.js +0 -1
  128. package/dashboard/dist/assets/LimitsPage-PvqkOOwp.js +0 -6
  129. package/dashboard/dist/assets/PetPage-CNlWHF4z.js +0 -1
  130. package/dashboard/dist/assets/PopoverPopup-D9jadD3j.js +0 -1
  131. package/dashboard/dist/assets/ProviderIcon-Caz5PuAm.js +0 -1
  132. package/dashboard/dist/assets/Select-B5o-1sEl.js +0 -1
  133. package/dashboard/dist/assets/SelectItemText-CiA6L7Wk.js +0 -1
  134. package/dashboard/dist/assets/SettingsPage-DJrb_fsK.js +0 -2
  135. package/dashboard/dist/assets/SkillsPage-COk-YQQ5.js +0 -1
  136. package/dashboard/dist/assets/ToolbarRootContext-CozJT6Vs.js +0 -1
  137. package/dashboard/dist/assets/__vite-browser-external-BGnq8Ebm.js +0 -45
  138. package/dashboard/dist/assets/composite-Dxmivlk4.js +0 -1
  139. package/dashboard/dist/assets/limits-providers-BUoquSA-.js +0 -1
  140. package/dashboard/dist/assets/module-xpSFyNBK.js +0 -3
  141. package/dashboard/dist/assets/provider-display-BOBtUyFi.js +0 -42
  142. package/dashboard/dist/assets/store-oQZR4d9M.js +0 -1
  143. package/dashboard/dist/assets/use-limits-display-prefs-B-pbwUS8.js +0 -1
  144. package/dashboard/dist/assets/use-reduced-motion-DGXcZBX3.js +0 -1
  145. package/dashboard/dist/assets/use-usage-limits-CSMb2zYW.js +0 -1
@@ -15,10 +15,13 @@ const {
15
15
  const { prompt, promptHidden } = require("../lib/prompt");
16
16
  const {
17
17
  upsertCodexNotify,
18
+ upsertAcodeNotify,
18
19
  upsertEveryCodeNotify,
19
20
  readCodexNotify,
21
+ readAcodeNotify,
20
22
  readEveryCodeNotify,
21
23
  buildCodexNotifyCmd,
24
+ buildAcodeNotifyCmd,
22
25
  buildEveryCodeNotifyCmd,
23
26
  isManagedNotifyCmd,
24
27
  } = require("../lib/codex-config");
@@ -69,6 +72,7 @@ const {
69
72
  resolveLmstudioLogFiles,
70
73
  resolveUnslothDbPath,
71
74
  resolveAnythingllmDbPath,
75
+ resolveDevinDbPath,
72
76
  resolveReasonixHome,
73
77
  resolveTraeStoragePath,
74
78
  } = require("../lib/rollout");
@@ -109,6 +113,7 @@ const DEFAULT_DASHBOARD_URL = "https://www.tokentracker.cc";
109
113
  const SUPPORTED_PROVIDERS = [
110
114
  "Claude Code",
111
115
  "Codex CLI",
116
+ "AStudio",
112
117
  "Cursor",
113
118
  "Gemini CLI",
114
119
  "Antigravity",
@@ -143,6 +148,7 @@ const SUPPORTED_PROVIDERS = [
143
148
  "TRAE Work CN",
144
149
  "LM Studio",
145
150
  "Unsloth Studio",
151
+ "Devin CLI",
146
152
  ];
147
153
 
148
154
  async function cmdInit(argv) {
@@ -445,6 +451,50 @@ async function repairCodexNotifyIntegration({ home = os.homedir(), trackerDir, b
445
451
  return { ...result, skippedReason: null, notifyPath };
446
452
  }
447
453
 
454
+ async function repairAcodeNotifyIntegration({ home = os.homedir(), trackerDir, binDir, safeMode = true } = {}) {
455
+ const paths = trackerDir && binDir ? { trackerDir, binDir } : await resolveTrackerPaths({ home });
456
+ const resolvedTrackerDir = trackerDir || paths.trackerDir;
457
+ const resolvedBinDir = binDir || paths.binDir;
458
+ const notifyPath = await writeNotifyHandler({
459
+ trackerDir: resolvedTrackerDir,
460
+ binDir: resolvedBinDir,
461
+ });
462
+ const context = buildIntegrationTargets({
463
+ home,
464
+ trackerDir: resolvedTrackerDir,
465
+ notifyPath,
466
+ });
467
+ const acodeProbe = await probeFile(context.acodeConfigPath);
468
+ if (!acodeProbe.exists) {
469
+ return { changed: false, skippedReason: "config-missing", notifyPath };
470
+ }
471
+
472
+ const currentNotify = await readAcodeNotify(context.acodeConfigPath);
473
+ if (arraysEqual(currentNotify, context.acodeNotifyCmd)) {
474
+ return { changed: false, skippedReason: null, notifyPath };
475
+ }
476
+
477
+ const repairDecision = safeMode
478
+ ? await shouldRepairCodexNotify({
479
+ currentNotify,
480
+ expectedNotify: context.acodeNotifyCmd,
481
+ notifyOriginalPath: context.acodeNotifyOriginalPath,
482
+ })
483
+ : { repair: true, captureOriginal: true, replaceOriginal: false };
484
+ if (!repairDecision.repair) {
485
+ return { changed: false, skippedReason: repairDecision.reason || "external-notify", notifyPath };
486
+ }
487
+
488
+ const result = await upsertAcodeNotify({
489
+ acodeConfigPath: context.acodeConfigPath,
490
+ notifyCmd: context.acodeNotifyCmd,
491
+ notifyOriginalPath: context.acodeNotifyOriginalPath,
492
+ captureOriginal: repairDecision.captureOriginal,
493
+ replaceOriginal: repairDecision.replaceOriginal,
494
+ });
495
+ return { ...result, skippedReason: null, notifyPath };
496
+ }
497
+
448
498
  async function repairRuntimeIntegrations({
449
499
  home = os.homedir(),
450
500
  trackerDir,
@@ -481,6 +531,12 @@ async function repairRuntimeIntegrations({
481
531
  binDir: resolvedBinDir,
482
532
  safeMode,
483
533
  }));
534
+ await attempt("acode", () => repairAcodeNotifyIntegration({
535
+ home,
536
+ trackerDir: resolvedTrackerDir,
537
+ binDir: resolvedBinDir,
538
+ safeMode,
539
+ }));
484
540
 
485
541
  const hookRepairs = [
486
542
  ["claude", context.claudeDir, () => upsertClaudeUsageHooks({
@@ -520,11 +576,15 @@ async function repairRuntimeIntegrations({
520
576
  function buildIntegrationTargets({ home, trackerDir, notifyPath }) {
521
577
  const codexHome = process.env.CODEX_HOME || path.join(home, ".codex");
522
578
  const codexConfigPath = path.join(codexHome, "config.toml");
579
+ const acodeHome = process.env.TOKENTRACKER_ACODE_HOME || path.join(home, ".acode");
580
+ const acodeConfigPath = path.join(acodeHome, "config.toml");
523
581
  const codeHome = process.env.CODE_HOME || path.join(home, ".code");
524
582
  const codeConfigPath = path.join(codeHome, "config.toml");
525
583
  const notifyOriginalPath = path.join(trackerDir, "codex_notify_original.json");
584
+ const acodeNotifyOriginalPath = path.join(trackerDir, "acode_notify_original.json");
526
585
  const codeNotifyOriginalPath = path.join(trackerDir, "code_notify_original.json");
527
586
  const notifyCmd = buildCodexNotifyCmd(notifyPath);
587
+ const acodeNotifyCmd = buildAcodeNotifyCmd(notifyPath);
528
588
  const codeNotifyCmd = buildEveryCodeNotifyCmd(notifyPath);
529
589
  const claudeDir = path.join(home, ".claude");
530
590
  const claudeSettingsPath = path.join(claudeDir, "settings.json");
@@ -547,10 +607,13 @@ function buildIntegrationTargets({ home, trackerDir, notifyPath }) {
547
607
  return {
548
608
  trackerDir,
549
609
  codexConfigPath,
610
+ acodeConfigPath,
550
611
  codeConfigPath,
551
612
  notifyOriginalPath,
613
+ acodeNotifyOriginalPath,
552
614
  codeNotifyOriginalPath,
553
615
  notifyCmd,
616
+ acodeNotifyCmd,
554
617
  codeNotifyCmd,
555
618
  claudeDir,
556
619
  claudeSettingsPath,
@@ -598,6 +661,24 @@ async function applyIntegrationSetup({
598
661
  summary.push({ label: "Codex CLI", status: "skipped", detail: renderSkipDetail(codexProbe) });
599
662
  }
600
663
 
664
+ const acodeProbe = await probeFile(context.acodeConfigPath);
665
+ if (acodeProbe.exists) {
666
+ const currentNotify = await readAcodeNotify(context.acodeConfigPath);
667
+ const result = await upsertAcodeNotify({
668
+ acodeConfigPath: context.acodeConfigPath,
669
+ notifyCmd: context.acodeNotifyCmd,
670
+ notifyOriginalPath: context.acodeNotifyOriginalPath,
671
+ replaceOriginal: shouldReplaceStoredOriginalNotify(currentNotify, context.acodeNotifyCmd),
672
+ });
673
+ summary.push({
674
+ label: "AStudio",
675
+ status: result.changed ? "updated" : "set",
676
+ detail: result.changed ? "Updated config" : "Config already set",
677
+ });
678
+ } else {
679
+ summary.push({ label: "AStudio", status: "skipped", detail: renderSkipDetail(acodeProbe) });
680
+ }
681
+
601
682
  const claudeDirExists = await isDir(context.claudeDir);
602
683
  if (claudeDirExists) {
603
684
  await upsertClaudeUsageHooks({
@@ -835,6 +916,18 @@ async function applyIntegrationSetup({
835
916
  }
836
917
  }
837
918
 
919
+ // Devin CLI (Cognition): passive SQLite reader — no hook installation needed.
920
+ {
921
+ const devinDbPath = resolveDevinDbPath(process.env);
922
+ if (devinDbPath && fssync.existsSync(devinDbPath)) {
923
+ summary.push({
924
+ label: "Devin CLI",
925
+ status: "detected",
926
+ detail: "Passive reader (no hook needed)",
927
+ });
928
+ }
929
+ }
930
+
838
931
  // Kilo Code VS Code extension (kilocode.kilo-code): passive reader — no hook
839
932
  // installation needed. Scans ui_messages.json under every detected VS Code-
840
933
  // family install (Code, Cursor, CodeBuddy, Windsurf, …).
@@ -973,6 +1066,19 @@ async function previewIntegrations({ context }) {
973
1066
  summary.push({ label: "Codex CLI", status: "skipped", detail: renderSkipDetail(codexProbe) });
974
1067
  }
975
1068
 
1069
+ const acodeProbe = await probeFile(context.acodeConfigPath);
1070
+ if (acodeProbe.exists) {
1071
+ const existing = await readAcodeNotify(context.acodeConfigPath);
1072
+ const matches = arraysEqual(existing, context.acodeNotifyCmd);
1073
+ summary.push({
1074
+ label: "AStudio",
1075
+ status: matches ? "set" : "updated",
1076
+ detail: matches ? "Already configured" : "Will update config",
1077
+ });
1078
+ } else {
1079
+ summary.push({ label: "AStudio", status: "skipped", detail: renderSkipDetail(acodeProbe) });
1080
+ }
1081
+
976
1082
  const claudeDirExists = await isDir(context.claudeDir);
977
1083
  if (claudeDirExists) {
978
1084
  const configured = await areClaudeUsageHooksConfigured({
@@ -1254,6 +1360,7 @@ for (let i = 0; i < rawArgs.length; i++) {
1254
1360
  const trackerDir = ${JSON.stringify(trackerDir)};
1255
1361
  const signalPath = ${JSON.stringify(queueSignalPath)};
1256
1362
  const codexOriginalPath = ${JSON.stringify(originalPath)};
1363
+ const acodeOriginalPath = ${JSON.stringify(path.join(trackerDir, "acode_notify_original.json"))};
1257
1364
  const codeOriginalPath = ${JSON.stringify(path.join(trackerDir, "code_notify_original.json"))};
1258
1365
  const trackerBinPath = ${JSON.stringify(trackerBinPath)};
1259
1366
  const depsMarkerPath = path.join(trackerDir, 'app', 'bin', 'tracker.js');
@@ -1310,14 +1417,16 @@ try {
1310
1417
  }
1311
1418
  } catch (_) {}
1312
1419
 
1313
- // Chain the original notify if present (Codex/Every Code only).
1420
+ // Chain the existing Codex, AStudio, or Every Code notify hook.
1314
1421
  try {
1315
1422
  const originalPath =
1316
1423
  source === 'every-code'
1317
1424
  ? codeOriginalPath
1318
- : source === 'claude' || source === 'opencode' || source === 'gemini' || source === 'codebuddy' || source === 'workbuddy'
1319
- ? null
1320
- : codexOriginalPath;
1425
+ : source === 'acode'
1426
+ ? acodeOriginalPath
1427
+ : source === 'claude' || source === 'opencode' || source === 'gemini' || source === 'codebuddy' || source === 'workbuddy'
1428
+ ? null
1429
+ : codexOriginalPath;
1321
1430
  if (originalPath) {
1322
1431
  const original = JSON.parse(fs.readFileSync(originalPath, 'utf8'));
1323
1432
  const cmd = Array.isArray(original?.notify) ? original.notify : null;
@@ -2009,6 +2118,7 @@ module.exports = {
2009
2118
  buildNotifyHandler,
2010
2119
  installLocalTrackerApp,
2011
2120
  repairCodexNotifyIntegration,
2121
+ repairAcodeNotifyIntegration,
2012
2122
  repairRuntimeIntegrations,
2013
2123
  applyIntegrationSetup,
2014
2124
  };
@@ -8,8 +8,10 @@ const { readJson } = require("../lib/fs");
8
8
  const { readCursorStateSummary } = require("../lib/cursor-store");
9
9
  const {
10
10
  readCodexNotify,
11
+ readAcodeNotify,
11
12
  readEveryCodeNotify,
12
13
  buildCodexNotifyCmd,
14
+ buildAcodeNotifyCmd,
13
15
  isManagedNotifyCmd,
14
16
  } = require("../lib/codex-config");
15
17
  const {
@@ -79,6 +81,7 @@ const {
79
81
  listQoderNewSessionFiles,
80
82
  resolveClaudeScienceDbPaths,
81
83
  resolveAnythingllmDbPath,
84
+ resolveDevinDbPath,
82
85
  resolveGooseDbPath,
83
86
  listDroidSettingsFiles,
84
87
  resolveDroidSessionsDir,
@@ -188,6 +191,8 @@ async function cmdStatus(argv = []) {
188
191
  const syncSkipPath = path.join(trackerDir, "sync.skip.json");
189
192
  const codexHome = process.env.CODEX_HOME || path.join(home, ".codex");
190
193
  const codexConfigPath = path.join(codexHome, "config.toml");
194
+ const acodeHome = process.env.TOKENTRACKER_ACODE_HOME || path.join(home, ".acode");
195
+ const acodeConfigPath = path.join(acodeHome, "config.toml");
191
196
  const codeHome = process.env.CODE_HOME || path.join(home, ".code");
192
197
  const codeConfigPath = path.join(codeHome, "config.toml");
193
198
  const claudeSettingsPath = path.join(home, ".claude", "settings.json");
@@ -209,6 +214,7 @@ async function cmdStatus(argv = []) {
209
214
  });
210
215
  const notifyPath = path.join(binDir, "notify.cjs");
211
216
  const codexNotifyCmd = buildCodexNotifyCmd(notifyPath);
217
+ const acodeNotifyCmd = buildAcodeNotifyCmd(notifyPath);
212
218
  const claudeHookCommand = buildClaudeHookCommand(notifyPath);
213
219
  const codebuddyHookCommand = buildHookCommand(notifyPath, "codebuddy");
214
220
  const workbuddyHookCommand = buildHookCommand(notifyPath, "workbuddy");
@@ -240,6 +246,8 @@ async function cmdStatus(argv = []) {
240
246
  // terminal `tokentracker` run, so a correctly-installed integration would
241
247
  // otherwise report as not configured. See isManagedNotifyCmd.
242
248
  const notifyConfigured = isManagedNotifyCmd(codexNotify, codexNotifyCmd);
249
+ const acodeNotify = await readAcodeNotify(acodeConfigPath);
250
+ const acodeConfigured = isManagedNotifyCmd(acodeNotify, acodeNotifyCmd);
243
251
  const everyCodeNotify = await readEveryCodeNotify(codeConfigPath);
244
252
  const everyCodeConfigured =
245
253
  Array.isArray(everyCodeNotify) && everyCodeNotify.length > 0;
@@ -643,6 +651,15 @@ async function cmdStatus(argv = []) {
643
651
  const codexActive = formatResolvedPaths(codexPaths, ["sessions", "archived_sessions"]);
644
652
  const codexInstalledStatus = codexActive.length > 0;
645
653
 
654
+ const acodePaths = resolveInstallPaths({
655
+ nativeValue: acodeHome,
656
+ wslDir: ".acode",
657
+ requireAnyChild: ["sessions", "archived_sessions"],
658
+ union: true,
659
+ });
660
+ const acodeActive = formatResolvedPaths(acodePaths, ["sessions", "archived_sessions"]);
661
+ const acodeInstalled = acodeActive.length > 0;
662
+
646
663
  // Kimi (passive sessions scan)
647
664
  const kimiPaths = resolveInstallPaths({
648
665
  nativeValue: path.join(home, ".kimi"),
@@ -689,6 +706,10 @@ async function cmdStatus(argv = []) {
689
706
  const unslothDbPath = resolveUnslothDbPath(process.env);
690
707
  const unslothInstalled = Boolean(unslothDbPath && fssync.existsSync(unslothDbPath));
691
708
 
709
+ // Devin CLI (Cognition) — passive reader of message_nodes usage metrics.
710
+ const devinDbPath = resolveDevinDbPath(process.env);
711
+ const devinInstalled = Boolean(devinDbPath && fssync.existsSync(devinDbPath));
712
+
692
713
  // Trae SOLO (ByteDance AI IDE) — passive entitlement snapshot reader.
693
714
  const traeStoragePath = resolveTraeStoragePath(process.env);
694
715
  const traeInstalled = Boolean(traeStoragePath);
@@ -880,8 +901,10 @@ async function cmdStatus(argv = []) {
880
901
  // syscalls (~5–10ms cold). Memoize.
881
902
  const passiveProviders = detectPassiveProviders({
882
903
  home,
904
+ env: process.env,
883
905
  hookStatus: {
884
906
  codex_notify: notifyConfigured,
907
+ acode_notify: acodeConfigured,
885
908
  every_code_notify: everyCodeConfigured,
886
909
  claude: claudeHookConfigured,
887
910
  gemini: geminiHookConfigured,
@@ -916,6 +939,7 @@ async function cmdStatus(argv = []) {
916
939
  auto_retry: autoRetry || null,
917
940
  hooks: {
918
941
  codex_notify: notifyConfigured,
942
+ acode_notify: acodeConfigured,
919
943
  every_code_notify: everyCodeConfigured,
920
944
  claude: claudeHookConfigured,
921
945
  gemini: geminiHookConfigured,
@@ -930,6 +954,9 @@ async function cmdStatus(argv = []) {
930
954
  grok: grokInstalled ? Boolean(grokHookState?.configured) : null,
931
955
  },
932
956
  providers: {
957
+ acode: acodeInstalled
958
+ ? { installed: true, detail: acodeActive.join(" | ") }
959
+ : { installed: false },
933
960
  kimi_code: kimiInstalled || kimiCodeInstalled
934
961
  ? { installed: true, files: kimiWireFiles.length + kimiCodeWireFiles.length }
935
962
  : { installed: false },
@@ -1022,6 +1049,9 @@ async function cmdStatus(argv = []) {
1022
1049
  unsloth: unslothInstalled
1023
1050
  ? { installed: true, detail: unslothDbPath }
1024
1051
  : { installed: false },
1052
+ devin: devinInstalled
1053
+ ? { installed: true, detail: devinDbPath }
1054
+ : { installed: false },
1025
1055
  trae: traeInstalled
1026
1056
  ? {
1027
1057
  installed: true,
@@ -1097,6 +1127,7 @@ async function cmdStatus(argv = []) {
1097
1127
  syncSkipLine,
1098
1128
  autoRetryLine,
1099
1129
  `- Codex notify: ${notifyConfigured ? JSON.stringify(codexNotify) : "unset"}`,
1130
+ `- AStudio notify: ${acodeConfigured ? JSON.stringify(acodeNotify) : "unset"}`,
1100
1131
  `- Every Code notify: ${everyCodeConfigured ? JSON.stringify(everyCodeNotify) : "unset"}`,
1101
1132
  `- Claude hooks: ${claudeHookConfigured ? "set" : "unset"}`,
1102
1133
  claudeCodeInstalled
@@ -1176,6 +1207,9 @@ async function cmdStatus(argv = []) {
1176
1207
  codexInstalledStatus
1177
1208
  ? `- Codex CLI: sessions found (${codexActive.join(" | ")})`
1178
1209
  : null,
1210
+ acodeInstalled
1211
+ ? `- AStudio: sessions found (${acodeActive.join(" | ")})`
1212
+ : null,
1179
1213
  kilocodeInstalled
1180
1214
  ? `- Kilo Code (VS Code extension): passive reader (${kilocodeTaskFiles.length} task${kilocodeTaskFiles.length !== 1 ? "s" : ""} across ${new Set(kilocodeTaskFiles.map((t) => t.ide)).size} IDE${new Set(kilocodeTaskFiles.map((t) => t.ide)).size !== 1 ? "s" : ""})`
1181
1215
  : null,
@@ -1204,6 +1238,9 @@ async function cmdStatus(argv = []) {
1204
1238
  unslothInstalled
1205
1239
  ? `- Unsloth Studio: passive reader (${unslothDbPath})`
1206
1240
  : null,
1241
+ devinInstalled
1242
+ ? `- Devin CLI: passive reader (${devinDbPath})`
1243
+ : null,
1207
1244
  traeInstalled
1208
1245
  // Deliberately NOT "passive reader": every other line with that wording
1209
1246
  // means tokens are being counted. Trae encrypts its session transcripts
@@ -115,6 +115,8 @@ const {
115
115
  parseUnslothIncremental,
116
116
  resolveAnythingllmDbPath,
117
117
  parseAnythingllmIncremental,
118
+ resolveDevinDbPath,
119
+ parseDevinIncremental,
118
120
  resolveGooseDbPath,
119
121
  parseGooseIncremental,
120
122
  listDroidSettingsFiles,
@@ -280,6 +282,7 @@ const AUTO_SYNC_SOURCE_ALIASES = new Map([
280
282
  ["roo-code", "roocode"],
281
283
  ]);
282
284
  const AUTO_SYNC_SOURCES = new Set([
285
+ "acode",
283
286
  "antigravity",
284
287
  "anythingllm",
285
288
  "claude",
@@ -289,6 +292,7 @@ const AUTO_SYNC_SOURCES = new Set([
289
292
  "copilot",
290
293
  "craft",
291
294
  "cursor",
295
+ "devin",
292
296
  "droid",
293
297
  "dsh",
294
298
  "every-code",
@@ -319,6 +323,7 @@ const AUTO_SYNC_SOURCES = new Set([
319
323
  ]);
320
324
  const BACKGROUND_AUTO_SYNC_SOURCES = new Set([
321
325
  // Keep unscoped native 5-minute syncs bounded to dated local session trees.
326
+ "acode",
322
327
  "codex",
323
328
  "every-code",
324
329
  "reasonix",
@@ -706,18 +711,56 @@ async function cmdSync(argv, context = {}) {
706
711
  union: true,
707
712
  });
708
713
  if (codexPaths.native) {
709
- sources.push({ source: "codex", sessionsDir: path.join(codexPaths.native, "sessions"), codexInventoryCache: true });
714
+ sources.push({ source: "codex", sessionsDir: path.join(codexPaths.native, "sessions"), inventoryCacheKey: "codexDayInventoryCache" });
710
715
  if (!isBackgroundLightweightSync || backgroundCodexUsageRepair) {
711
716
  sources.push({ source: "codex", sessionsDir: path.join(codexPaths.native, "archived_sessions"), deep: true });
712
717
  }
713
718
  }
714
719
  if (codexPaths.wsl) {
715
- sources.push({ source: "codex", sessionsDir: path.join(codexPaths.wsl, "sessions"), codexInventoryCache: true });
720
+ sources.push({ source: "codex", sessionsDir: path.join(codexPaths.wsl, "sessions"), inventoryCacheKey: "codexDayInventoryCache" });
716
721
  if (!isBackgroundLightweightSync || backgroundCodexUsageRepair) {
717
722
  sources.push({ source: "codex", sessionsDir: path.join(codexPaths.wsl, "archived_sessions"), deep: true });
718
723
  }
719
724
  }
720
725
  }
726
+ if (sourceAllowed("acode")) {
727
+ const acodeNativeValue =
728
+ process.env.TOKENTRACKER_ACODE_HOME || path.join(home, ".acode");
729
+ const acodePaths = resolveInstallPaths({
730
+ nativeValue: acodeNativeValue,
731
+ wslDir: ".acode",
732
+ requireAnyChild: ["sessions", "archived_sessions"],
733
+ union: true,
734
+ });
735
+ if (acodePaths.native) {
736
+ sources.push({
737
+ source: "acode",
738
+ sessionsDir: path.join(acodePaths.native, "sessions"),
739
+ inventoryCacheKey: "acodeDayInventoryCache",
740
+ });
741
+ if (!isBackgroundLightweightSync) {
742
+ sources.push({
743
+ source: "acode",
744
+ sessionsDir: path.join(acodePaths.native, "archived_sessions"),
745
+ deep: true,
746
+ });
747
+ }
748
+ }
749
+ if (acodePaths.wsl) {
750
+ sources.push({
751
+ source: "acode",
752
+ sessionsDir: path.join(acodePaths.wsl, "sessions"),
753
+ inventoryCacheKey: "acodeDayInventoryCache",
754
+ });
755
+ if (!isBackgroundLightweightSync) {
756
+ sources.push({
757
+ source: "acode",
758
+ sessionsDir: path.join(acodePaths.wsl, "archived_sessions"),
759
+ deep: true,
760
+ });
761
+ }
762
+ }
763
+ }
721
764
  if (sourceAllowed("every-code")) {
722
765
  const codeNativeValue = process.env.CODE_HOME || path.join(home, ".code");
723
766
  const wslCodeDir = process.platform === "win32" && wsl.shouldProbeWsl(process.env)
@@ -734,21 +777,28 @@ async function cmdSync(argv, context = {}) {
734
777
 
735
778
  const rolloutFiles = [];
736
779
  const seenSessions = new Set();
737
- const codexDayInventoryCache =
738
- cursors.codexDayInventoryCache && typeof cursors.codexDayInventoryCache === "object"
739
- ? cursors.codexDayInventoryCache
740
- : { version: 1, days: {} };
741
- if (sourceAllowed("codex")) cursors.codexDayInventoryCache = codexDayInventoryCache;
742
780
  const uniqueSources = sources.filter((entry) => {
743
781
  if (seenSessions.has(entry.sessionsDir)) return false;
744
782
  seenSessions.add(entry.sessionsDir);
745
783
  return true;
746
784
  });
785
+ const inventoryCaches = new Map();
786
+ const inventoryCacheKeys = new Set(
787
+ uniqueSources.map((entry) => entry.inventoryCacheKey).filter(Boolean),
788
+ );
789
+ for (const cursorKey of inventoryCacheKeys) {
790
+ const inventoryCache =
791
+ cursors[cursorKey] && typeof cursors[cursorKey] === "object"
792
+ ? cursors[cursorKey]
793
+ : { version: 1, days: {} };
794
+ cursors[cursorKey] = inventoryCache;
795
+ inventoryCaches.set(cursorKey, inventoryCache);
796
+ }
747
797
  const sourceFileGroups = await Promise.all(uniqueSources.map((entry) => (
748
798
  entry.deep
749
799
  ? listRolloutFilesDeep(entry.sessionsDir)
750
- : listRolloutFiles(entry.sessionsDir, entry.codexInventoryCache
751
- ? { dayInventoryCache: codexDayInventoryCache }
800
+ : listRolloutFiles(entry.sessionsDir, entry.inventoryCacheKey
801
+ ? { dayInventoryCache: inventoryCaches.get(entry.inventoryCacheKey) }
752
802
  : undefined)
753
803
  )));
754
804
  for (let sourceIndex = 0; sourceIndex < uniqueSources.length; sourceIndex++) {
@@ -1546,7 +1596,7 @@ async function cmdSync(argv, context = {}) {
1546
1596
  }
1547
1597
 
1548
1598
  // ── DeepSeek Harness — passive read of ~/.dsh/sessions session logs ──
1549
- let dshResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
1599
+ let dshResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0, deferredMigrations: 0 };
1550
1600
  if (sourceAllowed("dsh")) {
1551
1601
  await migrateLegacyDeepseekHarnessSource({ cursors, queuePath, queueStatePath });
1552
1602
  const dshSessionFiles = await resolveDshSessionFiles(process.env);
@@ -1565,6 +1615,15 @@ async function cmdSync(argv, context = {}) {
1565
1615
  queuePath,
1566
1616
  onProgress: makeProviderProgress("DeepSeek Harness"),
1567
1617
  });
1618
+ if (dshResult.deferredMigrations > 0) {
1619
+ warnProviderParseFailure(
1620
+ "DeepSeek Harness",
1621
+ new Error(
1622
+ `${dshResult.deferredMigrations} artifact migration(s) deferred; inspect cursors.dsh.deferredMigrations and retry after the replacement is complete`,
1623
+ ),
1624
+ opts,
1625
+ );
1626
+ }
1568
1627
  } catch (err) {
1569
1628
  warnProviderParseFailure("DeepSeek Harness", err, opts);
1570
1629
  }
@@ -1631,6 +1690,26 @@ async function cmdSync(argv, context = {}) {
1631
1690
  }
1632
1691
  }
1633
1692
 
1693
+ // ── Devin CLI (Cognition) — SQLite message_nodes chat_message metrics ──
1694
+ let devinResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
1695
+ if (sourceAllowed("devin")) {
1696
+ const devinDbPath = resolveDevinDbPath(process.env);
1697
+ if (devinDbPath && fssync.existsSync(devinDbPath)) {
1698
+ if (progress?.enabled) progress.start(`Parsing Devin ${renderBar(0)} | buckets 0`);
1699
+ try {
1700
+ devinResult = await parseDevinIncremental({
1701
+ dbPath: devinDbPath,
1702
+ cursors,
1703
+ queuePath,
1704
+ projectQueuePath,
1705
+ onProgress: makeProviderProgress("Devin"),
1706
+ });
1707
+ } catch (err) {
1708
+ warnProviderParseFailure("Devin", err, opts);
1709
+ }
1710
+ }
1711
+ }
1712
+
1634
1713
  // ── Kilo Code VS Code extension (Cline-style ui_messages.json) ──
1635
1714
  const kilocodeTaskFiles = sourceAllowed("kilocode")
1636
1715
  ? mergeBothFileSources({ resolveFiles: resolveKilocodeTaskFiles, env: process.env })
@@ -2337,6 +2416,7 @@ async function cmdSync(argv, context = {}) {
2337
2416
  sessionFiles: piFiles,
2338
2417
  cursors,
2339
2418
  queuePath,
2419
+ projectQueuePath,
2340
2420
  env: process.env,
2341
2421
  onProgress: (p) => {
2342
2422
  if (!progress?.enabled) return;
@@ -2885,6 +2965,7 @@ async function cmdSync(argv, context = {}) {
2885
2965
  lmstudioResult.recordsProcessed +
2886
2966
  unslothResult.recordsProcessed +
2887
2967
  anythingllmResult.recordsProcessed +
2968
+ devinResult.recordsProcessed +
2888
2969
  kiloResult.recordsProcessed +
2889
2970
  mimoResult.recordsProcessed +
2890
2971
  zcodeResult.recordsProcessed +
@@ -2923,6 +3004,7 @@ async function cmdSync(argv, context = {}) {
2923
3004
  lmstudioResult.bucketsQueued +
2924
3005
  unslothResult.bucketsQueued +
2925
3006
  anythingllmResult.bucketsQueued +
3007
+ devinResult.bucketsQueued +
2926
3008
  kiloResult.bucketsQueued +
2927
3009
  mimoResult.bucketsQueued +
2928
3010
  zcodeResult.bucketsQueued +
@@ -4,8 +4,10 @@ const fs = require("node:fs/promises");
4
4
 
5
5
  const {
6
6
  restoreCodexNotify,
7
+ restoreAcodeNotify,
7
8
  restoreEveryCodeNotify,
8
9
  buildCodexNotifyCmd,
10
+ buildAcodeNotifyCmd,
9
11
  buildEveryCodeNotifyCmd,
10
12
  } = require("../lib/codex-config");
11
13
  const {
@@ -33,6 +35,8 @@ async function cmdUninstall(argv) {
33
35
  const { trackerDir, binDir } = await resolveTrackerPaths({ home });
34
36
  const codexHome = process.env.CODEX_HOME || path.join(home, ".codex");
35
37
  const codexConfigPath = path.join(codexHome, "config.toml");
38
+ const acodeHome = process.env.TOKENTRACKER_ACODE_HOME || path.join(home, ".acode");
39
+ const acodeConfigPath = path.join(acodeHome, "config.toml");
36
40
  const codeHome = process.env.CODE_HOME || path.join(home, ".code");
37
41
  const codeConfigPath = path.join(codeHome, "config.toml");
38
42
  const claudeSettingsPath = path.join(home, ".claude", "settings.json");
@@ -45,8 +49,10 @@ async function cmdUninstall(argv) {
45
49
  const opencodeConfigDir = resolveOpencodeConfigDir({ home, env: process.env });
46
50
  const notifyPath = path.join(binDir, "notify.cjs");
47
51
  const notifyOriginalPath = path.join(trackerDir, "codex_notify_original.json");
52
+ const acodeNotifyOriginalPath = path.join(trackerDir, "acode_notify_original.json");
48
53
  const codeNotifyOriginalPath = path.join(trackerDir, "code_notify_original.json");
49
54
  const codexNotifyCmd = buildCodexNotifyCmd(notifyPath);
55
+ const acodeNotifyCmd = buildAcodeNotifyCmd(notifyPath);
50
56
  const codeNotifyCmd = buildEveryCodeNotifyCmd(notifyPath);
51
57
  const claudeHookCommand = buildClaudeHookCommand(notifyPath);
52
58
  const codebuddyHookCommand = buildHookCommand(notifyPath, "codebuddy");
@@ -54,6 +60,7 @@ async function cmdUninstall(argv) {
54
60
  const geminiHookCommand = buildGeminiHookCommand(notifyPath);
55
61
 
56
62
  const codexConfigExists = await isFile(codexConfigPath);
63
+ const acodeConfigExists = await isFile(acodeConfigPath);
57
64
  const codeConfigExists = await isFile(codeConfigPath);
58
65
  const claudeConfigExists = await isFile(claudeSettingsPath);
59
66
  const codebuddyConfigExists = await isFile(codebuddySettingsPath);
@@ -67,6 +74,13 @@ async function cmdUninstall(argv) {
67
74
  notifyCmd: codexNotifyCmd,
68
75
  })
69
76
  : { restored: false, skippedReason: "config-missing" };
77
+ const acodeRestore = acodeConfigExists
78
+ ? await restoreAcodeNotify({
79
+ acodeConfigPath,
80
+ notifyOriginalPath: acodeNotifyOriginalPath,
81
+ notifyCmd: acodeNotifyCmd,
82
+ })
83
+ : { restored: false, skippedReason: "config-missing" };
70
84
  const codeRestore = codeConfigExists
71
85
  ? await restoreEveryCodeNotify({
72
86
  codeConfigPath,
@@ -138,6 +152,15 @@ async function cmdUninstall(argv) {
138
152
  ? "- Codex notify: skipped (current notify is not managed by TokenTracker)"
139
153
  : "- Codex notify: no change"
140
154
  : "- Codex notify: skipped (config.toml not found)",
155
+ acodeConfigExists
156
+ ? acodeRestore?.restored
157
+ ? `- AStudio notify restored: ${acodeConfigPath}`
158
+ : acodeRestore?.skippedReason === "no-backup-not-installed"
159
+ ? "- AStudio notify: skipped (no backup; not installed)"
160
+ : acodeRestore?.skippedReason === "current-not-managed"
161
+ ? "- AStudio notify: skipped (current notify is not managed by TokenTracker)"
162
+ : "- AStudio notify: no change"
163
+ : "- AStudio notify: skipped (config.toml not found)",
141
164
  codeConfigExists
142
165
  ? codeRestore?.restored
143
166
  ? `- Every Code notify restored: ${codeConfigPath}`