chainlesschain 0.162.31 → 0.162.33

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 (170) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/assets/{AIOps-BqWP6FKu.js → AIOps-3TazCYWE.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CXwMgOvX.js → ActionButton-DUPN0PST.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DAebZ4IY.js → Analytics-CemvhkzD.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-CYsqYoME.js → AppLayout-BL_tAU3M.js} +5 -5
  6. package/src/assets/web-panel/assets/{Audit-BbTtX1Nf.js → Audit-Dl9l-cxF.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-DgqY2Eb-.js → Backup-BKDDX75m.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-Cq2ZuSoO.js → BaseInput-CDYePvMI.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-D2kqpUyO.js → Chat-CGtR0sg3.js} +5 -5
  10. package/src/assets/web-panel/assets/ChatBubbleRenderer-DZjc9uKn.js +1 -0
  11. package/src/assets/web-panel/assets/{Checkbox-_9swHpyc.js → Checkbox-CwYIHOOo.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-Cr9YbCPl.js → Codegen-CIF5tbtd.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col--wdpCMxx.js → Col-z7d4kxeP.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DuFcVnLu.js → Community-DUlDrqF7.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-1yzYeT04.js → Compact-CJ1o8QQR.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-Dq3aU9Df.js → Compliance-D3i9d_uO.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-CrWcnIg8.js → Cowork-Wm7JTkfB.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-Bh6fKZ0h.js → Cron-B0QnHhZx.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-8ofPaWVW.js → Crosschain-3yPrnNgd.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-D3EiYm3w.js → DID-cfdkiDWF.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-BFjEdFne.js → Dashboard-DFkgM4gT.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-pYVPcP6O.js → Dropdown-YYWE81DL.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-zBPodwJ1.js → EmailListRenderer-BXfHK1Bn.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CyQTW6PW.js → FamilyGuardDashboard-DInUxJ2G.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-Ctaq3zYq.js → Federation-DNUYeFsv.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-CWYJCLq1.js → FormItemContext-Cr7eVEBB.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-B1g6t9R9.js → GenericCardRenderer-_gF4cmDa.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-DH-v8iwd.js → Git-BqldmUbO.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-jZxXvOs5.js → Governance-BF59ZiQ8.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-D07LRghn.js → Inference-Cy7y1eb9.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-DnGtRZhx.js → KnowledgeGraph-B3fVocTO.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-D2pM9C4W.js → Logs-BDirsUVk.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-UyIO7C7r.js → Marketplace-GhXpZgp2.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-Bf1gvZPf.js → McpTools-0VvfIhKx.js} +3 -3
  35. package/src/assets/web-panel/assets/{Memory-C1bWj4RN.js → Memory-CJLBgAUT.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-C_Ot1H_a.js → MobileBridge-BMedY9Yg.js} +2 -2
  37. package/src/assets/web-panel/assets/MobileProjects-mdohgRlL.js +1 -0
  38. package/src/assets/web-panel/assets/{Mtc-CnzFUz5J.js → Mtc-CgEuUg0g.js} +5 -5
  39. package/src/assets/web-panel/assets/{MtcAudit-CAAh99wz.js → MtcAudit-1pWNe_xi.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-D6IAg6HE.js → Multisig-DPIQ7oZL.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-BFMarxb0.js → NLProgramming-W__P_P4Z.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-BRp9ro3t.js → Notes-C_MCDhFk.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-C0Au3Cxb.js → NotificationSettings-CDFotapL.js} +1 -1
  44. package/src/assets/web-panel/assets/OrderTableRenderer-Dtht0cEs.js +1 -0
  45. package/src/assets/web-panel/assets/{Organization-DYoxLBRX.js → Organization-D6lMumhD.js} +2 -2
  46. package/src/assets/web-panel/assets/{Overflow-rO8JJWGJ.js → Overflow-BMOvUMW6.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-DJleeXIK.js → P2P-DsQTEw1t.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-DM5qghFp.js → PdhVaultBrowser-CncRtN1Z.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-D5v4Beya.js → Permissions-DDC-DkUl.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-c2ZTX0Pv.js → PersonalDataHub-DVKY_NnT.js} +4 -4
  51. package/src/assets/web-panel/assets/{Pipeline-Crrkyhpz.js → Pipeline-C7oDVTl-.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-DZVyrJKa.js → Privacy-DReGvTEJ.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-DKg7J0gz.js → ProjectInit-C-j2dzxJ.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-3ndmTvVH.js → ProjectSettings-DcUsvFnc.js} +2 -2
  55. package/src/assets/web-panel/assets/Projects-jSjWnmr6.js +1 -0
  56. package/src/assets/web-panel/assets/{Providers-BeqBVMhB.js → Providers-DIpohWG5.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-DKAAxzuA.js → QuickAsk-DdvLtpEU.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-Byu7IGei.js → Recommend-DPAi2zo3.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-BKhWAmCu.js → Reputation-DJD7qXSI.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-BFtn11O6.js → Row-XERdPDHk.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-D5a0PT0k.js → RssFeed-Cl_VlCLg.js} +3 -3
  62. package/src/assets/web-panel/assets/{Search-DAkuaZNe.js → Search-C-poG9P5.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-C79Ml2Ms.js → Security-DjjCrw8v.js} +2 -2
  64. package/src/assets/web-panel/assets/{Services-BBk_jH6-.js → Services-BuWeB4YJ.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-Cy0VvL0M.js → Skeleton-VZXOKwC_.js} +1 -1
  66. package/src/assets/web-panel/assets/Skills-B76ONTfP.js +1 -0
  67. package/src/assets/web-panel/assets/{Sla-CbX1f8xN.js → Sla-DIj1KREq.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-BIkoUjws.js → SpeechSettings-BrAp3Yk3.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-DG6Swk7G.js → SyncSettings--mJcpccF.js} +2 -2
  70. package/src/assets/web-panel/assets/Tasks-DM8cMr83.js +1 -0
  71. package/src/assets/web-panel/assets/{Templates-AaJPeCIz.js → Templates-kOBK6m1Z.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-jVFRofww.js → Tenant-BjSzYPzn.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-DHBMzfK6.js → Terminal-DwpY-Ay7.js} +2 -2
  74. package/src/assets/web-panel/assets/{TimelineRenderer-9RFfOHSI.js → TimelineRenderer-aoI0DazM.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-ZTfwuABF.js → Tokens-YwE0LqSZ.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-Xo7uZNQs.js → Trigger-CwSKzvlX.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-C0cTPYvn.js → Trust-B__Jqdzn.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-DmMKio71.js → UkeySign-mty0jwmx.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-DP7B-oGT.js → VideoEditing-Ddsx_OQ6.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-B1kZDARo.js → Wallet-D4Q8yXZm.js} +4 -4
  81. package/src/assets/web-panel/assets/{WebAuthn-Bo5kBx27.js → WebAuthn-CLUaKUr5.js} +5 -5
  82. package/src/assets/web-panel/assets/{WorkflowEditor-DGI9SNHH.js → WorkflowEditor-Di5pOaeC.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-y97W1CIG.js → chat-CELatHkT.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-DtTNo0sH.js → colors-CawDLjXV.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-D0q0exuS.js → compact-item-DeMp-K0j.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-D7pLFs2I.js → createContext-zY9kXivd.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-zLjV7g6r.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-CXjG5B2j.js → hasIn-VEBMW8E4.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-TrBGgrwG.js → index-8kqE_cVD.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-D_4WcI1V.js → index-B8AZpx7d.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-YWOEx3rP.js → index-BFc0vBN9.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-BqVjUN8b.js → index-BVkrfyuk.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-BnLrbXDA.js → index-BfGGKoo8.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CKrbutAQ.js → index-BjctklSd.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CFsPe2N7.js → index-BqJ2r12F.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CSdhC7Qo.js → index-C0GhuYLk.js} +1 -1
  97. package/src/assets/web-panel/assets/index-CDtUWCtX.js +1 -0
  98. package/src/assets/web-panel/assets/{index-BO644Q4S.js → index-CHqvj9uz.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-gFLQe31v.js → index-CHxHLv2b.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BgyrM0UN.js → index-CWbbB1MI.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-1dwtkcJv.js → index-CbJZzK9B.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B3y_4OdG.js → index-CfZV3FXN.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-Dr45Nm9V.js → index-Cr7lnIeI.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CkGFqlYX.js → index-CtLZammH.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-6np5ESBM.js → index-CtoauqWt.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BU944DeT.js → index-CyeYs7SG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-POaFzYGS.js → index-DALuVdhu.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DjCawXk1.js → index-DClGYjBM.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-8jxbZupG.js → index-DPHe9NYG.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-_3wPBMKt.js → index-DSiL_W2n.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Cbqu804A.js → index-DXNe_zIP.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-EaIfumgW.js → index-DhsfyHcg.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BzCPx1cq.js → index-Dna2psGz.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-kvV0f4tV.js → index-GRNVdvoA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BdhEYW2a.js → index-JseP3-5X.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-aarO4HT9.js → index-KcOEkUCM.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BgmvrPJH.js → index-S9JZDSaa.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DY6KLlgG.js → index-SrQIPYq8.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Ct6xtKkc.js → index-TfXODan7.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B_hjkMtX.js → index-V3K9gvKR.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-4mWZhCzz.js → index-VJnHvkv2.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BJUf19Wd.js → index-XFyv3Sg_.js} +3 -3
  123. package/src/assets/web-panel/assets/{index-B4dPdrvC.js → index-b3ZuAreb.js} +1 -1
  124. package/src/assets/web-panel/assets/index-d_RPqH7u.js +1 -0
  125. package/src/assets/web-panel/assets/{index-BPXhU-jp.js → index-fBNVDEf2.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-bVJvqDAz.js → index-u_1aiNTA.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-qoB3whR9.js → index-vaD1iHg5.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-BnXISaAa.js → initDefaultProps-Sd7Eayz4.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-ChY7C0zJ.js → motion-DlToY72q.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-ByFZMFM5.js → move-DvS7EmAP.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-BYeliY1H.js → omit-CzLq4QKW.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-B9dcAKnu.js → pickAttrs-BcM75Jx_.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-D3F_txz7.js → placementArrow-B7xXXiwd.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-ClkwY7wS.js → responsiveObserve-CrYPRB-g.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-BNgy2Eea.js → slide-CSYTtsRt.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-Bv3heMCD.js → statusUtils-CeSuOVT_.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-DVdlHbQm.js → styleChecker-KiQethca.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-alrRY5BK.js → useFlexGapSupport-CSQnQdiv.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-CcVh0-Vu.js → useFs-Br8Kr1pr.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-CkkHPhyq.js → usePersonalDataHub-DGJtDcMm.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-DWi0X9WN.js → vnode-C-jVtGka.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-DCbqxxLH.js → zoom-CeWySTPF.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/agent.js +47 -0
  145. package/src/commands/checkpoint.js +253 -53
  146. package/src/commands/compact.js +150 -0
  147. package/src/commands/goal.js +417 -0
  148. package/src/commands/hub.js +7 -0
  149. package/src/harness/prompt-compressor.js +71 -1
  150. package/src/index.js +4 -0
  151. package/src/lib/agent-core.js +1 -0
  152. package/src/lib/checkpoint-store.js +523 -0
  153. package/src/lib/goal-assess.js +228 -0
  154. package/src/lib/goal-context.js +87 -0
  155. package/src/lib/goal-store.js +341 -0
  156. package/src/repl/agent-repl.js +43 -7
  157. package/src/runtime/agent-core.js +267 -1
  158. package/src/runtime/headless-runner.js +147 -0
  159. package/src/runtime/headless-stream.js +34 -0
  160. package/src/runtime/mcp-config.js +139 -0
  161. package/src/runtime/policies/agent-policy.js +1 -0
  162. package/src/assets/web-panel/assets/ChatBubbleRenderer-C-svYkrC.js +0 -1
  163. package/src/assets/web-panel/assets/MobileProjects-zr-PpsT_.js +0 -1
  164. package/src/assets/web-panel/assets/OrderTableRenderer-ISp6btRY.js +0 -1
  165. package/src/assets/web-panel/assets/Projects-ll5wnj2L.js +0 -1
  166. package/src/assets/web-panel/assets/Skills-OQNky3uI.js +0 -1
  167. package/src/assets/web-panel/assets/Tasks-C9R8sgyi.js +0 -1
  168. package/src/assets/web-panel/assets/devWarning-BDK34w0I.js +0 -1
  169. package/src/assets/web-panel/assets/index-B6SaRuCI.js +0 -1
  170. package/src/assets/web-panel/assets/index-B9ekWb3I.js +0 -1
@@ -1,42 +1,182 @@
1
1
  /**
2
- * cc checkpoint — manual file-state snapshot / rewind (Claude-Code rewind parity).
2
+ * cc checkpoint — file-state snapshot / rewind (Claude-Code rewind parity).
3
3
  *
4
- * cc checkpoint create <paths...> [--label <l>] snapshot files/dirs
4
+ * cc checkpoint create [paths...] [--label <l>] snapshot the work tree (git)
5
+ * or the given paths (fallback)
5
6
  * cc checkpoint list list checkpoints
6
7
  * cc checkpoint show <id> [--diff] manifest, or diff vs current
7
- * cc checkpoint restore <id> [--dry-run] [--force] roll files back
8
+ * cc checkpoint restore <id> [--dry-run] [--force] roll back (alias: rewind)
8
9
  * cc checkpoint delete <id> [--force] remove a checkpoint
10
+ * cc checkpoint clear remove all (a session)
9
11
  *
10
- * Restore takes an automatic safety snapshot of the current contents first, so
11
- * a rewind is itself reversible. Distinct from `cc workflow checkpoint`
12
+ * Engine: inside a git work tree it uses git-plumbing shadow commits
13
+ * (whole-tree, content-addressed, .gitignore-aware, accurate add/modify/delete
14
+ * rewind — refs/cc-checkpoints/*). Outside git it falls back to the copy-based
15
+ * store (file-checkpoint.js) which snapshots the explicit paths you name.
16
+ *
17
+ * Restore takes an automatic safety snapshot of the current state first, so a
18
+ * rewind is itself reversible. Distinct from `cc workflow checkpoint`
12
19
  * (workflow execution state, not files).
13
20
  */
14
21
 
15
22
  import chalk from "chalk";
23
+ import { resolve } from "path";
16
24
  import { logger } from "../lib/logger.js";
17
25
 
26
+ /** git-plumbing engine adapter (normalized interface). */
27
+ function gitEngine(gs, dir, session) {
28
+ return {
29
+ kind: "git",
30
+ create: ({ label }) => {
31
+ const r = gs.createCheckpoint(dir, { session, label });
32
+ return {
33
+ id: r.id,
34
+ label: r.label,
35
+ createdAt: r.createdAt,
36
+ fileCount: r.files ?? r.fileCount,
37
+ };
38
+ },
39
+ list: () =>
40
+ gs.listCheckpoints(dir, { session }).map((r) => ({
41
+ id: r.id,
42
+ label: r.label,
43
+ createdAt: r.createdAt,
44
+ fileCount: null,
45
+ })),
46
+ show: (id) => gs.showCheckpoint(dir, id, { session }),
47
+ status: (id) => gs.statusAgainst(dir, id, { session }),
48
+ diffText: (id, o) => gs.diffCheckpoint(dir, id, { session, stat: o?.stat }),
49
+ restore: (id, o) => {
50
+ const r = gs.rewindTo(dir, id, { session, dryRun: o?.dryRun });
51
+ return {
52
+ dryRun: !!r.dryRun,
53
+ restoredCount: r.modified + r.recreated,
54
+ modified: r.modified,
55
+ recreated: r.recreated,
56
+ deleted: r.deleted,
57
+ safetyId: r.safetyId,
58
+ };
59
+ },
60
+ remove: (id) => gs.deleteCheckpoint(dir, id, { session }),
61
+ clear: () => gs.clearCheckpoints(dir, { session }),
62
+ };
63
+ }
64
+
65
+ /** copy-based engine adapter (normalized interface). */
66
+ function copyEngine(cs, dir) {
67
+ return {
68
+ kind: "copy",
69
+ create: ({ paths, label }) => {
70
+ const m = cs.createCheckpoint(paths, { cwd: dir, label });
71
+ return {
72
+ id: m.id,
73
+ label: m.label,
74
+ createdAt: m.createdAt,
75
+ fileCount: m.fileCount,
76
+ };
77
+ },
78
+ list: () =>
79
+ cs.listCheckpoints().map((c) => ({
80
+ id: c.id,
81
+ label: c.label,
82
+ createdAt: c.createdAt,
83
+ fileCount: c.fileCount,
84
+ })),
85
+ show: (id) => {
86
+ const m = cs.getCheckpoint(id);
87
+ if (!m) throw new Error(`no such checkpoint: ${id}`);
88
+ return {
89
+ id: m.id,
90
+ label: m.label,
91
+ createdAt: m.createdAt,
92
+ fileCount: m.fileCount,
93
+ files: m.files.map((f) => ({ rel: f.rel, bytes: f.bytes })),
94
+ };
95
+ },
96
+ status: (id) => {
97
+ const d = cs.diffCheckpoint(id);
98
+ return { modified: d.modified, added: [], deleted: d.deleted };
99
+ },
100
+ diffText: () => null, // copy engine has no raw patch — caller uses status()
101
+ restore: (id, o) => {
102
+ const r = cs.restoreCheckpoint(id, { cwd: dir, dryRun: o?.dryRun });
103
+ return {
104
+ dryRun: !!r.dryRun,
105
+ restoredCount: r.restored.length,
106
+ restored: r.restored,
107
+ missingBlob: r.missingBlob,
108
+ safetyId: r.safetyId,
109
+ };
110
+ },
111
+ remove: (id) => cs.deleteCheckpoint(id),
112
+ clear: () => {
113
+ const all = cs.listCheckpoints();
114
+ for (const c of all) cs.deleteCheckpoint(c.id);
115
+ return all.length;
116
+ },
117
+ };
118
+ }
119
+
120
+ /** Choose the engine for `dir`: git-plumbing when available, else copy-based. */
121
+ async function pickEngine(dir, session) {
122
+ const gs = await import("../lib/checkpoint-store.js");
123
+ if (gs.isCheckpointAvailable(dir)) return gitEngine(gs, dir, session);
124
+ const cs = await import("../lib/file-checkpoint.js");
125
+ return copyEngine(cs, dir);
126
+ }
127
+
128
+ const tag = (engine) => chalk.dim(engine.kind === "git" ? "[git]" : "[copy]");
129
+
18
130
  export function registerCheckpointCommand(program) {
19
131
  const cp = program
20
132
  .command("checkpoint")
21
- .description("Snapshot / rewind file state (manual checkpoints)");
133
+ .description("Snapshot / rewind file state (git-plumbing, copy fallback)");
22
134
 
23
- cp.command("create <paths...>")
24
- .description("Snapshot the given files/directories")
135
+ cp.command("create [paths...]")
136
+ .description(
137
+ "Snapshot the work tree (git) or the given files/dirs (fallback)",
138
+ )
139
+ .option("-d, --dir <dir>", "Target directory", ".")
140
+ .option("-s, --session <id>", "Checkpoint session (git engine)", "default")
25
141
  .option("--label <label>", "Human label for this checkpoint")
26
142
  .option("--json", "Output as JSON")
27
143
  .action(async (paths, options) => {
28
144
  try {
29
- const { createCheckpoint } = await import("../lib/file-checkpoint.js");
30
- const m = createCheckpoint(paths, { label: options.label });
145
+ const dir = resolve(options.dir);
146
+ const engine = await pickEngine(dir, options.session);
147
+ if (engine.kind === "copy" && (!paths || paths.length === 0)) {
148
+ logger.error(
149
+ chalk.red(
150
+ "Not a git repo here — specify paths to snapshot: cc checkpoint create <paths...>",
151
+ ),
152
+ );
153
+ process.exitCode = 1;
154
+ return;
155
+ }
156
+ if (engine.kind === "git" && paths && paths.length > 0) {
157
+ logger.log(
158
+ chalk.gray(
159
+ " (git engine snapshots the whole work tree; paths ignored)",
160
+ ),
161
+ );
162
+ }
163
+ const m = engine.create({ paths, label: options.label });
31
164
  if (options.json) {
32
- console.log(JSON.stringify(m, null, 2));
165
+ console.log(JSON.stringify({ ...m, engine: engine.kind }, null, 2));
33
166
  return;
34
167
  }
35
168
  logger.log(
36
169
  chalk.green(`✓ checkpoint ${chalk.bold(m.id)}`) +
170
+ ` ${tag(engine)}` +
37
171
  (m.label ? chalk.gray(` "${m.label}"`) : ""),
38
172
  );
39
173
  logger.log(chalk.gray(` ${m.fileCount} file(s) snapshotted`));
174
+ logger.log(
175
+ chalk.gray(
176
+ ` rewind with: cc checkpoint restore ${m.id}` +
177
+ (options.session !== "default" ? ` -s ${options.session}` : ""),
178
+ ),
179
+ );
40
180
  } catch (err) {
41
181
  logger.error(chalk.red(`checkpoint create failed: ${err.message}`));
42
182
  process.exitCode = 1;
@@ -46,11 +186,14 @@ export function registerCheckpointCommand(program) {
46
186
  cp.command("list")
47
187
  .alias("ls")
48
188
  .description("List checkpoints (newest first)")
189
+ .option("-d, --dir <dir>", "Target directory", ".")
190
+ .option("-s, --session <id>", "Checkpoint session (git engine)", "default")
49
191
  .option("--json", "Output as JSON")
50
192
  .action(async (options) => {
51
193
  try {
52
- const { listCheckpoints } = await import("../lib/file-checkpoint.js");
53
- const all = listCheckpoints();
194
+ const dir = resolve(options.dir);
195
+ const engine = await pickEngine(dir, options.session);
196
+ const all = engine.list();
54
197
  if (options.json) {
55
198
  console.log(JSON.stringify(all, null, 2));
56
199
  return;
@@ -58,15 +201,19 @@ export function registerCheckpointCommand(program) {
58
201
  if (all.length === 0) {
59
202
  logger.log(
60
203
  chalk.gray(
61
- "No checkpoints. Create one: cc checkpoint create <paths...>",
204
+ "No checkpoints. Create one: cc checkpoint create" +
205
+ (engine.kind === "copy" ? " <paths...>" : ""),
62
206
  ),
63
207
  );
64
208
  return;
65
209
  }
66
210
  for (const c of all) {
211
+ const count =
212
+ c.fileCount == null
213
+ ? ""
214
+ : `${String(c.fileCount).padStart(4)} files`;
67
215
  logger.log(
68
- `${chalk.cyan(c.id.padEnd(22))} ${chalk.gray(c.createdAt)} ` +
69
- `${String(c.fileCount).padStart(4)} files` +
216
+ `${chalk.cyan(c.id.padEnd(22))} ${chalk.gray(c.createdAt)} ${count}` +
70
217
  (c.label ? chalk.gray(` "${c.label}"`) : ""),
71
218
  );
72
219
  }
@@ -78,16 +225,30 @@ export function registerCheckpointCommand(program) {
78
225
 
79
226
  cp.command("show <id>")
80
227
  .description("Show a checkpoint's files, or its diff vs current state")
228
+ .option("-d, --dir <dir>", "Target directory", ".")
229
+ .option("-s, --session <id>", "Checkpoint session (git engine)", "default")
81
230
  .option("--diff", "Compare snapshot against current on-disk files")
231
+ .option("--stat", "With --diff: summary (diffstat) instead of full patch")
82
232
  .option("--json", "Output as JSON")
83
233
  .action(async (id, options) => {
84
234
  try {
85
- const { getCheckpoint, diffCheckpoint } =
86
- await import("../lib/file-checkpoint.js");
235
+ const dir = resolve(options.dir);
236
+ const engine = await pickEngine(dir, options.session);
237
+
87
238
  if (options.diff) {
88
- const d = diffCheckpoint(id);
239
+ const text = engine.diffText(id, { stat: options.stat });
240
+ if (text != null) {
241
+ if (options.json) {
242
+ console.log(JSON.stringify({ id, diff: text }, null, 2));
243
+ return;
244
+ }
245
+ if (!text.trim()) logger.info(`No changes since checkpoint ${id}.`);
246
+ else logger.log(text);
247
+ return;
248
+ }
249
+ const d = engine.status(id);
89
250
  if (options.json) {
90
- console.log(JSON.stringify(d, null, 2));
251
+ console.log(JSON.stringify({ id, ...d }, null, 2));
91
252
  return;
92
253
  }
93
254
  logger.log(chalk.bold(`Diff vs current — ${id}`));
@@ -95,20 +256,15 @@ export function registerCheckpointCommand(program) {
95
256
  d.modified.forEach((f) => logger.log(chalk.yellow(` M ${f}`)));
96
257
  logger.log(` ${chalk.red("deleted")}: ${d.deleted.length}`);
97
258
  d.deleted.forEach((f) => logger.log(chalk.red(` D ${f}`)));
98
- logger.log(chalk.gray(` unchanged: ${d.unchanged.length}`));
99
- return;
100
- }
101
- const m = getCheckpoint(id);
102
- if (!m) {
103
- logger.error(chalk.red(`no such checkpoint: ${id}`));
104
- process.exitCode = 1;
105
259
  return;
106
260
  }
261
+
262
+ const m = engine.show(id);
107
263
  if (options.json) {
108
264
  console.log(JSON.stringify(m, null, 2));
109
265
  return;
110
266
  }
111
- logger.log(chalk.bold(`Checkpoint ${m.id}`));
267
+ logger.log(chalk.bold(`Checkpoint ${m.id}`) + ` ${tag(engine)}`);
112
268
  if (m.label) logger.log(chalk.gray(` label: ${m.label}`));
113
269
  logger.log(
114
270
  chalk.gray(` created: ${m.createdAt} files: ${m.fileCount}`),
@@ -123,31 +279,30 @@ export function registerCheckpointCommand(program) {
123
279
  });
124
280
 
125
281
  cp.command("restore <id>")
282
+ .alias("rewind")
126
283
  .description(
127
284
  "Restore files from a checkpoint (auto-snapshots current state first)",
128
285
  )
286
+ .option("-d, --dir <dir>", "Target directory", ".")
287
+ .option("-s, --session <id>", "Checkpoint session (git engine)", "default")
129
288
  .option("--dry-run", "Show what would change without writing")
130
289
  .option("--force", "Restore without the interactive confirm prompt")
131
290
  .option("--json", "Output as JSON")
132
291
  .action(async (id, options) => {
133
292
  try {
134
- const { restoreCheckpoint, diffCheckpoint } =
135
- await import("../lib/file-checkpoint.js");
293
+ const dir = resolve(options.dir);
294
+ const engine = await pickEngine(dir, options.session);
136
295
 
137
296
  if (options.dryRun) {
138
- const r = restoreCheckpoint(id, { dryRun: true });
297
+ const r = engine.restore(id, { dryRun: true });
139
298
  if (options.json) {
140
299
  console.log(JSON.stringify(r, null, 2));
141
300
  return;
142
301
  }
143
302
  logger.log(chalk.bold(`Dry-run restore — ${id}`));
144
- logger.log(` would restore: ${r.restored.length} file(s)`);
145
- r.restored.forEach((f) => logger.log(chalk.yellow(` ~ ${f}`)));
146
- logger.log(chalk.gray(` already matching: ${r.unchanged.length}`));
147
- if (r.missingBlob.length) {
148
- logger.log(
149
- chalk.red(` missing blobs: ${r.missingBlob.join(", ")}`),
150
- );
303
+ logger.log(` would restore: ${r.restoredCount} file(s)`);
304
+ if (typeof r.deleted === "number") {
305
+ logger.log(` would remove: ${r.deleted} file(s) created since`);
151
306
  }
152
307
  return;
153
308
  }
@@ -155,12 +310,13 @@ export function registerCheckpointCommand(program) {
155
310
  // Destructive: overwrites current files. Require --force when not a TTY;
156
311
  // prompt when interactive.
157
312
  if (!options.force) {
158
- const d = diffCheckpoint(id);
159
- const willChange = d.modified.length + d.deleted.length;
313
+ const d = engine.status(id);
314
+ const willChange =
315
+ d.modified.length + d.deleted.length + (d.added?.length || 0);
160
316
  if (process.stdin.isTTY) {
161
317
  const { confirm } = await import("@inquirer/prompts");
162
318
  const ok = await confirm({
163
- message: `Restore ${id}? ${willChange} file(s) will be overwritten (a safety checkpoint is taken first).`,
319
+ message: `Restore ${id}? ${willChange} file(s) affected (a safety checkpoint is taken first).`,
164
320
  default: false,
165
321
  }).catch(() => false);
166
322
  if (!ok) {
@@ -178,23 +334,30 @@ export function registerCheckpointCommand(program) {
178
334
  }
179
335
  }
180
336
 
181
- const r = restoreCheckpoint(id);
337
+ const r = engine.restore(id);
182
338
  if (options.json) {
183
339
  console.log(JSON.stringify(r, null, 2));
184
340
  return;
185
341
  }
186
342
  logger.log(
187
- chalk.green(`✓ restored ${r.restored.length} file(s) from ${id}`),
343
+ chalk.green(`✓ restored ${r.restoredCount} file(s) from ${id}`) +
344
+ (typeof r.deleted === "number" && r.deleted > 0
345
+ ? chalk.gray(` (${r.deleted} removed)`)
346
+ : ""),
188
347
  );
189
- r.restored.forEach((f) => logger.log(chalk.gray(` ~ ${f}`)));
190
348
  if (r.safetyId) {
191
349
  logger.log(
192
350
  chalk.gray(
193
- ` safety checkpoint of prior state: ${r.safetyId} (undo with: cc checkpoint restore ${r.safetyId})`,
351
+ ` safety checkpoint of prior state: ${r.safetyId}` +
352
+ ` (undo with: cc checkpoint restore ${r.safetyId}` +
353
+ (options.session !== "default"
354
+ ? ` -s ${options.session}`
355
+ : "") +
356
+ `)`,
194
357
  ),
195
358
  );
196
359
  }
197
- if (r.missingBlob.length) {
360
+ if (r.missingBlob && r.missingBlob.length) {
198
361
  logger.log(
199
362
  chalk.red(` missing blobs (skipped): ${r.missingBlob.join(", ")}`),
200
363
  );
@@ -208,20 +371,51 @@ export function registerCheckpointCommand(program) {
208
371
  cp.command("delete <id>")
209
372
  .alias("rm")
210
373
  .description("Delete a checkpoint")
374
+ .option("-d, --dir <dir>", "Target directory", ".")
375
+ .option("-s, --session <id>", "Checkpoint session (git engine)", "default")
211
376
  .option("--force", "Skip confirmation")
212
377
  .action(async (id, options) => {
213
378
  try {
214
- const { deleteCheckpoint, getCheckpoint } =
215
- await import("../lib/file-checkpoint.js");
216
- if (!getCheckpoint(id)) {
379
+ const dir = resolve(options.dir);
380
+ const engine = await pickEngine(dir, options.session);
381
+ if (!options.force && process.stdin.isTTY) {
382
+ const { confirm } = await import("@inquirer/prompts");
383
+ const ok = await confirm({
384
+ message: `Delete checkpoint ${id}?`,
385
+ default: false,
386
+ }).catch(() => false);
387
+ if (!ok) {
388
+ logger.log(chalk.gray("Aborted."));
389
+ return;
390
+ }
391
+ }
392
+ const existed = engine.remove(id);
393
+ if (!existed) {
217
394
  logger.error(chalk.red(`no such checkpoint: ${id}`));
218
395
  process.exitCode = 1;
219
396
  return;
220
397
  }
398
+ logger.log(chalk.green(`✓ deleted ${id}`));
399
+ } catch (err) {
400
+ logger.error(chalk.red(`checkpoint delete failed: ${err.message}`));
401
+ process.exitCode = 1;
402
+ }
403
+ });
404
+
405
+ cp.command("clear")
406
+ .description("Delete all checkpoints (in a session, for the git engine)")
407
+ .option("-d, --dir <dir>", "Target directory", ".")
408
+ .option("-s, --session <id>", "Checkpoint session (git engine)", "default")
409
+ .option("--force", "Skip confirmation")
410
+ .option("--json", "Output as JSON")
411
+ .action(async (options) => {
412
+ try {
413
+ const dir = resolve(options.dir);
414
+ const engine = await pickEngine(dir, options.session);
221
415
  if (!options.force && process.stdin.isTTY) {
222
416
  const { confirm } = await import("@inquirer/prompts");
223
417
  const ok = await confirm({
224
- message: `Delete checkpoint ${id}?`,
418
+ message: `Delete ALL checkpoints${engine.kind === "git" ? ` in session "${options.session}"` : ""}?`,
225
419
  default: false,
226
420
  }).catch(() => false);
227
421
  if (!ok) {
@@ -229,10 +423,16 @@ export function registerCheckpointCommand(program) {
229
423
  return;
230
424
  }
231
425
  }
232
- deleteCheckpoint(id);
233
- logger.log(chalk.green(`✓ deleted ${id}`));
426
+ const removed = engine.clear();
427
+ if (options.json) {
428
+ console.log(
429
+ JSON.stringify({ removed, engine: engine.kind }, null, 2),
430
+ );
431
+ return;
432
+ }
433
+ logger.log(chalk.green(`✓ removed ${removed} checkpoint(s)`));
234
434
  } catch (err) {
235
- logger.error(chalk.red(`checkpoint delete failed: ${err.message}`));
435
+ logger.error(chalk.red(`checkpoint clear failed: ${err.message}`));
236
436
  process.exitCode = 1;
237
437
  }
238
438
  });
@@ -0,0 +1,150 @@
1
+ /**
2
+ * cc compact — compact a stored session's history (Claude-Code `/compact` parity,
3
+ * headless). Complements the interactive agent REPL's `/compact`, which only
4
+ * works on the live in-memory conversation.
5
+ *
6
+ * cc compact <session-id> compact and persist (writes a `compact`
7
+ * checkpoint event the resume path honors)
8
+ * cc compact <session-id> --dry-run preview the reduction, write nothing
9
+ *
10
+ * Engine: the existing PromptCompressor (snip + dedup + collapse + truncate).
11
+ * Runs OFFLINE and deterministic — no LLM summarization is wired here, so a
12
+ * compaction never makes a network call and is reproducible. By default it
13
+ * sizes its thresholds to the session's recorded model/provider context window;
14
+ * override with --model/--provider or the hard --max-tokens/--max-messages.
15
+ *
16
+ * After compaction the new history is appended as a JSONL `compact` event;
17
+ * `rebuildMessages()` already rebuilds from the last such event, so a later
18
+ * `cc agent --resume <id>` picks up the shortened history automatically.
19
+ * Distinct from `cc checkpoint` (file state) and `cc workflow checkpoint`
20
+ * (execution state).
21
+ */
22
+
23
+ import chalk from "chalk";
24
+ import { logger } from "../lib/logger.js";
25
+ import {
26
+ sessionExists,
27
+ readEvents,
28
+ rebuildMessages,
29
+ appendCompactEvent,
30
+ } from "../harness/jsonl-session-store.js";
31
+ import { PromptCompressor } from "../harness/prompt-compressor.js";
32
+
33
+ /** Build a compressor sized to the session (or explicit overrides). */
34
+ function buildCompressor(options, recorded) {
35
+ const maxTokens = options.maxTokens ? Number(options.maxTokens) : undefined;
36
+ const maxMessages = options.maxMessages
37
+ ? Number(options.maxMessages)
38
+ : undefined;
39
+ if (maxTokens || maxMessages) {
40
+ // Hard thresholds win — adaptive sizing is bypassed by the constructor.
41
+ return new PromptCompressor({ maxTokens, maxMessages });
42
+ }
43
+ const model = options.model || recorded.model || undefined;
44
+ const provider = options.provider || recorded.provider || undefined;
45
+ // model/provider → adaptive context-window thresholds; neither → defaults.
46
+ return new PromptCompressor({ model, provider });
47
+ }
48
+
49
+ export function registerCompactCommand(program) {
50
+ program
51
+ .command("compact <session-id>")
52
+ .description(
53
+ "Compact a stored session's history (offline; persists for --resume)",
54
+ )
55
+ .option(
56
+ "-m, --model <model>",
57
+ "Model for adaptive context-window sizing (default: session's recorded model)",
58
+ )
59
+ .option(
60
+ "-p, --provider <provider>",
61
+ "Provider for adaptive sizing (default: session's recorded provider)",
62
+ )
63
+ .option("--max-tokens <n>", "Override the token threshold (skips adaptive)")
64
+ .option(
65
+ "--max-messages <n>",
66
+ "Override the message-count threshold (skips adaptive)",
67
+ )
68
+ .option("--dry-run", "Preview the reduction without writing")
69
+ .option("--json", "Output as JSON")
70
+ .action(async (sessionId, options) => {
71
+ try {
72
+ if (!sessionExists(sessionId)) {
73
+ logger.error(chalk.red(`no such session: ${sessionId}`));
74
+ logger.log(chalk.gray(" list sessions with: cc session list"));
75
+ process.exitCode = 1;
76
+ return;
77
+ }
78
+
79
+ const events = readEvents(sessionId);
80
+ const start = events.find((e) => e.type === "session_start");
81
+ const recorded = {
82
+ model: start?.data?.model || "",
83
+ provider: start?.data?.provider || "",
84
+ };
85
+
86
+ const messages = rebuildMessages(sessionId);
87
+ const compressor = buildCompressor(options, recorded);
88
+ const { messages: compacted, stats } =
89
+ await compressor.compress(messages);
90
+
91
+ const reduced = stats.saved > 0 || compacted.length < messages.length;
92
+
93
+ if (!reduced) {
94
+ if (options.json) {
95
+ console.log(
96
+ JSON.stringify({ sessionId, compacted: false, stats }, null, 2),
97
+ );
98
+ return;
99
+ }
100
+ logger.log(
101
+ chalk.gray(
102
+ `Nothing to compact — ${messages.length} message(s), ${stats.originalTokens} tokens (under threshold).`,
103
+ ),
104
+ );
105
+ return;
106
+ }
107
+
108
+ if (!options.dryRun) {
109
+ appendCompactEvent(sessionId, { ...stats, messages: compacted });
110
+ }
111
+
112
+ if (options.json) {
113
+ console.log(
114
+ JSON.stringify(
115
+ { sessionId, dryRun: !!options.dryRun, stats },
116
+ null,
117
+ 2,
118
+ ),
119
+ );
120
+ return;
121
+ }
122
+
123
+ const verb = options.dryRun ? "Would compact" : "Compacted";
124
+ logger.log(
125
+ (options.dryRun ? chalk.cyan : chalk.green)(
126
+ `${options.dryRun ? "" : "✓ "}${verb} ${sessionId}`,
127
+ ),
128
+ );
129
+ logger.log(
130
+ chalk.gray(
131
+ ` ${stats.originalMessages} → ${stats.compressedMessages} messages` +
132
+ `, ${stats.originalTokens} → ${stats.compressedTokens} tokens` +
133
+ ` (saved ${stats.saved}, ${stats.strategy})`,
134
+ ),
135
+ );
136
+ if (options.dryRun) {
137
+ logger.log(
138
+ chalk.gray(` re-run without --dry-run to persist the compaction`),
139
+ );
140
+ } else {
141
+ logger.log(
142
+ chalk.gray(` resume with: cc agent --resume ${sessionId}`),
143
+ );
144
+ }
145
+ } catch (err) {
146
+ logger.error(chalk.red(`compact failed: ${err.message}`));
147
+ process.exitCode = 1;
148
+ }
149
+ });
150
+ }