chainlesschain 0.162.38 → 0.162.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +368 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-DV0Q9zKL.js → AIOps-CPmKv82o.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-C6vH8rhL.js → ActionButton-BNDYY7Qd.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BvPDc2ui.js → Analytics-BgCMCOsk.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CWnyqTqY.js → AppLayout-Dv4oJcqS.js} +5 -5
  7. package/src/assets/web-panel/assets/{Audit-BzenidV4.js → Audit-5iV3yrGa.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-CSl7bNwK.js → Backup-CHDhnbzF.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-DAY3iHIq.js → BaseInput-B6reFkra.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-Jyhm9fgk.js → Chat-DwS5YyE2.js} +6 -6
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CwlAnVjy.js → ChatBubbleRenderer-CqXa87Hw.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-D4rwURAi.js → Checkbox-yiW0M4RE.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-DYdjTEfC.js → Codegen-DoiVuD_g.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-DsVyZ_fS.js → Col-BVASLexk.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-CjCpl27Q.js → Community-D6KQ7JoU.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-kt18dsjm.js → Compact-Bl9Uhb6v.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-BV5urquU.js → Compliance-MM31-dba.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-C4SovPWC.js → Cowork-PjU_1ieD.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-uuNs_xzA.js → Cron-DorNtPZL.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-DR5a65tR.js → Crosschain-Bm5ts2Kw.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-B1KTf2-5.js → DID-7Y3jlFdY.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-Dkj7XgED.js → Dashboard-1oE532bG.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-BhXCuJ19.js → Dropdown-hJlOPs0s.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-DG8365Iv.js → EmailListRenderer-BEqJxKaO.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BdHGPu39.js → FamilyGuardDashboard-BvCGwB6X.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-Dwvxl0zR.js → Federation-CsXI72e5.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-BVmhCVWU.js → FormItemContext-Dh9SMul-.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-DDPjvF2s.js → GenericCardRenderer-9edWzrtG.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-foK6WTSr.js → Git-ZYhNL8Xk.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-CfqMdu6Y.js → Governance-BwAdp8QA.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-BKrLO4GO.js → Inference-5C-M1XsH.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-6o6Q-mmF.js → KnowledgeGraph-zFAi-zCi.js} +1 -1
  33. package/src/assets/web-panel/assets/Logs-BZsEdbgE.js +2 -0
  34. package/src/assets/web-panel/assets/{Marketplace-BWkfEocP.js → Marketplace-BP6gErRK.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-BPebQbWU.js → McpTools-CXVzoLrd.js} +4 -4
  36. package/src/assets/web-panel/assets/{Memory-C0Dq-X3C.js → Memory-BIpChb4-.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-DRBoutTY.js → MobileBridge-B4O7wDT8.js} +2 -2
  38. package/src/assets/web-panel/assets/MobileProjects-7VPMoHus.js +1 -0
  39. package/src/assets/web-panel/assets/{Mtc-Cj3QPM9p.js → Mtc-BTmEyTM5.js} +6 -6
  40. package/src/assets/web-panel/assets/{MtcAudit-rBQYbfQR.js → MtcAudit-CsbG9LlV.js} +2 -2
  41. package/src/assets/web-panel/assets/{Multisig-Dbuy4OY4.js → Multisig-CL8yoGon.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-CMnt1se-.js → NLProgramming-C2cIlIp_.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-BX9tSCiF.js → Notes-7aBk_n_M.js} +4 -4
  44. package/src/assets/web-panel/assets/{NotificationSettings-BFeirVRq.js → NotificationSettings-BuhQk4rJ.js} +1 -1
  45. package/src/assets/web-panel/assets/{OrderTableRenderer-ybiMlKQW.js → OrderTableRenderer-mqMFZu0x.js} +1 -1
  46. package/src/assets/web-panel/assets/{Organization-kTfRxKqk.js → Organization-CAdq-170.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow-CtuCAzwV.js → Overflow--Xn0E787.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-KfbciaP3.js → P2P-DYt3YAXI.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-bqEUFhgC.js → PdhVaultBrowser-Bgb_v8WN.js} +5 -5
  50. package/src/assets/web-panel/assets/{Permissions-BgMypz-z.js → Permissions-DoFlmoaW.js} +3 -3
  51. package/src/assets/web-panel/assets/{PersonalDataHub-C3zUE-1z.js → PersonalDataHub-C-FJB3a0.js} +2 -2
  52. package/src/assets/web-panel/assets/{Pipeline-iX-pYHpC.js → Pipeline-3bL2RzzL.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-B01uzeFM.js → Privacy-c4igYUCF.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-TsfbzJp7.js → ProjectInit-C0QS1UPR.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-iGvMp8sM.js → ProjectSettings-CkYC0xkE.js} +2 -2
  56. package/src/assets/web-panel/assets/{Projects-Be9k29iQ.js → Projects-Di17SYft.js} +1 -1
  57. package/src/assets/web-panel/assets/{Providers-C9Pc8dqo.js → Providers-41NySsLt.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-DN_yFiVO.js → QuickAsk-DHq9pD7z.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-CvSNgl7H.js → Recommend-CLjgFPLv.js} +1 -1
  60. package/src/assets/web-panel/assets/{Reputation-S6BCz8xH.js → Reputation-EIrgErm3.js} +1 -1
  61. package/src/assets/web-panel/assets/{Row-CTRYCaqP.js → Row-GAvKzKH7.js} +1 -1
  62. package/src/assets/web-panel/assets/{RssFeed-Cu8_P5ll.js → RssFeed-CYCNsVmD.js} +3 -3
  63. package/src/assets/web-panel/assets/{Search-rZ1Xza_U.js → Search-DWOE32k8.js} +1 -1
  64. package/src/assets/web-panel/assets/{Security-CF43IJHX.js → Security-Dgh8Jevn.js} +4 -4
  65. package/src/assets/web-panel/assets/{Services-BobNHzne.js → Services-BxdgP67N.js} +2 -2
  66. package/src/assets/web-panel/assets/{Skeleton-DWJ2kfuI.js → Skeleton-D-xT4ZkA.js} +1 -1
  67. package/src/assets/web-panel/assets/{Skills-AmEZgHYr.js → Skills-BKN4lfSa.js} +1 -1
  68. package/src/assets/web-panel/assets/{Sla-DTS-fBiY.js → Sla--N1TudpS.js} +1 -1
  69. package/src/assets/web-panel/assets/{SpeechSettings-DEr6MHRU.js → SpeechSettings-B0vfJpEh.js} +1 -1
  70. package/src/assets/web-panel/assets/{SyncSettings-CVs9alv_.js → SyncSettings-BuBAbPAh.js} +2 -2
  71. package/src/assets/web-panel/assets/Tasks-4XugjJ87.js +1 -0
  72. package/src/assets/web-panel/assets/{Templates-CTNjZRKA.js → Templates-DI2giLgc.js} +1 -1
  73. package/src/assets/web-panel/assets/{Tenant-DPbXg0Pg.js → Tenant-BiTWvm0g.js} +1 -1
  74. package/src/assets/web-panel/assets/{Terminal-DhKXcPw2.js → Terminal-vV6AWGDi.js} +2 -2
  75. package/src/assets/web-panel/assets/{TimelineRenderer-B0DMZOpk.js → TimelineRenderer-BmgzKdAp.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tokens-RvWuBXgg.js → Tokens-Nvupdm6p.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trigger-2O-BaTQG.js → Trigger-DRfR77WJ.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trust-6qY35L-C.js → Trust-De0Jal_6.js} +1 -1
  79. package/src/assets/web-panel/assets/{UkeySign-DhV1wYtQ.js → UkeySign-Dzo4-VAM.js} +1 -1
  80. package/src/assets/web-panel/assets/{VideoEditing-DgqA5UZm.js → VideoEditing-hg2ytiJB.js} +1 -1
  81. package/src/assets/web-panel/assets/{Wallet-DJRYdUAK.js → Wallet--bU5-gRh.js} +4 -4
  82. package/src/assets/web-panel/assets/{WebAuthn-C2W-x0cg.js → WebAuthn-DZptt-PV.js} +5 -5
  83. package/src/assets/web-panel/assets/{WorkflowEditor-BP2tkDHe.js → WorkflowEditor-Dy9223bY.js} +1 -1
  84. package/src/assets/web-panel/assets/{chat-CGVfeoTn.js → chat-DaxGeI9w.js} +1 -1
  85. package/src/assets/web-panel/assets/{colors-BmjRolM1.js → colors-Cu2VEci3.js} +1 -1
  86. package/src/assets/web-panel/assets/{compact-item-BvJJkjZE.js → compact-item-CGolhyJq.js} +1 -1
  87. package/src/assets/web-panel/assets/{createContext-DyhlvRYs.js → createContext-DY7EFhkD.js} +1 -1
  88. package/src/assets/web-panel/assets/devWarning-DV2BNd59.js +1 -0
  89. package/src/assets/web-panel/assets/{hasIn-BoBMR89s.js → hasIn-Bpc-NoFN.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-CSgbOGaP.js → index-1D4sfByw.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-C1t-r7yV.js → index-8h9y5S6X.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-FKFT-QTk.js → index-BP9P6chP.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-CIaGw7vl.js → index-BQ2z6Ky5.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CQJVedQ3.js → index-BRAgl2J_.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BycpeGfj.js → index-BTvwiqJE.js} +1 -1
  96. package/src/assets/web-panel/assets/index-BZqtTmyG.js +1 -0
  97. package/src/assets/web-panel/assets/{index-D0YToIi_.js → index-BjfxHEmX.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BT1SQ9nj.js → index-BlHq81Ow.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DyS4I4L-.js → index-Bn5gM9Oy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-81tWFqfN.js → index-Bz83ngs0.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-xZdOioVg.js → index-C-Hkl_2G.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cbh-lCxq.js → index-C0_zeYnx.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-xPSzUoWT.js → index-C2RpsAiO.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-n-N19np-.js → index-CBSk_VrT.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-VXVukhBA.js → index-CFAnEzRW.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DtKdCXHW.js → index-CGqeHu_F.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Beh7jDbS.js → index-CJFYF8F9.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BvvNnWXe.js → index-CLNqZF55.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DeeLHcMY.js → index-CaKXhpEu.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BuQrONgf.js → index-Ciw5-X1B.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CDPMHKQi.js → index-D0GN5tdM.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Bm_MmdwP.js → index-D63ObMdQ.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DIPZ6hbJ.js → index-DAov-rJR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-39VDXdn6.js → index-DElatOQ0.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DwTgvhOL.js → index-DNX81oSR.js} +1 -1
  116. package/src/assets/web-panel/assets/index-DUpwdJt9.js +1 -0
  117. package/src/assets/web-panel/assets/{index-DgbWSwr5.js → index-DZ4Vm8dQ.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-D-93XwJd.js → index-DexYD87j.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C0xn6hOr.js → index-DfKmAEtE.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-ZNIms1nA.js → index-DldaToUA.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-wLAjVpmJ.js → index-DpRSzAFl.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Te0ruvY_.js → index-DxXkr-NS.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Czsbrn75.js → index-RumxOD0S.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-vF1pR00A.js → index-VBRPxZeE.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BqGNmoKy.js → index-eF9RV_4c.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CzDVBBcg.js → index-lfP8sdzB.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Y1b8i0NV.js → index-oJQgRCrR.js} +3 -3
  128. package/src/assets/web-panel/assets/{index-ByWpNjTj.js → index-rkm7dHwG.js} +1 -1
  129. package/src/assets/web-panel/assets/{initDefaultProps-BLKSE8he.js → initDefaultProps-CkJZfCo8.js} +1 -1
  130. package/src/assets/web-panel/assets/{motion-Bb59qqLK.js → motion-BerbusV1.js} +1 -1
  131. package/src/assets/web-panel/assets/{move-CB3pYCk6.js → move-DyRzKPD4.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-iImQWuU7.js → omit-CCdrTUAs.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-DRP2Chqo.js → pickAttrs-mVDeZx2m.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-BrlfD4tF.js → placementArrow-Bb_-Fs_o.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-Cqxkuh5H.js → responsiveObserve-C6TMj1R_.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-nxKEuLMj.js → slide-CdCNsy1J.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-30E47KSk.js → statusUtils-Ccxd1rFd.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-Dn2_-5bn.js → styleChecker-3IL-yw1V.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-DkZ00X6F.js → useFlexGapSupport-CH8DjUHl.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-ByrwSCOr.js → useFs-Cn9nE2sp.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-BDY6jtUD.js → usePersonalDataHub-BPyT0HO7.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-BL2q5BLv.js → vnode-Mfm7vy07.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-BSkPKE42.js → zoom-CTpAiAE9.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/init.js +84 -2
  146. package/src/commands/session.js +36 -12
  147. package/src/index.js +10 -0
  148. package/src/lib/agent-session-export.js +124 -0
  149. package/src/lib/ide-context.js +333 -0
  150. package/src/lib/project-instructions.js +275 -0
  151. package/src/lib/project-inventory.js +355 -0
  152. package/src/lib/repl-bang-memorize.js +142 -0
  153. package/src/lib/repl-completer.js +154 -0
  154. package/src/lib/update-notice-refresh.mjs +10 -0
  155. package/src/lib/update-notice.js +154 -0
  156. package/src/repl/agent-repl.js +154 -0
  157. package/src/runtime/agent-core.js +195 -0
  158. package/src/runtime/headless-runner.js +19 -0
  159. package/src/runtime/headless-stream.js +19 -9
  160. package/src/runtime/system-prompt.js +21 -1
  161. package/src/assets/web-panel/assets/Logs-L5ZIW0Dz.js +0 -2
  162. package/src/assets/web-panel/assets/MobileProjects-BMP6eLp1.js +0 -1
  163. package/src/assets/web-panel/assets/Tasks-BcVDAxdi.js +0 -1
  164. package/src/assets/web-panel/assets/devWarning-CetO0WH0.js +0 -1
  165. package/src/assets/web-panel/assets/index-BZVz-WfV.js +0 -1
  166. package/src/assets/web-panel/assets/index-D0-bvFy3.js +0 -1
@@ -0,0 +1,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
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * REPL `@` file-reference tab-completion (Claude-Code @-mention parity).
3
+ *
4
+ * Typing `@src/fo<TAB>` in the agent REPL completes file/dir paths the same
5
+ * way the @-token will later be expanded by file-ref-expander. Two candidate
6
+ * sources, merged:
7
+ * 1. the filesystem under cwd (dirs get a trailing `/` so TAB can descend)
8
+ * 2. the connected IDE's OPEN EDITOR TABS (when the IDE bridge is up) —
9
+ * the files you are working on rank first, before any fs listing.
10
+ *
11
+ * IDE tabs come from an injected async `getIdeOpenFiles()`; the completer
12
+ * caches the last list (TTL) and refreshes in the background, so completion
13
+ * stays synchronous-fast and a slow/dead IDE costs nothing (first TAB simply
14
+ * has no IDE entries yet).
15
+ */
16
+ import fs from "fs";
17
+ import path from "path";
18
+
19
+ /** Refresh the IDE open-editors list at most this often. */
20
+ const IDE_CACHE_TTL_MS = 5000;
21
+ /** Hard cap on candidates shown per TAB. */
22
+ const MAX_CANDIDATES = 30;
23
+
24
+ /**
25
+ * Find a trailing `@token` being typed at the end of the line. Returns
26
+ * `{ prefix }` (token without `@`, may be "") or null when the cursor is not
27
+ * on an @-token. An @ must start the line or follow whitespace, mirroring
28
+ * file-ref-expander's token rules.
29
+ */
30
+ export function extractAtPrefix(line) {
31
+ const m = /(^|\s)@([^\s@]*)$/.exec(line || "");
32
+ return m ? { prefix: m[2] } : null;
33
+ }
34
+
35
+ /** Normalize to forward slashes (what @refs use on every platform). */
36
+ function fwd(p) {
37
+ return String(p).replace(/\\/g, "/");
38
+ }
39
+
40
+ /**
41
+ * Filesystem candidates for an @-prefix, relative to cwd. Directories get a
42
+ * trailing `/`. Missing dirs / unreadable entries → empty (best-effort).
43
+ */
44
+ export function fileCandidates(prefix, { cwd = process.cwd(), deps } = {}) {
45
+ const readdir = deps?.readdir || ((d) => fs.readdirSync(d));
46
+ const isDir =
47
+ deps?.isDir ||
48
+ ((p) => {
49
+ try {
50
+ return fs.statSync(p).isDirectory();
51
+ } catch {
52
+ return false;
53
+ }
54
+ });
55
+ const norm = fwd(prefix);
56
+ const slash = norm.lastIndexOf("/");
57
+ const dirPart = slash >= 0 ? norm.slice(0, slash + 1) : "";
58
+ const basePart = slash >= 0 ? norm.slice(slash + 1) : norm;
59
+ const absDir = path.resolve(cwd, dirPart || ".");
60
+ let names;
61
+ try {
62
+ names = readdir(absDir);
63
+ } catch {
64
+ return [];
65
+ }
66
+ const out = [];
67
+ for (const name of names) {
68
+ if (basePart && !name.toLowerCase().startsWith(basePart.toLowerCase())) {
69
+ continue;
70
+ }
71
+ if (name === "node_modules" || name === ".git") continue;
72
+ const rel = dirPart + name;
73
+ out.push(isDir(path.join(absDir, name)) ? `${rel}/` : rel);
74
+ if (out.length >= MAX_CANDIDATES) break;
75
+ }
76
+ return out;
77
+ }
78
+
79
+ /**
80
+ * Build a readline completer. Returns `completer(line)` → `[hits, replaced]`
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.
84
+ *
85
+ * @param {object} opts { cwd?, getIdeOpenFiles?: () => Promise<string[]>,
86
+ * slashCommands?: string[], deps? }
87
+ */
88
+ export function makeAtCompleter(opts = {}) {
89
+ const cwd = opts.cwd || process.cwd();
90
+ const getIde = opts.getIdeOpenFiles || null;
91
+ const now = opts.deps?.now || Date.now;
92
+ let ideFiles = [];
93
+ let ideFetchedAt = -Infinity; // "never" — the first @ must always fetch
94
+ let ideInFlight = false;
95
+
96
+ const refreshIde = () => {
97
+ if (!getIde || ideInFlight || now() - ideFetchedAt < IDE_CACHE_TTL_MS) {
98
+ return;
99
+ }
100
+ ideInFlight = true;
101
+ Promise.resolve()
102
+ .then(() => getIde())
103
+ .then((files) => {
104
+ ideFiles = Array.isArray(files)
105
+ ? files
106
+ .filter((f) => typeof f === "string" && f.length > 0)
107
+ .map((f) => {
108
+ const rel = path.relative(cwd, f);
109
+ // Keep workspace files relative (the natural @ref form);
110
+ // out-of-workspace files keep their absolute path.
111
+ return rel && !rel.startsWith("..") ? fwd(rel) : fwd(f);
112
+ })
113
+ : [];
114
+ ideFetchedAt = now();
115
+ })
116
+ .catch(() => {
117
+ ideFetchedAt = now(); // don't hammer a dead IDE
118
+ })
119
+ .finally(() => {
120
+ ideInFlight = false;
121
+ });
122
+ };
123
+
124
+ const slashCommands = Array.isArray(opts.slashCommands)
125
+ ? [...opts.slashCommands].sort()
126
+ : [];
127
+
128
+ const completer = (line) => {
129
+ // `/command` completion (Claude-Code parity): only while typing the
130
+ // command token itself — once a space follows, args are the user's.
131
+ const slash = /^\/([A-Za-z_-]*)$/.exec(line);
132
+ if (slash && slashCommands.length) {
133
+ const pref = `/${slash[1].toLowerCase()}`;
134
+ const hits = slashCommands.filter((c) => c.toLowerCase().startsWith(pref));
135
+ return [hits, line];
136
+ }
137
+ const at = extractAtPrefix(line);
138
+ if (!at) return [[], line];
139
+ refreshIde(); // async top-up for the NEXT tab; this one uses the cache
140
+ const norm = fwd(at.prefix).toLowerCase();
141
+ const fromIde = ideFiles.filter((f) => f.toLowerCase().startsWith(norm));
142
+ const fromFs = fileCandidates(at.prefix, { cwd, deps: opts.deps });
143
+ const merged = [...new Set([...fromIde, ...fromFs])].slice(
144
+ 0,
145
+ MAX_CANDIDATES,
146
+ );
147
+ // readline replaces `replaced` with the chosen hit — keep the `@`.
148
+ return [merged.map((m) => `@${m}`), `@${at.prefix}`];
149
+ };
150
+ // test seam: expose the cache refresher state
151
+ completer._refreshIde = refreshIde;
152
+ completer._ideState = () => ({ ideFiles, ideFetchedAt });
153
+ return completer;
154
+ }
@@ -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));