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.
- package/README.md +368 -1
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-DCjoAX_u.js → AIOps-Ut7EevnG.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-XHoOmsbP.js → ActionButton-Dv6BlfJg.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics--xaFkDnL.js → Analytics-TQVQuJ7u.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CSa3FBn8.js → AppLayout-MSqLm2WK.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-ONWXiAwG.js → Audit-mw81HwVy.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CKOPNdgy.js → Backup-BQcPWDb1.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-PNj4uVqg.js → BaseInput-BYo_pwBH.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CZCulyXV.js → Chat-zi3YUKx2.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjuJpfpV.js → ChatBubbleRenderer-DWSm1XJJ.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-jvy668lD.js → Checkbox-BvC8Erjt.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DhUebOQD.js → Codegen-C32vx0OP.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BiBvHfdT.js → Col-DMBwmqyZ.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CmEdEti-.js → Community-nDWncmKV.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CtxpF4R5.js → Compact-lIc1HFn8.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CvPTrTAJ.js → Compliance-D14I_gd2.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BMafGHjy.js → Cowork-BiNI-_ZL.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-mdg_4TR1.js → Cron-N13sFzHb.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain--dGxsUvn.js → Crosschain-Dlnl0-v6.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C9oKaCml.js → DID-CxtYS31I.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CoGxKMvy.js → Dashboard-G4UnHlTR.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CDDu3ZZ3.js → Dropdown-BazlxFGY.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Dy7_r9Ag.js → EmailListRenderer-BrpNdihm.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CNg6vImJ.js → FamilyGuardDashboard-HD7jbOOR.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CT61bf3u.js → Federation-Bz8lzAGI.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CSLRnXhg.js → FormItemContext-CcyzGS00.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CZ4NE5N3.js → GenericCardRenderer-DRo9cwmp.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DBuOma3L.js → Git-B7bn333J.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BTU_SEef.js → Governance-DZX9CWAM.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-47SAmLC_.js → Inference-B3XhsL6W.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DCrK5vP4.js → KnowledgeGraph-CxFRTlQe.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BqiDxdav.js → Logs-xuys6mKH.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-CReUjsDt.js → Marketplace-CXyxv4WU.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-agZBV3p8.js → McpTools-BzZLQVI3.js} +6 -6
- package/src/assets/web-panel/assets/{Memory-C_YvUtyS.js → Memory-BANtaBa7.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-41fP1Tui.js → MobileBridge-BJIwjmxr.js} +3 -3
- package/src/assets/web-panel/assets/{MobileProjects-BkqLvGfL.js → MobileProjects-B857uSAZ.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-JFJCXUnk.js → Mtc-Cn7ceFEz.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BHNpPZC9.js → MtcAudit-B0zE978G.js} +6 -6
- package/src/assets/web-panel/assets/{Multisig-DuCRumiz.js → Multisig-CQFT0wXW.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DK-g0fKY.js → NLProgramming-DSxKdVY-.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BSMcjsPf.js → Notes-DtlTfam8.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-9ouC118H.js → NotificationSettings-CHQwayAg.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Brpmzh9n.js +1 -0
- package/src/assets/web-panel/assets/{Organization-DSV7oRnR.js → Organization-nF_tzZDT.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DVkkORc3.js → Overflow-CgCSf_PH.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BXXjkkQD.js → P2P-Bvn46bLY.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-O5hNnLTP.js → PdhVaultBrowser-Bzl9k7Gj.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-D_s0H5Av.js → Permissions-Dmezbuo8.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CzMDrwUi.js → PersonalDataHub-lCKRxwZr.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-i9krLVTL.js → Pipeline-DDCGm9PA.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-cMQcj9I8.js → Privacy-Cgu18Kjl.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Ca_l7avo.js → ProjectInit-CkF1AeRY.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BkaIhd6b.js → ProjectSettings-D0Q-orz1.js} +2 -2
- package/src/assets/web-panel/assets/Projects-KfGELrSY.js +1 -0
- package/src/assets/web-panel/assets/{Providers-D0nzYiqz.js → Providers-BACLV0z8.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bzzr9d0f.js → QuickAsk-CPsZUqDl.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-C-UFbQnX.js → Recommend-5jX0OI1-.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-BKMIKO5F.js → Reputation-5JKv54z0.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Bs7htK1T.js → Row-DLiTF5LY.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-v6MdULUh.js → RssFeed-CFdGmCKW.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DlRWYzvz.js → Search-BjIOnmA7.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DXWO37xX.js → Security-BujPqQSo.js} +4 -4
- package/src/assets/web-panel/assets/{Services-C2tWA-O0.js → Services-ChciPnMu.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Q8pIYY4a.js → Skeleton-Cwswp1Jv.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-D7XBlErj.js → Skills-CtwR4vJV.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CiyMVPJ1.js → Sla-pRIevich.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CadCeeiR.js → SpeechSettings-BRqB28Ai.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DzNAUhQq.js → SyncSettings-BYyj58_h.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-DTLpT48U.js +1 -0
- package/src/assets/web-panel/assets/{Templates-DfgEpUa4.js → Templates-Bbz_h7oW.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-C8ajkuYi.js → Tenant-D-H4E3cu.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B9rHwQQx.js → Terminal-CLLi0-lV.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D1ZVNezX.js → TimelineRenderer-BKI6eG0k.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CAkED4mx.js → Tokens-rsE_yDjM.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CJSrm6X0.js → Trigger-8TpwuTGk.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-B-TeorSk.js → Trust-sMtZkHPs.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Di7Ymofy.js → UkeySign-BAy2bAdG.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DM1eYNZe.js → VideoEditing-CBeR_DYK.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DvRWkbmR.js → Wallet-BymDnBcq.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CeZ3Y622.js → WebAuthn-DQIjmqNz.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Cq8c4h5j.js → WorkflowEditor-Cj7PB73f.js} +1 -1
- package/src/assets/web-panel/assets/{chat-7-WfML6Q.js → chat-DYnGj4vi.js} +1 -1
- package/src/assets/web-panel/assets/{colors-D6FgCmB-.js → colors-qOLKZNvN.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-ClYV25qi.js → compact-item-BpjCLPcW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CDhtjdkV.js → createContext-CfakUZVQ.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DgtRXlrj.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DZSH5LQd.js → hasIn-C9RW1s7t.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4PMzmOx.js → index-8Ia91vNV.js} +1 -1
- package/src/assets/web-panel/assets/{index-B78X5S22.js → index-B4kS312z.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDX4QU3k.js → index-BE67I0SW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPEYvNvq.js → index-BFOSDeeo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKgS8E_X.js → index-BIz-pX0k.js} +1 -1
- package/src/assets/web-panel/assets/{index-Di9pFrHV.js → index-BJoWi1aR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHeK8I5A.js → index-B_K0YtG2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpzOUiSb.js → index-BdR8XRyF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWRoh3_3.js → index-BfyRXPyV.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7pQa2is.js → index-Bl5LBZJM.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZ4zuoCP.js → index-BlxRICmz.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_mMFQ4S.js → index-BxiHBsfU.js} +1 -1
- package/src/assets/web-panel/assets/{index---azBCXl.js → index-C2S1hUWG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ7mrOaB.js → index-CEHyZ77C.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxwfFZ1u.js → index-CJZ2noI2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGj1orXm.js → index-COYEuArt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DL6GFJAd.js → index-CVZTLSL1.js} +1 -1
- package/src/assets/web-panel/assets/{index-z-R0KaJS.js → index-CbnJ6FsO.js} +1 -1
- package/src/assets/web-panel/assets/{index-tU6pZ1TP.js → index-CvWFTG56.js} +1 -1
- package/src/assets/web-panel/assets/{index-rCs9VJJp.js → index-D-RzTqlR.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6VWGnwq.js → index-DA80prWe.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0YzTJJO.js → index-DAjszh8P.js} +1 -1
- package/src/assets/web-panel/assets/index-DIGTMmnW.js +1 -0
- package/src/assets/web-panel/assets/{index-DjG82V0v.js → index-DQvVYNoJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLizxxId.js → index-DSWdpR3c.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7sC56w8.js → index-DadPmrxI.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlBF_l8m.js → index-DgMJagCq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bj8hZiyL.js → index-DkmLJFE_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrTmxbL8.js → index-DzXYG5YJ.js} +1 -1
- package/src/assets/web-panel/assets/index-Ef5jERRW.js +1 -0
- package/src/assets/web-panel/assets/{index-BUOPjAUM.js → index-JkOMWGMX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmU631Je.js → index-T3bIqK_p.js} +3 -3
- package/src/assets/web-panel/assets/{index-BqOIoEo6.js → index-UiiqS5k2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSjoWPxB.js → index-VYIJmPvJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B13QnrnE.js → index-ZCtDWP2C.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgaF1F0W.js → index-f9yoj84i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Or_McYjX.js → index-lPc7EzUi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGJK8D0l.js → index-m9JeDv6B.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWOkL-8O.js → index-qf0fAus7.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CSdsIGy3.js → initDefaultProps-DgsgQr1H.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Do-AcZV4.js → motion-TeUH7wzx.js} +1 -1
- package/src/assets/web-panel/assets/{move-BmgOoMsi.js → move-DdkIeWQx.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D4Tm7-s9.js → omit-BH_PH6HT.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CuWA8-lj.js → pickAttrs-CllCh-Nl.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BSbEF5op.js → placementArrow-BCjE2AzM.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-GIMJwB_9.js → responsiveObserve-BAVGAvRQ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DlZxpIBe.js → slide-D4ZW-Inn.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BZ26LPlh.js → statusUtils-j4pxhmKV.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Yn_3FZ0l.js → styleChecker-DH2SLtPg.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-O_LOE1AB.js → useFlexGapSupport-CYMMs-_Q.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-VFMyQqtl.js → useFs-BOX2ddKh.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-B_hyrGB-.js → usePersonalDataHub-BwcnN5z_.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D4LttGy7.js → vnode-Cwalh7Hj.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-KnTK1fjj.js → zoom-B2_q_nbu.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +38 -4
- package/src/commands/init.js +115 -2
- package/src/commands/mcp.js +57 -0
- package/src/commands/memory.js +62 -0
- package/src/commands/session.js +106 -12
- package/src/index.js +10 -0
- package/src/lib/agent-core.js +1 -0
- package/src/lib/agent-session-export.js +124 -0
- package/src/lib/ide-context.js +62 -0
- package/src/lib/init-ai-refine.js +66 -0
- package/src/lib/json-schema-output.js +181 -0
- package/src/lib/mcp-serve.js +259 -0
- package/src/lib/project-instructions.js +364 -0
- package/src/lib/project-inventory.js +355 -0
- package/src/lib/repl-bang-memorize.js +142 -0
- package/src/lib/repl-completer.js +25 -4
- package/src/lib/repl-rewind.js +107 -0
- package/src/lib/update-notice-refresh.mjs +10 -0
- package/src/lib/update-notice.js +154 -0
- package/src/repl/agent-repl.js +263 -1
- package/src/runtime/agent-core.js +162 -0
- package/src/runtime/system-prompt.js +21 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-LG2nUO5y.js +0 -1
- package/src/assets/web-panel/assets/Projects-Dy9yNmDg.js +0 -1
- package/src/assets/web-panel/assets/Tasks-BjdHjZeb.js +0 -1
- package/src/assets/web-panel/assets/devWarning-O0FVFeZg.js +0 -1
- package/src/assets/web-panel/assets/index--ANIKvhL.js +0 -1
- 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, """);
|
|
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 };
|