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,364 @@
1
+ /**
2
+ * Project-memory loader — file-based project instructions for `cc agent`
3
+ * (Claude-Code CLAUDE.md-hierarchy parity, with our own file name).
4
+ *
5
+ * The primary instruction file is **`cc.md`** (ChainlessChain branding);
6
+ * `CLAUDE.md` and `AGENTS.md` are accepted as compatibility fallbacks so any
7
+ * repo that already carries Claude-Code/agent memory works with zero setup.
8
+ *
9
+ * Discovery order (first existing name wins per location):
10
+ *
11
+ * 1. user scope : `~/.chainlesschain/cc.md`, else `~/.claude/CLAUDE.md`
12
+ * 2. project scope: per directory from <git-root> down to <cwd> —
13
+ * `cc.md` → `CLAUDE.md` → `AGENTS.md`
14
+ * (root-first, so deeper files refine shallower ones)
15
+ * 3. local scope : `cc.local.md` → `CLAUDE.local.md` next to each project
16
+ * file (gitignored personal notes)
17
+ *
18
+ * `@path` import lines inside an instruction file pull in the referenced file
19
+ * (resolved relative to the importing file; `~/` works too), recursively up to
20
+ * MAX_IMPORT_DEPTH with cycle protection. Tokens inside fenced code blocks and
21
+ * tokens that don't resolve to a real file (npm scopes like `@scope/pkg`,
22
+ * emails) are ignored silently.
23
+ *
24
+ * Loading is fail-open: any I/O error yields an empty block — composing the
25
+ * system prompt must never crash because of a bad memory file. All fs access
26
+ * goes through an injectable `deps` seam (project `_deps` philosophy) and all
27
+ * reads are explicit UTF-8 (encoding.md rule).
28
+ *
29
+ * Disable globally with `CC_PROJECT_MEMORY=0`, per-call with
30
+ * `projectMemory: false` on composeSystemPrompt.
31
+ */
32
+
33
+ import fsDefault from "fs";
34
+ import pathDefault from "path";
35
+ import osDefault from "os";
36
+
37
+ export const DEFAULT_MAX_FILE_BYTES = 48 * 1024; // per instruction/import file
38
+ export const DEFAULT_MAX_TOTAL_BYTES = 192 * 1024; // whole block budget
39
+ export const MAX_IMPORT_DEPTH = 5;
40
+
41
+ /** Per-directory project file names, first match wins. */
42
+ export const PROJECT_FILE_NAMES = ["cc.md", "CLAUDE.md", "AGENTS.md"];
43
+ /** Local (gitignored) companion names, first match wins. */
44
+ export const LOCAL_FILE_NAMES = ["cc.local.md", "CLAUDE.local.md"];
45
+
46
+ // Same boundary rule as file-ref-expander: `@` at start / after whitespace or
47
+ // an opening bracket-quote, so emails and decorative @ never match.
48
+ const IMPORT_TOKEN_RE = /(^|[\s("'`[{])@([^\s"'`)\]}]+)/g;
49
+
50
+ function resolveDeps(opts) {
51
+ return {
52
+ fs: opts.deps?.fs || fsDefault,
53
+ path: opts.deps?.path || pathDefault,
54
+ os: opts.deps?.os || osDefault,
55
+ };
56
+ }
57
+
58
+ function isFile(fs, p) {
59
+ try {
60
+ return fs.statSync(p).isFile();
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ /** First existing candidate among `names` inside `dir`, or null. */
67
+ function firstExisting(fs, path, dir, names) {
68
+ for (const name of names) {
69
+ const p = path.join(dir, name);
70
+ if (isFile(fs, p)) return p;
71
+ }
72
+ return null;
73
+ }
74
+
75
+ /** Walk up from cwd looking for a `.git` marker; null when not in a repo. */
76
+ export function findProjectRoot(cwd, opts = {}) {
77
+ const { fs, path } = resolveDeps(opts);
78
+ let dir = path.resolve(cwd);
79
+ for (;;) {
80
+ try {
81
+ if (fs.existsSync(path.join(dir, ".git"))) return dir;
82
+ } catch {
83
+ /* keep walking */
84
+ }
85
+ const parent = path.dirname(dir);
86
+ if (parent === dir) return null;
87
+ dir = parent;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Ordered instruction-file discovery (user → project root → … → cwd, with the
93
+ * local companion right after its project file). Only existing files are
94
+ * returned. Deduped by absolute path (covers cwd == home corner cases).
95
+ *
96
+ * @returns {Array<{path:string, scope:"user"|"project"|"local"|"rules"}>}
97
+ */
98
+ export function findInstructionFiles(opts = {}) {
99
+ const { fs, path, os } = resolveDeps(opts);
100
+ const cwd = path.resolve(opts.cwd || process.cwd());
101
+ const home = opts.home || os.homedir() || "";
102
+
103
+ const seen = new Set();
104
+ const out = [];
105
+ const push = (p, scope) => {
106
+ if (!p) return;
107
+ const abs = path.resolve(p);
108
+ if (seen.has(abs) || !isFile(fs, abs)) return;
109
+ seen.add(abs);
110
+ out.push({ path: abs, scope });
111
+ };
112
+
113
+ if (home) {
114
+ push(
115
+ firstExisting(fs, path, home, [
116
+ path.join(".chainlesschain", "cc.md"),
117
+ path.join(".claude", "CLAUDE.md"),
118
+ ]),
119
+ "user",
120
+ );
121
+ }
122
+
123
+ const root = findProjectRoot(cwd, opts) || cwd;
124
+ const chain = [];
125
+ let dir = cwd;
126
+ for (;;) {
127
+ chain.unshift(dir);
128
+ if (dir === root) break;
129
+ const parent = path.dirname(dir);
130
+ if (parent === dir) break;
131
+ dir = parent;
132
+ }
133
+ for (const d of chain) {
134
+ push(firstExisting(fs, path, d, PROJECT_FILE_NAMES), "project");
135
+ push(firstExisting(fs, path, d, LOCAL_FILE_NAMES), "local");
136
+ // Template-scaffolded project rules (`cc init -t` writes these) join the
137
+ // chain too, so scaffold-flow and memory-flow projects both feed the agent.
138
+ push(path.join(d, ".chainlesschain", "rules.md"), "rules");
139
+ // Path-scoped rule files (`.claude/rules/*.md`, YAML frontmatter `paths:`
140
+ // globs). Glob filtering happens at LOAD time where content is available.
141
+ try {
142
+ const rulesDir = path.join(d, ".claude", "rules");
143
+ for (const f of fs
144
+ .readdirSync(rulesDir)
145
+ .filter((n) => n.endsWith(".md"))
146
+ .sort()) {
147
+ push(path.join(rulesDir, f), "rule");
148
+ }
149
+ } catch {
150
+ /* no rules dir */
151
+ }
152
+ }
153
+ return out;
154
+ }
155
+
156
+ /**
157
+ * Parse a rule file's YAML-ish frontmatter (zero-dep): `paths:`/`globs:` as a
158
+ * dash-list or inline value. Returns { globs, body } with frontmatter
159
+ * stripped from body; files without frontmatter pass through unchanged.
160
+ */
161
+ export function parseRuleFrontmatter(text) {
162
+ const str = String(text);
163
+ const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(str);
164
+ if (!m) return { globs: [], body: str };
165
+ const globs = [];
166
+ let inPaths = false;
167
+ const take = (raw) => {
168
+ const v = raw.trim().replace(/^["']|["']$/g, "");
169
+ if (v) globs.push(v);
170
+ };
171
+ for (const rawLine of m[1].split(/\r?\n/)) {
172
+ const line = rawLine.trim();
173
+ const key = /^(paths|globs)\s*:\s*(.*)$/.exec(line);
174
+ if (key) {
175
+ inPaths = true;
176
+ const inline = key[2].trim();
177
+ if (inline) {
178
+ for (const g of inline.startsWith("[")
179
+ ? inline.replace(/^\[|\]$/g, "").split(",")
180
+ : [inline]) {
181
+ take(g);
182
+ }
183
+ }
184
+ continue;
185
+ }
186
+ if (inPaths) {
187
+ const item = /^-\s*(.+)$/.exec(line);
188
+ if (item) take(item[1]);
189
+ else if (line) inPaths = false;
190
+ }
191
+ }
192
+ return { globs, body: str.slice(m[0].length) };
193
+ }
194
+
195
+ /**
196
+ * Does a path-scoped rule apply when the agent runs at `relCwd` (cwd relative
197
+ * to the dir holding `.claude/`)? v1 prefix-overlap semantics: the glob's
198
+ * literal prefix and the cwd must sit on the same path line — running at the
199
+ * project root loads every rule; running inside packages/cli loads rules
200
+ * whose glob prefix is packages/cli plus prefixless globs (star-star
201
+ * patterns). Finer tool-time injection is a later phase (module 99 §5.3).
202
+ */
203
+ export function ruleApplies(globs, relCwd) {
204
+ if (!globs || globs.length === 0) return true;
205
+ const cwd = String(relCwd || "")
206
+ .replace(/\\/g, "/")
207
+ .replace(/^\.\/?/, "");
208
+ if (!cwd) return true; // at the project root every rule is in play
209
+ for (const glob of globs) {
210
+ const g = String(glob).replace(/\\/g, "/");
211
+ const star = g.search(/[*?[]/);
212
+ const prefix = (star === -1 ? g : g.slice(0, star)).replace(/\/+$/, "");
213
+ if (!prefix) return true; // "**/*.js" — applies everywhere
214
+ if (cwd === prefix || cwd.startsWith(`${prefix}/`) || prefix.startsWith(`${cwd}/`)) {
215
+ return true;
216
+ }
217
+ }
218
+ return false;
219
+ }
220
+
221
+ /**
222
+ * Collect `@path` import tokens from instruction text, skipping fenced code
223
+ * blocks (``` / ~~~). Line-level scanning is good enough for memory files,
224
+ * which use imports on their own prose lines.
225
+ */
226
+ export function collectImportTokens(text) {
227
+ const found = [];
228
+ let inFence = false;
229
+ for (const line of String(text).split(/\r?\n/)) {
230
+ if (/^\s*(```|~~~)/.test(line)) {
231
+ inFence = !inFence;
232
+ continue;
233
+ }
234
+ if (inFence) continue;
235
+ IMPORT_TOKEN_RE.lastIndex = 0;
236
+ let m;
237
+ while ((m = IMPORT_TOKEN_RE.exec(line)) !== null) {
238
+ if (m[2]) found.push(m[2]);
239
+ }
240
+ }
241
+ return found;
242
+ }
243
+
244
+ function readCapped(fs, abs, maxFileBytes) {
245
+ const buf = fs.readFileSync(abs);
246
+ const truncated = buf.length > maxFileBytes;
247
+ const content = (truncated ? buf.slice(0, maxFileBytes) : buf).toString(
248
+ "utf-8",
249
+ );
250
+ return { content, bytes: buf.length, truncated };
251
+ }
252
+
253
+ /**
254
+ * Load the full instruction set: hierarchy files + recursive imports.
255
+ *
256
+ * @param {object} [opts] { cwd, home, deps, maxFileBytes, maxTotalBytes }
257
+ * @returns {{ files: Array<{path,scope,bytes,truncated,content}>, warnings: string[] }}
258
+ */
259
+ export function loadProjectInstructions(opts = {}) {
260
+ const { fs, path, os } = resolveDeps(opts);
261
+ const home = opts.home || os.homedir() || "";
262
+ const maxFileBytes = Number.isFinite(opts.maxFileBytes)
263
+ ? opts.maxFileBytes
264
+ : DEFAULT_MAX_FILE_BYTES;
265
+ const maxTotalBytes = Number.isFinite(opts.maxTotalBytes)
266
+ ? opts.maxTotalBytes
267
+ : DEFAULT_MAX_TOTAL_BYTES;
268
+
269
+ const roots = findInstructionFiles(opts);
270
+ const visited = new Set(roots.map((r) => r.path));
271
+ const out = [];
272
+ const warnings = [];
273
+ let total = 0;
274
+
275
+ // Queue of { abs, scope, depth } — imports inherit "import" scope.
276
+ const queue = roots.map((r) => ({ abs: r.path, scope: r.scope, depth: 0 }));
277
+
278
+ while (queue.length) {
279
+ const { abs, scope, depth } = queue.shift();
280
+ if (total >= maxTotalBytes) {
281
+ warnings.push(
282
+ `project-memory budget (${maxTotalBytes} bytes) exhausted — remaining files skipped`,
283
+ );
284
+ break;
285
+ }
286
+ let entry;
287
+ try {
288
+ entry = readCapped(fs, abs, maxFileBytes);
289
+ } catch (err) {
290
+ warnings.push(`${abs} — cannot read: ${err.message}`);
291
+ continue;
292
+ }
293
+ if (scope === "rule") {
294
+ // <base>/.claude/rules/<file>.md → base is three dirs up.
295
+ const base = path.dirname(path.dirname(path.dirname(abs)));
296
+ const relCwd = path.relative(
297
+ base,
298
+ path.resolve(opts.cwd || process.cwd()),
299
+ );
300
+ const { globs, body } = parseRuleFrontmatter(entry.content);
301
+ if (!ruleApplies(globs, relCwd)) continue; // out of scope for this cwd
302
+ entry = { ...entry, content: body };
303
+ }
304
+ total += Math.min(entry.bytes, maxFileBytes);
305
+ out.push({ path: abs, scope, ...entry });
306
+
307
+ if (depth >= MAX_IMPORT_DEPTH) continue;
308
+ const baseDir = path.dirname(abs);
309
+ for (const raw of collectImportTokens(entry.content)) {
310
+ let target = raw;
311
+ if (target.startsWith("~/") || target === "~") {
312
+ if (!home) continue;
313
+ target = path.join(home, target.slice(1));
314
+ }
315
+ const resolved = path.resolve(baseDir, target);
316
+ if (visited.has(resolved) || !isFile(fs, resolved)) continue; // silent:
317
+ // non-files are decorative @tokens (npm scopes, emails), not imports.
318
+ visited.add(resolved);
319
+ queue.push({ abs: resolved, scope: "import", depth: depth + 1 });
320
+ }
321
+ }
322
+ return { files: out, warnings };
323
+ }
324
+
325
+ function escapeAttr(s) {
326
+ return String(s).replace(/"/g, "&quot;");
327
+ }
328
+
329
+ /** Render loaded instructions as a single system-prompt block ("" if none). */
330
+ export function renderProjectInstructionsBlock(loaded) {
331
+ const files = loaded?.files || [];
332
+ if (!files.length) return "";
333
+ const parts = [
334
+ '<project-instructions note="project memory auto-loaded from cc.md / CLAUDE.md / AGENTS.md; follow these as authoritative project conventions">',
335
+ ];
336
+ for (const f of files) {
337
+ const attrs =
338
+ `path="${escapeAttr(f.path)}" scope="${f.scope}"` +
339
+ (f.truncated ? ` truncated="true" total-bytes="${f.bytes}"` : "");
340
+ parts.push(`<file ${attrs}>`);
341
+ parts.push(f.content.trimEnd());
342
+ if (f.truncated) {
343
+ parts.push(`… [truncated — file is ${f.bytes} bytes]`);
344
+ }
345
+ parts.push(`</file>`);
346
+ }
347
+ parts.push("</project-instructions>");
348
+ return parts.join("\n");
349
+ }
350
+
351
+ /**
352
+ * One-call convenience for composeSystemPrompt: returns the rendered block or
353
+ * "" — and never throws (fail-open by design).
354
+ */
355
+ export function loadProjectInstructionsBlock(opts = {}) {
356
+ try {
357
+ const loaded = loadProjectInstructions(opts);
358
+ return renderProjectInstructionsBlock(loaded);
359
+ } catch {
360
+ return "";
361
+ }
362
+ }
363
+
364
+ export const _deps = { fs: fsDefault, path: pathDefault, os: osDefault };