chainlesschain 0.162.38 → 0.162.40

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 (166) hide show
  1. package/README.md +368 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-DV0Q9zKL.js → AIOps-CPmKv82o.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-C6vH8rhL.js → ActionButton-BNDYY7Qd.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BvPDc2ui.js → Analytics-BgCMCOsk.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CWnyqTqY.js → AppLayout-Dv4oJcqS.js} +5 -5
  7. package/src/assets/web-panel/assets/{Audit-BzenidV4.js → Audit-5iV3yrGa.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CSl7bNwK.js → Backup-CHDhnbzF.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-DAY3iHIq.js → BaseInput-B6reFkra.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-Jyhm9fgk.js → Chat-DwS5YyE2.js} +6 -6
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CwlAnVjy.js → ChatBubbleRenderer-CqXa87Hw.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-D4rwURAi.js → Checkbox-yiW0M4RE.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-DYdjTEfC.js → Codegen-DoiVuD_g.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-DsVyZ_fS.js → Col-BVASLexk.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-CjCpl27Q.js → Community-D6KQ7JoU.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-kt18dsjm.js → Compact-Bl9Uhb6v.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-BV5urquU.js → Compliance-MM31-dba.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-C4SovPWC.js → Cowork-PjU_1ieD.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-uuNs_xzA.js → Cron-DorNtPZL.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-DR5a65tR.js → Crosschain-Bm5ts2Kw.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-B1KTf2-5.js → DID-7Y3jlFdY.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-Dkj7XgED.js → Dashboard-1oE532bG.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-BhXCuJ19.js → Dropdown-hJlOPs0s.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-DG8365Iv.js → EmailListRenderer-BEqJxKaO.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BdHGPu39.js → FamilyGuardDashboard-BvCGwB6X.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-Dwvxl0zR.js → Federation-CsXI72e5.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-BVmhCVWU.js → FormItemContext-Dh9SMul-.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-DDPjvF2s.js → GenericCardRenderer-9edWzrtG.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-foK6WTSr.js → Git-ZYhNL8Xk.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-CfqMdu6Y.js → Governance-BwAdp8QA.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-BKrLO4GO.js → Inference-5C-M1XsH.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-6o6Q-mmF.js → KnowledgeGraph-zFAi-zCi.js} +1 -1
  33. package/src/assets/web-panel/assets/Logs-BZsEdbgE.js +2 -0
  34. package/src/assets/web-panel/assets/{Marketplace-BWkfEocP.js → Marketplace-BP6gErRK.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-BPebQbWU.js → McpTools-CXVzoLrd.js} +4 -4
  36. package/src/assets/web-panel/assets/{Memory-C0Dq-X3C.js → Memory-BIpChb4-.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-DRBoutTY.js → MobileBridge-B4O7wDT8.js} +2 -2
  38. package/src/assets/web-panel/assets/MobileProjects-7VPMoHus.js +1 -0
  39. package/src/assets/web-panel/assets/{Mtc-Cj3QPM9p.js → Mtc-BTmEyTM5.js} +6 -6
  40. package/src/assets/web-panel/assets/{MtcAudit-rBQYbfQR.js → MtcAudit-CsbG9LlV.js} +2 -2
  41. package/src/assets/web-panel/assets/{Multisig-Dbuy4OY4.js → Multisig-CL8yoGon.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-CMnt1se-.js → NLProgramming-C2cIlIp_.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-BX9tSCiF.js → Notes-7aBk_n_M.js} +4 -4
  44. package/src/assets/web-panel/assets/{NotificationSettings-BFeirVRq.js → NotificationSettings-BuhQk4rJ.js} +1 -1
  45. package/src/assets/web-panel/assets/{OrderTableRenderer-ybiMlKQW.js → OrderTableRenderer-mqMFZu0x.js} +1 -1
  46. package/src/assets/web-panel/assets/{Organization-kTfRxKqk.js → Organization-CAdq-170.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-CtuCAzwV.js → Overflow--Xn0E787.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-KfbciaP3.js → P2P-DYt3YAXI.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-bqEUFhgC.js → PdhVaultBrowser-Bgb_v8WN.js} +5 -5
  50. package/src/assets/web-panel/assets/{Permissions-BgMypz-z.js → Permissions-DoFlmoaW.js} +3 -3
  51. package/src/assets/web-panel/assets/{PersonalDataHub-C3zUE-1z.js → PersonalDataHub-C-FJB3a0.js} +2 -2
  52. package/src/assets/web-panel/assets/{Pipeline-iX-pYHpC.js → Pipeline-3bL2RzzL.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-B01uzeFM.js → Privacy-c4igYUCF.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-TsfbzJp7.js → ProjectInit-C0QS1UPR.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-iGvMp8sM.js → ProjectSettings-CkYC0xkE.js} +2 -2
  56. package/src/assets/web-panel/assets/{Projects-Be9k29iQ.js → Projects-Di17SYft.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-C9Pc8dqo.js → Providers-41NySsLt.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-DN_yFiVO.js → QuickAsk-DHq9pD7z.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-CvSNgl7H.js → Recommend-CLjgFPLv.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-S6BCz8xH.js → Reputation-EIrgErm3.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-CTRYCaqP.js → Row-GAvKzKH7.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-Cu8_P5ll.js → RssFeed-CYCNsVmD.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-rZ1Xza_U.js → Search-DWOE32k8.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CF43IJHX.js → Security-Dgh8Jevn.js} +4 -4
  65. package/src/assets/web-panel/assets/{Services-BobNHzne.js → Services-BxdgP67N.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-DWJ2kfuI.js → Skeleton-D-xT4ZkA.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-AmEZgHYr.js → Skills-BKN4lfSa.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-DTS-fBiY.js → Sla--N1TudpS.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-DEr6MHRU.js → SpeechSettings-B0vfJpEh.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-CVs9alv_.js → SyncSettings-BuBAbPAh.js} +2 -2
  71. package/src/assets/web-panel/assets/Tasks-4XugjJ87.js +1 -0
  72. package/src/assets/web-panel/assets/{Templates-CTNjZRKA.js → Templates-DI2giLgc.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-DPbXg0Pg.js → Tenant-BiTWvm0g.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-DhKXcPw2.js → Terminal-vV6AWGDi.js} +2 -2
  75. package/src/assets/web-panel/assets/{TimelineRenderer-B0DMZOpk.js → TimelineRenderer-BmgzKdAp.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-RvWuBXgg.js → Tokens-Nvupdm6p.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-2O-BaTQG.js → Trigger-DRfR77WJ.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-6qY35L-C.js → Trust-De0Jal_6.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-DhV1wYtQ.js → UkeySign-Dzo4-VAM.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-DgqA5UZm.js → VideoEditing-hg2ytiJB.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-DJRYdUAK.js → Wallet--bU5-gRh.js} +4 -4
  82. package/src/assets/web-panel/assets/{WebAuthn-C2W-x0cg.js → WebAuthn-DZptt-PV.js} +5 -5
  83. package/src/assets/web-panel/assets/{WorkflowEditor-BP2tkDHe.js → WorkflowEditor-Dy9223bY.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-CGVfeoTn.js → chat-DaxGeI9w.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-BmjRolM1.js → colors-Cu2VEci3.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-BvJJkjZE.js → compact-item-CGolhyJq.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-DyhlvRYs.js → createContext-DY7EFhkD.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-DV2BNd59.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-BoBMR89s.js → hasIn-Bpc-NoFN.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-CSgbOGaP.js → index-1D4sfByw.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-C1t-r7yV.js → index-8h9y5S6X.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-FKFT-QTk.js → index-BP9P6chP.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-CIaGw7vl.js → index-BQ2z6Ky5.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CQJVedQ3.js → index-BRAgl2J_.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BycpeGfj.js → index-BTvwiqJE.js} +1 -1
  96. package/src/assets/web-panel/assets/index-BZqtTmyG.js +1 -0
  97. package/src/assets/web-panel/assets/{index-D0YToIi_.js → index-BjfxHEmX.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BT1SQ9nj.js → index-BlHq81Ow.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DyS4I4L-.js → index-Bn5gM9Oy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-81tWFqfN.js → index-Bz83ngs0.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-xZdOioVg.js → index-C-Hkl_2G.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cbh-lCxq.js → index-C0_zeYnx.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-xPSzUoWT.js → index-C2RpsAiO.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-n-N19np-.js → index-CBSk_VrT.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-VXVukhBA.js → index-CFAnEzRW.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DtKdCXHW.js → index-CGqeHu_F.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Beh7jDbS.js → index-CJFYF8F9.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BvvNnWXe.js → index-CLNqZF55.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DeeLHcMY.js → index-CaKXhpEu.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BuQrONgf.js → index-Ciw5-X1B.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CDPMHKQi.js → index-D0GN5tdM.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Bm_MmdwP.js → index-D63ObMdQ.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DIPZ6hbJ.js → index-DAov-rJR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-39VDXdn6.js → index-DElatOQ0.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DwTgvhOL.js → index-DNX81oSR.js} +1 -1
  116. package/src/assets/web-panel/assets/index-DUpwdJt9.js +1 -0
  117. package/src/assets/web-panel/assets/{index-DgbWSwr5.js → index-DZ4Vm8dQ.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-D-93XwJd.js → index-DexYD87j.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C0xn6hOr.js → index-DfKmAEtE.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-ZNIms1nA.js → index-DldaToUA.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-wLAjVpmJ.js → index-DpRSzAFl.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Te0ruvY_.js → index-DxXkr-NS.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Czsbrn75.js → index-RumxOD0S.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-vF1pR00A.js → index-VBRPxZeE.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BqGNmoKy.js → index-eF9RV_4c.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CzDVBBcg.js → index-lfP8sdzB.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Y1b8i0NV.js → index-oJQgRCrR.js} +3 -3
  128. package/src/assets/web-panel/assets/{index-ByWpNjTj.js → index-rkm7dHwG.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-BLKSE8he.js → initDefaultProps-CkJZfCo8.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-Bb59qqLK.js → motion-BerbusV1.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-CB3pYCk6.js → move-DyRzKPD4.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-iImQWuU7.js → omit-CCdrTUAs.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-DRP2Chqo.js → pickAttrs-mVDeZx2m.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-BrlfD4tF.js → placementArrow-Bb_-Fs_o.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-Cqxkuh5H.js → responsiveObserve-C6TMj1R_.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-nxKEuLMj.js → slide-CdCNsy1J.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-30E47KSk.js → statusUtils-Ccxd1rFd.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-Dn2_-5bn.js → styleChecker-3IL-yw1V.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-DkZ00X6F.js → useFlexGapSupport-CH8DjUHl.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-ByrwSCOr.js → useFs-Cn9nE2sp.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-BDY6jtUD.js → usePersonalDataHub-BPyT0HO7.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-BL2q5BLv.js → vnode-Mfm7vy07.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-BSkPKE42.js → zoom-CTpAiAE9.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/init.js +84 -2
  146. package/src/commands/session.js +36 -12
  147. package/src/index.js +10 -0
  148. package/src/lib/agent-session-export.js +124 -0
  149. package/src/lib/ide-context.js +333 -0
  150. package/src/lib/project-instructions.js +275 -0
  151. package/src/lib/project-inventory.js +355 -0
  152. package/src/lib/repl-bang-memorize.js +142 -0
  153. package/src/lib/repl-completer.js +154 -0
  154. package/src/lib/update-notice-refresh.mjs +10 -0
  155. package/src/lib/update-notice.js +154 -0
  156. package/src/repl/agent-repl.js +154 -0
  157. package/src/runtime/agent-core.js +195 -0
  158. package/src/runtime/headless-runner.js +19 -0
  159. package/src/runtime/headless-stream.js +19 -9
  160. package/src/runtime/system-prompt.js +21 -1
  161. package/src/assets/web-panel/assets/Logs-L5ZIW0Dz.js +0 -2
  162. package/src/assets/web-panel/assets/MobileProjects-BMP6eLp1.js +0 -1
  163. package/src/assets/web-panel/assets/Tasks-BcVDAxdi.js +0 -1
  164. package/src/assets/web-panel/assets/devWarning-CetO0WH0.js +0 -1
  165. package/src/assets/web-panel/assets/index-BZVz-WfV.js +0 -1
  166. package/src/assets/web-panel/assets/index-D0-bvFy3.js +0 -1
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Startup update notice — Claude-Code-style "new version available" line.
3
+ *
4
+ * Zero startup cost by design:
5
+ * - the CLI entry only does ONE sync cache read (~/.chainlesschain/
6
+ * update-check.json) and prints a single gray stderr line when the cached
7
+ * latest version is newer than the running one (TTY only, never pollutes
8
+ * piped/JSON output);
9
+ * - when the cache is stale (>24h) it spawns a DETACHED, unref'd child that
10
+ * refreshes the cache from the npm registry for the NEXT run — the current
11
+ * invocation never waits on the network. The cache's checkedAt is touched
12
+ * optimistically before spawning so concurrent invocations don't stampede.
13
+ *
14
+ * Disable with CC_UPDATE_NOTICE=0. `cc update` remains the full interactive
15
+ * checker (GitHub releases + assets); this is just the passive nudge.
16
+ */
17
+
18
+ import fsDefault from "fs";
19
+ import pathDefault from "path";
20
+ import osDefault from "os";
21
+ import { spawn as spawnDefault } from "child_process";
22
+ import { fileURLToPath } from "url";
23
+ import chalk from "chalk";
24
+ import semver from "semver";
25
+ import { VERSION } from "../constants.js";
26
+
27
+ export const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
28
+ export const NPM_LATEST_URL = "https://registry.npmjs.org/chainlesschain/latest";
29
+
30
+ export const _deps = {
31
+ fs: fsDefault,
32
+ path: pathDefault,
33
+ os: osDefault,
34
+ spawn: spawnDefault,
35
+ };
36
+
37
+ export function cachePath(deps = _deps) {
38
+ return deps.path.join(
39
+ deps.os.homedir() || "",
40
+ ".chainlesschain",
41
+ "update-check.json",
42
+ );
43
+ }
44
+
45
+ function readCache(deps) {
46
+ try {
47
+ return JSON.parse(deps.fs.readFileSync(cachePath(deps), "utf-8"));
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function writeCache(deps, cache) {
54
+ try {
55
+ const p = cachePath(deps);
56
+ deps.fs.mkdirSync(deps.path.dirname(p), { recursive: true });
57
+ deps.fs.writeFileSync(p, JSON.stringify(cache), "utf-8");
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Entry-point hook. Cheap and fail-open — never throws, never blocks.
66
+ *
67
+ * @returns {{ printed: boolean, spawned: boolean }}
68
+ */
69
+ export function maybeNotifyUpdate(opts = {}) {
70
+ const deps = { ..._deps, ...(opts.deps || {}) };
71
+ const env = opts.env || process.env;
72
+ const now = opts.now ?? Date.now();
73
+ const isTTY = opts.isTTY ?? Boolean(process.stderr.isTTY);
74
+ const current = opts.currentVersion || VERSION;
75
+ const print =
76
+ opts.print || ((line) => process.stderr.write(chalk.gray(line) + "\n"));
77
+
78
+ const out = { printed: false, spawned: false };
79
+ try {
80
+ if (env.CC_UPDATE_NOTICE === "0") return out;
81
+
82
+ const cache = readCache(deps);
83
+
84
+ if (
85
+ isTTY &&
86
+ cache?.latest &&
87
+ semver.valid(cache.latest) &&
88
+ semver.valid(current) &&
89
+ semver.gt(cache.latest, current)
90
+ ) {
91
+ print(
92
+ `Update available: chainlesschain ${current} → ${cache.latest} (npm i -g chainlesschain · CC_UPDATE_NOTICE=0 to hide)`,
93
+ );
94
+ out.printed = true;
95
+ }
96
+
97
+ const stale = !cache?.checkedAt || now - cache.checkedAt > CACHE_TTL_MS;
98
+ if (stale) {
99
+ // Optimistic touch first: parallel `cc` invocations inside the stale
100
+ // window won't each spawn a refresher.
101
+ writeCache(deps, { ...(cache || {}), checkedAt: now });
102
+ const refresher = deps.path.join(
103
+ deps.path.dirname(fileURLToPath(import.meta.url)),
104
+ "update-notice-refresh.mjs",
105
+ );
106
+ const child = deps.spawn(
107
+ process.execPath,
108
+ [refresher, cachePath(deps)],
109
+ { detached: true, stdio: "ignore", windowsHide: true },
110
+ );
111
+ if (child && typeof child.unref === "function") child.unref();
112
+ out.spawned = true;
113
+ }
114
+ } catch {
115
+ /* fail-open: a broken cache or spawn must never affect the CLI */
116
+ }
117
+ return out;
118
+ }
119
+
120
+ /**
121
+ * One cache refresh (used by the detached child; exported for tests).
122
+ * npm registry only — light, unauthenticated, no GitHub rate-limit risk.
123
+ */
124
+ export async function refreshCacheOnce({
125
+ cacheFile,
126
+ fetchImpl = fetch,
127
+ deps = _deps,
128
+ now = Date.now(),
129
+ timeoutMs = 10_000,
130
+ } = {}) {
131
+ const ctrl = new AbortController();
132
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
133
+ try {
134
+ const res = await fetchImpl(NPM_LATEST_URL, {
135
+ headers: { Accept: "application/json" },
136
+ signal: ctrl.signal,
137
+ });
138
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
139
+ const data = await res.json();
140
+ if (!data?.version) throw new Error("no version field");
141
+ const file = cacheFile || cachePath(deps);
142
+ deps.fs.mkdirSync(deps.path.dirname(file), { recursive: true });
143
+ deps.fs.writeFileSync(
144
+ file,
145
+ JSON.stringify({ checkedAt: now, latest: data.version }),
146
+ "utf-8",
147
+ );
148
+ return { ok: true, latest: data.version };
149
+ } catch (err) {
150
+ return { ok: false, error: err.message };
151
+ } finally {
152
+ clearTimeout(timer);
153
+ }
154
+ }
@@ -732,11 +732,59 @@ export async function startAgentRepl(options = {}) {
732
732
  return chalk.green("> ");
733
733
  };
734
734
 
735
+ // `@` tab-completion (Claude-Code @-mention parity): filesystem paths +
736
+ // (when the IDE bridge is connected) the editor's open tabs ranked first.
737
+ const { makeAtCompleter } = await import("../lib/repl-completer.js");
738
+ const atCompleter = makeAtCompleter({
739
+ cwd: process.cwd(),
740
+ // Keep in sync with the rl.on("line") handlers + /help below.
741
+ slashCommands: [
742
+ "/auto",
743
+ "/clear",
744
+ "/compact",
745
+ "/context",
746
+ "/cowork",
747
+ "/exit",
748
+ "/help",
749
+ "/mcp",
750
+ "/model",
751
+ "/output-style",
752
+ "/plan",
753
+ "/profile",
754
+ "/provider",
755
+ "/quit",
756
+ "/reindex",
757
+ "/search",
758
+ "/session",
759
+ "/stats",
760
+ "/statusline",
761
+ "/sub-agents",
762
+ "/task",
763
+ ],
764
+ getIdeOpenFiles: async () => {
765
+ const exec = _adhocMcp?.externalToolExecutors?.mcp__ide__getOpenEditors;
766
+ if (!exec || exec.kind !== "mcp" || !_adhocMcp?.mcpClient?.callTool) {
767
+ return [];
768
+ }
769
+ const { parseToolResultJson } = await import("../lib/ide-context.js");
770
+ const res = await _adhocMcp.mcpClient.callTool(
771
+ exec.serverName,
772
+ exec.toolName,
773
+ {},
774
+ );
775
+ const data = parseToolResultJson(res);
776
+ return Array.isArray(data?.editors)
777
+ ? data.editors.map((e) => e?.file).filter(Boolean)
778
+ : [];
779
+ },
780
+ });
781
+
735
782
  const rl = readline.createInterface({
736
783
  input: process.stdin,
737
784
  output: process.stdout,
738
785
  prompt: getPrompt(),
739
786
  terminal: true,
787
+ completer: atCompleter,
740
788
  });
741
789
 
742
790
  logger.log(chalk.bold("\nChainlessChain Agent"));
@@ -830,6 +878,45 @@ export async function startAgentRepl(options = {}) {
830
878
  return;
831
879
  }
832
880
 
881
+ // `!` bash passthrough (Claude-Code parity): run the command right here —
882
+ // no LLM round-trip — and fold the output into the conversation context.
883
+ if (trimmed.startsWith("!") && trimmed.slice(1).trim()) {
884
+ try {
885
+ const { runBangCommand } = await import("../lib/repl-bang-memorize.js");
886
+ const res = runBangCommand(trimmed, { cwd: process.cwd() });
887
+ logger.log(chalk.gray(`$ ${res.cmd}`));
888
+ if (res.stdout) process.stdout.write(res.stdout.endsWith("\n") ? res.stdout : `${res.stdout}\n`);
889
+ if (res.stderr) process.stderr.write(chalk.red(res.stderr.endsWith("\n") ? res.stderr : `${res.stderr}\n`));
890
+ if (res.error) logger.error(`shell error: ${res.error.message}`);
891
+ logger.log(chalk.gray(`(exit ${res.exitCode})`));
892
+ messages.push(res.contextMessage);
893
+ } catch (err) {
894
+ logger.error(`! command failed: ${err.message}`);
895
+ }
896
+ prompt();
897
+ return;
898
+ }
899
+
900
+ // `#` quick-memorize (Claude-Code parity): append a note to the project
901
+ // cc.md (auto-loaded next session) and keep it active in this one.
902
+ if (trimmed.startsWith("#") && trimmed.slice(1).trim()) {
903
+ try {
904
+ const { appendMemoryNote } = await import("../lib/repl-bang-memorize.js");
905
+ const res = appendMemoryNote(trimmed, { cwd: process.cwd() });
906
+ messages.push({
907
+ role: "system",
908
+ content: `<memory-note source="${res.target}">${res.note}</memory-note>`,
909
+ });
910
+ logger.log(
911
+ chalk.green(`✔ remembered in ${res.target}${res.created ? " (created)" : ""}`),
912
+ );
913
+ } catch (err) {
914
+ logger.error(`# memorize failed: ${err.message}`);
915
+ }
916
+ prompt();
917
+ return;
918
+ }
919
+
833
920
  // Slash commands
834
921
  if (trimmed === "/exit" || trimmed === "/quit") {
835
922
  logger.log(chalk.gray("\nGoodbye!"));
@@ -839,6 +926,12 @@ export async function startAgentRepl(options = {}) {
839
926
 
840
927
  if (trimmed === "/help") {
841
928
  logger.log(chalk.bold("\nCommands:"));
929
+ logger.log(
930
+ ` ${chalk.cyan("! <cmd>")} Run a shell command directly (output joins context)`,
931
+ );
932
+ logger.log(
933
+ ` ${chalk.cyan("# <note>")} Remember a note in the project cc.md`,
934
+ );
842
935
  logger.log(` ${chalk.cyan("/exit")} Exit the agent`);
843
936
  logger.log(
844
937
  ` ${chalk.cyan("/model")} Show/change model (/model <name>)`,
@@ -848,6 +941,9 @@ export async function startAgentRepl(options = {}) {
848
941
  logger.log(
849
942
  ` ${chalk.cyan("/statusline")} Context-usage line on/off (/statusline [on|off])`,
850
943
  );
944
+ logger.log(
945
+ ` ${chalk.cyan("/context")} Live context-window usage by role`,
946
+ );
851
947
  logger.log(
852
948
  ` ${chalk.cyan("/compact")} Smart compact (importance-based)`,
853
949
  );
@@ -1086,6 +1182,52 @@ export async function startAgentRepl(options = {}) {
1086
1182
  return;
1087
1183
  }
1088
1184
 
1185
+ if (trimmed === "/context") {
1186
+ // Live-session twin of `cc context` (Claude-Code /context parity):
1187
+ // bucket the CURRENT in-memory conversation by role against the model
1188
+ // window. Reuses the same categorizer + estimator as the archived view.
1189
+ try {
1190
+ const { categorizeContext } = await import("../commands/context.js");
1191
+ const { estimateTokens } = await import(
1192
+ "../harness/prompt-compressor.js"
1193
+ );
1194
+ const { buckets, counts, total } = categorizeContext(
1195
+ messages,
1196
+ estimateTokens,
1197
+ );
1198
+ const window = getContextWindow(model, provider) || 0;
1199
+ logger.log(chalk.bold("\nContext usage (live session):"));
1200
+ const rows = [
1201
+ ["system", buckets.system, counts.system],
1202
+ ["user", buckets.user, counts.user],
1203
+ ["assistant", buckets.assistant, counts.assistant],
1204
+ ["tool", buckets.tool, counts.tool],
1205
+ ["tool_calls", buckets.toolCalls, null],
1206
+ ];
1207
+ for (const [label, tok, n] of rows) {
1208
+ if (!tok) continue;
1209
+ const share = total ? Math.round((tok / total) * 100) : 0;
1210
+ logger.log(
1211
+ ` ${label.padEnd(11)}${String(tok).padStart(9)} tok ${String(share).padStart(3)}%${
1212
+ n != null ? chalk.gray(` (${n} msgs)`) : ""
1213
+ }`,
1214
+ );
1215
+ }
1216
+ const pct = window ? Math.round((total / window) * 100) : null;
1217
+ logger.log(
1218
+ ` ${"total".padEnd(11)}${String(total).padStart(9)} tok${
1219
+ window
1220
+ ? ` ${pct}% of ${window} (${Math.max(0, window - total)} left)`
1221
+ : ""
1222
+ }`,
1223
+ );
1224
+ } catch (err) {
1225
+ logger.error(`/context failed: ${err.message}`);
1226
+ }
1227
+ prompt();
1228
+ return;
1229
+ }
1230
+
1089
1231
  if (trimmed === "/compact") {
1090
1232
  if (_compressor && messages.length > 3) {
1091
1233
  const { messages: compacted, stats } = await _compressor.compress(
@@ -1922,6 +2064,18 @@ export async function startAgentRepl(options = {}) {
1922
2064
  }
1923
2065
  }
1924
2066
 
2067
+ // IDE live context (Claude-Code parity): re-shared on every prompt while
2068
+ // an IDE bridge is connected — the user's selection moves between turns.
2069
+ // Ephemeral: persistence stores effectivePrompt, not this snapshot.
2070
+ // Best-effort; CC_IDE_CONTEXT=0 disables.
2071
+ try {
2072
+ const { buildIdePromptContext } = await import("../lib/ide-context.js");
2073
+ const ideCtx = await buildIdePromptContext(_adhocMcp);
2074
+ if (ideCtx) userContent += `\n\n${ideCtx}`;
2075
+ } catch (_err) {
2076
+ // optional polish — never fail the turn over it
2077
+ }
2078
+
1925
2079
  // Add user message
1926
2080
  messages.push({ role: "user", content: userContent });
1927
2081
 
@@ -230,6 +230,25 @@ async function runSettingsPreToolUseHooks(name, args, context, cwd) {
230
230
  return { blocked: true, reason: outcome.reason, hook: outcome.hook };
231
231
  }
232
232
  if (outcome.decision === "ask") {
233
+ // File edits in an interactive session with an IDE bridge: route the ask
234
+ // through the editor's openDiff review (same machinery as settings ask —
235
+ // accepted means the IDE wrote the file, so the caller must skip
236
+ // execution; see tryIdeDiffApprovalForEdit).
237
+ const ide = await tryIdeDiffApprovalForEdit(name, args, context, cwd, {
238
+ rule: `hook:${outcome.hook}`,
239
+ source: "PreToolUse hook",
240
+ });
241
+ if (ide?.outcome === "accepted") {
242
+ return { blocked: false, ideApplied: ide.result };
243
+ }
244
+ if (ide?.outcome === "rejected") {
245
+ return {
246
+ blocked: true,
247
+ reason: ide.result.error,
248
+ hook: outcome.hook,
249
+ ideResult: ide.result,
250
+ };
251
+ }
233
252
  const confirm = context.permissionConfirm || context.shellConfirm || null;
234
253
  const ok =
235
254
  typeof confirm === "function"
@@ -603,6 +622,133 @@ export function buildSystemPrompt(cwd, opts = {}) {
603
622
 
604
623
  // ─── Tool execution ──────────────────────────────────────────────────────
605
624
 
625
+ /** The file-mutating tools whose `ask` can be reviewed as an IDE diff. */
626
+ const IDE_DIFF_EDIT_TOOLS = new Set([
627
+ "write_file",
628
+ "edit_file",
629
+ "edit_file_hashed",
630
+ ]);
631
+
632
+ /**
633
+ * Compute the content an edit tool WOULD write, without writing it — the
634
+ * left/right sides for an IDE diff review. Mirrors the corresponding
635
+ * executeToolInner cases exactly (write_file / edit_file / edit_file_hashed,
636
+ * the latter via the same pure replaceByHash). Returns
637
+ * `{ filePath, newContent, originalText|null }` or null when the edit cannot
638
+ * be computed (missing file, anchor/old_string miss, bad args) — the caller
639
+ * then falls back to the normal confirmation path so the tool can produce its
640
+ * own diagnostics.
641
+ */
642
+ export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
643
+ try {
644
+ if (!args.path) return null;
645
+ const filePath = path.resolve(cwd, args.path);
646
+ if (name === "write_file") {
647
+ if (typeof args.content !== "string") return null;
648
+ const originalText = fs.existsSync(filePath)
649
+ ? fs.readFileSync(filePath, "utf8")
650
+ : "";
651
+ return { filePath, newContent: args.content, originalText };
652
+ }
653
+ if (name === "edit_file") {
654
+ if (!fs.existsSync(filePath)) return null;
655
+ const content = fs.readFileSync(filePath, "utf8");
656
+ if (
657
+ typeof args.old_string !== "string" ||
658
+ !content.includes(args.old_string)
659
+ ) {
660
+ return null;
661
+ }
662
+ return {
663
+ filePath,
664
+ newContent: content.replace(args.old_string, args.new_string),
665
+ originalText: content,
666
+ };
667
+ }
668
+ if (name === "edit_file_hashed") {
669
+ if (!fs.existsSync(filePath)) return null;
670
+ if (!args.anchor_hash || typeof args.new_line !== "string") return null;
671
+ const original = fs.readFileSync(filePath, "utf8");
672
+ const result = replaceByHash(original, {
673
+ anchorHash: args.anchor_hash,
674
+ expectedLine: args.expected_line,
675
+ newLine: args.new_line,
676
+ });
677
+ if (!result.success) return null;
678
+ return { filePath, newContent: result.content, originalText: original };
679
+ }
680
+ } catch {
681
+ // unreadable file etc. → no proposal, normal path handles it
682
+ }
683
+ return null;
684
+ }
685
+
686
+ /**
687
+ * Shared IDE-diff approval routing for an `ask` decision about a file edit
688
+ * (used by BOTH the settings-rules ask and the PreToolUse-hook ask). Returns
689
+ * { outcome:"accepted", result } — the IDE wrote the file; the caller MUST
690
+ * return `result` and skip execution
691
+ * { outcome:"rejected", result } — deny with `result`, file untouched
692
+ * null — not applicable (non-edit tool, headless,
693
+ * no IDE, disabled, no proposal, IDE died)
694
+ * → caller falls back to its own confirm.
695
+ */
696
+ async function tryIdeDiffApprovalForEdit(
697
+ name,
698
+ args,
699
+ context,
700
+ cwd,
701
+ { rule, source } = {},
702
+ ) {
703
+ if (!IDE_DIFF_EDIT_TOOLS.has(name)) return null;
704
+ if (typeof context.permissionConfirm !== "function") return null; // interactive only
705
+ if (!context.mcpClient || !context.externalToolExecutors) return null;
706
+ try {
707
+ const { ideDiffApprovalEnabled, hasIdeOpenDiff, requestIdeDiffApproval } =
708
+ await import("../lib/ide-context.js");
709
+ const mcpLike = {
710
+ mcpClient: context.mcpClient,
711
+ externalToolExecutors: context.externalToolExecutors,
712
+ };
713
+ if (!ideDiffApprovalEnabled() || !hasIdeOpenDiff(mcpLike)) return null;
714
+ const proposal = computeProposedEdit(name, args, cwd);
715
+ if (!proposal) return null;
716
+ const verdict = await requestIdeDiffApproval(mcpLike, {
717
+ path: proposal.filePath,
718
+ modifiedText: proposal.newContent,
719
+ originalText: proposal.originalText,
720
+ title: `cc agent: ${name} ${path.basename(proposal.filePath)}`,
721
+ });
722
+ if (verdict?.outcome === "accepted") {
723
+ return {
724
+ outcome: "accepted",
725
+ result: {
726
+ success: true,
727
+ path: proposal.filePath,
728
+ appliedVia: "ide-diff",
729
+ ...(verdict.finalText != null &&
730
+ verdict.finalText !== proposal.newContent
731
+ ? { userEdited: true }
732
+ : {}),
733
+ policy: { decision: "allow", rule, via: "ide-diff" },
734
+ },
735
+ };
736
+ }
737
+ if (verdict?.outcome === "rejected") {
738
+ return {
739
+ outcome: "rejected",
740
+ result: {
741
+ error: `[Permission] "${name}" was rejected in the IDE diff review (${source}: ${rule}).`,
742
+ policy: { decision: "deny", rule, via: "ide-diff" },
743
+ },
744
+ };
745
+ }
746
+ } catch (_err) {
747
+ // diff-approval routing is best-effort — fall back to the normal confirm
748
+ }
749
+ return null;
750
+ }
751
+
606
752
  /**
607
753
  * Execute a single tool call with plan-mode filtering and hook pipeline.
608
754
  *
@@ -719,6 +865,17 @@ export async function executeTool(name, args, context = {}) {
719
865
  // 3 + 4. settings ask / allow (only reached when neither layer denied)
720
866
  let ruleAllowed = false;
721
867
  if (settingsVerdict.decision === "ask") {
868
+ // IDE-native diff approval (Claude-Code parity): for file edits in an
869
+ // interactive session with an IDE bridge connected, review the edit in
870
+ // the editor instead of a terminal y/N. Accepted = the IDE wrote the
871
+ // file → return the synthetic result and SKIP execution; rejected =
872
+ // deny; null = fall through to the normal confirm below. Shared with the
873
+ // PreToolUse-hook ask path (tryIdeDiffApprovalForEdit).
874
+ const ide = await tryIdeDiffApprovalForEdit(name, args, context, cwd, {
875
+ rule: settingsVerdict.rule,
876
+ source: "settings rule",
877
+ });
878
+ if (ide) return ide.result;
722
879
  const confirm = context.permissionConfirm || context.shellConfirm || null;
723
880
  const ok =
724
881
  typeof confirm === "function"
@@ -792,7 +949,12 @@ export async function executeTool(name, args, context = {}) {
792
949
  }
793
950
  if (context.settingsHooks) {
794
951
  const pre = await runSettingsPreToolUseHooks(name, args, context, cwd);
952
+ // A hook `ask` resolved by the IDE diff review: accepted → the IDE
953
+ // already wrote the file, return the synthetic result and skip the tool;
954
+ // rejected → the ide-diff deny shape (via:"ide-diff", not via:"hook").
955
+ if (pre.ideApplied) return pre.ideApplied;
795
956
  if (pre.blocked) {
957
+ if (pre.ideResult) return pre.ideResult;
796
958
  return {
797
959
  error: `[Hook] PreToolUse blocked "${name}"${pre.reason ? ": " + pre.reason : ""}`,
798
960
  policy: { decision: "block", via: "hook", hook: pre.hook || null },
@@ -930,6 +1092,39 @@ export async function executeTool(name, args, context = {}) {
930
1092
  }
931
1093
  }
932
1094
 
1095
+ // IDE post-edit diagnostics (Claude-Code parity): after a successful file
1096
+ // mutation with an IDE bridge connected, pull the editor's fresh
1097
+ // error/warning diagnostics for that file into the tool result so the model
1098
+ // can fix what it just broke in the same loop. Best-effort, bounded;
1099
+ // CC_IDE_CONTEXT=0 disables alongside prompt-time context.
1100
+ if (
1101
+ (name === "write_file" ||
1102
+ name === "edit_file" ||
1103
+ name === "edit_file_hashed") &&
1104
+ toolResult &&
1105
+ typeof toolResult === "object" &&
1106
+ !toolResult.error &&
1107
+ args?.path &&
1108
+ context.mcpClient &&
1109
+ context.externalToolExecutors
1110
+ ) {
1111
+ try {
1112
+ const { collectIdeDiagnostics, formatIdeDiagnostics } =
1113
+ await import("../lib/ide-context.js");
1114
+ const diags = await collectIdeDiagnostics(
1115
+ {
1116
+ mcpClient: context.mcpClient,
1117
+ externalToolExecutors: context.externalToolExecutors,
1118
+ },
1119
+ path.resolve(cwd, args.path),
1120
+ );
1121
+ const feedback = formatIdeDiagnostics(diags);
1122
+ if (feedback) toolResult.ideDiagnostics = feedback;
1123
+ } catch (_err) {
1124
+ // diagnostics feedback is optional polish — never fail the tool
1125
+ }
1126
+ }
1127
+
933
1128
  return toolResult;
934
1129
  }
935
1130
 
@@ -546,6 +546,25 @@ export async function runAgentHeadless(options = {}, deps = {}) {
546
546
  }
547
547
  }
548
548
 
549
+ // IDE live context (Claude-Code parity): when an IDE bridge is connected,
550
+ // share the editor's selection/active file/open tabs with this turn. Appended
551
+ // to the in-flight user message only — AFTER persistence — so a resumed
552
+ // session replays the prompt, not a stale editor snapshot. Best-effort with
553
+ // a short timeout; CC_IDE_CONTEXT=0 disables.
554
+ try {
555
+ const { buildIdePromptContext, appendTextToContent } =
556
+ await import("../lib/ide-context.js");
557
+ const ideCtx = await (deps.buildIdePromptContext || buildIdePromptContext)(
558
+ mcp,
559
+ );
560
+ if (ideCtx) {
561
+ const last = messages[messages.length - 1];
562
+ last.content = appendTextToContent(last.content, ideCtx);
563
+ }
564
+ } catch {
565
+ // IDE context is optional polish — never fail the run over it.
566
+ }
567
+
549
568
  // --permission-prompt-tool: route every CONFIRM-tier approval to an MCP tool
550
569
  // (loaded via --mcp-config) instead of headless fail-closed. Overrides the
551
570
  // permission-mode confirmer on the gate for this session.
@@ -245,9 +245,8 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
245
245
  // settings.json SessionStart hooks → inject session context once (observe-only).
246
246
  if (settingsHooks) {
247
247
  try {
248
- const { runSessionStartHooks } = await import(
249
- "../lib/settings-hook-events.cjs"
250
- );
248
+ const { runSessionStartHooks } =
249
+ await import("../lib/settings-hook-events.cjs");
251
250
  const ctx = runSessionStartHooks(settingsHooks, {
252
251
  source: "startup",
253
252
  cwd,
@@ -428,9 +427,8 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
428
427
  // settings.json UserPromptSubmit hooks. block → skip this turn; context → inject.
429
428
  if (settingsHooks) {
430
429
  try {
431
- const { runUserPromptSubmitHooks } = await import(
432
- "../lib/settings-hook-events.cjs"
433
- );
430
+ const { runUserPromptSubmitHooks } =
431
+ await import("../lib/settings-hook-events.cjs");
434
432
  const ups = runUserPromptSubmitHooks(settingsHooks, {
435
433
  prompt: userContent,
436
434
  cwd,
@@ -454,6 +452,19 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
454
452
  }
455
453
  }
456
454
 
455
+ // IDE live context (Claude-Code parity): re-shared on every turn while an
456
+ // IDE bridge is connected — the user's selection moves between prompts.
457
+ // Best-effort; CC_IDE_CONTEXT=0 disables.
458
+ try {
459
+ const { buildIdePromptContext } = await import("../lib/ide-context.js");
460
+ const ideCtx = await (
461
+ deps.buildIdePromptContext || buildIdePromptContext
462
+ )(mcp);
463
+ if (ideCtx) userContent += `\n\n${ideCtx}`;
464
+ } catch {
465
+ // optional polish — never fail the turn over it
466
+ }
467
+
457
468
  messages.push({ role: "user", content: userContent });
458
469
  turns += 1;
459
470
 
@@ -508,9 +519,8 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
508
519
  // settings.json SessionEnd hooks (observe-only) when stdin closes.
509
520
  if (settingsHooks) {
510
521
  try {
511
- const { runObserveHooks } = await import(
512
- "../lib/settings-hook-events.cjs"
513
- );
522
+ const { runObserveHooks } =
523
+ await import("../lib/settings-hook-events.cjs");
514
524
  runObserveHooks(
515
525
  settingsHooks,
516
526
  "SessionEnd",