chainlesschain 0.162.39 → 0.162.41

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 (172) hide show
  1. package/README.md +368 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-DCjoAX_u.js → AIOps-Ut7EevnG.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-XHoOmsbP.js → ActionButton-Dv6BlfJg.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics--xaFkDnL.js → Analytics-TQVQuJ7u.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CSa3FBn8.js → AppLayout-MSqLm2WK.js} +5 -5
  7. package/src/assets/web-panel/assets/{Audit-ONWXiAwG.js → Audit-mw81HwVy.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CKOPNdgy.js → Backup-BQcPWDb1.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-PNj4uVqg.js → BaseInput-BYo_pwBH.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-CZCulyXV.js → Chat-zi3YUKx2.js} +5 -5
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjuJpfpV.js → ChatBubbleRenderer-DWSm1XJJ.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-jvy668lD.js → Checkbox-BvC8Erjt.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-DhUebOQD.js → Codegen-C32vx0OP.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-BiBvHfdT.js → Col-DMBwmqyZ.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-CmEdEti-.js → Community-nDWncmKV.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-CtxpF4R5.js → Compact-lIc1HFn8.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-CvPTrTAJ.js → Compliance-D14I_gd2.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-BMafGHjy.js → Cowork-BiNI-_ZL.js} +3 -3
  19. package/src/assets/web-panel/assets/{Cron-mdg_4TR1.js → Cron-N13sFzHb.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain--dGxsUvn.js → Crosschain-Dlnl0-v6.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-C9oKaCml.js → DID-CxtYS31I.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-CoGxKMvy.js → Dashboard-G4UnHlTR.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-CDDu3ZZ3.js → Dropdown-BazlxFGY.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-Dy7_r9Ag.js → EmailListRenderer-BrpNdihm.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CNg6vImJ.js → FamilyGuardDashboard-HD7jbOOR.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-CT61bf3u.js → Federation-Bz8lzAGI.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-CSLRnXhg.js → FormItemContext-CcyzGS00.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-CZ4NE5N3.js → GenericCardRenderer-DRo9cwmp.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-DBuOma3L.js → Git-B7bn333J.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-BTU_SEef.js → Governance-DZX9CWAM.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-47SAmLC_.js → Inference-B3XhsL6W.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-DCrK5vP4.js → KnowledgeGraph-CxFRTlQe.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-BqiDxdav.js → Logs-xuys6mKH.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-CReUjsDt.js → Marketplace-CXyxv4WU.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-agZBV3p8.js → McpTools-BzZLQVI3.js} +6 -6
  36. package/src/assets/web-panel/assets/{Memory-C_YvUtyS.js → Memory-BANtaBa7.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-41fP1Tui.js → MobileBridge-BJIwjmxr.js} +3 -3
  38. package/src/assets/web-panel/assets/{MobileProjects-BkqLvGfL.js → MobileProjects-B857uSAZ.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-JFJCXUnk.js → Mtc-Cn7ceFEz.js} +5 -5
  40. package/src/assets/web-panel/assets/{MtcAudit-BHNpPZC9.js → MtcAudit-B0zE978G.js} +6 -6
  41. package/src/assets/web-panel/assets/{Multisig-DuCRumiz.js → Multisig-CQFT0wXW.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-DK-g0fKY.js → NLProgramming-DSxKdVY-.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-BSMcjsPf.js → Notes-DtlTfam8.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-9ouC118H.js → NotificationSettings-CHQwayAg.js} +1 -1
  45. package/src/assets/web-panel/assets/OrderTableRenderer-Brpmzh9n.js +1 -0
  46. package/src/assets/web-panel/assets/{Organization-DSV7oRnR.js → Organization-nF_tzZDT.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-DVkkORc3.js → Overflow-CgCSf_PH.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-BXXjkkQD.js → P2P-Bvn46bLY.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-O5hNnLTP.js → PdhVaultBrowser-Bzl9k7Gj.js} +5 -5
  50. package/src/assets/web-panel/assets/{Permissions-D_s0H5Av.js → Permissions-Dmezbuo8.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-CzMDrwUi.js → PersonalDataHub-lCKRxwZr.js} +3 -3
  52. package/src/assets/web-panel/assets/{Pipeline-i9krLVTL.js → Pipeline-DDCGm9PA.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-cMQcj9I8.js → Privacy-Cgu18Kjl.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-Ca_l7avo.js → ProjectInit-CkF1AeRY.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-BkaIhd6b.js → ProjectSettings-D0Q-orz1.js} +2 -2
  56. package/src/assets/web-panel/assets/Projects-KfGELrSY.js +1 -0
  57. package/src/assets/web-panel/assets/{Providers-D0nzYiqz.js → Providers-BACLV0z8.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-Bzzr9d0f.js → QuickAsk-CPsZUqDl.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-C-UFbQnX.js → Recommend-5jX0OI1-.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-BKMIKO5F.js → Reputation-5JKv54z0.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-Bs7htK1T.js → Row-DLiTF5LY.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-v6MdULUh.js → RssFeed-CFdGmCKW.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-DlRWYzvz.js → Search-BjIOnmA7.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-DXWO37xX.js → Security-BujPqQSo.js} +4 -4
  65. package/src/assets/web-panel/assets/{Services-C2tWA-O0.js → Services-ChciPnMu.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-Q8pIYY4a.js → Skeleton-Cwswp1Jv.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-D7XBlErj.js → Skills-CtwR4vJV.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-CiyMVPJ1.js → Sla-pRIevich.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-CadCeeiR.js → SpeechSettings-BRqB28Ai.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-DzNAUhQq.js → SyncSettings-BYyj58_h.js} +2 -2
  71. package/src/assets/web-panel/assets/Tasks-DTLpT48U.js +1 -0
  72. package/src/assets/web-panel/assets/{Templates-DfgEpUa4.js → Templates-Bbz_h7oW.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-C8ajkuYi.js → Tenant-D-H4E3cu.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-B9rHwQQx.js → Terminal-CLLi0-lV.js} +2 -2
  75. package/src/assets/web-panel/assets/{TimelineRenderer-D1ZVNezX.js → TimelineRenderer-BKI6eG0k.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-CAkED4mx.js → Tokens-rsE_yDjM.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-CJSrm6X0.js → Trigger-8TpwuTGk.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-B-TeorSk.js → Trust-sMtZkHPs.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-Di7Ymofy.js → UkeySign-BAy2bAdG.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-DM1eYNZe.js → VideoEditing-CBeR_DYK.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-DvRWkbmR.js → Wallet-BymDnBcq.js} +4 -4
  82. package/src/assets/web-panel/assets/{WebAuthn-CeZ3Y622.js → WebAuthn-DQIjmqNz.js} +5 -5
  83. package/src/assets/web-panel/assets/{WorkflowEditor-Cq8c4h5j.js → WorkflowEditor-Cj7PB73f.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-7-WfML6Q.js → chat-DYnGj4vi.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-D6FgCmB-.js → colors-qOLKZNvN.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-ClYV25qi.js → compact-item-BpjCLPcW.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-CDhtjdkV.js → createContext-CfakUZVQ.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-DgtRXlrj.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-DZSH5LQd.js → hasIn-C9RW1s7t.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-B4PMzmOx.js → index-8Ia91vNV.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-B78X5S22.js → index-B4kS312z.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-CDX4QU3k.js → index-BE67I0SW.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DPEYvNvq.js → index-BFOSDeeo.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CKgS8E_X.js → index-BIz-pX0k.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Di9pFrHV.js → index-BJoWi1aR.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BHeK8I5A.js → index-B_K0YtG2.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BpzOUiSb.js → index-BdR8XRyF.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DWRoh3_3.js → index-BfyRXPyV.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-C7pQa2is.js → index-Bl5LBZJM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DZ4zuoCP.js → index-BlxRICmz.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B_mMFQ4S.js → index-BxiHBsfU.js} +1 -1
  102. package/src/assets/web-panel/assets/{index---azBCXl.js → index-C2S1hUWG.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BJ7mrOaB.js → index-CEHyZ77C.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CxwfFZ1u.js → index-CJZ2noI2.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DGj1orXm.js → index-COYEuArt.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DL6GFJAd.js → index-CVZTLSL1.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-z-R0KaJS.js → index-CbnJ6FsO.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-tU6pZ1TP.js → index-CvWFTG56.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-rCs9VJJp.js → index-D-RzTqlR.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B6VWGnwq.js → index-DA80prWe.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D0YzTJJO.js → index-DAjszh8P.js} +1 -1
  112. package/src/assets/web-panel/assets/index-DIGTMmnW.js +1 -0
  113. package/src/assets/web-panel/assets/{index-DjG82V0v.js → index-DQvVYNoJ.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DLizxxId.js → index-DSWdpR3c.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C7sC56w8.js → index-DadPmrxI.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BlBF_l8m.js → index-DgMJagCq.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Bj8hZiyL.js → index-DkmLJFE_.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CrTmxbL8.js → index-DzXYG5YJ.js} +1 -1
  119. package/src/assets/web-panel/assets/index-Ef5jERRW.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BUOPjAUM.js → index-JkOMWGMX.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CmU631Je.js → index-T3bIqK_p.js} +3 -3
  122. package/src/assets/web-panel/assets/{index-BqOIoEo6.js → index-UiiqS5k2.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CSjoWPxB.js → index-VYIJmPvJ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B13QnrnE.js → index-ZCtDWP2C.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DgaF1F0W.js → index-f9yoj84i.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Or_McYjX.js → index-lPc7EzUi.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DGJK8D0l.js → index-m9JeDv6B.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CWOkL-8O.js → index-qf0fAus7.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-CSdsIGy3.js → initDefaultProps-DgsgQr1H.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-Do-AcZV4.js → motion-TeUH7wzx.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-BmgOoMsi.js → move-DdkIeWQx.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-D4Tm7-s9.js → omit-BH_PH6HT.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-CuWA8-lj.js → pickAttrs-CllCh-Nl.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-BSbEF5op.js → placementArrow-BCjE2AzM.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-GIMJwB_9.js → responsiveObserve-BAVGAvRQ.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-DlZxpIBe.js → slide-D4ZW-Inn.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-BZ26LPlh.js → statusUtils-j4pxhmKV.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-Yn_3FZ0l.js → styleChecker-DH2SLtPg.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-O_LOE1AB.js → useFlexGapSupport-CYMMs-_Q.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-VFMyQqtl.js → useFs-BOX2ddKh.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-B_hyrGB-.js → usePersonalDataHub-BwcnN5z_.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-D4LttGy7.js → vnode-Cwalh7Hj.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-KnTK1fjj.js → zoom-B2_q_nbu.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/agent.js +38 -4
  146. package/src/commands/init.js +115 -2
  147. package/src/commands/mcp.js +57 -0
  148. package/src/commands/memory.js +62 -0
  149. package/src/commands/session.js +106 -12
  150. package/src/index.js +10 -0
  151. package/src/lib/agent-core.js +1 -0
  152. package/src/lib/agent-session-export.js +124 -0
  153. package/src/lib/ide-context.js +62 -0
  154. package/src/lib/init-ai-refine.js +66 -0
  155. package/src/lib/json-schema-output.js +181 -0
  156. package/src/lib/mcp-serve.js +259 -0
  157. package/src/lib/project-instructions.js +364 -0
  158. package/src/lib/project-inventory.js +355 -0
  159. package/src/lib/repl-bang-memorize.js +142 -0
  160. package/src/lib/repl-completer.js +25 -4
  161. package/src/lib/repl-rewind.js +107 -0
  162. package/src/lib/update-notice-refresh.mjs +10 -0
  163. package/src/lib/update-notice.js +154 -0
  164. package/src/repl/agent-repl.js +263 -1
  165. package/src/runtime/agent-core.js +162 -0
  166. package/src/runtime/system-prompt.js +21 -1
  167. package/src/assets/web-panel/assets/OrderTableRenderer-LG2nUO5y.js +0 -1
  168. package/src/assets/web-panel/assets/Projects-Dy9yNmDg.js +0 -1
  169. package/src/assets/web-panel/assets/Tasks-BjdHjZeb.js +0 -1
  170. package/src/assets/web-panel/assets/devWarning-O0FVFeZg.js +0 -1
  171. package/src/assets/web-panel/assets/index--ANIKvhL.js +0 -1
  172. package/src/assets/web-panel/assets/index-DUfp4rnQ.js +0 -1
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Project inventory — offline resource census for `cc init --memory`.
3
+ *
4
+ * Scans the target folder (bounded walk: depth/entry caps, heavy dirs
5
+ * skipped) and produces a deterministic snapshot of what is already there:
6
+ * languages by extension, package manager, npm scripts, workspaces/monorepo
7
+ * layout, notable tool configs (TS/bundlers/docker/CI/lint), test runners and
8
+ * a README summary. `renderMemoryFile` turns that snapshot into a starter
9
+ * `cc.md` project-memory file (the file `cc agent` auto-loads — see
10
+ * project-instructions.js). No LLM, no network: the census is pure I/O and
11
+ * unit-testable via the `deps` seam; reads are explicit UTF-8 (encoding.md).
12
+ */
13
+
14
+ import fsDefault from "fs";
15
+ import pathDefault from "path";
16
+
17
+ export const SKIP_DIRS = new Set([
18
+ "node_modules",
19
+ ".git",
20
+ "dist",
21
+ "build",
22
+ "out",
23
+ "coverage",
24
+ ".next",
25
+ ".nuxt",
26
+ ".venv",
27
+ "venv",
28
+ "__pycache__",
29
+ "target",
30
+ ".gradle",
31
+ ".idea",
32
+ ".vscode",
33
+ "Pods",
34
+ "DerivedData",
35
+ ]);
36
+
37
+ export const DEFAULT_MAX_DEPTH = 4;
38
+ export const DEFAULT_MAX_ENTRIES = 20000;
39
+
40
+ const EXT_LANG = {
41
+ ".js": "JavaScript",
42
+ ".cjs": "JavaScript",
43
+ ".mjs": "JavaScript",
44
+ ".ts": "TypeScript",
45
+ ".tsx": "TypeScript",
46
+ ".jsx": "JavaScript",
47
+ ".vue": "Vue",
48
+ ".py": "Python",
49
+ ".java": "Java",
50
+ ".kt": "Kotlin",
51
+ ".kts": "Kotlin",
52
+ ".swift": "Swift",
53
+ ".go": "Go",
54
+ ".rs": "Rust",
55
+ ".rb": "Ruby",
56
+ ".php": "PHP",
57
+ ".cs": "C#",
58
+ ".c": "C",
59
+ ".h": "C/C++ header",
60
+ ".cpp": "C++",
61
+ ".cc": "C++",
62
+ ".m": "Objective-C",
63
+ ".sql": "SQL",
64
+ ".sh": "Shell",
65
+ ".ps1": "PowerShell",
66
+ ".md": "Markdown",
67
+ };
68
+
69
+ const CONFIG_MARKERS = [
70
+ ["tsconfig.json", "TypeScript"],
71
+ ["vite.config.js", "Vite"],
72
+ ["vite.config.ts", "Vite"],
73
+ ["webpack.config.js", "Webpack"],
74
+ ["rollup.config.js", "Rollup"],
75
+ ["next.config.js", "Next.js"],
76
+ ["nuxt.config.ts", "Nuxt"],
77
+ ["docker-compose.yml", "Docker Compose"],
78
+ ["docker-compose.yaml", "Docker Compose"],
79
+ ["Dockerfile", "Docker"],
80
+ ["pom.xml", "Maven"],
81
+ ["build.gradle", "Gradle"],
82
+ ["build.gradle.kts", "Gradle (Kotlin DSL)"],
83
+ ["settings.gradle.kts", "Gradle (Kotlin DSL)"],
84
+ ["Cargo.toml", "Cargo / Rust"],
85
+ ["go.mod", "Go modules"],
86
+ ["pyproject.toml", "Python (pyproject)"],
87
+ ["requirements.txt", "Python (requirements)"],
88
+ ["pubspec.yaml", "Flutter/Dart"],
89
+ [".eslintrc.json", "ESLint"],
90
+ ["eslint.config.js", "ESLint"],
91
+ [".prettierrc", "Prettier"],
92
+ ["vitest.config.js", "Vitest"],
93
+ ["vitest.config.ts", "Vitest"],
94
+ ["jest.config.js", "Jest"],
95
+ ["playwright.config.ts", "Playwright"],
96
+ ["electron-builder.yml", "electron-builder"],
97
+ ];
98
+
99
+ const LOCKFILE_PM = [
100
+ ["package-lock.json", "npm"],
101
+ ["pnpm-lock.yaml", "pnpm"],
102
+ ["yarn.lock", "yarn"],
103
+ ["bun.lockb", "bun"],
104
+ ];
105
+
106
+ function resolveDeps(opts) {
107
+ return {
108
+ fs: opts.deps?.fs || fsDefault,
109
+ path: opts.deps?.path || pathDefault,
110
+ };
111
+ }
112
+
113
+ function readJson(fs, p) {
114
+ try {
115
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
116
+ } catch {
117
+ return null;
118
+ }
119
+ }
120
+
121
+ function readText(fs, p) {
122
+ try {
123
+ return fs.readFileSync(p, "utf-8");
124
+ } catch {
125
+ return null;
126
+ }
127
+ }
128
+
129
+ /** First markdown paragraph after the title — a cheap README synopsis. */
130
+ export function readmeSynopsis(text, maxChars = 400) {
131
+ if (!text) return null;
132
+ const lines = String(text).split(/\r?\n/);
133
+ const out = [];
134
+ let started = false;
135
+ for (const line of lines) {
136
+ const t = line.trim();
137
+ if (!t) {
138
+ if (started) break;
139
+ continue;
140
+ }
141
+ if (/^#/.test(t) || /^!\[/.test(t) || /^<.*>$/.test(t) || /^\[!\[/.test(t))
142
+ continue; // headings / badges / bare HTML
143
+ out.push(t);
144
+ started = true;
145
+ }
146
+ const para = out.join(" ").trim();
147
+ if (!para) return null;
148
+ return para.length > maxChars ? `${para.slice(0, maxChars)}…` : para;
149
+ }
150
+
151
+ /**
152
+ * Walk the tree (bounded) counting files per language and remembering
153
+ * top-level directories with file counts.
154
+ */
155
+ function census(root, { fs, path, maxDepth, maxEntries }) {
156
+ const languages = new Map(); // lang -> count
157
+ const topDirs = new Map(); // top-level dir -> file count
158
+ let entries = 0;
159
+ let truncated = false;
160
+
161
+ const walk = (dir, depth, top) => {
162
+ if (entries >= maxEntries) {
163
+ truncated = true;
164
+ return;
165
+ }
166
+ let list;
167
+ try {
168
+ list = fs.readdirSync(dir, { withFileTypes: true });
169
+ } catch {
170
+ return;
171
+ }
172
+ for (const e of list) {
173
+ if (entries >= maxEntries) {
174
+ truncated = true;
175
+ return;
176
+ }
177
+ entries += 1;
178
+ if (e.isDirectory()) {
179
+ if (SKIP_DIRS.has(e.name) || e.name.startsWith(".")) continue;
180
+ if (depth < maxDepth) {
181
+ walk(path.join(dir, e.name), depth + 1, top || e.name);
182
+ }
183
+ } else if (e.isFile()) {
184
+ if (top) topDirs.set(top, (topDirs.get(top) || 0) + 1);
185
+ const ext = path.extname(e.name).toLowerCase();
186
+ const lang = EXT_LANG[ext];
187
+ if (lang) languages.set(lang, (languages.get(lang) || 0) + 1);
188
+ }
189
+ }
190
+ };
191
+ walk(root, 0, null);
192
+ return { languages, topDirs, truncated };
193
+ }
194
+
195
+ /**
196
+ * Inventory the folder. Pure data out — rendering is separate.
197
+ *
198
+ * @param {string} cwd
199
+ * @param {object} [opts] { deps, maxDepth, maxEntries }
200
+ */
201
+ export function inventoryProject(cwd, opts = {}) {
202
+ const { fs, path } = resolveDeps(opts);
203
+ const root = path.resolve(cwd);
204
+ const maxDepth = Number.isFinite(opts.maxDepth)
205
+ ? opts.maxDepth
206
+ : DEFAULT_MAX_DEPTH;
207
+ const maxEntries = Number.isFinite(opts.maxEntries)
208
+ ? opts.maxEntries
209
+ : DEFAULT_MAX_ENTRIES;
210
+
211
+ const pkg = readJson(fs, path.join(root, "package.json"));
212
+
213
+ let packageManager = null;
214
+ for (const [lock, pm] of LOCKFILE_PM) {
215
+ if (fs.existsSync(path.join(root, lock))) {
216
+ packageManager = pm;
217
+ break;
218
+ }
219
+ }
220
+
221
+ const configs = [];
222
+ for (const [file, label] of CONFIG_MARKERS) {
223
+ if (fs.existsSync(path.join(root, file)) && !configs.includes(label)) {
224
+ configs.push(label);
225
+ }
226
+ }
227
+
228
+ let ciWorkflows = 0;
229
+ try {
230
+ ciWorkflows = fs
231
+ .readdirSync(path.join(root, ".github", "workflows"))
232
+ .filter((f) => /\.ya?ml$/.test(f)).length;
233
+ } catch {
234
+ /* no CI dir */
235
+ }
236
+
237
+ const readme =
238
+ readText(fs, path.join(root, "README.md")) ||
239
+ readText(fs, path.join(root, "readme.md"));
240
+
241
+ const { languages, topDirs, truncated } = census(root, {
242
+ fs,
243
+ path,
244
+ maxDepth,
245
+ maxEntries,
246
+ });
247
+
248
+ const workspaces = Array.isArray(pkg?.workspaces)
249
+ ? pkg.workspaces
250
+ : Array.isArray(pkg?.workspaces?.packages)
251
+ ? pkg.workspaces.packages
252
+ : [];
253
+
254
+ const scripts = pkg?.scripts ? Object.entries(pkg.scripts) : [];
255
+
256
+ const existingMemory = ["cc.md", "CLAUDE.md", "AGENTS.md"].filter((f) =>
257
+ fs.existsSync(path.join(root, f)),
258
+ );
259
+
260
+ return {
261
+ root,
262
+ name: pkg?.name || path.basename(root),
263
+ description: pkg?.description || null,
264
+ synopsis: readmeSynopsis(readme),
265
+ packageManager,
266
+ configs,
267
+ ciWorkflows,
268
+ workspaces,
269
+ scripts,
270
+ languages: [...languages.entries()].sort((a, b) => b[1] - a[1]),
271
+ topDirs: [...topDirs.entries()].sort((a, b) => b[1] - a[1]),
272
+ truncated,
273
+ existingMemory,
274
+ };
275
+ }
276
+
277
+ /** Render the inventory as a starter cc.md project-memory file. */
278
+ export function renderMemoryFile(inv, opts = {}) {
279
+ const today = opts.date || new Date().toISOString().slice(0, 10);
280
+ const L = [];
281
+ L.push(`# ${inv.name} — Project Memory`);
282
+ L.push("");
283
+ L.push(
284
+ `> Generated by \`cc init --memory\` on ${today} from an inventory of this folder.`,
285
+ );
286
+ L.push(
287
+ "> `cc agent` auto-loads this file as authoritative project context — edit freely.",
288
+ );
289
+ L.push("");
290
+
291
+ // cc.md outranks CLAUDE.md/AGENTS.md in discovery — import any existing
292
+ // memory file so generating cc.md never shadows hand-maintained content.
293
+ const existing = (inv.existingMemory || []).filter((f) => f !== "cc.md");
294
+ if (existing.length) {
295
+ L.push("## Existing project memory (imported)");
296
+ L.push("");
297
+ for (const f of existing) L.push(`@${f}`);
298
+ L.push("");
299
+ }
300
+
301
+ if (inv.synopsis || inv.description) {
302
+ L.push("## Overview");
303
+ L.push("");
304
+ L.push(inv.synopsis || inv.description);
305
+ L.push("");
306
+ }
307
+
308
+ L.push("## Stack");
309
+ L.push("");
310
+ if (inv.languages.length) {
311
+ const langs = inv.languages
312
+ .slice(0, 8)
313
+ .map(([lang, n]) => `${lang} (${n})`)
314
+ .join(", ");
315
+ L.push(`- Languages by file count: ${langs}${inv.truncated ? " — scan truncated" : ""}`);
316
+ }
317
+ if (inv.packageManager) L.push(`- Package manager: ${inv.packageManager}`);
318
+ if (inv.configs.length) L.push(`- Tooling: ${inv.configs.join(", ")}`);
319
+ if (inv.ciWorkflows)
320
+ L.push(`- CI: GitHub Actions (${inv.ciWorkflows} workflows)`);
321
+ L.push("");
322
+
323
+ if (inv.scripts.length) {
324
+ L.push("## Commands");
325
+ L.push("");
326
+ const pm = inv.packageManager || "npm";
327
+ for (const [name, cmd] of inv.scripts.slice(0, 12)) {
328
+ L.push(`- \`${pm} run ${name}\` — \`${cmd}\``);
329
+ }
330
+ if (inv.scripts.length > 12) {
331
+ L.push(`- … ${inv.scripts.length - 12} more scripts in package.json`);
332
+ }
333
+ L.push("");
334
+ }
335
+
336
+ if (inv.workspaces.length || inv.topDirs.length) {
337
+ L.push("## Layout");
338
+ L.push("");
339
+ if (inv.workspaces.length) {
340
+ L.push(`- Workspaces: ${inv.workspaces.join(", ")}`);
341
+ }
342
+ for (const [dir, n] of inv.topDirs.slice(0, 10)) {
343
+ L.push(`- \`${dir}/\` — ${n} files`);
344
+ }
345
+ L.push("");
346
+ }
347
+
348
+ L.push("## Conventions");
349
+ L.push("");
350
+ L.push("- (add project rules here — code style, commit format, test policy)");
351
+ L.push("");
352
+ return L.join("\n");
353
+ }
354
+
355
+ export const _deps = { fs: fsDefault, path: pathDefault };
@@ -0,0 +1,142 @@
1
+ /**
2
+ * REPL `!` bash passthrough + `#` quick-memorize (Claude-Code parity).
3
+ *
4
+ * Pure logic for two agent-REPL input prefixes, extracted so it is unit
5
+ * testable without driving readline:
6
+ *
7
+ * - `! <cmd>` — run the shell command immediately (no LLM round-trip) and
8
+ * return a `<bash-input>/<bash-output>` context message so the model sees
9
+ * what happened on the next turn. Windows runs through `cmd.exe /d /s /c`
10
+ * with a `chcp 65001` prefix (encoding.md rule); POSIX through `/bin/sh -c`.
11
+ *
12
+ * - `# <note>` — append a one-line note to the project memory file (cc.md at
13
+ * the git root — the file project-instructions.js auto-loads). Creates the
14
+ * file/`## Notes` section when missing; next sessions pick it up
15
+ * automatically and the caller can also inject it into the live context.
16
+ *
17
+ * All process/fs access goes through `_deps` for tests.
18
+ */
19
+
20
+ import { spawnSync as spawnSyncDefault } from "child_process";
21
+ import fsDefault from "fs";
22
+ import pathDefault from "path";
23
+ import { findProjectRoot } from "./project-instructions.js";
24
+
25
+ export const BANG_TIMEOUT_MS = 120_000;
26
+ export const BANG_MAX_BUFFER = 10 * 1024 * 1024;
27
+ export const BANG_OUTPUT_CAP = 30_000;
28
+
29
+ export const _deps = {
30
+ spawnSync: spawnSyncDefault,
31
+ fs: fsDefault,
32
+ path: pathDefault,
33
+ };
34
+
35
+ function cap(s) {
36
+ const str = s || "";
37
+ return str.length > BANG_OUTPUT_CAP
38
+ ? `${str.slice(0, BANG_OUTPUT_CAP)}\n… [truncated]`
39
+ : str;
40
+ }
41
+
42
+ /** True when the REPL line is a `!` bash passthrough. */
43
+ export function isBangCommand(trimmed) {
44
+ return (
45
+ typeof trimmed === "string" &&
46
+ trimmed.startsWith("!") &&
47
+ trimmed.slice(1).trim().length > 0
48
+ );
49
+ }
50
+
51
+ /** True when the REPL line is a `#` quick-memorize. */
52
+ export function isMemorizeLine(trimmed) {
53
+ return (
54
+ typeof trimmed === "string" &&
55
+ trimmed.startsWith("#") &&
56
+ trimmed.slice(1).trim().length > 0
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Run a `!` command synchronously.
62
+ *
63
+ * @returns {{ cmd, stdout, stderr, exitCode, error, contextMessage }}
64
+ * `contextMessage` is ready to push as a user-role message.
65
+ */
66
+ export function runBangCommand(line, opts = {}) {
67
+ const spawnSync = opts.deps?.spawnSync || _deps.spawnSync;
68
+ const cwd = opts.cwd || process.cwd();
69
+ const isWin =
70
+ opts.platform != null ? opts.platform === "win32" : process.platform === "win32";
71
+ const cmd = String(line).replace(/^!/, "").trim();
72
+
73
+ const res = isWin
74
+ ? spawnSync("cmd.exe", ["/d", "/s", "/c", `chcp 65001 >nul && ${cmd}`], {
75
+ encoding: "utf-8",
76
+ timeout: BANG_TIMEOUT_MS,
77
+ maxBuffer: BANG_MAX_BUFFER,
78
+ cwd,
79
+ })
80
+ : spawnSync("/bin/sh", ["-c", cmd], {
81
+ encoding: "utf-8",
82
+ timeout: BANG_TIMEOUT_MS,
83
+ maxBuffer: BANG_MAX_BUFFER,
84
+ cwd,
85
+ });
86
+
87
+ const exitCode = res.status == null ? (res.error ? -1 : 0) : res.status;
88
+ const stdout = cap(res.stdout);
89
+ const stderr = cap(res.stderr);
90
+ const body = [stdout, stderr].filter(Boolean).join("\n");
91
+ return {
92
+ cmd,
93
+ stdout,
94
+ stderr,
95
+ exitCode,
96
+ error: res.error || null,
97
+ contextMessage: {
98
+ role: "user",
99
+ content: `<bash-input>${cmd}</bash-input>\n<bash-output exit-code="${exitCode}">\n${body}\n</bash-output>`,
100
+ },
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Append a `#` note to the project cc.md (created at the git root — falls
106
+ * back to cwd outside a repo). Inserts under a `## Notes` heading, creating
107
+ * file/section as needed.
108
+ *
109
+ * @returns {{ target, line, created }}
110
+ */
111
+ export function appendMemoryNote(rawLine, opts = {}) {
112
+ const fs = opts.deps?.fs || _deps.fs;
113
+ const path = opts.deps?.path || _deps.path;
114
+ const cwd = opts.cwd || process.cwd();
115
+ const note = String(rawLine).replace(/^#/, "").trim();
116
+ const stamp = opts.date || new Date().toISOString().slice(0, 10);
117
+
118
+ const root =
119
+ findProjectRoot(cwd, { deps: { fs, path } }) || path.resolve(cwd);
120
+ const target = opts.target || path.join(root, "cc.md");
121
+ const line = `- ${note} _(noted ${stamp})_`;
122
+
123
+ let text = null;
124
+ try {
125
+ text = fs.readFileSync(target, "utf-8");
126
+ } catch {
127
+ /* file does not exist yet */
128
+ }
129
+
130
+ let created = false;
131
+ if (text == null) {
132
+ text = `# Project Memory\n\n## Notes\n\n${line}\n`;
133
+ created = true;
134
+ } else if (/^## Notes\s*$/m.test(text)) {
135
+ // insert right after the heading (keeps newest notes on top)
136
+ text = text.replace(/^## Notes\s*$/m, (m) => `${m}\n\n${line}`);
137
+ } else {
138
+ text = `${text.trimEnd()}\n\n## Notes\n\n${line}\n`;
139
+ }
140
+ fs.writeFileSync(target, text, "utf-8");
141
+ return { target, line, note, created };
142
+ }
@@ -78,9 +78,12 @@ export function fileCandidates(prefix, { cwd = process.cwd(), deps } = {}) {
78
78
 
79
79
  /**
80
80
  * Build a readline completer. Returns `completer(line)` → `[hits, replaced]`
81
- * per the readline contract; non-@ lines complete to nothing.
81
+ * per the readline contract. Completes `@path` tokens anywhere in the line
82
+ * and `/command` names at line start (while the command token is still being
83
+ * typed); everything else completes to nothing.
82
84
  *
83
- * @param {object} opts { cwd?, getIdeOpenFiles?: () => Promise<string[]>, deps? }
85
+ * @param {object} opts { cwd?, getIdeOpenFiles?: () => Promise<string[]>,
86
+ * slashCommands?: string[], deps? }
84
87
  */
85
88
  export function makeAtCompleter(opts = {}) {
86
89
  const cwd = opts.cwd || process.cwd();
@@ -104,8 +107,12 @@ export function makeAtCompleter(opts = {}) {
104
107
  .map((f) => {
105
108
  const rel = path.relative(cwd, f);
106
109
  // Keep workspace files relative (the natural @ref form);
107
- // out-of-workspace files keep their absolute path.
108
- return rel && !rel.startsWith("..") ? fwd(rel) : fwd(f);
110
+ // out-of-workspace files keep their absolute path. On
111
+ // Windows a cross-drive relative() returns an *absolute*
112
+ // path (no ".." prefix), so isAbsolute must also gate it.
113
+ return rel && !rel.startsWith("..") && !path.isAbsolute(rel)
114
+ ? fwd(rel)
115
+ : fwd(f);
109
116
  })
110
117
  : [];
111
118
  ideFetchedAt = now();
@@ -118,7 +125,21 @@ export function makeAtCompleter(opts = {}) {
118
125
  });
119
126
  };
120
127
 
128
+ const slashCommands = Array.isArray(opts.slashCommands)
129
+ ? [...opts.slashCommands].sort()
130
+ : [];
131
+
121
132
  const completer = (line) => {
133
+ // `/command` completion (Claude-Code parity): only while typing the
134
+ // command token itself — once a space follows, args are the user's.
135
+ const slash = /^\/([A-Za-z_-]*)$/.exec(line);
136
+ if (slash && slashCommands.length) {
137
+ const pref = `/${slash[1].toLowerCase()}`;
138
+ const hits = slashCommands.filter((c) =>
139
+ c.toLowerCase().startsWith(pref),
140
+ );
141
+ return [hits, line];
142
+ }
122
143
  const at = extractAtPrefix(line);
123
144
  if (!at) return [[], line];
124
145
  refreshIde(); // async top-up for the NEXT tab; this one uses the cache
@@ -0,0 +1,107 @@
1
+ /**
2
+ * REPL conversation rewind — Claude-Code double-Esc parity (v1: conversation
3
+ * state; file state stays on `cc checkpoint restore`, hinted alongside).
4
+ *
5
+ * Pure helpers over the REPL's live `messages` array so the picker logic is
6
+ * unit-testable without readline:
7
+ * - listUserTurns(): newest-first numbered list of user messages
8
+ * - rewindToTurn(): truncate the conversation BACK TO BEFORE turn #n and
9
+ * return the original text so the caller can prefill the input line
10
+ * (edit-and-resend, like Claude Code's rewind).
11
+ *
12
+ * Trigger surfaces (wired in agent-repl): `/rewind` lists, `/rewind <n>`
13
+ * rewinds, and a double-Esc while idle prints the same list as a shortcut.
14
+ */
15
+
16
+ export const DEFAULT_LIST_LIMIT = 10;
17
+ export const PREVIEW_CHARS = 60;
18
+
19
+ function previewOf(content) {
20
+ const text =
21
+ typeof content === "string" ? content : JSON.stringify(content || "");
22
+ const flat = text.replace(/\s+/g, " ").trim();
23
+ return flat.length > PREVIEW_CHARS
24
+ ? `${flat.slice(0, PREVIEW_CHARS)}…`
25
+ : flat;
26
+ }
27
+
28
+ /**
29
+ * Newest-first user turns.
30
+ * @returns {Array<{n:number, index:number, preview:string, content:any}>}
31
+ * n is the 1-based pick number (1 = most recent user message).
32
+ */
33
+ export function listUserTurns(messages, { limit = DEFAULT_LIST_LIMIT } = {}) {
34
+ const turns = [];
35
+ for (let i = (messages || []).length - 1; i >= 0; i--) {
36
+ const m = messages[i];
37
+ if (!m || m.role !== "user") continue;
38
+ turns.push({
39
+ n: turns.length + 1,
40
+ index: i,
41
+ preview: previewOf(m.content),
42
+ content: m.content,
43
+ });
44
+ if (turns.length >= limit) break;
45
+ }
46
+ return turns;
47
+ }
48
+
49
+ /**
50
+ * Rewind the conversation to BEFORE the picked user turn (mutates `messages`
51
+ * in place — everything from that user message onward is dropped).
52
+ *
53
+ * @param {Array} messages live conversation array
54
+ * @param {number} n 1-based pick from listUserTurns
55
+ * @returns {{ removed:number, text:string|null }|null} null on bad pick;
56
+ * `text` is the original user text when it was a plain string
57
+ * (caller prefills the input line with it).
58
+ */
59
+ export function rewindToTurn(messages, n) {
60
+ const turns = listUserTurns(messages, { limit: 1000 });
61
+ const turn = turns.find((t) => t.n === Number(n));
62
+ if (!turn) return null;
63
+ const removed = messages.length - turn.index;
64
+ messages.splice(turn.index);
65
+ return {
66
+ removed,
67
+ text: typeof turn.content === "string" ? turn.content : null,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Offline extractive recap for a resumed conversation ("where were we") —
73
+ * no LLM call: turn counts + last ask + last reply previews.
74
+ * @returns {string[]|null} lines to print, or null when nothing to recap.
75
+ */
76
+ export function buildResumeRecap(messages, { previewChars = 160 } = {}) {
77
+ const list = messages || [];
78
+ const flat = (c) =>
79
+ (typeof c === "string" ? c : JSON.stringify(c || ""))
80
+ .replace(/\s+/g, " ")
81
+ .trim();
82
+ const cap = (s) =>
83
+ s.length > previewChars ? `${s.slice(0, previewChars)}…` : s;
84
+ const lastOf = (role) => {
85
+ for (let i = list.length - 1; i >= 0; i--) {
86
+ if (list[i]?.role === role) return list[i].content;
87
+ }
88
+ return null;
89
+ };
90
+ const users = list.filter((m) => m?.role === "user").length;
91
+ const assistants = list.filter((m) => m?.role === "assistant").length;
92
+ if (!users && !assistants) return null;
93
+ const lines = [`${users} user / ${assistants} assistant turns`];
94
+ const lu = lastOf("user");
95
+ if (lu) lines.push(`last ask : ${cap(flat(lu))}`);
96
+ const la = lastOf("assistant");
97
+ if (la) lines.push(`last reply: ${cap(flat(la))}`);
98
+ return lines;
99
+ }
100
+
101
+ /** Render the picker list (shared by /rewind and double-Esc). */
102
+ export function renderTurnList(turns) {
103
+ if (!turns.length) return " (no user turns yet)";
104
+ return turns
105
+ .map((t) => ` ${String(t.n).padStart(2)}. ${t.preview}`)
106
+ .join("\n");
107
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Detached cache refresher for the startup update notice.
3
+ * Spawned unref'd by update-notice.js; argv[2] = cache file path.
4
+ * Exits quietly on any failure — the notice is strictly best-effort.
5
+ */
6
+ import { refreshCacheOnce } from "./update-notice.js";
7
+
8
+ refreshCacheOnce({ cacheFile: process.argv[2] })
9
+ .catch(() => {})
10
+ .finally(() => process.exit(0));