chainlesschain 0.162.157 → 0.162.159

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 (212) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CfMP2Oud.js → AIOps-ByIiF5bT.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Zu0N8BQL.js → ActionButton-NlgSFZq5.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DZTwj5T_.js → Analytics-D17cOUcb.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CVFJh47v.js → AppLayout-B4mEZcbl.js} +5 -5
  8. package/src/assets/web-panel/assets/Artifacts-CASsmyQw.js +1 -0
  9. package/src/assets/web-panel/assets/Artifacts-CTVt96zW.css +1 -0
  10. package/src/assets/web-panel/assets/{Audit-BMfiLj7T.js → Audit-DYAia2e2.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-BslPCdGF.js → BackgroundAgents-C1lvYUSu.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CQ_fMC1r.js → Backup-B6agK6bA.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-CsFsARiC.js → BaseInput-DM43R2vy.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-CwfBobco.js → Chat-Dtdp3eXp.js} +6 -6
  15. package/src/assets/web-panel/assets/Chat-KSGPtU0L.css +1 -0
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-K6Nf3xeN.js → ChatBubbleRenderer-BL5c_OQ1.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-DkSyjjPp.js → Checkbox-D2xf7uZt.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-B7Fx3zZ6.js → Codegen-D3j45jJL.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-Dzfqn8Ep.js → Col-voPJ9fUZ.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-DhHPdzMM.js → Community-DEsyTESF.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-Ct18_8y7.js → Compact-OHV_QXLD.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-CY64IqBi.js → Compliance-eDTJWC-J.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-BYSmWlUl.js → Cowork-BBvy-6d1.js} +2 -2
  24. package/src/assets/web-panel/assets/{Cron-_4xtP3Ug.js → Cron-Dw2eoJxu.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-B4qY-Ycr.js → Crosschain-DQHms_Bh.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-CkCzWkx1.js → DID-Cyu7Id1c.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-C1scT7Ke.js → Dashboard-sIDzkG92.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-DBnQ2EWd.js → Dropdown-DUW3T2ey.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-C9JhOTq0.js → EmailListRenderer-CECELXW8.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CWXv5RUv.js → FamilyGuardDashboard-BIZR37ov.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-fYdcbz9W.js → Federation-BEZIQX84.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-CJqOT3Yj.js → FormItemContext-BcdUMznL.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-DGCsHc5K.js → GenericCardRenderer-DsN_fr5H.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-EH2K-H0Q.js → Git-63LhjBx2.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-DJn1X2pR.js → Governance-CYtQtIcH.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-Dch1E8qd.js → Inference-BwR6UUox.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-QIur8j8-.js → KnowledgeGraph-D9lVNTUA.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-DUgHldPg.js → Logs-C4q5h7Ut.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-DbzhFwvz.css +1 -0
  40. package/src/assets/web-panel/assets/MarkdownRenderer-DexLlXfN.js +1 -0
  41. package/src/assets/web-panel/assets/{Marketplace-DafrSfvd.js → Marketplace-Plksliuw.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-D2xRbfJx.js → McpTools-DZUFfeuG.js} +3 -3
  43. package/src/assets/web-panel/assets/{Memory-C6RbVHAo.js → Memory-NoDYt29V.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-BPtVBKqn.js → MobileBridge-CEXGTkdh.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-Cuw832cr.js → MobileProjects-DnbJkbZo.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-D6O9fWFa.js → Mtc-DTAa0cwy.js} +5 -5
  47. package/src/assets/web-panel/assets/{MtcAudit-CWZcXIH1.js → MtcAudit-CVV7LnNS.js} +4 -4
  48. package/src/assets/web-panel/assets/{Multisig-DhIBxTFd.js → Multisig-BqqGrjt_.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-wkBFS33e.js → NLProgramming-C4WP8DRp.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-B09FxncA.js → Notes-Zo4DJUVI.js} +3 -3
  51. package/src/assets/web-panel/assets/{NotificationSettings-oQwtH6-9.js → NotificationSettings-DBcjRaz2.js} +1 -1
  52. package/src/assets/web-panel/assets/OrderTableRenderer-BCQel9bD.js +1 -0
  53. package/src/assets/web-panel/assets/{Organization-CsF-NIjz.js → Organization-BT9WPBOX.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-bLxv9Dqi.js → Overflow-D6b4bZ3q.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-DVwQtm4q.js → P2P-Cw3OsYx-.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-Bgic3QKm.js → PdhVaultBrowser-C0nyiK2b.js} +4 -4
  57. package/src/assets/web-panel/assets/{Permissions-as80-9ep.js → Permissions-zVBZEB_C.js} +3 -3
  58. package/src/assets/web-panel/assets/{PersonalDataHub-BGmUw36a.js → PersonalDataHub-BOspsFew.js} +2 -2
  59. package/src/assets/web-panel/assets/{Pipeline-BM96zBxG.js → Pipeline-CQWfZ4XH.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-BBOzBSWe.js → Privacy-C2oGJ_WL.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-GHX41V28.js → ProjectInit-BZMR4qY1.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-DraqzxKD.js → ProjectSettings-DAujoP63.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-BzzdUml4.js → Projects-DvH_P2Uq.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-CSeYNAtW.js → Providers-D_RGHM-v.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-DlzBCdXh.js → QrScannerModal-Mf6KfL2J.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-p8iB7od_.js → QuickAsk-5D8URoI_.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-DjOVXacd.js → Recommend-Df4kC8Gy.js} +1 -1
  68. package/src/assets/web-panel/assets/RemoteSession-DGi3f3vO.js +4 -0
  69. package/src/assets/web-panel/assets/{Reputation-CjT3M0Di.js → Reputation-jmkGFNWb.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-tw8m6T0c.js → Row-DP6Pcu3m.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-B47KBx_f.js → RssFeed-D60hSuPO.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-D2sx1PBe.js → Search-0mf1BQUK.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-JmtL8g7D.js → Security-CfZtLFYV.js} +3 -3
  74. package/src/assets/web-panel/assets/{Services-BxrNqjin.js → Services-D9zpZLWz.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-udk8L18t.js → Skeleton-D1_44eMD.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-DooLT9of.js → Skills-cVFa8zkz.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-ITVZE1xI.js → Sla-Cinpzc3L.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-DCkfqY1n.js → SpeechSettings-bqdiJE_P.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-CkNlgFjM.js → SyncSettings-Dgl_SLgj.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-BMAoyCM_.js → Tasks-DawWAEhy.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-BnrFTlrz.js → Templates-Ca0nY651.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-BjNsy3-i.js → Tenant-CgaGEhLm.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-C0gxLL7V.js → Terminal-D-9EnpRx.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-CUtj_KHA.js → TimelineRenderer-CSOxDHZ6.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-DOg61zlq.js → Tokens-BpwLZpL3.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-D4ldCewj.js → Trigger-DgowwBK5.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-3lk_Zd8j.js → Trust-BZutapjE.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-kWhFR6Li.js → UkeySign-QZPt8_Sp.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-D3GOFVWg.js → VideoEditing-DlQx5UDJ.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-DzrZfBuh.js → Wallet-ZfdTgtWA.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CyVJVp_g.js → WebAuthn-CAp0y_Cr.js} +5 -5
  92. package/src/assets/web-panel/assets/{WorkflowEditor-i_b0wi9b.js → WorkflowEditor-BVITZEmr.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-DwPCK8I7.js → chat-CC2tPaNg.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-D0vaVqZU.js → colors-31brc9CA.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-Don7nGW2.js → compact-item-DLMkC9C-.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-D84R5qy2.js → createContext-BMtveI6F.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-CB8pPOtd.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-BeUqVVKU.js → hasIn-DbD6WHW2.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-m9bn0F1p.js → index-3oBsXZmF.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CfYRv-zA.js → index-ALvoNTRF.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-4MkfPLGv.js → index-AaIKG60c.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-LMo9bBLJ.js → index-BFRLmCR3.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-wesQQ2cH.js → index-BKAB6-6V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CzT56x-_.js → index-BOM-VFlv.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-L73NrmlW.js → index-BPpS-lGb.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-WRw1pM5E.js → index-Bbc2JeQG.js} +1 -1
  107. package/src/assets/web-panel/assets/index-BhXiaiev.js +1 -0
  108. package/src/assets/web-panel/assets/{index-Br3SOdER.js → index-BiRQUoyE.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D8r7WKk-.js → index-BnFLzejT.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CAdCTTBC.js → index-BoO4WEJB.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BlfTf0ka.js → index-C-cGlCDW.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B57al0Z5.js → index-C1PfeI88.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-D4b5XqIy.js → index-C21pdBqs.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CnnxzIx0.js → index-C7LHNw_N.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BNpJf-sf.js → index-CEXa-4zd.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DT40zd7K.js → index-CEwjBA9q.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CL71rIwo.js → index-CFXKbOek.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BZ1N07Sp.js → index-CJhREOlG.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C0_sKVi7.js → index-COHHQ9cU.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DBzBLX-K.js → index-CUmLMFsy.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-De85D9Pc.js → index-CWgfipcC.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-x7VYlV41.js → index-C_eUKGYP.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-hjgrIX5I.js → index-CcVszn2b.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DMT8n9vB.js → index-Cod97Urs.js} +3 -3
  125. package/src/assets/web-panel/assets/{index-DFFzwrNs.js → index-Cwbqh0KZ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-YKLCotoo.js → index-D67kIagl.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-IGGttsCv.js → index-D7cV0tu0.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DQor_7NF.js → index-DJvxFyIz.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-D84BnuFg.js → index-DZl-UXB6.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CrnVEEQO.js → index-Dm0byjm1.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BER98o4P.js → index-DndNnYc0.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DA0-EJKx.js → index-WQUO2vvK.js} +1 -1
  133. package/src/assets/web-panel/assets/index-WpaYaE3D.js +1 -0
  134. package/src/assets/web-panel/assets/{index-CExWtm98.js → index-bdaJwTXJ.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-MKZrJrP_.js → index-ep6JchNG.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-7QUwcGTg.js → index-jG6h22Qg.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-BohrJpBf.js → index-ntVQqSML.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-DGeU4vPS.js → initDefaultProps-D5UoBRhN.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-Dgqj9ab3.js → motion-DltOw0CR.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-BFyEU6Lr.js → move-_oPkk5zj.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-CueCXXPC.js → mtc-parser-EES8ROCS.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-D2pU5OyE.js → omit-B-_39x_M.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-CKTf6zD6.js → pickAttrs-l6D0yJHz.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-DieJaGCN.js → placementArrow-DIeUK31L.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-Bxr-rmU6.js → responsiveObserve-dG4EzUQo.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-CDEDk3Se.js → slide-DWFzYGdH.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-icf2eCTx.js → statusUtils-CECdTA5H.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-qKvgmBhE.js → styleChecker-D6kP-mMK.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-1_UhiyJZ.js → useFlexGapSupport-BWfna_rd.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-CFGLXDWh.js → useFs-Bb-j6rzo.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-UafOXvQ6.js → usePersonalDataHub-D5OIsYM4.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-BfPqSx79.js → vnode-DNpwzFTM.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-CXsVcRB6.js → zoom-vNLpFQBW.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-manifest.json +17 -3
  156. package/src/commands/agent.js +11 -1
  157. package/src/commands/background-session.js +143 -3
  158. package/src/commands/browse.js +86 -0
  159. package/src/commands/cloud.js +232 -0
  160. package/src/commands/doctor.js +61 -6
  161. package/src/commands/mcp.js +61 -0
  162. package/src/commands/plugin.js +63 -1
  163. package/src/commands/routine.js +367 -0
  164. package/src/commands/session.js +326 -0
  165. package/src/data/changelog.json +12 -2
  166. package/src/harness/jsonl-session-store.js +82 -2
  167. package/src/harness/session-index.js +295 -0
  168. package/src/harness/session-mirror.js +142 -0
  169. package/src/harness/transcript-integrity.js +182 -0
  170. package/src/index.js +8 -0
  171. package/src/lib/background-agent-supervisor.js +126 -2
  172. package/src/lib/channels/channel-manager.js +126 -0
  173. package/src/lib/channels/telegram-channel.js +112 -0
  174. package/src/lib/channels/webhook-channel.js +140 -0
  175. package/src/lib/chrome-connector.js +425 -4
  176. package/src/lib/cloud/bundle.js +110 -0
  177. package/src/lib/cloud/cloud-client.js +152 -0
  178. package/src/lib/computer-use/authorization.js +173 -0
  179. package/src/lib/computer-use/computer-use-server.js +276 -0
  180. package/src/lib/computer-use/control-backend.js +150 -0
  181. package/src/lib/doctor-checkup.js +652 -0
  182. package/src/lib/jsonl-session-store.js +4 -0
  183. package/src/lib/lsp/lsp-server-registry.js +48 -21
  184. package/src/lib/plugin-runtime/governance.js +174 -0
  185. package/src/lib/pr-link-ledger.js +221 -0
  186. package/src/lib/remote-control.js +35 -0
  187. package/src/lib/routine-store.js +352 -0
  188. package/src/lib/session-store-guard.js +40 -0
  189. package/src/lib/session-usage.js +318 -2
  190. package/src/lib/sub-agent-context.js +23 -0
  191. package/src/lib/web-ui-server.js +120 -7
  192. package/src/repl/agent-repl.js +592 -61
  193. package/src/repl/bg-dashboard.js +355 -0
  194. package/src/repl/fast-mode.js +103 -0
  195. package/src/repl/fullscreen-tui.js +197 -0
  196. package/src/repl/remote-approval.js +111 -0
  197. package/src/repl/slash-command-registry.js +11 -3
  198. package/src/repl/voice-dictation.js +132 -0
  199. package/src/runtime/agent-core.js +278 -1
  200. package/src/runtime/coding-agent-contract-shared.cjs +141 -0
  201. package/src/runtime/coding-agent-policy.cjs +33 -0
  202. package/src/runtime/headless-runner.js +71 -7
  203. package/src/runtime/headless-stream.js +14 -8
  204. package/src/runtime/policies/agent-policy.js +6 -0
  205. package/src/assets/web-panel/assets/Artifacts-BbN5V7au.js +0 -1
  206. package/src/assets/web-panel/assets/Artifacts-CRkepl4t.css +0 -1
  207. package/src/assets/web-panel/assets/Chat-DDh58PCf.css +0 -1
  208. package/src/assets/web-panel/assets/OrderTableRenderer-iFKeNf6Y.js +0 -1
  209. package/src/assets/web-panel/assets/RemoteSession-CIGk7gk-.js +0 -4
  210. package/src/assets/web-panel/assets/devWarning-BBwWehQ7.js +0 -1
  211. package/src/assets/web-panel/assets/index-DJ8etkBT.js +0 -1
  212. package/src/assets/web-panel/assets/index-OeaK-zXZ.js +0 -1
@@ -0,0 +1,652 @@
1
+ /**
2
+ * Doctor checkup — layered, fixable environment diagnosis.
3
+ *
4
+ * Extends the base `cc doctor` report (runtime/diagnostics.js) with the
5
+ * setup-checkup layers Claude Code's `/doctor` grew: config load chain,
6
+ * provider auth, MCP config, IDE bridge, plugin signature coverage, duplicate
7
+ * subagents, transcript tamper, background-agent hygiene, and git worktree
8
+ * cleanup. Every check degrades gracefully (a broken subsystem becomes an
9
+ * `err` check, never a thrown doctor) and SAFE fixes are applied only via the
10
+ * explicit `--fix` flag; anything needing judgement emits a copyable command
11
+ * instead.
12
+ *
13
+ * Report shape (JSON-serializable):
14
+ * sections: [{ id, title, checks: [{ id, name, level, detail, fix? }] }]
15
+ * fix: { id, safe, description, command? } — `safe:true` ids are the ONLY
16
+ * ones runCheckupFixes will execute.
17
+ */
18
+
19
+ import {
20
+ existsSync,
21
+ readdirSync,
22
+ readFileSync,
23
+ statSync,
24
+ rmSync,
25
+ } from "node:fs";
26
+ import { execSync } from "node:child_process";
27
+ import { join, basename } from "node:path";
28
+ import { getHomeDir, getConfigPath } from "./paths.js";
29
+
30
+ export const CHECK_LEVELS = Object.freeze({
31
+ OK: "ok",
32
+ WARN: "warn",
33
+ ERR: "err",
34
+ INFO: "info",
35
+ });
36
+
37
+ const FREE_PROVIDERS = ["ollama", "local", "llamacpp", "mediapipe"];
38
+ const STALE_JOB_MS = 24 * 60 * 60 * 1000;
39
+
40
+ export const _deps = {
41
+ existsSync,
42
+ readdirSync,
43
+ readFileSync,
44
+ statSync,
45
+ rmSync,
46
+ execSync,
47
+ now: () => Date.now(),
48
+ };
49
+
50
+ function check(id, name, level, detail = "", fix = null) {
51
+ const c = { id, name, level, detail };
52
+ if (fix) c.fix = fix;
53
+ return c;
54
+ }
55
+
56
+ function failedCheck(id, name, err) {
57
+ return check(id, name, CHECK_LEVELS.ERR, `check failed: ${err.message}`);
58
+ }
59
+
60
+ // ── config load chain ──────────────────────────────────────────────────────
61
+ async function configSection(opts, deps) {
62
+ const checks = [];
63
+ const configPath = getConfigPath();
64
+
65
+ try {
66
+ if (!deps.existsSync(configPath)) {
67
+ checks.push(
68
+ check(
69
+ "config-json",
70
+ "config.json",
71
+ CHECK_LEVELS.WARN,
72
+ `not created yet — run "cc setup"`,
73
+ ),
74
+ );
75
+ } else {
76
+ try {
77
+ JSON.parse(deps.readFileSync(configPath, "utf-8"));
78
+ checks.push(check("config-json", "config.json", CHECK_LEVELS.OK, ""));
79
+ } catch (err) {
80
+ checks.push(
81
+ check(
82
+ "config-json",
83
+ "config.json",
84
+ CHECK_LEVELS.ERR,
85
+ `invalid JSON (${err.message}) — cc auto-backs it up as .corrupted and falls back to defaults`,
86
+ {
87
+ id: "config-invalid",
88
+ safe: false,
89
+ description: "Review / repair the config by hand",
90
+ command: `notepad "${configPath}"`,
91
+ },
92
+ ),
93
+ );
94
+ }
95
+ }
96
+
97
+ if (deps.existsSync(`${configPath}.corrupted`)) {
98
+ checks.push(
99
+ check(
100
+ "config-corrupted-backup",
101
+ "config.json.corrupted backup",
102
+ CHECK_LEVELS.INFO,
103
+ "a previously corrupted config was backed up — review then delete it",
104
+ ),
105
+ );
106
+ }
107
+ } catch (err) {
108
+ checks.push(failedCheck("config-json", "config.json", err));
109
+ }
110
+
111
+ // settings.json layers (permission rules / hooks) — every existing layer
112
+ // must at least parse; a broken layer silently drops its rules otherwise.
113
+ try {
114
+ const { settingsPaths } = await import("./settings-loader.cjs").then(
115
+ (m) => m.default || m,
116
+ );
117
+ const layers = settingsPaths(opts.cwd || process.cwd()) || [];
118
+ let bad = 0;
119
+ for (const file of layers) {
120
+ if (!deps.existsSync(file)) continue;
121
+ try {
122
+ JSON.parse(deps.readFileSync(file, "utf-8"));
123
+ } catch {
124
+ bad++;
125
+ checks.push(
126
+ check(
127
+ "settings-json",
128
+ `settings layer ${basename(file)}`,
129
+ CHECK_LEVELS.ERR,
130
+ `invalid JSON — its permission rules and hooks are being IGNORED (${file})`,
131
+ ),
132
+ );
133
+ }
134
+ }
135
+ if (bad === 0) {
136
+ checks.push(
137
+ check(
138
+ "settings-json",
139
+ "settings.json layers",
140
+ CHECK_LEVELS.OK,
141
+ "all present layers parse",
142
+ ),
143
+ );
144
+ }
145
+ } catch (err) {
146
+ checks.push(failedCheck("settings-json", "settings.json layers", err));
147
+ }
148
+
149
+ return { id: "config", title: "Config load chain", checks };
150
+ }
151
+
152
+ // ── provider auth ──────────────────────────────────────────────────────────
153
+ async function providerSection(_opts, _deps2) {
154
+ const checks = [];
155
+ try {
156
+ const { loadConfig } = await import("./config-manager.js");
157
+ let config = {};
158
+ try {
159
+ config = loadConfig();
160
+ } catch {
161
+ config = {};
162
+ }
163
+ const llm = config.llm || {};
164
+ if (!llm.provider) {
165
+ checks.push(
166
+ check(
167
+ "provider",
168
+ "LLM provider",
169
+ CHECK_LEVELS.WARN,
170
+ `not configured — run "cc setup" or "cc config"`,
171
+ ),
172
+ );
173
+ } else if (FREE_PROVIDERS.includes(String(llm.provider).toLowerCase())) {
174
+ checks.push(
175
+ check(
176
+ "provider",
177
+ `LLM provider: ${llm.provider}`,
178
+ CHECK_LEVELS.OK,
179
+ "local provider — no API key required",
180
+ ),
181
+ );
182
+ } else if (llm.apiKey) {
183
+ checks.push(
184
+ check(
185
+ "provider",
186
+ `LLM provider: ${llm.provider}`,
187
+ CHECK_LEVELS.OK,
188
+ "API key configured",
189
+ ),
190
+ );
191
+ } else {
192
+ checks.push(
193
+ check(
194
+ "provider",
195
+ `LLM provider: ${llm.provider}`,
196
+ CHECK_LEVELS.WARN,
197
+ "no API key in config — relies on the provider env var being set at runtime",
198
+ ),
199
+ );
200
+ }
201
+ } catch (err) {
202
+ checks.push(failedCheck("provider", "LLM provider", err));
203
+ }
204
+ return { id: "provider", title: "Provider auth", checks };
205
+ }
206
+
207
+ // ── MCP config ─────────────────────────────────────────────────────────────
208
+ async function mcpSection(opts, deps) {
209
+ const checks = [];
210
+ const cwd = opts.cwd || process.cwd();
211
+ const mcpJson = join(cwd, ".mcp.json");
212
+ try {
213
+ if (deps.existsSync(mcpJson)) {
214
+ try {
215
+ const parsed = JSON.parse(deps.readFileSync(mcpJson, "utf-8"));
216
+ const count = Object.keys(
217
+ parsed.mcpServers || parsed.servers || {},
218
+ ).length;
219
+ checks.push(
220
+ check(
221
+ "mcp-json",
222
+ ".mcp.json",
223
+ CHECK_LEVELS.OK,
224
+ `${count} server(s) declared`,
225
+ ),
226
+ );
227
+ } catch (err) {
228
+ checks.push(
229
+ check(
230
+ "mcp-json",
231
+ ".mcp.json",
232
+ CHECK_LEVELS.ERR,
233
+ `invalid JSON — declared MCP servers are being IGNORED (${err.message})`,
234
+ ),
235
+ );
236
+ }
237
+ } else {
238
+ checks.push(
239
+ check(
240
+ "mcp-json",
241
+ ".mcp.json",
242
+ CHECK_LEVELS.INFO,
243
+ "none in this project (optional)",
244
+ ),
245
+ );
246
+ }
247
+ } catch (err) {
248
+ checks.push(failedCheck("mcp-json", ".mcp.json", err));
249
+ }
250
+ return { id: "mcp", title: "MCP servers", checks };
251
+ }
252
+
253
+ // ── IDE bridge ─────────────────────────────────────────────────────────────
254
+ async function ideSection(opts, _deps2) {
255
+ const checks = [];
256
+ try {
257
+ const { diagnoseIde, readIdeLocks } = await import("./ide-bridge.js");
258
+ const locks = readIdeLocks() || [];
259
+ const diag = diagnoseIde({ cwd: opts.cwd || process.cwd() });
260
+ const level = locks.length > 0 ? CHECK_LEVELS.OK : CHECK_LEVELS.INFO;
261
+ checks.push(
262
+ check(
263
+ "ide-bridge",
264
+ "IDE bridge",
265
+ level,
266
+ typeof diag === "string"
267
+ ? diag
268
+ : diag?.reason || `${locks.length} lockfile(s)`,
269
+ ),
270
+ );
271
+ } catch (err) {
272
+ checks.push(failedCheck("ide-bridge", "IDE bridge", err));
273
+ }
274
+ return { id: "ide", title: "IDE bridge", checks };
275
+ }
276
+
277
+ // ── plugin signature coverage ──────────────────────────────────────────────
278
+ async function pluginSection(opts, _deps2) {
279
+ const checks = [];
280
+ try {
281
+ const { listInstalled } = await import("./plugin-runtime/install.js");
282
+ const { readPluginLock } = await import("./plugin-runtime/signature.js");
283
+ const plugins = listInstalled({ cwd: opts.cwd || process.cwd() }) || [];
284
+ if (plugins.length === 0) {
285
+ checks.push(
286
+ check(
287
+ "plugins",
288
+ "Installed plugins",
289
+ CHECK_LEVELS.INFO,
290
+ "none installed",
291
+ ),
292
+ );
293
+ } else {
294
+ let unsigned = 0;
295
+ for (const p of plugins) {
296
+ try {
297
+ const lock = p.dir ? readPluginLock(p.dir) : null;
298
+ if (!lock || !lock.signature) unsigned++;
299
+ } catch {
300
+ unsigned++;
301
+ }
302
+ }
303
+ checks.push(
304
+ check(
305
+ "plugins",
306
+ `Installed plugins: ${plugins.length}`,
307
+ unsigned > 0 ? CHECK_LEVELS.WARN : CHECK_LEVELS.OK,
308
+ unsigned > 0
309
+ ? `${unsigned} without a signature lock — they will be refused if plugins.requireSignedPlugins is enabled`
310
+ : "all have signature locks",
311
+ ),
312
+ );
313
+ }
314
+ } catch (err) {
315
+ checks.push(failedCheck("plugins", "Installed plugins", err));
316
+ }
317
+ return { id: "plugins", title: "Plugin trust", checks };
318
+ }
319
+
320
+ // ── duplicate subagents ────────────────────────────────────────────────────
321
+ async function subagentSection(opts, deps) {
322
+ const checks = [];
323
+ try {
324
+ const { agentDirs } = await import("./agents.js");
325
+ const cwd = opts.cwd || process.cwd();
326
+ const seen = new Map(); // name → [scopes]
327
+ for (const { dir, scope } of agentDirs(cwd)) {
328
+ if (!deps.existsSync(dir)) continue;
329
+ for (const f of deps.readdirSync(dir)) {
330
+ if (!f.endsWith(".md")) continue;
331
+ const name = basename(f, ".md");
332
+ const scopes = seen.get(name) || [];
333
+ scopes.push(scope);
334
+ seen.set(name, scopes);
335
+ }
336
+ }
337
+ const dupes = [...seen.entries()].filter(([, scopes]) => scopes.length > 1);
338
+ if (dupes.length > 0) {
339
+ checks.push(
340
+ check(
341
+ "subagents-duplicate",
342
+ "Duplicate subagents",
343
+ CHECK_LEVELS.WARN,
344
+ dupes
345
+ .map(([name, scopes]) => `${name} (${scopes.join(" + ")})`)
346
+ .join(", ") + " — the project copy shadows the personal one",
347
+ ),
348
+ );
349
+ } else {
350
+ checks.push(
351
+ check(
352
+ "subagents-duplicate",
353
+ "Subagents",
354
+ CHECK_LEVELS.OK,
355
+ `${seen.size} defined, no cross-scope duplicates`,
356
+ ),
357
+ );
358
+ }
359
+ } catch (err) {
360
+ checks.push(failedCheck("subagents-duplicate", "Subagents", err));
361
+ }
362
+ return { id: "subagents", title: "Subagents", checks };
363
+ }
364
+
365
+ // ── transcript tamper ──────────────────────────────────────────────────────
366
+ async function transcriptSection(_opts, _deps2) {
367
+ const checks = [];
368
+ try {
369
+ const { verifyAllSessions } =
370
+ await import("../harness/jsonl-session-store.js");
371
+ const results = verifyAllSessions({ limit: 200 });
372
+ const tampered = results.filter((r) => r.status === "tampered");
373
+ if (tampered.length > 0) {
374
+ checks.push(
375
+ check(
376
+ "transcript-tamper",
377
+ "Transcript integrity",
378
+ CHECK_LEVELS.ERR,
379
+ `${tampered.length} tampered transcript(s): ${tampered
380
+ .slice(0, 5)
381
+ .map((t) => t.sessionId)
382
+ .join(", ")}${tampered.length > 5 ? ", …" : ""}`,
383
+ {
384
+ id: "transcript-tamper",
385
+ safe: false,
386
+ description: "Inspect the flagged transcripts",
387
+ command: `cc session verify ${tampered[0].sessionId}`,
388
+ },
389
+ ),
390
+ );
391
+ } else {
392
+ checks.push(
393
+ check(
394
+ "transcript-tamper",
395
+ "Transcript integrity",
396
+ CHECK_LEVELS.OK,
397
+ `${results.length} session(s) checked, no tamper`,
398
+ ),
399
+ );
400
+ }
401
+ } catch (err) {
402
+ checks.push(failedCheck("transcript-tamper", "Transcript integrity", err));
403
+ }
404
+ return { id: "transcripts", title: "Session transcripts", checks };
405
+ }
406
+
407
+ // ── background-agent hygiene ───────────────────────────────────────────────
408
+ async function backgroundSection(_opts, deps) {
409
+ const checks = [];
410
+ try {
411
+ const { backgroundAgentsDir, listBackgroundAgents } =
412
+ await import("./background-agent-supervisor.js");
413
+
414
+ const agents = listBackgroundAgents({ all: true }) || [];
415
+ const lost = agents.filter((a) => a.status === "lost");
416
+ if (lost.length > 0) {
417
+ checks.push(
418
+ check(
419
+ "bg-lost",
420
+ "Background agents",
421
+ CHECK_LEVELS.WARN,
422
+ `${lost.length} lost session(s) (worker died without finalizing) — "cc daemon status --all" to review`,
423
+ ),
424
+ );
425
+ } else {
426
+ checks.push(
427
+ check(
428
+ "bg-lost",
429
+ "Background agents",
430
+ CHECK_LEVELS.OK,
431
+ `${agents.length} tracked, none lost`,
432
+ ),
433
+ );
434
+ }
435
+
436
+ // Orphan job handoff files: the worker deletes its .job.<pid>.json on
437
+ // start, so any file older than a day belongs to a launch that never ran.
438
+ const dir = backgroundAgentsDir();
439
+ const staleJobs = [];
440
+ if (deps.existsSync(dir)) {
441
+ for (const f of deps.readdirSync(dir)) {
442
+ if (!f.includes(".job.")) continue;
443
+ try {
444
+ const st = deps.statSync(join(dir, f));
445
+ if (deps.now() - st.mtimeMs > STALE_JOB_MS) staleJobs.push(f);
446
+ } catch {
447
+ // raced with the worker deleting it — fine
448
+ }
449
+ }
450
+ }
451
+ if (staleJobs.length > 0) {
452
+ checks.push(
453
+ check(
454
+ "bg-stale-jobs",
455
+ "Stale job files",
456
+ CHECK_LEVELS.WARN,
457
+ `${staleJobs.length} orphan .job files >24h old in ${dir}`,
458
+ {
459
+ id: "prune-stale-jobs",
460
+ safe: true,
461
+ description: `delete ${staleJobs.length} orphan background-agent job file(s)`,
462
+ },
463
+ ),
464
+ );
465
+ } else {
466
+ checks.push(
467
+ check("bg-stale-jobs", "Stale job files", CHECK_LEVELS.OK, "none"),
468
+ );
469
+ }
470
+ } catch (err) {
471
+ checks.push(failedCheck("bg-lost", "Background agents", err));
472
+ }
473
+ return { id: "background", title: "Background agents", checks };
474
+ }
475
+
476
+ // ── git worktree cleanup ───────────────────────────────────────────────────
477
+ async function worktreeSection(opts, deps) {
478
+ const checks = [];
479
+ const cwd = opts.cwd || process.cwd();
480
+ try {
481
+ const inRepo = (() => {
482
+ try {
483
+ deps.execSync("git rev-parse --is-inside-work-tree", {
484
+ cwd,
485
+ encoding: "utf-8",
486
+ stdio: ["ignore", "pipe", "ignore"],
487
+ });
488
+ return true;
489
+ } catch {
490
+ return false;
491
+ }
492
+ })();
493
+ if (!inRepo) {
494
+ checks.push(
495
+ check(
496
+ "worktree-prune",
497
+ "Git worktrees",
498
+ CHECK_LEVELS.INFO,
499
+ "not a git repository",
500
+ ),
501
+ );
502
+ } else {
503
+ const out =
504
+ deps.execSync("git worktree prune --dry-run -v", {
505
+ cwd,
506
+ encoding: "utf-8",
507
+ stdio: ["ignore", "pipe", "ignore"],
508
+ }) || "";
509
+ const prunable = out.split("\n").filter((l) => l.trim()).length;
510
+ if (prunable > 0) {
511
+ checks.push(
512
+ check(
513
+ "worktree-prune",
514
+ "Git worktrees",
515
+ CHECK_LEVELS.WARN,
516
+ `${prunable} stale worktree entr${prunable === 1 ? "y" : "ies"} (deleted on disk, still registered)`,
517
+ {
518
+ id: "git-worktree-prune",
519
+ safe: true,
520
+ description:
521
+ "git worktree prune (removes stale admin entries only)",
522
+ command: "git worktree prune",
523
+ },
524
+ ),
525
+ );
526
+ } else {
527
+ checks.push(
528
+ check(
529
+ "worktree-prune",
530
+ "Git worktrees",
531
+ CHECK_LEVELS.OK,
532
+ "no stale entries",
533
+ ),
534
+ );
535
+ }
536
+ }
537
+ } catch (err) {
538
+ checks.push(failedCheck("worktree-prune", "Git worktrees", err));
539
+ }
540
+ return { id: "worktrees", title: "Git worktrees", checks };
541
+ }
542
+
543
+ /**
544
+ * Collect all checkup sections. Never throws — a failing subsystem becomes an
545
+ * `err` check inside its section.
546
+ */
547
+ export async function collectCheckupSections(opts = {}) {
548
+ const deps = { ..._deps, ...(opts.deps || {}) };
549
+ const sections = [];
550
+ for (const build of [
551
+ configSection,
552
+ providerSection,
553
+ mcpSection,
554
+ ideSection,
555
+ pluginSection,
556
+ subagentSection,
557
+ transcriptSection,
558
+ backgroundSection,
559
+ worktreeSection,
560
+ ]) {
561
+ try {
562
+ sections.push(await build(opts, deps));
563
+ } catch (err) {
564
+ sections.push({
565
+ id: build.name.replace(/Section$/, ""),
566
+ title: build.name,
567
+ checks: [failedCheck(build.name, build.name, err)],
568
+ });
569
+ }
570
+ }
571
+ return sections;
572
+ }
573
+
574
+ /**
575
+ * Apply the SAFE fixes present in a checkup report. Anything with
576
+ * `fix.safe === false` is skipped (its copyable `command` is the remedy).
577
+ * Returns [{ id, applied, detail }].
578
+ */
579
+ export async function runCheckupFixes(sections, opts = {}) {
580
+ const deps = { ..._deps, ...(opts.deps || {}) };
581
+ const results = [];
582
+ const fixes = [];
583
+ for (const section of sections || []) {
584
+ for (const c of section.checks || []) {
585
+ if (c.fix && c.fix.safe) fixes.push(c.fix);
586
+ }
587
+ }
588
+
589
+ for (const fix of fixes) {
590
+ try {
591
+ if (fix.id === "prune-stale-jobs") {
592
+ const { backgroundAgentsDir } =
593
+ await import("./background-agent-supervisor.js");
594
+ const dir = backgroundAgentsDir();
595
+ let removed = 0;
596
+ for (const f of deps.readdirSync(dir)) {
597
+ if (!f.includes(".job.")) continue;
598
+ try {
599
+ const st = deps.statSync(join(dir, f));
600
+ if (deps.now() - st.mtimeMs > STALE_JOB_MS) {
601
+ deps.rmSync(join(dir, f), { force: true });
602
+ removed++;
603
+ }
604
+ } catch {
605
+ // raced — fine
606
+ }
607
+ }
608
+ results.push({
609
+ id: fix.id,
610
+ applied: true,
611
+ detail: `removed ${removed} file(s)`,
612
+ });
613
+ } else if (fix.id === "git-worktree-prune") {
614
+ deps.execSync("git worktree prune", {
615
+ cwd: opts.cwd || process.cwd(),
616
+ encoding: "utf-8",
617
+ stdio: ["ignore", "pipe", "ignore"],
618
+ });
619
+ results.push({
620
+ id: fix.id,
621
+ applied: true,
622
+ detail: "pruned stale worktree entries",
623
+ });
624
+ } else {
625
+ results.push({
626
+ id: fix.id,
627
+ applied: false,
628
+ detail: "no safe fixer registered",
629
+ });
630
+ }
631
+ } catch (err) {
632
+ results.push({ id: fix.id, applied: false, detail: err.message });
633
+ }
634
+ }
635
+ return results;
636
+ }
637
+
638
+ /** Copyable commands for problems that need the user's judgement. */
639
+ export function unsafeFixCommands(sections) {
640
+ const commands = [];
641
+ for (const section of sections || []) {
642
+ for (const c of section.checks || []) {
643
+ if (c.fix && !c.fix.safe && c.fix.command) {
644
+ commands.push({
645
+ description: c.fix.description,
646
+ command: c.fix.command,
647
+ });
648
+ }
649
+ }
650
+ }
651
+ return commands;
652
+ }
@@ -15,6 +15,7 @@ export {
15
15
  appendUserMessage,
16
16
  appendAssistantMessage,
17
17
  appendToolCall,
18
+ appendToolCallCompact,
18
19
  appendToolResult,
19
20
  appendCompactEvent,
20
21
  readEvents,
@@ -33,4 +34,7 @@ export {
33
34
  isUnsafeSessionId,
34
35
  appendTokenUsage,
35
36
  toIsoSafe,
37
+ verifySession,
38
+ verifyAllSessions,
39
+ TRANSCRIPT_CHAIN_STATUS,
36
40
  } from "../harness/jsonl-session-store.js";