chainlesschain 0.162.35 → 0.162.37

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 (193) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-CJn02U42.js → AIOps-_oxz4VHy.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-ewURAAoy.js → ActionButton-uaeqFuDj.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BiSadESb.js → Analytics-BPVV0OUf.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-BR0WOEug.js → AppLayout-ppCYKm3I.js} +4 -4
  6. package/src/assets/web-panel/assets/{Audit-CrqcYx0e.js → Audit-DFAY6umk.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-DtbSBn4e.js → Backup-pAPBFDyP.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-BjSc9j0o.js → BaseInput-BbBl0uT2.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-ixzrlCJE.js → Chat-Ct22JUnT.js} +6 -6
  10. package/src/assets/web-panel/assets/{ChatBubbleRenderer-B78nEq05.js → ChatBubbleRenderer-DPlsLl22.js} +1 -1
  11. package/src/assets/web-panel/assets/{Checkbox-UGYeSsgr.js → Checkbox-DEkCollc.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-B97OOAg4.js → Codegen-Tor-de39.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-D9aGkaZ6.js → Col-ojNrLQU7.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-Dc2v2RGS.js → Community-CLOGhqMF.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-B_FYlUQR.js → Compact-CYKNlSZ4.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-C4FiTHyC.js → Compliance-C5E6ABuA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-CQ8j3LIg.js → Cowork-CHeEsZ3W.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-Dzjs9Z9Z.js → Cron-B4e1n2e7.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-BXI24uzI.js → Crosschain-DbNV8P9R.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-C-I4_d07.js → DID-C5_Tk3nC.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-BzzGh5mo.js → Dashboard-BhdV_c4N.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-Bh8H70De.js → Dropdown-CEi5AMtM.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-DI_qybJP.js → EmailListRenderer-DOhPiYng.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DkKTsfc4.js → FamilyGuardDashboard-fu4NRP3X.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-DS7CmvVG.js → Federation-B7BtIWKL.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-CI97WsB5.js → FormItemContext-BmPWZVLP.js} +1 -1
  27. package/src/assets/web-panel/assets/GenericCardRenderer-hsOPNJq8.js +1 -0
  28. package/src/assets/web-panel/assets/{Git-CEh0gR2W.js → Git-Bi_EFBUH.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-kIr3tls2.js → Governance-emf2ubDK.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-CC1GzyC1.js → Inference-B7KjKzkI.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-BNgTiWOB.js → KnowledgeGraph-uAaBK0F3.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-B2P10gB1.js → Logs-utK7hNpj.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-HPfBvbFZ.js → Marketplace-CzQe6n3z.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-ByYotSKb.js → McpTools-CuAaJr51.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-BGIAzFVS.js → Memory-CRuZZJ75.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-CroNYTAH.js → MobileBridge-Cp06wunh.js} +2 -2
  37. package/src/assets/web-panel/assets/MobileProjects-DJEdUwhr.js +1 -0
  38. package/src/assets/web-panel/assets/{Mtc-BqhyIwo9.js → Mtc-8YY4dR7g.js} +2 -2
  39. package/src/assets/web-panel/assets/{MtcAudit-BpEKOvx9.js → MtcAudit-BmPJYHar.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-DST1d_Qo.js → Multisig-d-ydyVdq.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-DlMsZcK_.js → NLProgramming-DA_ikw_n.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-C734UJvD.js → Notes-DIyF-fRe.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-C0-pPxvk.js → NotificationSettings-CzPZXEtK.js} +1 -1
  44. package/src/assets/web-panel/assets/OrderTableRenderer-BiLtg-LY.js +1 -0
  45. package/src/assets/web-panel/assets/{Organization-C5iHC_yW.js → Organization-DdDZ_Ap6.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-CovuHHVR.js → Overflow-BnMBkttv.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-Dx9QL-Gy.js → P2P-Es1050f-.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-IP1dEt6-.js → PdhVaultBrowser-CKkRmyn9.js} +4 -4
  49. package/src/assets/web-panel/assets/{Permissions-BrR1XZG5.js → Permissions-zU9n9cAD.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-BgqxVE5m.js → PersonalDataHub-BZi5Xwas.js} +2 -2
  51. package/src/assets/web-panel/assets/{Pipeline-DzMk5HAz.js → Pipeline-CRfeGiFc.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-CDoLa6tk.js → Privacy-CQA_IgLA.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-Dy5gc6ve.js → ProjectInit-C9hmEvoT.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-DXy-k4hG.js → ProjectSettings-yXA72ws4.js} +2 -2
  55. package/src/assets/web-panel/assets/Projects-BpWS-qam.js +1 -0
  56. package/src/assets/web-panel/assets/Providers-Cxe55dRD.js +1 -0
  57. package/src/assets/web-panel/assets/{QuickAsk-B8KEHCnd.js → QuickAsk-Do0aUTQr.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-DNVHGYYZ.js → Recommend--ysZHjyA.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-CaDhWP03.js → Reputation-BOBU8JrH.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-CrGLI02x.js → Row-C6X7bRKE.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-BX7P8I6i.js → RssFeed-D8AwqlkQ.js} +3 -3
  62. package/src/assets/web-panel/assets/Search-Bi3rCZD4.js +1 -0
  63. package/src/assets/web-panel/assets/{Security-B6J7IFc1.js → Security-DxUDVrtY.js} +4 -4
  64. package/src/assets/web-panel/assets/{Services-vvdcO3mM.js → Services-BXXN7yC1.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-BoAoPTzZ.js → Skeleton-B3BR34tZ.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-CyIQV5b3.js → Skills-BjYu8OQ1.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-BAQVgdZV.js → Sla-DDkCtD8w.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-Bxcn1Jkj.js → SpeechSettings-CGhYzP7V.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-Dpaj3hDM.js → SyncSettings-CYNKVAHA.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-Bwqo89En.js → Tasks-DavmlJpd.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-Bowcqifn.js → Templates-CQuYFf2C.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-DOkf85uG.js → Tenant-DdzZh8vE.js} +1 -1
  73. package/src/assets/web-panel/assets/Terminal-D75WeG9d.js +3 -0
  74. package/src/assets/web-panel/assets/{TimelineRenderer-B9A3zDXA.js → TimelineRenderer-DKOARnc_.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-jtVVqKFr.js → Tokens-D7QRNG8y.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-26Iw-iIl.js → Trigger-BCsqLZl4.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-DqY5ORrH.js → Trust-BarGUa6p.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-BFsbr3y7.js → UkeySign-pHrg5a8E.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-BtDbj3oa.js → VideoEditing-Dug3m1py.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-BAwmwHbk.js → Wallet-BfK3Z_Ez.js} +4 -4
  81. package/src/assets/web-panel/assets/{WebAuthn-DINJTsfq.js → WebAuthn-CYRdl9td.js} +5 -5
  82. package/src/assets/web-panel/assets/{WorkflowEditor-BEorm8SK.js → WorkflowEditor-DTW5AcqM.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-CE39-Dxg.js → chat-CCXz4j38.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-C_cLZ93a.js → colors-BJBOhAqa.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-BSioWA2c.js → compact-item-E9M6BQcM.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-CGTk4mhN.js → createContext-Cg9CAws4.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-BrsbTJUv.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-Dl1fRwS_.js → hasIn-DhVtqv5L.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-pngH1and.js → index--7o5YdL6.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-BmbVyhk1.js → index-4N5lNXGP.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-BnEPB1Mz.js → index-6-04M2Nx.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-Cxw3p73X.js → index-B111fZ21.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-CST381Qf.js → index-B4NBF4Sa.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-ChwpS1f0.js → index-B8bjEHrQ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index--SWvw6yW.js → index-BAB0nGP7.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CAwVwBOL.js → index-BFZPRd0T.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-hv4jUdG3.js → index-B_SMPD4L.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Qj2x55mz.js → index-BxSzyly9.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BWpfxzVm.js → index-ByazO4Q9.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Di6nvW1N.js → index-C-2dUIli.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BhqOTuMW.js → index-CFarAlXj.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CA6K7lZB.js → index-CFp-wdrQ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DTKEXyaW.js → index-CJ8nNT8h.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-iiZfONfx.js → index-CSiyjCYi.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DTpCUi0m.js → index-CUp_c8Le.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Bvi14vJ7.js → index-CVR_s-pT.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DKEipmR8.js → index-Ca8BYV1g.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DJyeeygd.js → index-CeRlLp3F.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-C9tq8Da8.js → index-ChsSljaN.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B2QiUEgK.js → index-CkTeBHI9.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-OCxo0X6J.js → index-Cm1m7BJh.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DrWERr8C.js → index-ComyTKz-.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B016Fsqr.js → index-CznfPnOx.js} +3 -3
  114. package/src/assets/web-panel/assets/{index-CisXVbSt.js → index-D5yC2Ps8.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C-VVk1Jg.js → index-D7DXdf7x.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DDQx2YFc.js → index-DDcJO27F.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Ds2RzRG0.js → index-DSQazU6J.js} +1 -1
  118. package/src/assets/web-panel/assets/index-DSTQDO-Y.js +1 -0
  119. package/src/assets/web-panel/assets/{index-C4JXchTG.js → index-DaFe1aqY.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BAhinBPR.js → index-DdhnGez0.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-9_mmaR42.js → index-Di5LBXcE.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D9D4q-qI.js → index-Dwvewrul.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CbXnyoSO.js → index-MdXEhfdJ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-II3JhQu2.js → index-_PNqQ5mE.js} +1 -1
  125. package/src/assets/web-panel/assets/index-c2U6LV3Q.js +1 -0
  126. package/src/assets/web-panel/assets/{index-C2ly7sCw.js → index-kz1oXl1a.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Ceo9P9tQ.js → index-wkt-o5q5.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-GOhLA2-f.js → initDefaultProps-iyBaePF-.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-jqxFzHTx.js → motion-RWtj4rgu.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-CSLsp6TA.js → move-CqPRVzpH.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-Cnlrb25c.js → omit-DsvJze25.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-CLqlxWWD.js → pickAttrs-B4tfZBhc.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-BAWIWtul.js → placementArrow-KvHUwXMA.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-CSR1DayS.js → responsiveObserve-DGdJ-b7W.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-CNhoPJOp.js → slide-Cd6ebRmw.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-BZiYHRHW.js → statusUtils-Bg9GcIAn.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-BMoY-Fm5.js → styleChecker-MQjKsG84.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-DhtNdlaS.js → useFlexGapSupport-C241WujP.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-DNPtDOZ4.js → useFs-CMpy7RS4.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-DTdjNvAI.js → usePersonalDataHub-BLHtapKb.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-C9zW9IJ2.js → vnode-DmcTV67c.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-D-6RYJJr.js → zoom-DHL8_0Y8.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/agent.js +161 -6
  145. package/src/commands/agents.js +8 -2
  146. package/src/commands/cli-anything.js +14 -6
  147. package/src/commands/command.js +7 -2
  148. package/src/commands/hook.js +136 -28
  149. package/src/commands/ide.js +168 -0
  150. package/src/commands/loop.js +450 -0
  151. package/src/commands/mcp.js +92 -0
  152. package/src/commands/output-style.js +127 -0
  153. package/src/commands/permissions.js +211 -0
  154. package/src/commands/statusline.js +93 -0
  155. package/src/index.js +10 -2
  156. package/src/lib/agent-core.js +7 -0
  157. package/src/lib/agents.js +5 -0
  158. package/src/lib/hook-manager.js +1 -0
  159. package/src/lib/hook-runner.cjs +183 -0
  160. package/src/lib/ide-bridge.js +310 -0
  161. package/src/lib/image-input.js +156 -0
  162. package/src/lib/loop.js +198 -0
  163. package/src/lib/mcp-oauth.js +415 -0
  164. package/src/lib/output-styles.js +179 -0
  165. package/src/lib/permission-rules.cjs +325 -0
  166. package/src/lib/provider-options.js +11 -7
  167. package/src/lib/settings-hook-events.cjs +102 -0
  168. package/src/lib/settings-hooks.cjs +163 -0
  169. package/src/lib/settings-loader.cjs +244 -0
  170. package/src/lib/slash-commands.js +4 -0
  171. package/src/lib/status-line.cjs +204 -0
  172. package/src/lib/sub-agent-profiles.js +3 -0
  173. package/src/lib/web-search.js +487 -0
  174. package/src/repl/agent-repl.js +450 -35
  175. package/src/repl/slash-macro.js +45 -0
  176. package/src/runtime/agent-core.js +799 -21
  177. package/src/runtime/coding-agent-contract-shared.cjs +94 -4
  178. package/src/runtime/coding-agent-policy.cjs +24 -0
  179. package/src/runtime/headless-runner.js +162 -6
  180. package/src/runtime/headless-stream.js +133 -7
  181. package/src/runtime/mcp-config.js +161 -15
  182. package/src/runtime/policies/agent-policy.js +4 -0
  183. package/src/runtime/system-prompt.js +6 -1
  184. package/src/assets/web-panel/assets/GenericCardRenderer-Da27EdR4.js +0 -1
  185. package/src/assets/web-panel/assets/MobileProjects-CH-qnGEV.js +0 -1
  186. package/src/assets/web-panel/assets/OrderTableRenderer-C7zT9eFc.js +0 -1
  187. package/src/assets/web-panel/assets/Projects-DvsaEbZR.js +0 -1
  188. package/src/assets/web-panel/assets/Providers-Demck9PO.js +0 -1
  189. package/src/assets/web-panel/assets/Search-laS6rz8M.js +0 -1
  190. package/src/assets/web-panel/assets/Terminal-v4MM9dCj.js +0 -3
  191. package/src/assets/web-panel/assets/devWarning-PObcVnJR.js +0 -1
  192. package/src/assets/web-panel/assets/index-BNwIzLyX.js +0 -1
  193. package/src/assets/web-panel/assets/index-Dh6FxR9B.js +0 -1
@@ -0,0 +1,450 @@
1
+ /**
2
+ * cc loop — repeatedly run a command or agent prompt on a fixed interval
3
+ * (Claude-Code `/loop` parity, MVP). Lightweight by design: unlike `cc ccron`
4
+ * (in-memory profile governance, runs nothing) or `cc automation` (DB-backed
5
+ * flow/trigger engine), this just re-runs ONE thing on a timer until a stop
6
+ * condition fires or you Ctrl-C.
7
+ *
8
+ * cc loop "check if CI passed, summarize failures" # wraps `cc agent -p`
9
+ * cc loop --every 30s -- npm test # external command
10
+ * cc loop --every 1m --max-iterations 10 -- npm test
11
+ * cc loop --until-exit-zero --every 30s -- npm test # stop when it passes
12
+ * cc loop --until "DONE" --every 1m "poll the deploy"
13
+ * cc loop "review the diff" --think --provider openai # extra flags → cc agent
14
+ * cc loop --dynamic "watch the deploy; stop when it's live" # agent self-paces
15
+ * cc loop --save ci-watch --every 1m -- npm test # persist a resumable loop
16
+ * cc loop --resume ci-watch --max-iterations 20 # continue it (cumulative)
17
+ *
18
+ * Two modes, disambiguated by the literal `--` separator:
19
+ * - no `--` → the single operand is a PROMPT, run via `cc agent -p <prompt>`
20
+ * - with `--` → the operands after it are an EXTERNAL command (shell-resolved)
21
+ *
22
+ * The loop driver lives in src/lib/loop.js (pure, clock-injected). This layer
23
+ * only builds the concrete iteration (spawn + tee output) and wires SIGINT.
24
+ */
25
+
26
+ import { spawn } from "node:child_process";
27
+ import { fileURLToPath } from "node:url";
28
+ import chalk from "chalk";
29
+ import { logger } from "../lib/logger.js";
30
+ import {
31
+ runLoop,
32
+ parseDuration,
33
+ formatDuration,
34
+ makeSleep,
35
+ parseLoopDirectives,
36
+ summarizeLoopEvents,
37
+ } from "../lib/loop.js";
38
+ import {
39
+ startSession,
40
+ appendEvent,
41
+ readEvents,
42
+ sessionExists,
43
+ } from "../harness/jsonl-session-store.js";
44
+
45
+ /**
46
+ * Appended to the prompt under `--dynamic` so the model can self-pace: it ends
47
+ * its reply with at most one control directive the loop parses (parseLoopDirectives).
48
+ */
49
+ const DYNAMIC_PROMPT_SUFFIX = `
50
+
51
+ ---
52
+ You are running inside a \`cc loop --dynamic\` controller. After deciding what happens next, end your reply with EXACTLY ONE control directive alone on the final line:
53
+ [[loop:next <interval>]] run me again after <interval> (e.g. 30s, 5m, 1h)
54
+ [[loop:stop]] the task is complete — stop looping
55
+ Emit neither and the loop falls back to its default --every interval.`;
56
+
57
+ /** Absolute path to this CLI's bin entry, for self-spawning the prompt mode. */
58
+ const BIN_PATH = fileURLToPath(
59
+ new URL("../../bin/chainlesschain.js", import.meta.url),
60
+ );
61
+
62
+ /**
63
+ * Run one child process to completion. Tees stdout/stderr to the parent (so
64
+ * the user sees live output) while capturing it, so `--until <regex>` can match
65
+ * against what was printed. Resolves with { exitCode, output }.
66
+ */
67
+ function spawnIteration(cmd, args, { shell, onChild, capture }) {
68
+ return new Promise((resolve) => {
69
+ const child = spawn(cmd, args, {
70
+ shell,
71
+ stdio: capture ? ["inherit", "pipe", "pipe"] : "inherit",
72
+ env: process.env,
73
+ });
74
+ if (onChild) onChild(child);
75
+
76
+ let output = "";
77
+ if (capture) {
78
+ child.stdout?.on("data", (d) => {
79
+ output += d;
80
+ process.stdout.write(d);
81
+ });
82
+ child.stderr?.on("data", (d) => {
83
+ output += d;
84
+ process.stderr.write(d);
85
+ });
86
+ }
87
+
88
+ // `close` (not `exit`) so piped stdio is fully drained before we resolve.
89
+ child.on("close", (code, signal) => {
90
+ resolve({ exitCode: code == null ? null : code, output, signal });
91
+ });
92
+ child.on("error", (err) => {
93
+ resolve({
94
+ exitCode: 127,
95
+ output: String(err.message || err),
96
+ signal: null,
97
+ });
98
+ });
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Build the concrete child invocation from the resolved operands + mode.
104
+ * Shared by fresh runs and `--resume` (which reconstructs it from saved config).
105
+ * exec mode → shell-run the joined operands (resolves Windows .cmd shims).
106
+ * prompt mode → `cc agent -p <prompt>` with operands up to the first flag as
107
+ * the prompt and the rest forwarded verbatim to `cc agent`.
108
+ * Returns { cmd, args, shell, label }.
109
+ */
110
+ function buildInvocation({ operands, execMode, dynamic }) {
111
+ if (execMode) {
112
+ const cmd = operands.join(" ");
113
+ return { cmd, args: [], shell: true, label: cmd };
114
+ }
115
+ const flagIdx = operands.findIndex((p) => p.startsWith("-"));
116
+ const promptParts = flagIdx === -1 ? operands : operands.slice(0, flagIdx);
117
+ const agentFlags = flagIdx === -1 ? [] : operands.slice(flagIdx);
118
+ let prompt = promptParts.join(" ");
119
+ if (dynamic) prompt += DYNAMIC_PROMPT_SUFFIX;
120
+ const label =
121
+ `cc agent -p ${chalk.italic(promptParts.join(" "))}` +
122
+ (agentFlags.length ? ` ${chalk.gray(agentFlags.join(" "))}` : "");
123
+ return {
124
+ cmd: process.execPath,
125
+ args: [BIN_PATH, "agent", "-p", prompt, ...agentFlags],
126
+ shell: false,
127
+ label,
128
+ };
129
+ }
130
+
131
+ export function registerLoopCommand(program) {
132
+ program
133
+ .command("loop [parts...]")
134
+ .description(
135
+ "Repeatedly run an agent prompt or `-- <command>` on a fixed interval",
136
+ )
137
+ .option(
138
+ "--every <dur>",
139
+ "Interval between iterations (e.g. 30s, 5m, 1.5h; bare number = seconds)",
140
+ "5m",
141
+ )
142
+ .option("-n, --max-iterations <n>", "Stop after N iterations")
143
+ .option(
144
+ "--until-exit-zero",
145
+ "Stop once an iteration exits with code 0 (e.g. tests pass)",
146
+ )
147
+ .option(
148
+ "--until <regex>",
149
+ "Stop once an iteration's output matches this JS regex",
150
+ )
151
+ .option(
152
+ "--dynamic",
153
+ "Let each iteration self-pace via [[loop:next <dur>]] / [[loop:stop]] directives (prompt mode augments the prompt)",
154
+ )
155
+ .option(
156
+ "--save [id]",
157
+ "Persist this loop to a resumable session (auto-generates an id if omitted)",
158
+ )
159
+ .option("--resume <id>", "Continue a previously --save'd loop session")
160
+ .option("--json", "Print a JSON summary when the loop ends")
161
+ .allowUnknownOption(true) // pass-through flags for the wrapped agent/command
162
+ .action(async (parts, options, command) => {
163
+ try {
164
+ // Was an option explicitly given on the command line (vs a default)?
165
+ // Used so --resume inherits the saved config but still honors flags the
166
+ // user re-passes (e.g. extend --max-iterations).
167
+ const fromCli = (name) =>
168
+ command?.getOptionValueSource?.(name) === "cli";
169
+
170
+ // --- resolve session: --resume loads saved config; --save persists ---
171
+ let sessionId = null;
172
+ let persist = false;
173
+ let startIndex = 0;
174
+ let savedConfig = null;
175
+ if (options.resume) {
176
+ if (!sessionExists(options.resume)) {
177
+ logger.error(chalk.red(`no such loop session: ${options.resume}`));
178
+ logger.log(chalk.gray(" list sessions with: cc session list"));
179
+ process.exitCode = 1;
180
+ return;
181
+ }
182
+ const s = summarizeLoopEvents(readEvents(options.resume));
183
+ if (!s.config) {
184
+ logger.error(
185
+ chalk.red(`session ${options.resume} has no loop to resume`),
186
+ );
187
+ process.exitCode = 1;
188
+ return;
189
+ }
190
+ savedConfig = s.config;
191
+ startIndex = s.completedIterations;
192
+ sessionId = options.resume;
193
+ persist = true;
194
+ }
195
+
196
+ // --- resolve mode / operands (saved config wins on resume) ---
197
+ let execMode;
198
+ let operands;
199
+ let dynamic;
200
+ if (savedConfig) {
201
+ execMode = Boolean(savedConfig.execMode);
202
+ operands = savedConfig.operands || [];
203
+ dynamic = fromCli("dynamic")
204
+ ? Boolean(options.dynamic)
205
+ : Boolean(savedConfig.dynamic);
206
+ } else {
207
+ // `--` is the unambiguous signal for external-command mode. Commander
208
+ // folds the post-`--` operands into `parts`, so we sniff the parsed
209
+ // argv for the literal separator. `rawArgs` is what Commander actually
210
+ // parsed (process.argv in prod, the explicit array under test).
211
+ const argv = command?.parent?.rawArgs || process.argv;
212
+ execMode = argv.includes("--");
213
+ operands = (parts || []).filter((p) => p !== "--");
214
+ dynamic = Boolean(options.dynamic);
215
+ }
216
+
217
+ if (operands.length === 0) {
218
+ logger.error(
219
+ chalk.red(
220
+ 'nothing to loop: pass a prompt ("...") or a command after `--`',
221
+ ),
222
+ );
223
+ logger.log(chalk.gray(' cc loop --every 5m "check CI"'));
224
+ logger.log(chalk.gray(" cc loop --every 30s -- npm test"));
225
+ process.exitCode = 1;
226
+ return;
227
+ }
228
+
229
+ // --- resolve interval (CLI overrides saved on resume) ---
230
+ const everyRaw =
231
+ savedConfig && !fromCli("every") ? savedConfig.every : options.every;
232
+ let intervalMs;
233
+ try {
234
+ intervalMs = parseDuration(everyRaw);
235
+ } catch (e) {
236
+ logger.error(chalk.red(e.message));
237
+ process.exitCode = 1;
238
+ return;
239
+ }
240
+
241
+ // --- resolve stop conditions (CLI overrides saved on resume) ---
242
+ const maxRaw =
243
+ savedConfig && !fromCli("maxIterations")
244
+ ? savedConfig.maxIterations
245
+ : options.maxIterations;
246
+ let maxIterations;
247
+ if (maxRaw != null) {
248
+ maxIterations = Number(maxRaw);
249
+ if (!Number.isInteger(maxIterations) || maxIterations < 1) {
250
+ logger.error(
251
+ chalk.red("--max-iterations must be a positive integer"),
252
+ );
253
+ process.exitCode = 1;
254
+ return;
255
+ }
256
+ }
257
+ const untilRaw =
258
+ savedConfig && !fromCli("until") ? savedConfig.until : options.until;
259
+ let untilRegex = null;
260
+ if (untilRaw) {
261
+ try {
262
+ untilRegex = new RegExp(untilRaw);
263
+ } catch (e) {
264
+ logger.error(chalk.red(`invalid --until regex: ${e.message}`));
265
+ process.exitCode = 1;
266
+ return;
267
+ }
268
+ }
269
+ const untilExitZero =
270
+ savedConfig && !fromCli("untilExitZero")
271
+ ? Boolean(savedConfig.untilExitZero)
272
+ : Boolean(options.untilExitZero);
273
+
274
+ // --- build the child invocation (shared with resume) ---
275
+ const { cmd, args, shell, label } = buildInvocation({
276
+ operands,
277
+ execMode,
278
+ dynamic,
279
+ });
280
+
281
+ // --- --save creates a fresh session + writes the loop_config once ---
282
+ if (options.save != null && !options.resume) {
283
+ persist = true;
284
+ sessionId = startSession(
285
+ typeof options.save === "string" && options.save
286
+ ? options.save
287
+ : null,
288
+ { title: `loop: ${operands.join(" ")}`.slice(0, 80) },
289
+ );
290
+ appendEvent(sessionId, "loop_config", {
291
+ execMode,
292
+ operands,
293
+ dynamic,
294
+ every: everyRaw,
295
+ maxIterations: maxIterations ?? null,
296
+ untilExitZero,
297
+ until: untilRaw || null,
298
+ });
299
+ }
300
+
301
+ // --- SIGINT → graceful stop after the current iteration ---
302
+ const controller = new AbortController();
303
+ let activeChild = null;
304
+ let interrupted = false;
305
+ const onSigint = () => {
306
+ interrupted = true;
307
+ controller.abort();
308
+ if (activeChild && activeChild.exitCode == null) {
309
+ try {
310
+ activeChild.kill("SIGINT");
311
+ } catch {
312
+ /* already gone */
313
+ }
314
+ }
315
+ logger.log(chalk.yellow("\n⏹ stopping after current iteration…"));
316
+ };
317
+ process.on("SIGINT", onSigint);
318
+
319
+ // Capture output when we need to read it: regex matching or --dynamic
320
+ // directive parsing.
321
+ const capture = Boolean(untilRegex) || dynamic;
322
+ logger.log(
323
+ chalk.cyan(
324
+ `↻ loop: ${label} ${chalk.gray(
325
+ `(${dynamic ? "dynamic, fallback " : "every "}${formatDuration(
326
+ intervalMs,
327
+ )}${maxIterations ? `, max ${maxIterations}` : ""}${
328
+ startIndex ? `, resuming from ${startIndex}` : ""
329
+ }${persist ? `, session ${sessionId}` : ""})`,
330
+ )}`,
331
+ ),
332
+ );
333
+
334
+ const startedAt = Date.now();
335
+ let summary;
336
+ try {
337
+ summary = await runLoop({
338
+ intervalMs,
339
+ maxIterations,
340
+ untilExitZero,
341
+ untilRegex,
342
+ startIndex,
343
+ sleep: makeSleep(controller.signal),
344
+ shouldStop: () => controller.signal.aborted,
345
+ onIteration: (n, res) => {
346
+ const tag =
347
+ res.exitCode === 0
348
+ ? chalk.green(`exit 0`)
349
+ : chalk.red(`exit ${res.exitCode}`);
350
+ logger.log(chalk.gray(` ↳ iteration ${n} done (${tag})`));
351
+ // Persist a compact record per round (no output body — keeps the
352
+ // session small; resume only needs the count + config).
353
+ if (persist) {
354
+ appendEvent(sessionId, "loop_iteration", {
355
+ n,
356
+ exitCode: res.exitCode,
357
+ durationMs: res.durationMs ?? null,
358
+ done: Boolean(res.done),
359
+ nextDelayMs: res.nextDelayMs ?? null,
360
+ });
361
+ }
362
+ },
363
+ runIteration: async (n) => {
364
+ logger.log(chalk.gray(`\n▸ iteration ${n} — ${label}`));
365
+ const t0 = Date.now();
366
+ const res = await spawnIteration(cmd, args, {
367
+ shell,
368
+ capture,
369
+ onChild: (c) => {
370
+ activeChild = c;
371
+ },
372
+ });
373
+ res.durationMs = Date.now() - t0;
374
+ // --dynamic: read the iteration's [[loop:next]] / [[loop:stop]]
375
+ // directive and surface it to runLoop as done / nextDelayMs.
376
+ if (options.dynamic) {
377
+ const d = parseLoopDirectives(res.output);
378
+ res.done = d.done;
379
+ if (d.nextDelayMs != null) res.nextDelayMs = d.nextDelayMs;
380
+ if (d.done) {
381
+ logger.log(chalk.gray(` ↺ directive: stop`));
382
+ } else if (d.nextDelayMs != null) {
383
+ logger.log(
384
+ chalk.gray(
385
+ ` ↺ directive: next in ${formatDuration(d.nextDelayMs)}`,
386
+ ),
387
+ );
388
+ }
389
+ }
390
+ return res;
391
+ },
392
+ });
393
+ } finally {
394
+ process.removeListener("SIGINT", onSigint);
395
+ }
396
+
397
+ const elapsed = formatDuration(Date.now() - startedAt);
398
+ const lastExit =
399
+ summary.results.length > 0
400
+ ? summary.results[summary.results.length - 1].exitCode
401
+ : null;
402
+ const stoppedBy = interrupted ? "signal" : summary.stoppedBy;
403
+
404
+ if (persist) {
405
+ appendEvent(sessionId, "loop_end", {
406
+ stoppedBy,
407
+ iterations: summary.iterations,
408
+ });
409
+ }
410
+
411
+ if (options.json) {
412
+ logger.log(
413
+ JSON.stringify(
414
+ {
415
+ iterations: summary.iterations,
416
+ stoppedBy,
417
+ lastExitCode: lastExit,
418
+ elapsed,
419
+ ...(persist ? { sessionId } : {}),
420
+ },
421
+ null,
422
+ 2,
423
+ ),
424
+ );
425
+ } else {
426
+ logger.log(
427
+ chalk.cyan(
428
+ `\n✔ loop ended — ${summary.iterations} iteration(s), stopped by ${chalk.bold(
429
+ stoppedBy,
430
+ )} ${chalk.gray(`(${elapsed})`)}`,
431
+ ),
432
+ );
433
+ if (persist) {
434
+ logger.log(
435
+ chalk.gray(
436
+ ` session saved — resume with: cc loop --resume ${sessionId}`,
437
+ ),
438
+ );
439
+ }
440
+ }
441
+
442
+ // Exit code mirrors the last iteration when we stopped on a condition;
443
+ // an interrupt is a clean stop (0).
444
+ if (!interrupted && lastExit != null) process.exitCode = lastExit;
445
+ } catch (err) {
446
+ logger.error(chalk.red(`loop failed: ${err.message}`));
447
+ process.exitCode = 1;
448
+ }
449
+ });
450
+ }
@@ -65,6 +65,98 @@ export function registerMcpCommand(program) {
65
65
  .command("mcp")
66
66
  .description("MCP server management and tool execution");
67
67
 
68
+ // mcp login — OAuth 2.0 (Auth Code + PKCE) for a remote MCP server.
69
+ mcp
70
+ .command("login <url>")
71
+ .description("Authorize a remote MCP server via OAuth (opens a browser); stores the token")
72
+ .option("--scope <scope>", "OAuth scope(s) to request")
73
+ .option("--client-id <id>", "Use a pre-registered client_id instead of dynamic registration")
74
+ .option("--port <n>", "Localhost callback port", (v) => parseInt(v, 10), 53682)
75
+ .option("--no-open", "Print the authorize URL instead of opening a browser")
76
+ .action(async (url, options) => {
77
+ try {
78
+ const oauth = await import("../lib/mcp-oauth.js");
79
+ if (options.open === false) {
80
+ oauth._deps.openBrowser = () => false; // commander maps --no-open → open:false
81
+ }
82
+ logger.log(chalk.gray(`Authorizing ${url} …`));
83
+ const rec = await oauth.authorizeInteractive(url, {
84
+ scope: options.scope,
85
+ clientId: options.clientId,
86
+ port: options.port,
87
+ writeOut: (s) => process.stdout.write(s),
88
+ });
89
+ logger.log(
90
+ chalk.green(`✓ authorized ${rec.server}`) +
91
+ chalk.gray(
92
+ rec.expires_at
93
+ ? ` (expires ${new Date(rec.expires_at).toISOString()})`
94
+ : "",
95
+ ),
96
+ );
97
+ logger.log(
98
+ chalk.gray("The token is injected as a Bearer header on connect."),
99
+ );
100
+ } catch (err) {
101
+ logger.error(chalk.red(`mcp login failed: ${err.message}`));
102
+ process.exitCode = 1;
103
+ }
104
+ });
105
+
106
+ // mcp logout — forget a stored OAuth token.
107
+ mcp
108
+ .command("logout <url>")
109
+ .description("Delete the stored OAuth token for a remote MCP server")
110
+ .action(async (url) => {
111
+ try {
112
+ const { deleteStoredToken, serverKey } = await import("../lib/mcp-oauth.js");
113
+ const ok = deleteStoredToken(url);
114
+ logger.log(
115
+ ok
116
+ ? chalk.green(`✓ removed token for ${serverKey(url)}`)
117
+ : chalk.gray(`no stored token for ${serverKey(url)}`),
118
+ );
119
+ } catch (err) {
120
+ logger.error(chalk.red(`mcp logout failed: ${err.message}`));
121
+ process.exitCode = 1;
122
+ }
123
+ });
124
+
125
+ // mcp auth — list stored OAuth tokens.
126
+ mcp
127
+ .command("auth")
128
+ .description("List stored MCP OAuth tokens")
129
+ .option("--json", "Output as JSON")
130
+ .action(async (options) => {
131
+ try {
132
+ const { loadTokenStore, isTokenExpired } = await import("../lib/mcp-oauth.js");
133
+ const store = loadTokenStore();
134
+ const rows = Object.values(store).map((r) => ({
135
+ server: r.server,
136
+ expired: isTokenExpired(r),
137
+ expires_at: r.expires_at || null,
138
+ refresh: Boolean(r.refresh_token),
139
+ }));
140
+ if (options.json) {
141
+ console.log(JSON.stringify(rows, null, 2));
142
+ return;
143
+ }
144
+ if (rows.length === 0) {
145
+ logger.log(chalk.gray("No MCP OAuth tokens. Run: cc mcp login <url>"));
146
+ return;
147
+ }
148
+ for (const r of rows) {
149
+ const state = r.expired ? chalk.red("expired") : chalk.green("valid");
150
+ logger.log(
151
+ ` ${chalk.cyan(r.server)} [${state}]${r.refresh ? chalk.gray(" +refresh") : ""}`,
152
+ );
153
+ }
154
+ } catch (err) {
155
+ logger.error(chalk.red(`mcp auth failed: ${err.message}`));
156
+ process.exitCode = 1;
157
+ }
158
+ });
159
+
68
160
  // mcp servers — list configured servers
69
161
  mcp
70
162
  .command("servers")
@@ -0,0 +1,127 @@
1
+ /**
2
+ * cc output-style — list / show output-style personas (Claude-Code parity).
3
+ *
4
+ * cc output-style list [--json] list built-ins + .claude/output-styles/*.md
5
+ * cc output-style show <name> show a style's metadata + body
6
+ * cc output-style new <name> scaffold a new style file
7
+ *
8
+ * Apply one with `cc agent --output-style <name>` or the REPL `/output-style`,
9
+ * or set a default via `outputStyle` in .claude/settings.json.
10
+ */
11
+
12
+ import chalk from "chalk";
13
+ import { logger } from "../lib/logger.js";
14
+
15
+ export function registerOutputStyleCommand(program) {
16
+ const cmd = program
17
+ .command("output-style")
18
+ .alias("output-styles")
19
+ .description("List / show agent output-style personas");
20
+
21
+ cmd
22
+ .command("list", { isDefault: true })
23
+ .alias("ls")
24
+ .description("List built-in + .claude/output-styles/*.md personas")
25
+ .option("--json", "Output as JSON")
26
+ .action(async (options) => {
27
+ try {
28
+ const { discoverOutputStyles, settingsDefaultOutputStyle } =
29
+ await import("../lib/output-styles.js");
30
+ const all = discoverOutputStyles(process.cwd());
31
+ const dflt = settingsDefaultOutputStyle(process.cwd());
32
+ if (options.json) {
33
+ console.log(JSON.stringify({ default: dflt, styles: all }, null, 2));
34
+ return;
35
+ }
36
+ for (const s of all) {
37
+ const tag = s.builtin
38
+ ? chalk.gray("[builtin]")
39
+ : s.scope === "project"
40
+ ? chalk.cyan("[proj]")
41
+ : chalk.gray("[pers]");
42
+ const star = s.name === dflt ? chalk.green(" *") : "";
43
+ logger.log(
44
+ `${chalk.bold(s.name.padEnd(16))} ${tag}${star} ${chalk.gray(s.description || "")}`,
45
+ );
46
+ }
47
+ if (dflt) logger.log(chalk.dim(`\ndefault (settings.json): ${dflt}`));
48
+ } catch (err) {
49
+ logger.error(chalk.red(`output-style list failed: ${err.message}`));
50
+ process.exitCode = 1;
51
+ }
52
+ });
53
+
54
+ cmd
55
+ .command("show <name>")
56
+ .description("Show a style's metadata and body")
57
+ .option("--json", "Output as JSON")
58
+ .action(async (name, options) => {
59
+ try {
60
+ const { getOutputStyle } = await import("../lib/output-styles.js");
61
+ const s = getOutputStyle(name, process.cwd());
62
+ if (!s) {
63
+ logger.error(chalk.red(`no such output style: ${name}`));
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+ if (options.json) {
68
+ console.log(JSON.stringify(s, null, 2));
69
+ return;
70
+ }
71
+ logger.log(
72
+ chalk.bold(s.name) +
73
+ (s.builtin ? chalk.gray(" [builtin]") : chalk.gray(` [${s.scope}]`)),
74
+ );
75
+ if (s.description) logger.log(chalk.gray(` ${s.description}`));
76
+ if (s.file) logger.log(chalk.gray(` file: ${s.file}`));
77
+ logger.log(chalk.dim(" ───"));
78
+ logger.log(s.body || chalk.gray("(empty — no persona overlay)"));
79
+ } catch (err) {
80
+ logger.error(chalk.red(`output-style show failed: ${err.message}`));
81
+ process.exitCode = 1;
82
+ }
83
+ });
84
+
85
+ cmd
86
+ .command("new <name>")
87
+ .description("Scaffold a new style under .claude/output-styles/")
88
+ .option("--description <d>", "Frontmatter description")
89
+ .option("--personal", "Create under ~/.claude/output-styles instead of project")
90
+ .action(async (name, options) => {
91
+ try {
92
+ const fs = await import("node:fs");
93
+ const path = await import("node:path");
94
+ const { homedir } = await import("node:os");
95
+ const safe = String(name).replace(/[^\w.-]/g, "-");
96
+ const root = options.personal
97
+ ? path.join(homedir(), ".claude", "output-styles")
98
+ : path.join(process.cwd(), ".claude", "output-styles");
99
+ const file = path.join(root, `${safe}.md`);
100
+ if (fs.existsSync(file)) {
101
+ logger.error(chalk.red(`already exists: ${file}`));
102
+ process.exitCode = 1;
103
+ return;
104
+ }
105
+ fs.mkdirSync(path.dirname(file), { recursive: true });
106
+ const tpl = `---
107
+ name: ${safe}
108
+ description: ${options.description || name}
109
+ ---
110
+
111
+ ## Output style: ${safe}
112
+
113
+ Describe how the agent should behave in this style. This text is appended to
114
+ the system prompt (after the base coding instructions), so keep it focused on
115
+ *behaviour / tone*, not tool mechanics.
116
+ `;
117
+ fs.writeFileSync(file, tpl, "utf-8");
118
+ logger.log(chalk.green(`✓ created ${file}`));
119
+ logger.log(
120
+ chalk.gray(` use it: cc agent --output-style ${safe} -p "..."`),
121
+ );
122
+ } catch (err) {
123
+ logger.error(chalk.red(`output-style new failed: ${err.message}`));
124
+ process.exitCode = 1;
125
+ }
126
+ });
127
+ }