@rubytech/create-maxy-code 0.1.398 → 0.1.400
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/package.json +1 -1
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +17 -1
- package/payload/platform/plugins/docs/references/graph.md +15 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.js +123 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +9 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +36 -12
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +18 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +2 -0
- package/payload/platform/plugins/web-designer/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/web-designer/PLUGIN.md +46 -0
- package/payload/platform/plugins/web-designer/bin/resolve-cf.sh +56 -0
- package/payload/platform/plugins/web-designer/skills/brand-extract/SKILL.md +86 -0
- package/payload/platform/plugins/web-designer/skills/socials/SKILL.md +139 -0
- package/payload/platform/plugins/web-designer/skills/socials/references/pii-strip.md +64 -0
- package/payload/platform/plugins/web-designer/skills/socials/references/sources.md +66 -0
- package/payload/platform/plugins/web-designer/skills/socials/templates/og.html +92 -0
- package/payload/platform/plugins/web-designer/skills/socials/templates/social-posts.md +46 -0
- package/payload/platform/plugins/web-designer/skills/socials/templates/socials.html +87 -0
- package/payload/platform/plugins/web-designer/skills/web-design/SKILL.md +98 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/deploy.md +59 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/e-sign.md +39 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/gating.md +55 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/placeholders.md +110 -0
- package/payload/platform/plugins/web-designer/skills/web-design/references/sections.md +52 -0
- package/payload/platform/plugins/web-designer/skills/web-design/templates/index.html +1004 -0
- package/payload/platform/plugins/web-designer/skills/web-design/templates/llms.txt +32 -0
- package/payload/platform/plugins/web-designer/skills/web-intake/SKILL.md +107 -0
- package/payload/platform/plugins/web-designer/skills/web-intake/references/intake.md +103 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +7 -1
- package/payload/platform/scripts/merge-whatsapp-split-conversations.mjs +264 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +22 -6
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-Bex48co9.js → AdminLoginScreens-rlk263u9.js} +1 -1
- package/payload/server/public/assets/{AdminShell-BOmS1NPG.js → AdminShell-C5vWTy2n.js} +1 -1
- package/payload/server/public/assets/{Checkbox-DDlXC5tq.js → Checkbox-Chz6bfy-.js} +1 -1
- package/payload/server/public/assets/{Transcript-CwItbNkx.js → Transcript-Bsx-bGgY.js} +2 -2
- package/payload/server/public/assets/{admin-n9zWE158.js → admin-BnZH758N.js} +1 -1
- package/payload/server/public/assets/{arc-C6-nr5UV.js → arc-DyzjCWmw.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-XiVs-N7Z.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DIdho_T6.js → architectureDiagram-Q4EWVU46-qWfElD16.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-D5rTTBSy.js → blockDiagram-DXYQGD6D-DO2fFHC3.js} +1 -1
- package/payload/server/public/assets/{browser-CI8DR1YG.js → browser-CIrfyZWe.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-pfUVHdmy.js → c4Diagram-AHTNJAMY-Ca-q5gDe.js} +1 -1
- package/payload/server/public/assets/{calendar-WuIAN47H.js → calendar-D6Z8Kmrn.js} +1 -1
- package/payload/server/public/assets/channel-CYBkRz7E.js +1 -0
- package/payload/server/public/assets/chat-h7SytZHx.js +1 -0
- package/payload/server/public/assets/chevron-left-DSTeS1_g.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-EF6cdj8U.js → chunk-2KRD3SAO-CX2PxuDw.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-DE-lcZAc.js → chunk-336JU56O-D8l6U4Ne.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-CPhOHriU.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB--ANCzzyn.js → chunk-4BX2VUAB-CG32Lkbh.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-DTO_X80J.js → chunk-4TB4RGXK-BnAQbO-v.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-4J3nrz44.js → chunk-55IACEB6-ejqRH2M5.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-jH_eqAHL.js → chunk-5FUZZQ4R-ClkicQ-T.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-C-qU6p6k.js → chunk-5PVQY5BW-Be-mE_rx.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-DXxH9Snb.js → chunk-67CJDMHE-DPsemed2.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-CCkxuP7A.js → chunk-7N4EOEYR-BQRQJETl.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-CyItuM8d.js → chunk-AA7GKIK3-B1wcAVCK.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-Bg7gxYNf.js → chunk-BSJP7CBP-DIua053h.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-BAXbOqv5.js → chunk-CIAEETIT-C9GHSCS9.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-vdoErQoc.js → chunk-EDXVE4YY-C1T3l1xj.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-Cz_z_6GK.js → chunk-ENJZ2VHE-8kPcXEQC.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-Ck2jvOgW.js → chunk-FMBD7UC4-Bhoeq9sD.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-DucR0Qo8.js → chunk-FOC6F5B3-BIBpJi8I.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-DolYDD7g.js → chunk-ICPOFSXX-DELXp6fj.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-FRzx7E5I.js → chunk-K5T4RW27-DdckXldP.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-Bb5S4Bmc.js → chunk-KGLVRYIC-BJhXejKT.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-c8VZJ-lU.js → chunk-LIHQZDEY-BSd8nyM2.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-BAeh-LZ1.js → chunk-ORNJ4GCN-CBi55uxq.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BUg7Kysh.js → chunk-OYMX7WX6-Dfbzl8xh.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CDnAAZVJ.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-K4r17UTk.js → chunk-U2HBQHQK-BN1w8_Cq.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-Byh-CyxC.js → chunk-X2U36JSP-CrRay1NJ.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-DC7lhr93.js → chunk-XPW4576I-C_ZE776O.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-B2cu7AEP.js → chunk-YZCP3GAM-CUSlqWBY.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-DH_p8sFX.js → chunk-ZZ45TVLE-BdPUfSiD.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BdXUKVrS.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DAq-uJr_.js +1 -0
- package/payload/server/public/assets/clone-DQl28-jK.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-DeM6JI00.js → cose-bilkent-S5V4N54A-D4E9htTz.js} +1 -1
- package/payload/server/public/assets/{dagre-3drs3e4F.js → dagre-D1B5TQL1.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-C6S-VUtN.js → dagre-KV5264BT-ByuvYV7N.js} +1 -1
- package/payload/server/public/assets/data-CL7CyX1C.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-BkvA32I3.js → diagram-5BDNPKRD-B-0KduTa.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CVwtRMPB.js → diagram-G4DWMVQ6-CSQmo3eB.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DJ1JVMbd.js → diagram-MMDJMWI5-DK0kWiB3.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-rfj-crtX.js → diagram-TYMM5635-CYbK2reZ.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-TJPuweS5.js → erDiagram-SMLLAGMA-C-JQHvJF.js} +1 -1
- package/payload/server/public/assets/{flatten-BZkoyJ4e.js → flatten-ROVzlo1u.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-LQWEUjbV.js → flowDiagram-DWJPFMVM-OTOd2czg.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DdYsxKRw.js → ganttDiagram-T4ZO3ILL-BWJ778ou.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BM-KaDZy.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-sx8-fokM.js → gitGraphDiagram-UUTBAWPF-CkNcyISe.js} +1 -1
- package/payload/server/public/assets/graph-XZJZHzvv.js +51 -0
- package/payload/server/public/assets/{graph-labels-DigwgCWY.js → graph-labels-Cabqmf35.js} +1 -1
- package/payload/server/public/assets/{graphlib-DixeJdCf.js → graphlib-8z59Ng6C.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-CtZajyAB.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-C-_WYlmI.js +2 -0
- package/payload/server/public/assets/{isEmpty-VRKFml5R.js → isEmpty-DXf_sApt.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BKUCuKhZ.js → ishikawaDiagram-UXIWVN3A-Cg7pRRGp.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-BafZP6pM.js → journeyDiagram-VCZTEJTY-CZT_4r_Z.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DQ4c6wRv.js → kanban-definition-6JOO6SKY-Dp1Vd5YM.js} +1 -1
- package/payload/server/public/assets/{line-0_x1Wivu.js → line-UPAcwY66.js} +1 -1
- package/payload/server/public/assets/{linear-CZrU-J2V.js → linear-BeeahLKw.js} +1 -1
- package/payload/server/public/assets/maximize-2-DPe-Noig.js +1 -0
- package/payload/server/public/assets/{mermaid-parser.core-DwJ1cyfO.js → mermaid-parser.core-CLKle8df.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-DnUst0He.js → mermaid.core-CwRj5rL5.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-oc-GwerE.js → mindmap-definition-QFDTVHPH-CNQUY9UJ.js} +1 -1
- package/payload/server/public/assets/{operator-D-Ad9OQ5.js → operator-BM_CDyCb.js} +1 -1
- package/payload/server/public/assets/{ordinal-v4prQcuq.js → ordinal-B2r5L1v4.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-T-SYedp7.js +1 -0
- package/payload/server/public/assets/{page-BjAKwjlS.js → page-CMIbXzSg.js} +3 -3
- package/payload/server/public/assets/{page-CpSVszGe.js → page-E7I5sEVt.js} +1 -1
- package/payload/server/public/assets/{pdf-render-NQmflZ7K.js → pdf-render-m08Xm2Nm.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BWECFVVd.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BnBtQNHM.js → pieDiagram-DEJITSTG-Scnq58FX.js} +1 -1
- package/payload/server/public/assets/{public-CbkiISZZ.js → public-CrdWCSk2.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CkNqtzHI.js → quadrantDiagram-34T5L4WZ-wPfZo_vs.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-Dx0YUklX.js +1 -0
- package/payload/server/public/assets/{reduce-0Mm1v1Rr.js → reduce-CFlDlJf5.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-D7qc27KT.js → requirementDiagram-MS252O5E-yYkH12K2.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-BYmlylsD.js → rotate-ccw-Dwtj0NJ7.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-2Y4sqRY7.js → sankeyDiagram-XADWPNL6-fVWRBzzV.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-hXOiSHuT.js → sequenceDiagram-FGHM5R23-Dbap3X6h.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Dq4hhkLA.js → stateDiagram-FHFEXIEX-DkPLAqIh.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DceWO1u6.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-Dn9wBUEw.js → timeline-definition-GMOUNBTQ-DabKBJyd.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3197Rac.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-BPrpx29h.js +1 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-DxHQSOzT.css → useSubAccountSwitcher-BH-YjP0H.css} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BKVHJiIt.js → vennDiagram-DHZGUBPP-CaKT7c-c.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DW4Jlbhw.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Bn9vvpdK.js → wardleyDiagram-NUSXRM2D-BZ5TTSOL.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-FIoMdPB3.js → xychartDiagram-5P7HB3ND-CLfeklJ9.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +5 -5
- package/payload/server/public/chat.html +10 -9
- package/payload/server/public/data.html +9 -8
- package/payload/server/public/graph.html +9 -8
- package/payload/server/public/index.html +12 -11
- package/payload/server/public/operator.html +12 -11
- package/payload/server/public/public.html +10 -9
- package/payload/server/server.js +41 -6
- package/payload/server/public/assets/architecture-YZFGNWBL-DdgmZ93B.js +0 -1
- package/payload/server/public/assets/channel-Da-UUyqx.js +0 -1
- package/payload/server/public/assets/chat-L6IVvC9k.js +0 -1
- package/payload/server/public/assets/chevron-left-uJSGyCGe.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-B1kFAxo1.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BjE-Glwf.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-Dol3Ogeh.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-VUMj7gWH.js +0 -1
- package/payload/server/public/assets/clone-m7oGrdBC.js +0 -1
- package/payload/server/public/assets/data-4K_negqE.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-cJEaiQq4.js +0 -1
- package/payload/server/public/assets/graph-UHR4Ez-V.js +0 -51
- package/payload/server/public/assets/info-OMHHGYJF-BUxfOTKa.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-CD-d3Rgf.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-B9U_2639.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-Glh-lJOT.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-CTSgob8w.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DTj9o2vZ.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CXApkW1l.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-DzvTXAcT.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BExXV4oI.js +0 -1
- /package/payload/server/public/assets/{_baseFor-C0Y1mc3g.js → _baseFor-BVuIxZJh.js} +0 -0
- /package/payload/server/public/assets/{array-DqLCdDFv.js → array-B9UHiPd-.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-Cn5b4Cdu.js → cytoscape.esm-D5_TRwB-.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-CrJzLgRD.js → defaultLocale-5eAKkKJC.js} +0 -0
- /package/payload/server/public/assets/{dist-Dlks0Bfw.js → dist-CADCHk0t.js} +0 -0
- /package/payload/server/public/assets/{init-C0r9Gk5G.js → init-DlZdxViB.js} +0 -0
- /package/payload/server/public/assets/{katex-BFwFoEts.js → katex-Xo0SyWvP.js} +0 -0
- /package/payload/server/public/assets/{path-DIKpVbHL.js → path-6uRLdFF7.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-C5gCWwwF.js → preload-helper-CvXuAEud.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DsHEmXUZ.js → rough.esm-Djo4Abte.js} +0 -0
- /package/payload/server/public/assets/{src-Bv7ZWAzF.js → src-Bi0fLV9t.js} +0 -0
- /package/payload/server/public/assets/{useSubAccountSwitcher-BRWAIbzr.js → useSubAccountSwitcher-BVCMAnWK.js} +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# {{ COMPANY_LEGAL_NAME }}
|
|
2
|
+
|
|
3
|
+
> {{ LLMS_SUMMARY }}
|
|
4
|
+
|
|
5
|
+
## Facts
|
|
6
|
+
|
|
7
|
+
- Legal name: {{ COMPANY_LEGAL_NAME }}
|
|
8
|
+
- Company number: {{ COMPANY_NUMBER }} (registered in {{ COMPANY_JURISDICTION }})
|
|
9
|
+
- Registered office: {{ STREET_ADDRESS }}, {{ POST_TOWN }}, {{ COUNTY }}, {{ POSTCODE }}
|
|
10
|
+
- Established: {{ ESTABLISHED_YEAR }} ({{ YEARS_TRADING }} of experience)
|
|
11
|
+
- Ownership: {{ OWNERSHIP }}
|
|
12
|
+
- Areas served: {{ REGION }}
|
|
13
|
+
- Telephone: {{ PHONE_DISPLAY }} / {{ MOBILE_DISPLAY }}
|
|
14
|
+
- Email: {{ EMAIL }}
|
|
15
|
+
- Led by {{ FOUNDER_NAME }}, who runs the team and posts project progress directly.
|
|
16
|
+
- Recent projects: {{ RECENT_PROJECTS }}
|
|
17
|
+
|
|
18
|
+
## Services
|
|
19
|
+
|
|
20
|
+
- {{ SERVICE_1_NAME }}: {{ SERVICE_1_DESC }}
|
|
21
|
+
- {{ SERVICE_2_NAME }}: {{ SERVICE_2_DESC }}
|
|
22
|
+
- {{ SERVICE_3_NAME }}: {{ SERVICE_3_DESC }}
|
|
23
|
+
- {{ SERVICE_4_NAME }}: {{ SERVICE_4_DESC }}
|
|
24
|
+
- {{ SERVICE_5_NAME }}: {{ SERVICE_5_DESC }}
|
|
25
|
+
- {{ SERVICE_6_NAME }}: {{ SERVICE_6_DESC }}
|
|
26
|
+
|
|
27
|
+
## Links
|
|
28
|
+
|
|
29
|
+
- Website: {{ WEBSITE_URL }}
|
|
30
|
+
- Facebook: {{ FACEBOOK_URL }}
|
|
31
|
+
- Instagram: {{ INSTAGRAM_URL }}
|
|
32
|
+
- TikTok: {{ TIKTOK_URL }}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-intake
|
|
3
|
+
description: Single-entry orchestrator that turns an operator's raw inputs — a folder or zip of photos, an existing website URL, social links, and free-text notes — into a finished single-page website plus share-ready social tiles, starting from what the site is for. Leads with the objective (what a visitor should do), then routes brand-extract → web-design → socials and asks targeted questions to fill every field the sources do not supply. Use whenever the deliverable is "a website for this business" from mixed source material. Trigger phrases include "design a site for this business", "build a landing page from these photos and their website", "put together a one-page site and social tiles".
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
- WebFetch
|
|
12
|
+
- Skill
|
|
13
|
+
- AskUserQuestion
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# web-intake
|
|
17
|
+
|
|
18
|
+
Orchestrates the `brand-extract → web-design → socials` pipeline and the deploy
|
|
19
|
+
hand-off. The deliverable is a self-contained `site/` directory (a scrollable
|
|
20
|
+
single-page site, an `llms.txt`, and a `socials/` folder of Open Graph and social
|
|
21
|
+
tiles with a gallery and captions), built from whatever the operator provides and
|
|
22
|
+
whatever the orchestrator has to ask for, and shaped by what the site is for.
|
|
23
|
+
|
|
24
|
+
This skill is a playbook, not a programmatic dispatcher. It tells you the routing
|
|
25
|
+
and the gap-filling contract; you execute each step under the underlying skills'
|
|
26
|
+
own contracts. It dispatches no specialist worker.
|
|
27
|
+
|
|
28
|
+
## Start with the objective
|
|
29
|
+
|
|
30
|
+
Before collecting facts, settle what the site is **for** and what a visitor should
|
|
31
|
+
**do**. That decision drives the CTAs, the forms, and whether the page needs gated
|
|
32
|
+
access or signing — so it comes first, not last. The objective-first questions are
|
|
33
|
+
in [`references/intake.md`](references/intake.md) § 0; the rest of that file is the
|
|
34
|
+
field-by-field gap list.
|
|
35
|
+
|
|
36
|
+
## What the operator gives, and what you do with it
|
|
37
|
+
|
|
38
|
+
| Operator supplies | Used for | If missing |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| **Images** (folder or zip of photos) | hero rotator, photo grids, portrait, testimonial, social tiles | the site is image-led. If none, stop and ask. |
|
|
41
|
+
| **Website URL** | brand tokens (palette, accent), logo, company name, contact, services, "about" copy | run `brand-extract`; anything it cannot read becomes a question |
|
|
42
|
+
| **Social links** | footer + drawer links, handles, extra copy in the operator's own voice | login-walled platforms usually cannot be scraped — say so plainly and ask the operator to paste anything they want used |
|
|
43
|
+
| **Free-text notes / pasted quotes** | the person section, testimonials, latest-update cards, service descriptions, taglines | use verbatim where the operator marks a quote; never reword a real client testimonial into marketing prose |
|
|
44
|
+
| **Design tokens** (if supplied directly) | the palette `:root` — use them verbatim | if absent, `brand-extract` derives them from the site |
|
|
45
|
+
|
|
46
|
+
## The core rule: ask, never invent
|
|
47
|
+
|
|
48
|
+
Every `{{ PLACEHOLDER }}` in the templates is a fact about a real business. For
|
|
49
|
+
every placeholder still unfilled after `brand-extract`, ask the operator — do not
|
|
50
|
+
synthesise a plausible value. A fabricated phone number, a guessed founding year,
|
|
51
|
+
an invented testimonial, or a made-up company number is a defect. Two facts are
|
|
52
|
+
load-bearing and frequently wrong if guessed: the **established year** (a business
|
|
53
|
+
that "trades since 1987" may have incorporated much later — keep the two distinct)
|
|
54
|
+
and any **company-registration detail**, which comes from the register or the
|
|
55
|
+
operator, never from a marketing line.
|
|
56
|
+
|
|
57
|
+
## Process — high level
|
|
58
|
+
|
|
59
|
+
1. **Set the objective.** Ask `references/intake.md` § 0: what the site is for, what
|
|
60
|
+
a visitor should do, the CTAs, which forms (contact / waitlist / custom bucket),
|
|
61
|
+
whether any page or document needs gated access, whether anything needs signing.
|
|
62
|
+
These answers shape every later step.
|
|
63
|
+
2. **Resolve the workspace.** Default output root is the images directory's parent,
|
|
64
|
+
or the operator's stated path. The deliverable lands in `<output>/site/`.
|
|
65
|
+
3. **Run `brand-extract`** against the website URL for the palette, logo, icon, and
|
|
66
|
+
the facts it can read, plus a `brand.md` of what is still unknown. If the
|
|
67
|
+
operator supplied design tokens directly, use those verbatim.
|
|
68
|
+
4. **Curate the images.** Verify each photo's *content* by eye, measuring first and
|
|
69
|
+
previewing any image over 2000px on its longest edge (downscale to `/tmp`, read,
|
|
70
|
+
delete). Lead the hero with the strongest "arrive here" shot; map the rest to the
|
|
71
|
+
grids. A portrait is for the person section and a portrait tile, never a
|
|
72
|
+
landscape crop.
|
|
73
|
+
5. **Fill the gaps.** Compare the token inventory
|
|
74
|
+
([`../web-design/references/placeholders.md`](../web-design/references/placeholders.md))
|
|
75
|
+
against what brand-extract found and the operator's notes. Ask for everything
|
|
76
|
+
still missing, in grouped questions. Wait for answers. Do not proceed with blanks.
|
|
77
|
+
6. **Run `web-design`** to build `site/index.html` + `site/llms.txt`, substituting
|
|
78
|
+
every placeholder. No `{{ }}` may survive.
|
|
79
|
+
7. **Run `socials` in `microsite` mode** to render the tiles into `site/socials/`,
|
|
80
|
+
plus `site/socials.html` and `site/social-posts.md`, and to wire the site's
|
|
81
|
+
`og:image`.
|
|
82
|
+
8. **Hand off to deploy.** When the operator wants the site live, follow
|
|
83
|
+
`../web-design/references/deploy.md`. If the objective needs gated access or
|
|
84
|
+
signing, follow `../web-design/references/gating.md` and
|
|
85
|
+
`../web-design/references/e-sign.md`.
|
|
86
|
+
9. **Verify and report.** Confirm no placeholder survived, every asset resolves over
|
|
87
|
+
a local HTTP server, the site renders at desktop and mobile, and tiles are at
|
|
88
|
+
exact dimensions. Report the `site/` path and every value you had to ask for.
|
|
89
|
+
|
|
90
|
+
## Completion criteria
|
|
91
|
+
|
|
92
|
+
- `grep "{{" site/index.html site/llms.txt site/socials.html` returns nothing.
|
|
93
|
+
- No invented facts: every value traces to the website, the operator's notes, or an
|
|
94
|
+
answer the operator gave. Anything still unknown is shown as a clear gap.
|
|
95
|
+
- The site renders at desktop and a 390px mobile viewport with no horizontal
|
|
96
|
+
overflow and no broken images; the hero rotates; the back-to-top works.
|
|
97
|
+
- Social tiles exist at exact sizes (1200×628 / 1200×1200 / 1080×1350); any person
|
|
98
|
+
tile is portrait only.
|
|
99
|
+
- The report names the objective, the CTAs and forms wired, and every field asked for.
|
|
100
|
+
|
|
101
|
+
## When NOT to use this skill
|
|
102
|
+
|
|
103
|
+
- The operator only wants the site, or only the tiles → call `web-design` or
|
|
104
|
+
`socials` directly.
|
|
105
|
+
- The operator only wants brand tokens and a logo from a URL → call `brand-extract`.
|
|
106
|
+
- There are no usable photos of the business → the site is image-led; ask for photos
|
|
107
|
+
first.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Intake & gap-filling questions
|
|
2
|
+
|
|
3
|
+
Ask only for what the website and the operator's notes did not already supply.
|
|
4
|
+
Group the questions so the operator answers in as few turns as possible, and
|
|
5
|
+
present sensible candidates from the source material where you have them ("Your
|
|
6
|
+
site says you've traded since 1994 — is that the year to show?").
|
|
7
|
+
|
|
8
|
+
Never invent a value to avoid asking. A guessed phone number or founding year is
|
|
9
|
+
a defect. If the operator does not know a field (e.g. company number), leave that
|
|
10
|
+
slot out of the output rather than filling it with a guess.
|
|
11
|
+
|
|
12
|
+
## 0. Objective — ask this first
|
|
13
|
+
|
|
14
|
+
The site's purpose drives the CTAs, the forms, and whether it needs gating or
|
|
15
|
+
signing. Settle it before collecting facts.
|
|
16
|
+
|
|
17
|
+
- **What is the site for?** The single most important thing a visitor should do —
|
|
18
|
+
book a call, request a quote, join a waitlist, buy a service, sign a document,
|
|
19
|
+
or simply find and trust the business. Name one primary objective; secondary
|
|
20
|
+
ones are fine but rank them.
|
|
21
|
+
- **What behaviour do you want?** How a visitor should move through the page — read
|
|
22
|
+
the story first, jump to services, go straight to a contact form. This shapes the
|
|
23
|
+
section order and what the sticky sub-nav pushes.
|
|
24
|
+
- **CTAs.** The exact calls to action and where they point — a phone number, an
|
|
25
|
+
email enquiry, a booking link, a form. The hero CTA, the sub-nav CTA, and the
|
|
26
|
+
closing CTA can differ; name each.
|
|
27
|
+
- **Forms / buckets.** Which forms the site captures: a **contact** form, a
|
|
28
|
+
**waitlist** signup, a **callback** request, or a custom bucket (e.g. a quote
|
|
29
|
+
request with its own fields). Each form is its own D1 table. Name the fields each
|
|
30
|
+
form collects.
|
|
31
|
+
- **Gated access?** Whether any page or document must sit behind a login (a private
|
|
32
|
+
proposal, a client-only page). If yes, note who should be allowed in (an email
|
|
33
|
+
allowlist for external visitors).
|
|
34
|
+
- **E-signing?** Whether any document on the site must be signed (a quote, a
|
|
35
|
+
contract, terms). If yes, it is handed to the signing skill; capture the document
|
|
36
|
+
and the signer up front.
|
|
37
|
+
|
|
38
|
+
## 1. Identity
|
|
39
|
+
|
|
40
|
+
- Trading name and full legal name (e.g. "Meridian Advisory" / "Meridian Advisory Ltd").
|
|
41
|
+
- Short monogram / initials, if any (used as a small brand mark).
|
|
42
|
+
- Tagline / strapline, in their own words.
|
|
43
|
+
- Sector label for the nav and tiles — what the business is, in their words (e.g.
|
|
44
|
+
"Chartered accountants", "Dental practice", "Design studio"). No default category.
|
|
45
|
+
- Established year. **Keep "trading since" separate from "company incorporated".**
|
|
46
|
+
- Company registration number + jurisdiction, and registered office — only if the
|
|
47
|
+
operator wants legal detail in the footer, and what to call the number in their
|
|
48
|
+
jurisdiction. Take these from the register or the operator, never from a marketing
|
|
49
|
+
line.
|
|
50
|
+
|
|
51
|
+
## 2. Contact & place
|
|
52
|
+
|
|
53
|
+
- Phone (display + dialable), mobile if separate.
|
|
54
|
+
- Email.
|
|
55
|
+
- Website domain and full URL (for canonical + Open Graph).
|
|
56
|
+
- Address: street, post town, county, postcode (only the parts they want shown).
|
|
57
|
+
- Service area / region phrase (e.g. "the South East" or "Greater Manchester").
|
|
58
|
+
- Social profiles: Facebook, Instagram, TikTok, LinkedIn (URLs + handles).
|
|
59
|
+
|
|
60
|
+
## 3. The story (copy, in their voice)
|
|
61
|
+
|
|
62
|
+
- One or two paragraphs on who they are and how long they've been going.
|
|
63
|
+
- What they're proudest of / the standard they hold to.
|
|
64
|
+
- A headline sentence that sums the business up.
|
|
65
|
+
- The three or four "by the numbers" facts (established, experience, ownership,
|
|
66
|
+
coverage, specialism, approach, promise).
|
|
67
|
+
|
|
68
|
+
## 4. What they do (services)
|
|
69
|
+
|
|
70
|
+
- Up to six services, each a short name and a one-line description.
|
|
71
|
+
- A one-line lede for the services section.
|
|
72
|
+
|
|
73
|
+
## 5. The person (optional)
|
|
74
|
+
|
|
75
|
+
- Name and role of the person who fronts the business, if there is one.
|
|
76
|
+
- A short intro in their voice (what they do, how hands-on they are).
|
|
77
|
+
- A quote from them, ideally about a specific piece of work or their approach.
|
|
78
|
+
- 1–3 recent "latest update" cards in their own words (drop emoji per the plain
|
|
79
|
+
register).
|
|
80
|
+
- A portrait photo (used in the person section and a portrait social tile — a
|
|
81
|
+
person is never cropped to landscape). Drop this whole section if there is no
|
|
82
|
+
single figurehead.
|
|
83
|
+
|
|
84
|
+
## 6. Proof (testimonial)
|
|
85
|
+
|
|
86
|
+
- A real client quote, used verbatim. Note the one phrase to highlight.
|
|
87
|
+
- Who it's from and the context.
|
|
88
|
+
- Which photo best illustrates it.
|
|
89
|
+
- Any thank-you artefact (a card, a note) that adds a personal touch.
|
|
90
|
+
|
|
91
|
+
## 7. FAQ (answer-engine targets)
|
|
92
|
+
|
|
93
|
+
- 3–5 plain questions a customer actually asks, each with a one or two sentence
|
|
94
|
+
answer. Default set: what they do, how long they've been going, where they're
|
|
95
|
+
based / areas covered, plus two specific to the business.
|
|
96
|
+
|
|
97
|
+
## 8. Images (you curate, but confirm)
|
|
98
|
+
|
|
99
|
+
- Confirm which photo is the strongest "arrive here" shot (hero / main tile).
|
|
100
|
+
- Confirm the best result shot (testimonial + interior tile).
|
|
101
|
+
- Confirm a strong wide shot (full-bleed).
|
|
102
|
+
- Flag if the photo set is thin so expectations are set, and ask for any photos that
|
|
103
|
+
would strengthen the hero.
|
|
@@ -99,7 +99,13 @@ The agent links as a companion device on the operator's own WhatsApp account, so
|
|
|
99
99
|
|
|
100
100
|
## Live persistence
|
|
101
101
|
|
|
102
|
-
Every `messages.upsert` event (both `notify` and `append`, both `fromMe` directions) is captured at a single site in `platform/ui/app/lib/whatsapp/manager.ts` and split two ways: the sender's `:Person` node is MERGEd in Neo4j (identity linkage, shared with the rest of the platform's People model — unchanged from before), and the message content itself is appended to an on-disk JSONL store at `~/.<brand>/data/whatsapp-messages/<platformAccountId>/<
|
|
102
|
+
Every `messages.upsert` event (both `notify` and `append`, both `fromMe` directions) is captured at a single site in `platform/ui/app/lib/whatsapp/manager.ts` and split two ways: the sender's `:Person` node is MERGEd in Neo4j (identity linkage, shared with the rest of the platform's People model — unchanged from before), and the message content itself is appended to an on-disk JSONL store at `~/.<brand>/data/whatsapp-messages/<platformAccountId>/<canonicalConversationJid>.jsonl` — one file per **contact**, one line per message. `:Message:WhatsAppMessage` nodes and their `:PART_OF`/`:SENT`/`:NEXT` edges no longer exist; message ordering is a `dateSent` sort at read time instead of a graph edge. `:Conversation` nodes are unaffected — they remain the shared session anchor for the agent-path `:UserMessage`/`:AssistantMessage` writer too. Persist failures are loud (`[whatsapp-persist] FAIL …`) and never block dispatch.
|
|
103
|
+
|
|
104
|
+
**Conversation-key canonicalisation.** The store file is keyed on the *canonical* conversation JID, not the raw wire `remoteJid`. WhatsApp addresses one contact both by a linked-identity JID (`<lid>@lid`) and a phone-number JID (`<number>@s.whatsapp.net`), so inbound and in-thread replies accrete under the LID while number-addressed outbound sends echo under the PN — keying on the raw `remoteJid` splits one contact across two files. `resolveCanonicalConversationJid` (`normalize.ts`) resolves each message's `remoteJid` to a single key via Baileys' own LID→PN mapping (`getPNForLID`) — the phone-number JID when the LID resolves to one, else the raw JID unchanged (LID-only contacts stay LID-keyed; group and PN JIDs are already canonical) — before `appendMessage` chooses the file. The keying decision is logged on every store write: `[whatsapp-message-store] op=key raw=<jid> canonical=<jid> resolved=<lid2pn|none>`, so a future split (canonical diverging across a contact's records) is greppable at write time. `messageId` keeps the **raw** wire JID — `whatsapp-live:<waName>:<remoteJid>:<msg.key.id>` — so idempotency is stable across the canonicalisation boundary: a replay of the same wire message keeps its `messageId` regardless of which store file it lands in. Because a `@lid` remoteJid resolves to its PN key only once Baileys' LID mapping warms, a message's live delivery and its later history-sync replay can target different files; `appendMessage` therefore dedupes against the raw remoteJid as an alt key (passed by persist when the canonical key differs), so the second write no-ops instead of double-writing across the two files.
|
|
105
|
+
|
|
106
|
+
**Reader labelling.** A store row is never labelled with the install's own linked (self) number. A 1:1 is labelled by its **counterparty**: a phone-number-keyed conversation (`@s.whatsapp.net`) is addressed by the peer's number, so `buildStoreConversationRows` takes `senderId` straight from the conversation key (`jidToE164(remoteJid)`) — correct whether the earliest record is inbound or an outbound send that stamped the self phone, and needing no runtime self phone at all (so the label is right even before the live connection reports one). A LID-keyed conversation is always inbound-originated, so its earliest record's `senderTelephone` is the contact, never the self phone.
|
|
107
|
+
|
|
108
|
+
**Historical reconciliation.** Splits created before the canonicalisation fix are collapsed by the one-time `platform/scripts/merge-whatsapp-split-conversations.mjs` — dry-run by default, `--apply` to write, `--account=<uuid>` to scope. It merges each contact's split files into one canonical file (recovering the contact number from the records' resolved `senderTelephone`, with the persisted `whatsapp-lid-map.json` cache as a fallback), idempotent by `messageId`, dropping nothing; it logs per-account merged-file and merged-record counts and asserts one file per contact.
|
|
103
109
|
|
|
104
110
|
**Search coverage tradeoff.** `/graph`'s BM25/vector search no longer covers WhatsApp message bodies (`graph-search.ts`'s `MESSAGE_FAMILY_LABELS` dropped `WhatsAppMessage`) — this is an accepted, documented consequence of the storage move, not a bug. Every persisted message remains readable via `whatsapp-conversation-graph-state` (all-senders mode) and the `/whatsapp` reader panel's persisted-only rows.
|
|
105
111
|
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Task 1412 — one-time on-disk merge: collapse a contact's split WhatsApp store
|
|
3
|
+
// files into one canonical file per contact.
|
|
4
|
+
//
|
|
5
|
+
// WhatsApp addresses one contact by both a linked-identity JID (`<lid>@lid`) and
|
|
6
|
+
// a phone-number JID (`<number>@s.whatsapp.net`). Before the persist-time
|
|
7
|
+
// canonicalisation fix, inbound and in-thread replies accreted under the LID
|
|
8
|
+
// while number-addressed outbound sends echoed under the PN, so one contact's
|
|
9
|
+
// history split across two JSONL files (message-store.ts's per-remoteJid keying).
|
|
10
|
+
// This script reconciles the historical split: for every account it groups the
|
|
11
|
+
// store files by their canonical conversation key and merges each group into the
|
|
12
|
+
// single canonical file, idempotent by `messageId`, dropping nothing.
|
|
13
|
+
//
|
|
14
|
+
// Canonical form matches resolveCanonicalConversationJid (normalize.ts): the
|
|
15
|
+
// phone-number JID when the contact's number is known, else the LID unchanged.
|
|
16
|
+
// Offline (no live Baileys socket) the contact number is recovered from the
|
|
17
|
+
// records themselves — a LID file's inbound records carry the resolved
|
|
18
|
+
// senderTelephone — with the persisted whatsapp-lid-map.json cache as a fallback.
|
|
19
|
+
//
|
|
20
|
+
// Usage:
|
|
21
|
+
// node platform/scripts/merge-whatsapp-split-conversations.mjs # dry-run (default)
|
|
22
|
+
// node platform/scripts/merge-whatsapp-split-conversations.mjs --apply # write changes
|
|
23
|
+
// node platform/scripts/merge-whatsapp-split-conversations.mjs --account=<uuid>
|
|
24
|
+
//
|
|
25
|
+
// MAXY_DIR_OVERRIDE overrides the store root (mirrors message-store.ts), used by
|
|
26
|
+
// the verification harness to run against a fixture directory.
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, rmSync, readdirSync,
|
|
30
|
+
} from "node:fs";
|
|
31
|
+
import { join, resolve } from "node:path";
|
|
32
|
+
import { homedir } from "node:os";
|
|
33
|
+
|
|
34
|
+
const TAG = "[whatsapp-merge-split]";
|
|
35
|
+
|
|
36
|
+
// ─── Path derivation (mirrors paths.ts / message-store.ts, no app import) ──────
|
|
37
|
+
|
|
38
|
+
function resolveMaxyDir() {
|
|
39
|
+
if (process.env.MAXY_DIR_OVERRIDE) return process.env.MAXY_DIR_OVERRIDE;
|
|
40
|
+
let configDirName = ".maxy";
|
|
41
|
+
const platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
42
|
+
if (platformRoot) {
|
|
43
|
+
const brandPath = join(platformRoot, "config", "brand.json");
|
|
44
|
+
if (existsSync(brandPath)) {
|
|
45
|
+
try {
|
|
46
|
+
const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
|
|
47
|
+
if (typeof brand.configDir === "string") configDirName = brand.configDir;
|
|
48
|
+
} catch { /* fall through to default */ }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return resolve(homedir(), configDirName);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const storeRoot = (maxyDir) => join(maxyDir, "data", "whatsapp-messages");
|
|
55
|
+
const lidMapFile = (maxyDir) => join(maxyDir, "whatsapp-lid-map.json");
|
|
56
|
+
|
|
57
|
+
// ─── Pure JID helpers (semantics match normalize.ts) ───────────────────────────
|
|
58
|
+
|
|
59
|
+
const PN_RE = /^(\d+)(?::\d+)?@s\.whatsapp\.net$/i;
|
|
60
|
+
const LID_RE = /^(\d+)@lid$/i;
|
|
61
|
+
|
|
62
|
+
const isLidJid = (jid) => LID_RE.test(jid);
|
|
63
|
+
const isGroupJid = (jid) => /@g\.us$/i.test(jid);
|
|
64
|
+
|
|
65
|
+
function jidToE164(jid) {
|
|
66
|
+
const pn = jid.match(PN_RE);
|
|
67
|
+
if (pn) return pn[1];
|
|
68
|
+
const lid = jid.match(LID_RE);
|
|
69
|
+
if (lid) return lid[1];
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalizeE164(value) {
|
|
74
|
+
if (typeof value !== "string") return "";
|
|
75
|
+
let c = value.trim().replace(/[\s\-().]/g, "").replace(/^whatsapp:/i, "");
|
|
76
|
+
if (c.startsWith("+")) c = c.slice(1);
|
|
77
|
+
else if (c.startsWith("00")) c = c.slice(2);
|
|
78
|
+
return /^\d+$/.test(c) ? c : "";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── JSONL IO ──────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
function parseLines(raw) {
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const line of raw.split("\n")) {
|
|
86
|
+
if (!line.trim()) continue;
|
|
87
|
+
try { out.push(JSON.parse(line)); } catch { /* skip corrupt line */ }
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const byDateSentAsc = (a, b) => String(a.dateSent).localeCompare(String(b.dateSent));
|
|
93
|
+
|
|
94
|
+
function readLidMap(maxyDir) {
|
|
95
|
+
const file = lidMapFile(maxyDir);
|
|
96
|
+
try {
|
|
97
|
+
if (!existsSync(file)) return {};
|
|
98
|
+
const parsed = JSON.parse(readFileSync(file, "utf-8"));
|
|
99
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
100
|
+
} catch {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Canonical target for one file ──────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The canonical conversation JID a file's records belong under.
|
|
109
|
+
* - group JID → itself (single-keyed already; never merged).
|
|
110
|
+
* - phone-number JID → itself (already canonical).
|
|
111
|
+
* - LID JID → `<pn>@s.whatsapp.net` when the contact's number is recoverable
|
|
112
|
+
* (earliest inbound record's resolved senderTelephone, else the lid-map
|
|
113
|
+
* cache), matching the persist-time LID→PN resolution; else the LID unchanged.
|
|
114
|
+
*/
|
|
115
|
+
function canonicalTargetFor(remoteJid, records, lidMap) {
|
|
116
|
+
if (isGroupJid(remoteJid) || !isLidJid(remoteJid)) return remoteJid;
|
|
117
|
+
const numericLid = jidToE164(remoteJid); // digits of the LID
|
|
118
|
+
const inbound = records
|
|
119
|
+
.filter((r) => r.fromMe !== true)
|
|
120
|
+
.sort(byDateSentAsc);
|
|
121
|
+
for (const r of inbound) {
|
|
122
|
+
const phone = normalizeE164(r.senderTelephone);
|
|
123
|
+
if (phone && phone !== numericLid) return `${phone}@s.whatsapp.net`;
|
|
124
|
+
}
|
|
125
|
+
const cached = normalizeE164(lidMap[normalizeE164(numericLid)] ?? "");
|
|
126
|
+
if (cached && cached !== numericLid) return `${cached}@s.whatsapp.net`;
|
|
127
|
+
return remoteJid;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Per-account reconcile ──────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
function reconcileAccount(maxyDir, accountId, apply) {
|
|
133
|
+
const dir = join(storeRoot(maxyDir), accountId);
|
|
134
|
+
if (!existsSync(dir)) return null;
|
|
135
|
+
|
|
136
|
+
const lidMap = readLidMap(maxyDir);
|
|
137
|
+
const files = readdirSync(dir).filter((e) => e.endsWith(".jsonl"));
|
|
138
|
+
|
|
139
|
+
// remoteJid → { records, target }
|
|
140
|
+
const parsed = new Map();
|
|
141
|
+
for (const entry of files) {
|
|
142
|
+
const remoteJid = entry.slice(0, -".jsonl".length);
|
|
143
|
+
const records = parseLines(readFileSync(join(dir, entry), "utf-8"));
|
|
144
|
+
parsed.set(remoteJid, { records, target: null });
|
|
145
|
+
}
|
|
146
|
+
for (const [remoteJid, info] of parsed) {
|
|
147
|
+
info.target = canonicalTargetFor(remoteJid, info.records, lidMap);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Group source remoteJids by canonical target.
|
|
151
|
+
const groups = new Map(); // target → [sourceRemoteJid, ...]
|
|
152
|
+
for (const [remoteJid, info] of parsed) {
|
|
153
|
+
if (!groups.has(info.target)) groups.set(info.target, []);
|
|
154
|
+
groups.get(info.target).push(remoteJid);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let mergedGroups = 0;
|
|
158
|
+
let filesRemoved = 0;
|
|
159
|
+
let recordsMoved = 0;
|
|
160
|
+
let recordsDeduped = 0;
|
|
161
|
+
|
|
162
|
+
for (const [target, sources] of groups) {
|
|
163
|
+
// Nothing to do when the only source IS the canonical file already.
|
|
164
|
+
const nonCanonicalSources = sources.filter((s) => s !== target);
|
|
165
|
+
if (nonCanonicalSources.length === 0) continue;
|
|
166
|
+
mergedGroups += 1;
|
|
167
|
+
|
|
168
|
+
// Merge every source's records (target file first when it exists) into one
|
|
169
|
+
// list, dedup by messageId, rewrite remoteJid to the canonical target, sort.
|
|
170
|
+
const seen = new Set();
|
|
171
|
+
const merged = [];
|
|
172
|
+
const ordered = [target, ...nonCanonicalSources].filter((s) => parsed.has(s));
|
|
173
|
+
for (const src of ordered) {
|
|
174
|
+
const isMovedSource = src !== target;
|
|
175
|
+
for (const r of parsed.get(src).records) {
|
|
176
|
+
if (seen.has(r.messageId)) { recordsDeduped += 1; continue; }
|
|
177
|
+
seen.add(r.messageId);
|
|
178
|
+
merged.push({ ...r, remoteJid: target });
|
|
179
|
+
if (isMovedSource) recordsMoved += 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
merged.sort(byDateSentAsc);
|
|
183
|
+
|
|
184
|
+
if (apply) {
|
|
185
|
+
const targetPath = join(dir, `${target}.jsonl`);
|
|
186
|
+
const tmpPath = `${targetPath}.merge-tmp`;
|
|
187
|
+
mkdirSync(dir, { recursive: true });
|
|
188
|
+
// Write the canonical file first, then remove the moved sources — the
|
|
189
|
+
// canonical copy always exists before any source is deleted, so an
|
|
190
|
+
// interruption never loses records.
|
|
191
|
+
writeFileSync(tmpPath, merged.map((r) => JSON.stringify(r)).join("\n") + "\n");
|
|
192
|
+
renameSync(tmpPath, targetPath);
|
|
193
|
+
for (const src of nonCanonicalSources) {
|
|
194
|
+
rmSync(join(dir, `${src}.jsonl`));
|
|
195
|
+
filesRemoved += 1;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
filesRemoved += nonCanonicalSources.length;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Post-condition: after apply, no two remaining files share a canonical target.
|
|
203
|
+
const finalFiles = apply
|
|
204
|
+
? readdirSync(dir).filter((e) => e.endsWith(".jsonl"))
|
|
205
|
+
: [...groups.keys()].map((t) => `${t}.jsonl`); // dry-run projected shape
|
|
206
|
+
const finalTargets = new Set();
|
|
207
|
+
let residualDuplicates = 0;
|
|
208
|
+
if (apply) {
|
|
209
|
+
for (const entry of finalFiles) {
|
|
210
|
+
const remoteJid = entry.slice(0, -".jsonl".length);
|
|
211
|
+
const records = parseLines(readFileSync(join(dir, entry), "utf-8"));
|
|
212
|
+
const t = canonicalTargetFor(remoteJid, records, lidMap);
|
|
213
|
+
if (finalTargets.has(t)) residualDuplicates += 1;
|
|
214
|
+
finalTargets.add(t);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
accountId, filesScanned: files.length, mergedGroups, filesRemoved,
|
|
220
|
+
recordsMoved, recordsDeduped, finalFileCount: finalFiles.length, residualDuplicates,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
function main() {
|
|
227
|
+
const args = process.argv.slice(2);
|
|
228
|
+
const apply = args.includes("--apply");
|
|
229
|
+
const accountArg = args.find((a) => a.startsWith("--account="))?.slice("--account=".length) ?? null;
|
|
230
|
+
const maxyDir = resolveMaxyDir();
|
|
231
|
+
const root = storeRoot(maxyDir);
|
|
232
|
+
|
|
233
|
+
if (!existsSync(root)) {
|
|
234
|
+
console.log(`${TAG} op=merge mode=${apply ? "apply" : "dry-run"} accounts=0 reason=no-store-root root=${root}`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const accounts = accountArg
|
|
239
|
+
? [accountArg]
|
|
240
|
+
: readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
241
|
+
|
|
242
|
+
let totalResidual = 0;
|
|
243
|
+
for (const accountId of accounts) {
|
|
244
|
+
const r = reconcileAccount(maxyDir, accountId, apply);
|
|
245
|
+
if (!r) { console.log(`${TAG} op=merge account=${accountId} skip reason=no-store-dir`); continue; }
|
|
246
|
+
totalResidual += r.residualDuplicates;
|
|
247
|
+
console.log(
|
|
248
|
+
`${TAG} op=merge mode=${apply ? "apply" : "dry-run"} account=${r.accountId} ` +
|
|
249
|
+
`filesScanned=${r.filesScanned} mergedGroups=${r.mergedGroups} filesRemoved=${r.filesRemoved} ` +
|
|
250
|
+
`recordsMoved=${r.recordsMoved} recordsDeduped=${r.recordsDeduped} finalFileCount=${r.finalFileCount} ` +
|
|
251
|
+
`residualDuplicates=${r.residualDuplicates}`,
|
|
252
|
+
);
|
|
253
|
+
if (apply && r.residualDuplicates > 0) {
|
|
254
|
+
console.error(`${TAG} op=merge account=${r.accountId} WARNING residualDuplicates=${r.residualDuplicates} — a contact still spans >1 canonical file`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!apply) {
|
|
259
|
+
console.log(`${TAG} op=merge dry-run complete — re-run with --apply to write the merges above`);
|
|
260
|
+
}
|
|
261
|
+
if (apply && totalResidual > 0) process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
main();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAe3B,OAAO,EAkBL,KAAK,SAAS,EAEf,MAAM,kBAAkB,CAAA;AA8BzB,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAA;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAyFlE,eAAO,MAAM,kBAAkB,QAA2B,CAAA;AAS1D,eAAO,MAAM,4BAA4B,QAAS,CAAA;AAIlD,MAAM,WAAW,QAAS,SAAQ,IAAI,CAAC,SAAS,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC7E;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,SAAS,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,kBAAkB,EAAE,WAAW,CAAA;IAC/B,eAAe,EAAE,aAAa,CAAA;IAC9B;kFAC8E;IAC9E,WAAW,EAAE,kBAAkB,CAAA;IAC/B;;gFAE4E;IAC5E,cAAc,EAAE,cAAc,CAAA;IAC9B;;;6BAGyB;IACzB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC;;;sCAGkC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C;;gFAE4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAsFD;;;;;;mEAMmE;AACnE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAiC9D;AAMD;;;;;;iBAMiB;AACjB,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAmCvF;AAID;;;;;;yBAMyB;AACzB,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAiCzD;AAkCD,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEpG;AAgBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAUD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAMvE;AAMD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,MAAM,CAER;AAkCD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe5F;AAwJD;;;kEAGkE;AAClE,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG;IAC3B,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACpD;;;+CAG2C;IAC3C,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAChD,CAAA;AA8CD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAe3B,OAAO,EAkBL,KAAK,SAAS,EAEf,MAAM,kBAAkB,CAAA;AA8BzB,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAA;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAyFlE,eAAO,MAAM,kBAAkB,QAA2B,CAAA;AAS1D,eAAO,MAAM,4BAA4B,QAAS,CAAA;AAIlD,MAAM,WAAW,QAAS,SAAQ,IAAI,CAAC,SAAS,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC7E;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,SAAS,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,kBAAkB,EAAE,WAAW,CAAA;IAC/B,eAAe,EAAE,aAAa,CAAA;IAC9B;kFAC8E;IAC9E,WAAW,EAAE,kBAAkB,CAAA;IAC/B;;gFAE4E;IAC5E,cAAc,EAAE,cAAc,CAAA;IAC9B;;;6BAGyB;IACzB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC;;;sCAGkC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C;;gFAE4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAsFD;;;;;;mEAMmE;AACnE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAiC9D;AAMD;;;;;;iBAMiB;AACjB,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAmCvF;AAID;;;;;;yBAMyB;AACzB,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAiCzD;AAkCD,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEpG;AAgBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAUD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAMvE;AAMD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,MAAM,CAER;AAkCD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe5F;AAwJD;;;kEAGkE;AAClE,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG;IAC3B,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACpD;;;+CAG2C;IAC3C,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAChD,CAAA;AA8CD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAoyFpD"}
|
|
@@ -1468,8 +1468,17 @@ export function buildHttpApp(deps) {
|
|
|
1468
1468
|
timed(deps.logger, 'POST', `/${id}/archive`, 404, Date.now() - start);
|
|
1469
1469
|
return c.json({ error: 'session-not-found' }, 404);
|
|
1470
1470
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1471
|
+
// Task 1419 — resolve the holding slug the SAME way delete/stop and the
|
|
1472
|
+
// sidebar list do: scan every slug under projects/ for this session's
|
|
1473
|
+
// JSONL (slugForExistingJsonl checks the top level and archive/), falling
|
|
1474
|
+
// back to the boot-cwd slug only when the JSONL exists nowhere. The pre-1357
|
|
1475
|
+
// house-only `projectSlugForCwd(deps.spawnCwd)` 404'd any session whose JSONL
|
|
1476
|
+
// sat under a non-boot slug (e.g. a laptop→Mac migrated `-home-neo-…` session,
|
|
1477
|
+
// Task 1393): the sidebar listed it and its watcher row resolved, but archive
|
|
1478
|
+
// looked under the wrong slug dir and existsSync(fromJsonl) failed. Account/
|
|
1479
|
+
// slug scope is a view filter, not a write gate — this mirrors delete's resolvedSlug.
|
|
1480
|
+
const resolvedSlug = slugForExistingJsonl(deps.claudeConfigDir, row.sessionId) ?? projectSlugForCwd(deps.spawnCwd);
|
|
1481
|
+
const slugDir = join(claudeStateRoot(), 'projects', resolvedSlug);
|
|
1473
1482
|
const archiveDir = join(slugDir, 'archive');
|
|
1474
1483
|
const fileName = `${row.sessionId}.jsonl`;
|
|
1475
1484
|
// Task 260 — refuse archive whenever an alive PTY exists for this
|
|
@@ -1486,6 +1495,11 @@ export function buildHttpApp(deps) {
|
|
|
1486
1495
|
const fromJsonl = mode === 'archive' ? join(slugDir, fileName) : join(archiveDir, fileName);
|
|
1487
1496
|
const toJsonl = mode === 'archive' ? join(archiveDir, fileName) : join(slugDir, fileName);
|
|
1488
1497
|
if (!existsSync(fromJsonl)) {
|
|
1498
|
+
// Task 1419 — attributable not-found on the slug-resolution 404 branch:
|
|
1499
|
+
// name the id, mode, and the slug the scan resolved so a future
|
|
1500
|
+
// occurrence shows which slug was searched vs. where the file actually
|
|
1501
|
+
// is. Previously silent, which is why a mislocated JSONL 404'd with no trace.
|
|
1502
|
+
deps.logger(`archive-not-found sessionId=${row.sessionId.slice(0, 8)} mode=${mode} resolved-slug=${resolvedSlug}`);
|
|
1489
1503
|
timed(deps.logger, 'POST', `/${id}/archive`, 404, Date.now() - start);
|
|
1490
1504
|
return c.json({ error: 'session-not-found' }, 404);
|
|
1491
1505
|
}
|
|
@@ -1507,12 +1521,14 @@ export function buildHttpApp(deps) {
|
|
|
1507
1521
|
// log line surfaces.
|
|
1508
1522
|
let sidecarMoved = false;
|
|
1509
1523
|
try {
|
|
1524
|
+
// Task 1419 — sidecar derives from the resolved slug (not spawnCwd), so
|
|
1525
|
+
// the `.meta.json` travels with the JSONL under whichever slug holds it.
|
|
1510
1526
|
const fromSidecar = mode === 'archive'
|
|
1511
|
-
?
|
|
1512
|
-
:
|
|
1527
|
+
? sidecarPathForSlug(deps.claudeConfigDir, resolvedSlug, row.sessionId)
|
|
1528
|
+
: sidecarPathForSlug(deps.claudeConfigDir, resolvedSlug, row.sessionId, { archived: true });
|
|
1513
1529
|
const toSidecar = mode === 'archive'
|
|
1514
|
-
?
|
|
1515
|
-
:
|
|
1530
|
+
? sidecarPathForSlug(deps.claudeConfigDir, resolvedSlug, row.sessionId, { archived: true })
|
|
1531
|
+
: sidecarPathForSlug(deps.claudeConfigDir, resolvedSlug, row.sessionId);
|
|
1516
1532
|
if (existsSync(fromSidecar)) {
|
|
1517
1533
|
renameSync(fromSidecar, toSidecar);
|
|
1518
1534
|
sidecarMoved = true;
|