@geminilight/mindos 0.1.0
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/.env.local.example +38 -0
- package/LICENSE +21 -0
- package/README.md +423 -0
- package/README_zh.md +423 -0
- package/app/README.md +152 -0
- package/app/app/api/ask/route.ts +170 -0
- package/app/app/api/ask-sessions/route.ts +90 -0
- package/app/app/api/auth/route.ts +37 -0
- package/app/app/api/backlinks/route.ts +22 -0
- package/app/app/api/bootstrap/route.ts +37 -0
- package/app/app/api/extract-pdf/route.ts +82 -0
- package/app/app/api/file/route.ts +138 -0
- package/app/app/api/files/route.ts +12 -0
- package/app/app/api/git/route.ts +42 -0
- package/app/app/api/graph/route.ts +113 -0
- package/app/app/api/recent-files/route.ts +10 -0
- package/app/app/api/search/route.ts +17 -0
- package/app/app/api/settings/reset-token/route.ts +21 -0
- package/app/app/api/settings/route.ts +123 -0
- package/app/app/error.tsx +33 -0
- package/app/app/globals.css +368 -0
- package/app/app/icon.svg +35 -0
- package/app/app/layout.tsx +103 -0
- package/app/app/login/page.tsx +120 -0
- package/app/app/page.tsx +12 -0
- package/app/app/view/[...path]/ViewPageClient.tsx +343 -0
- package/app/app/view/[...path]/error.tsx +33 -0
- package/app/app/view/[...path]/loading.tsx +15 -0
- package/app/app/view/[...path]/page.tsx +93 -0
- package/app/components/AskFab.tsx +59 -0
- package/app/components/AskModal.tsx +398 -0
- package/app/components/Backlinks.tsx +75 -0
- package/app/components/Breadcrumb.tsx +31 -0
- package/app/components/CsvView.tsx +325 -0
- package/app/components/DirView.tsx +138 -0
- package/app/components/Editor.tsx +124 -0
- package/app/components/EditorWrapper.tsx +17 -0
- package/app/components/ErrorBoundary.tsx +53 -0
- package/app/components/FileTree.tsx +369 -0
- package/app/components/HomeContent.tsx +262 -0
- package/app/components/JsonView.tsx +27 -0
- package/app/components/MarkdownEditor.tsx +95 -0
- package/app/components/MarkdownView.tsx +118 -0
- package/app/components/SearchModal.tsx +193 -0
- package/app/components/SettingsModal.tsx +237 -0
- package/app/components/Sidebar.tsx +136 -0
- package/app/components/SidebarLayout.tsx +36 -0
- package/app/components/TableOfContents.tsx +150 -0
- package/app/components/ThemeToggle.tsx +34 -0
- package/app/components/WysiwygEditor.tsx +75 -0
- package/app/components/ask/FileChip.tsx +30 -0
- package/app/components/ask/MentionPopover.tsx +52 -0
- package/app/components/ask/MessageList.tsx +126 -0
- package/app/components/ask/SessionHistory.tsx +49 -0
- package/app/components/renderers/AgentInspectorRenderer.tsx +277 -0
- package/app/components/renderers/BacklinksRenderer.tsx +147 -0
- package/app/components/renderers/ConfigRenderer.tsx +236 -0
- package/app/components/renderers/CsvRenderer.tsx +77 -0
- package/app/components/renderers/DiffRenderer.tsx +310 -0
- package/app/components/renderers/GraphRenderer.tsx +428 -0
- package/app/components/renderers/SummaryRenderer.tsx +251 -0
- package/app/components/renderers/TimelineRenderer.tsx +213 -0
- package/app/components/renderers/TodoRenderer.tsx +474 -0
- package/app/components/renderers/WorkflowRenderer.tsx +404 -0
- package/app/components/renderers/csv/BoardView.tsx +146 -0
- package/app/components/renderers/csv/ConfigPanel.tsx +117 -0
- package/app/components/renderers/csv/EditableCell.tsx +43 -0
- package/app/components/renderers/csv/GalleryView.tsx +40 -0
- package/app/components/renderers/csv/TableView.tsx +164 -0
- package/app/components/renderers/csv/types.ts +87 -0
- package/app/components/settings/AiTab.tsx +111 -0
- package/app/components/settings/AppearanceTab.tsx +101 -0
- package/app/components/settings/KnowledgeTab.tsx +157 -0
- package/app/components/settings/PluginsTab.tsx +82 -0
- package/app/components/settings/Primitives.tsx +60 -0
- package/app/components/settings/ShortcutsTab.tsx +22 -0
- package/app/components/settings/types.ts +41 -0
- package/app/components/ui/button.tsx +60 -0
- package/app/components/ui/dialog.tsx +157 -0
- package/app/components/ui/input.tsx +20 -0
- package/app/components/ui/scroll-area.tsx +55 -0
- package/app/components/ui/toggle.tsx +44 -0
- package/app/components/ui/tooltip.tsx +66 -0
- package/app/components.json +25 -0
- package/app/data/pages/home-dark.png +0 -0
- package/app/data/pages/home-mobile-crop.png +0 -0
- package/app/data/pages/home-mobile.png +0 -0
- package/app/data/pages/home.png +0 -0
- package/app/data/pages/view-dir.png +0 -0
- package/app/data/pages/view-file-bot.png +0 -0
- package/app/data/pages/view-file-dark-crop.png +0 -0
- package/app/data/pages/view-file-dark.png +0 -0
- package/app/data/pages/view-file-mobile.png +0 -0
- package/app/data/pages/view-file-sm.png +0 -0
- package/app/data/pages/view-file-top.png +0 -0
- package/app/data/pages/view-file.png +0 -0
- package/app/eslint.config.mjs +18 -0
- package/app/hooks/useAskSession.ts +181 -0
- package/app/hooks/useFileUpload.ts +126 -0
- package/app/hooks/useMention.ts +65 -0
- package/app/lib/LocaleContext.tsx +40 -0
- package/app/lib/actions.ts +40 -0
- package/app/lib/agent/index.ts +3 -0
- package/app/lib/agent/model.ts +18 -0
- package/app/lib/agent/prompt.ts +32 -0
- package/app/lib/agent/tools.ts +151 -0
- package/app/lib/api.ts +55 -0
- package/app/lib/core/backlinks.ts +40 -0
- package/app/lib/core/csv.ts +28 -0
- package/app/lib/core/fs-ops.ts +118 -0
- package/app/lib/core/git.ts +50 -0
- package/app/lib/core/index.ts +58 -0
- package/app/lib/core/lines.ts +89 -0
- package/app/lib/core/search.ts +79 -0
- package/app/lib/core/security.ts +43 -0
- package/app/lib/core/tree.ts +113 -0
- package/app/lib/core/types.ts +40 -0
- package/app/lib/fs.ts +467 -0
- package/app/lib/i18n.ts +300 -0
- package/app/lib/jwt.ts +58 -0
- package/app/lib/renderers/index.ts +79 -0
- package/app/lib/renderers/registry.ts +70 -0
- package/app/lib/settings.ts +150 -0
- package/app/lib/types.ts +32 -0
- package/app/lib/utils.ts +34 -0
- package/app/next-env.d.ts +6 -0
- package/app/next.config.ts +10 -0
- package/app/package-lock.json +15306 -0
- package/app/package.json +71 -0
- package/app/postcss.config.mjs +7 -0
- package/app/proxy.ts +64 -0
- package/app/public/file.svg +1 -0
- package/app/public/globe.svg +1 -0
- package/app/public/landing/index.html +353 -0
- package/app/public/landing/style.css +216 -0
- package/app/public/logo-square.svg +37 -0
- package/app/public/logo.svg +37 -0
- package/app/public/next.svg +1 -0
- package/app/public/vercel.svg +1 -0
- package/app/public/window.svg +1 -0
- package/app/scripts/extract-pdf.cjs +56 -0
- package/app/tsconfig.json +34 -0
- package/app/vitest.config.ts +14 -0
- package/assets/demo-flow-zh.html +622 -0
- package/assets/images/demo-flow-dark.png +0 -0
- package/assets/images/demo-flow-light.png +0 -0
- package/assets/images/demo-flow-zh-dark.png +0 -0
- package/assets/images/demo-flow-zh-light.png +0 -0
- package/assets/images/gui-sync-cv.png +0 -0
- package/assets/logo-square.svg +37 -0
- package/bin/cli.js +894 -0
- package/mcp/README.md +113 -0
- package/mcp/package-lock.json +1717 -0
- package/mcp/package.json +18 -0
- package/mcp/src/index.ts +494 -0
- package/mcp/tsconfig.json +13 -0
- package/package.json +49 -0
- package/scripts/setup.js +675 -0
- package/scripts/upgrade-prompt.md +147 -0
- package/skills/mindos/SKILL.md +319 -0
- package/skills/mindos-zh/SKILL.md +318 -0
- package/templates/README.md +31 -0
- package/templates/empty/CHANGELOG.md +9 -0
- package/templates/empty/CONFIG.json +197 -0
- package/templates/empty/CONFIG.md +73 -0
- package/templates/empty/INSTRUCTION.md +177 -0
- package/templates/empty/README.md +27 -0
- package/templates/en/CHANGELOG.md +9 -0
- package/templates/en/CONFIG.json +197 -0
- package/templates/en/CONFIG.md +73 -0
- package/templates/en/INSTRUCTION.md +177 -0
- package/templates/en/README.md +27 -0
- package/templates/en/TODO.md +13 -0
- package/templates/en//360/237/221/244 Profile/INSTRUCTION.md" +21 -0
- package/templates/en//360/237/221/244 Profile/README.md" +15 -0
- package/templates/en//360/237/221/244 Profile//342/232/231/357/270/217 Preferences.md" +21 -0
- package/templates/en//360/237/221/244 Profile//360/237/216/257 Focus.md" +31 -0
- package/templates/en//360/237/221/244 Profile//360/237/221/244 Identity.md" +22 -0
- package/templates/en//360/237/223/232 Resources/INSTRUCTION.md" +29 -0
- package/templates/en//360/237/223/232 Resources/README.md" +21 -0
- package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Influencers.csv" +1 -0
- package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Products.csv" +1 -0
- package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Scholars.csv" +1 -0
- package/templates/en//360/237/223/232 Resources//360/237/247/276 AI Tools.csv" +1 -0
- package/templates/en//360/237/223/235 Notes/INSTRUCTION.md" +31 -0
- package/templates/en//360/237/223/235 Notes/Ideas/README.md" +8 -0
- package/templates/en//360/237/223/235 Notes/Ideas//360/237/247/252_example_product_idea.md" +16 -0
- package/templates/en//360/237/223/235 Notes/Inbox/README.md" +8 -0
- package/templates/en//360/237/223/235 Notes/Inbox//360/237/247/252_example_quick_capture.md" +14 -0
- package/templates/en//360/237/223/235 Notes/Meetings/README.md" +8 -0
- package/templates/en//360/237/223/235 Notes/Meetings//360/237/247/252_example_meeting_note.md" +17 -0
- package/templates/en//360/237/223/235 Notes/README.md" +24 -0
- package/templates/en//360/237/223/235 Notes/Waiting/README.md" +8 -0
- package/templates/en//360/237/223/235 Notes/Waiting//360/237/247/252_example_blocked_item.md" +16 -0
- package/templates/en//360/237/224/204 Workflows/Configurations/README.md" +3 -0
- package/templates/en//360/237/224/204 Workflows/Configurations//360/237/247/252_example_config_update_sop.md" +14 -0
- package/templates/en//360/237/224/204 Workflows/INSTRUCTION.md" +21 -0
- package/templates/en//360/237/224/204 Workflows/Information/README.md" +16 -0
- package/templates/en//360/237/224/204 Workflows/Information//360/237/247/252_example_info_capture_sop.md" +13 -0
- package/templates/en//360/237/224/204 Workflows/Media/README.md" +16 -0
- package/templates/en//360/237/224/204 Workflows/Media//360/237/247/252_example_content_publish_sop.md" +13 -0
- package/templates/en//360/237/224/204 Workflows/README.md" +22 -0
- package/templates/en//360/237/224/204 Workflows/Research/README.md" +16 -0
- package/templates/en//360/237/224/204 Workflows/Research//360/237/247/252_example_lit_review_sop.md" +16 -0
- package/templates/en//360/237/224/204 Workflows/Startup/README.md" +3 -0
- package/templates/en//360/237/224/204 Workflows/Startup//360/237/247/252_example_weekly_founder_ops.md" +22 -0
- package/templates/en//360/237/224/227 Connections/Classmates/README.md" +11 -0
- package/templates/en//360/237/224/227 Connections/Classmates//360/237/247/252_example_leo_chen.md" +16 -0
- package/templates/en//360/237/224/227 Connections/Colleagues/README.md" +11 -0
- package/templates/en//360/237/224/227 Connections/Colleagues//360/237/247/252_example_ethan_zhao.md" +16 -0
- package/templates/en//360/237/224/227 Connections/Connections Overview.csv" +5 -0
- package/templates/en//360/237/224/227 Connections/Family/README.md" +11 -0
- package/templates/en//360/237/224/227 Connections/Family//360/237/247/252_example_james_wang.md" +16 -0
- package/templates/en//360/237/224/227 Connections/Friends/README.md" +11 -0
- package/templates/en//360/237/224/227 Connections/Friends//360/237/247/252_example_lily_lin.md" +16 -0
- package/templates/en//360/237/224/227 Connections/INSTRUCTION.md" +56 -0
- package/templates/en//360/237/224/227 Connections/README.md" +20 -0
- package/templates/en//360/237/232/200 Projects/Archived/README.md" +9 -0
- package/templates/en//360/237/232/200 Projects/Archived//360/237/247/252_example_archived_project_note.md" +14 -0
- package/templates/en//360/237/232/200 Projects/INSTRUCTION.md" +29 -0
- package/templates/en//360/237/232/200 Projects/Products/README.md" +16 -0
- package/templates/en//360/237/232/200 Projects/Products//360/237/247/252_example_product_project_brief.md" +20 -0
- package/templates/en//360/237/232/200 Projects/README.md" +21 -0
- package/templates/en//360/237/232/200 Projects/Research/README.md" +16 -0
- package/templates/en//360/237/232/200 Projects/Research//360/237/247/252_example_research_project_brief.md" +16 -0
- package/templates/template-generation-skill.md +79 -0
- package/templates/zh/CHANGELOG.md +9 -0
- package/templates/zh/CONFIG.json +197 -0
- package/templates/zh/CONFIG.md +66 -0
- package/templates/zh/INSTRUCTION.md +177 -0
- package/templates/zh/README.md +27 -0
- package/templates/zh/TODO.md +13 -0
- package/templates/zh//360/237/221/244 /347/224/273/345/203/217/INSTRUCTION.md" +28 -0
- package/templates/zh//360/237/221/244 /347/224/273/345/203/217/README.md" +20 -0
- package/templates/zh//360/237/221/244 /347/224/273/345/203/217//342/232/231/357/270/217 Preferences.md" +21 -0
- package/templates/zh//360/237/221/244 /347/224/273/345/203/217//360/237/216/257 Focus.md" +31 -0
- package/templates/zh//360/237/221/244 /347/224/273/345/203/217//360/237/221/244 Identity.md" +22 -0
- package/templates/zh//360/237/223/232 /350/265/204/346/272/220/INSTRUCTION.md" +29 -0
- package/templates/zh//360/237/223/232 /350/265/204/346/272/220/README.md" +21 -0
- package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI Inferencers.csv" +1 -0
- package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI /344/272/247/345/223/201.csv" +1 -0
- package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI /345/255/246/350/200/205/346/270/205/345/215/225.csv" +1 -0
- package/templates/zh//360/237/223/232 /350/265/204/346/272/220//360/237/247/276 AI /345/267/245/345/205/267/346/270/205/345/215/225.csv" +1 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260/INSTRUCTION.md" +31 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260/README.md" +24 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//344/274/232/350/256/256/README.md" +8 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//344/274/232/350/256/256//360/237/247/252_example_/344/274/232/350/256/256/347/272/252/350/246/201.md" +17 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//345/276/205/345/217/215/351/246/210/README.md" +8 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//345/276/205/345/217/215/351/246/210//360/237/247/252_example_/345/276/205/345/217/215/351/246/210/344/272/213/351/241/271.md" +16 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/203/263/346/263/225/README.md" +8 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/203/263/346/263/225//360/237/247/252_example_/344/272/247/345/223/201/346/203/263/346/263/225.md" +16 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/224/266/344/273/266/347/256/261/README.md" +8 -0
- package/templates/zh//360/237/223/235 /347/254/224/350/256/260//346/224/266/344/273/266/347/256/261//360/237/247/252_example_/344/270/264/346/227/266/351/200/237/350/256/260.md" +13 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213/INSTRUCTION.md" +29 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213/README.md" +21 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//344/277/241/346/201/257/README.md" +16 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//344/277/241/346/201/257//360/237/247/252_example_/344/277/241/346/201/257/351/207/207/351/233/206/346/265/201/347/250/213.md" +13 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//345/252/222/344/275/223/README.md" +16 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//345/252/222/344/275/223//360/237/247/252_example_/345/206/205/345/256/271/345/217/221/345/270/203/346/265/201/347/250/213.md" +13 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//347/247/221/347/240/224/README.md" +16 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//347/247/221/347/240/224//360/237/247/252_example_/346/226/207/347/214/256/347/273/274/350/277/260/346/265/201/347/250/213.md" +16 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//351/205/215/347/275/256/README.md" +3 -0
- package/templates/zh//360/237/224/204 /346/265/201/347/250/213//351/205/215/347/275/256//360/237/247/252_example_/351/205/215/347/275/256/346/233/264/346/226/260/346/265/201/347/250/213.md" +26 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273/INSTRUCTION.md" +62 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273/README.md" +20 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/205/263/347/263/273/346/200/273/350/247/210.csv" +5 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/344/272/213/README.md" +11 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/344/272/213//360/237/247/252_example_/345/220/214/344/272/213/350/265/265/344/270/200/350/276/260.md" +16 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/345/255/246/README.md" +11 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/220/214/345/255/246//360/237/247/252_example_/345/220/214/345/255/246/351/231/210/347/253/213/346/254/247.md" +16 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/256/266/344/272/272/README.md" +11 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//345/256/266/344/272/272//360/237/247/252_example_/345/256/266/344/272/272/347/216/213/345/273/272/345/233/275.md" +16 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//346/234/213/345/217/213/README.md" +11 -0
- package/templates/zh//360/237/224/227 /345/205/263/347/263/273//346/234/213/345/217/213//360/237/247/252_example_/346/234/213/345/217/213/346/236/227/345/260/217/344/270/275.md" +16 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256/INSTRUCTION.md" +31 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256/README.md" +21 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256//344/272/247/345/223/201/README.md" +16 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256//344/272/247/345/223/201//360/237/247/252_example_/344/272/247/345/223/201/351/241/271/347/233/256/347/256/200/346/212/245.md" +20 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256//345/267/262/345/275/222/346/241/243/README.md" +9 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256//345/267/262/345/275/222/346/241/243//360/237/247/252_example_/345/275/222/346/241/243/351/241/271/347/233/256/350/256/260/345/275/225.md" +15 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256//347/247/221/347/240/224/README.md" +16 -0
- package/templates/zh//360/237/232/200 /351/241/271/347/233/256//347/247/221/347/240/224//360/237/247/252_example_/347/247/221/347/240/224/351/241/271/347/233/256/347/256/200/346/212/245.md" +16 -0
package/app/lib/fs.ts
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import Fuse, { FuseResultMatch } from 'fuse.js';
|
|
4
|
+
import {
|
|
5
|
+
readFile as coreReadFile,
|
|
6
|
+
writeFile as coreWriteFile,
|
|
7
|
+
createFile as coreCreateFile,
|
|
8
|
+
deleteFile as coreDeleteFile,
|
|
9
|
+
renameFile as coreRenameFile,
|
|
10
|
+
moveFile as coreMoveFile,
|
|
11
|
+
readLines as coreReadLines,
|
|
12
|
+
insertLines as coreInsertLines,
|
|
13
|
+
updateLines as coreUpdateLines,
|
|
14
|
+
appendToFile as coreAppendToFile,
|
|
15
|
+
insertAfterHeading as coreInsertAfterHeading,
|
|
16
|
+
updateSection as coreUpdateSection,
|
|
17
|
+
appendCsvRow as coreAppendCsvRow,
|
|
18
|
+
findBacklinks as coreFindBacklinks,
|
|
19
|
+
isGitRepo as coreIsGitRepo,
|
|
20
|
+
gitLog as coreGitLog,
|
|
21
|
+
gitShowFile as coreGitShowFile,
|
|
22
|
+
} from './core';
|
|
23
|
+
import { FileNode } from './core/types';
|
|
24
|
+
import { SearchMatch } from './types';
|
|
25
|
+
import { effectiveSopRoot } from './settings';
|
|
26
|
+
|
|
27
|
+
// ─── Root helpers ─────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
/** Resolved MIND_ROOT — respects settings file override, then env var, then default */
|
|
30
|
+
export function getMindRoot(): string {
|
|
31
|
+
return effectiveSopRoot();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const IGNORED_DIRS = new Set(['.git', 'node_modules', 'app', '.next', '.DS_Store']);
|
|
35
|
+
const ALLOWED_EXTENSIONS = new Set(['.md', '.csv', '.json']);
|
|
36
|
+
|
|
37
|
+
// ─── In-memory cache ──────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
interface FileTreeCache {
|
|
40
|
+
tree: FileNode[];
|
|
41
|
+
allFiles: string[];
|
|
42
|
+
timestamp: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let _cache: FileTreeCache | null = null;
|
|
46
|
+
const CACHE_TTL_MS = 5_000; // 5 seconds
|
|
47
|
+
|
|
48
|
+
function isCacheValid(): boolean {
|
|
49
|
+
return _cache !== null && (Date.now() - _cache.timestamp) < CACHE_TTL_MS;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Invalidate cache — call after any write/create/delete/rename operation */
|
|
53
|
+
export function invalidateCache(): void {
|
|
54
|
+
_cache = null;
|
|
55
|
+
_searchIndex = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function ensureCache(): FileTreeCache {
|
|
59
|
+
if (isCacheValid()) return _cache!;
|
|
60
|
+
const root = getMindRoot();
|
|
61
|
+
const tree = buildFileTree(root);
|
|
62
|
+
const allFiles = buildAllFiles(root);
|
|
63
|
+
_cache = { tree, allFiles, timestamp: Date.now() };
|
|
64
|
+
return _cache;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── Internal builders ────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
function buildFileTree(dirPath: string): FileNode[] {
|
|
70
|
+
const root = getMindRoot();
|
|
71
|
+
let entries: fs.Dirent[];
|
|
72
|
+
try {
|
|
73
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
74
|
+
} catch {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const nodes: FileNode[] = [];
|
|
79
|
+
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
82
|
+
const relativePath = path.relative(root, fullPath);
|
|
83
|
+
|
|
84
|
+
if (entry.isDirectory()) {
|
|
85
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
86
|
+
const children = buildFileTree(fullPath);
|
|
87
|
+
if (children.length > 0) {
|
|
88
|
+
nodes.push({ name: entry.name, path: relativePath, type: 'directory', children });
|
|
89
|
+
}
|
|
90
|
+
} else if (entry.isFile()) {
|
|
91
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
92
|
+
if (ALLOWED_EXTENSIONS.has(ext)) {
|
|
93
|
+
nodes.push({ name: entry.name, path: relativePath, type: 'file', extension: ext });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
nodes.sort((a, b) => {
|
|
99
|
+
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
|
|
100
|
+
return a.name.localeCompare(b.name);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return nodes;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function buildAllFiles(dirPath: string): string[] {
|
|
107
|
+
const root = getMindRoot();
|
|
108
|
+
let entries: fs.Dirent[];
|
|
109
|
+
try {
|
|
110
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
111
|
+
} catch {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const files: string[] = [];
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
118
|
+
if (entry.isDirectory()) {
|
|
119
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
120
|
+
files.push(...buildAllFiles(fullPath));
|
|
121
|
+
} else if (entry.isFile()) {
|
|
122
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
123
|
+
if (ALLOWED_EXTENSIONS.has(ext)) {
|
|
124
|
+
files.push(path.relative(root, fullPath));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return files;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ─── Public API: Tree & cache (app-specific) ─────────────────────────────────
|
|
132
|
+
|
|
133
|
+
/** Returns the cached file tree for the knowledge base. */
|
|
134
|
+
export function getFileTree(): FileNode[] {
|
|
135
|
+
return ensureCache().tree;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Returns cached list of all file paths (relative to MIND_ROOT). */
|
|
139
|
+
export function collectAllFiles(): string[] {
|
|
140
|
+
return ensureCache().allFiles;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Returns whether a relative path is a directory within MIND_ROOT. */
|
|
144
|
+
export function isDirectory(filePath: string): boolean {
|
|
145
|
+
try {
|
|
146
|
+
const root = path.resolve(getMindRoot());
|
|
147
|
+
const resolved = path.resolve(path.join(root, filePath));
|
|
148
|
+
return fs.statSync(resolved).isDirectory();
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Returns the immediate children (files + subdirs) of a directory. */
|
|
155
|
+
export function getDirEntries(dirPath: string): FileNode[] {
|
|
156
|
+
const root = getMindRoot();
|
|
157
|
+
const rootResolved = path.resolve(root);
|
|
158
|
+
const resolved = path.resolve(path.join(rootResolved, dirPath));
|
|
159
|
+
|
|
160
|
+
let entries: fs.Dirent[];
|
|
161
|
+
try {
|
|
162
|
+
entries = fs.readdirSync(resolved, { withFileTypes: true });
|
|
163
|
+
} catch {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const nodes: FileNode[] = [];
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
170
|
+
const fullPath = path.join(resolved, entry.name);
|
|
171
|
+
const relativePath = path.relative(rootResolved, fullPath);
|
|
172
|
+
if (entry.isDirectory()) {
|
|
173
|
+
const children = buildFileTree(fullPath);
|
|
174
|
+
if (children.length > 0) {
|
|
175
|
+
nodes.push({ name: entry.name, path: relativePath, type: 'directory', children });
|
|
176
|
+
}
|
|
177
|
+
} else if (entry.isFile()) {
|
|
178
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
179
|
+
if (ALLOWED_EXTENSIONS.has(ext)) {
|
|
180
|
+
let mtime: number | undefined;
|
|
181
|
+
try { mtime = fs.statSync(fullPath).mtimeMs; } catch { /* ignore */ }
|
|
182
|
+
nodes.push({ name: entry.name, path: relativePath, type: 'file', extension: ext, mtime });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
nodes.sort((a, b) => {
|
|
188
|
+
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
|
|
189
|
+
return a.name.localeCompare(b.name);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return nodes;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function getRecentlyModified(limit = 10): Array<{ path: string; mtime: number }> {
|
|
196
|
+
const root = getMindRoot();
|
|
197
|
+
const allFiles = collectAllFiles();
|
|
198
|
+
const withMtime = allFiles.map((filePath) => {
|
|
199
|
+
try {
|
|
200
|
+
const abs = path.join(root, filePath);
|
|
201
|
+
const stat = fs.statSync(abs);
|
|
202
|
+
return { path: filePath, mtime: stat.mtimeMs };
|
|
203
|
+
} catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}).filter(Boolean) as Array<{ path: string; mtime: number }>;
|
|
207
|
+
|
|
208
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
209
|
+
return withMtime.slice(0, limit);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ─── Public API: File operations (delegated to @mindos/core) ─────────────────
|
|
213
|
+
|
|
214
|
+
/** Reads the content of a file given a relative path from MIND_ROOT. */
|
|
215
|
+
export function getFileContent(filePath: string): string {
|
|
216
|
+
return coreReadFile(getMindRoot(), filePath);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Atomically writes content to a file given a relative path from MIND_ROOT. */
|
|
220
|
+
export function saveFileContent(filePath: string, content: string): void {
|
|
221
|
+
coreWriteFile(getMindRoot(), filePath, content);
|
|
222
|
+
invalidateCache();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Creates a new file at the given relative path. Creates parent dirs as needed. */
|
|
226
|
+
export function createFile(filePath: string, initialContent = ''): void {
|
|
227
|
+
coreCreateFile(getMindRoot(), filePath, initialContent);
|
|
228
|
+
invalidateCache();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function deleteFile(filePath: string): void {
|
|
232
|
+
coreDeleteFile(getMindRoot(), filePath);
|
|
233
|
+
invalidateCache();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Renames a file. newName must be a plain filename (no path separators). */
|
|
237
|
+
export function renameFile(oldPath: string, newName: string): string {
|
|
238
|
+
const result = coreRenameFile(getMindRoot(), oldPath, newName);
|
|
239
|
+
invalidateCache();
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ─── Public API: Line-level operations (delegated to @mindos/core) ───────────
|
|
244
|
+
|
|
245
|
+
export function readLines(filePath: string): string[] {
|
|
246
|
+
return coreReadLines(getMindRoot(), filePath);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function insertLines(filePath: string, afterIndex: number, lines: string[]): void {
|
|
250
|
+
coreInsertLines(getMindRoot(), filePath, afterIndex, lines);
|
|
251
|
+
invalidateCache();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function updateLines(filePath: string, startIndex: number, endIndex: number, newLines: string[]): void {
|
|
255
|
+
coreUpdateLines(getMindRoot(), filePath, startIndex, endIndex, newLines);
|
|
256
|
+
invalidateCache();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function deleteLines(filePath: string, startIndex: number, endIndex: number): void {
|
|
260
|
+
const existing = readLines(filePath);
|
|
261
|
+
if (startIndex < 0 || endIndex < 0) throw new Error('Invalid line index: indices must be >= 0');
|
|
262
|
+
if (startIndex > endIndex) throw new Error(`Invalid range: start (${startIndex}) > end (${endIndex})`);
|
|
263
|
+
if (startIndex >= existing.length) throw new Error(`Invalid line index: start (${startIndex}) >= total lines (${existing.length})`);
|
|
264
|
+
existing.splice(startIndex, endIndex - startIndex + 1);
|
|
265
|
+
saveFileContent(filePath, existing.join('\n'));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ─── Public API: High-level semantic operations (delegated to @mindos/core) ──
|
|
269
|
+
|
|
270
|
+
export function appendToFile(filePath: string, content: string): void {
|
|
271
|
+
coreAppendToFile(getMindRoot(), filePath, content);
|
|
272
|
+
invalidateCache();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function insertAfterHeading(filePath: string, heading: string, content: string): void {
|
|
276
|
+
coreInsertAfterHeading(getMindRoot(), filePath, heading, content);
|
|
277
|
+
invalidateCache();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function updateSection(filePath: string, heading: string, newContent: string): void {
|
|
281
|
+
coreUpdateSection(getMindRoot(), filePath, heading, newContent);
|
|
282
|
+
invalidateCache();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** App-level search result (extends core SearchResult with Fuse.js match details) */
|
|
286
|
+
export interface AppSearchResult {
|
|
287
|
+
path: string;
|
|
288
|
+
snippet: string;
|
|
289
|
+
score: number;
|
|
290
|
+
matches?: SearchMatch[];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ─── Search (app-specific: Fuse.js fuzzy search with CJK support) ────────────
|
|
294
|
+
//
|
|
295
|
+
// This is the frontend search used by the ⌘K overlay in the browser.
|
|
296
|
+
// It uses Fuse.js for fuzzy matching with CJK language support.
|
|
297
|
+
//
|
|
298
|
+
// NOTE: A separate literal search exists in `lib/core/search.ts`, used by
|
|
299
|
+
// the MCP server via the REST API. The two coexist intentionally:
|
|
300
|
+
// - App search (here): Fuse.js fuzzy match, best for interactive UI search
|
|
301
|
+
// - Core search (lib/core/search.ts): exact literal match with filters, best for MCP tools
|
|
302
|
+
|
|
303
|
+
const MAX_CONTENT_LENGTH = 50_000;
|
|
304
|
+
|
|
305
|
+
interface SearchIndex {
|
|
306
|
+
fuse: InstanceType<typeof Fuse<SearchDocument>>;
|
|
307
|
+
documents: SearchDocument[];
|
|
308
|
+
timestamp: number;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
interface SearchDocument {
|
|
312
|
+
path: string;
|
|
313
|
+
fileName: string;
|
|
314
|
+
content: string;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let _searchIndex: SearchIndex | null = null;
|
|
318
|
+
|
|
319
|
+
function getSearchIndex(): SearchIndex {
|
|
320
|
+
if (_searchIndex && isCacheValid()) return _searchIndex;
|
|
321
|
+
|
|
322
|
+
const allFiles = collectAllFiles();
|
|
323
|
+
const documents: SearchDocument[] = [];
|
|
324
|
+
|
|
325
|
+
for (const filePath of allFiles) {
|
|
326
|
+
let content: string;
|
|
327
|
+
try {
|
|
328
|
+
content = getFileContent(filePath);
|
|
329
|
+
} catch {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (content.length > MAX_CONTENT_LENGTH) {
|
|
333
|
+
content = content.slice(0, MAX_CONTENT_LENGTH);
|
|
334
|
+
}
|
|
335
|
+
documents.push({
|
|
336
|
+
path: filePath,
|
|
337
|
+
fileName: path.basename(filePath),
|
|
338
|
+
content,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const fuse = new Fuse(documents, {
|
|
343
|
+
keys: [
|
|
344
|
+
{ name: 'fileName', weight: 0.3 },
|
|
345
|
+
{ name: 'path', weight: 0.2 },
|
|
346
|
+
{ name: 'content', weight: 0.5 },
|
|
347
|
+
],
|
|
348
|
+
includeScore: true,
|
|
349
|
+
includeMatches: true,
|
|
350
|
+
threshold: 0.4,
|
|
351
|
+
ignoreLocation: true,
|
|
352
|
+
minMatchCharLength: 2,
|
|
353
|
+
useExtendedSearch: true,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
_searchIndex = { fuse, documents, timestamp: Date.now() };
|
|
357
|
+
return _searchIndex;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Full-text search across all files using Fuse.js fuzzy matching. */
|
|
361
|
+
export function searchFiles(query: string): AppSearchResult[] {
|
|
362
|
+
if (!query.trim()) return [];
|
|
363
|
+
|
|
364
|
+
const { fuse } = getSearchIndex();
|
|
365
|
+
|
|
366
|
+
const hasCJK = /[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(query);
|
|
367
|
+
const searchQuery = hasCJK ? `'${query}` : query;
|
|
368
|
+
|
|
369
|
+
const fuseResults = fuse.search(searchQuery, { limit: 20 });
|
|
370
|
+
|
|
371
|
+
return fuseResults.map((r) => {
|
|
372
|
+
const filePath = r.item.path;
|
|
373
|
+
const content = r.item.content;
|
|
374
|
+
const score = 1 - (r.score ?? 1);
|
|
375
|
+
|
|
376
|
+
const snippet = generateSnippet(content, r.matches);
|
|
377
|
+
|
|
378
|
+
const matches = r.matches?.map((m) => ({
|
|
379
|
+
indices: m.indices as [number, number][],
|
|
380
|
+
value: m.value ?? '',
|
|
381
|
+
key: m.key ?? '',
|
|
382
|
+
}));
|
|
383
|
+
|
|
384
|
+
return { path: filePath, snippet, score, matches };
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** Pick the best (longest) content match and build a context snippet around it. */
|
|
389
|
+
function generateSnippet(
|
|
390
|
+
content: string,
|
|
391
|
+
matches?: readonly FuseResultMatch[],
|
|
392
|
+
): string {
|
|
393
|
+
const contentMatch = matches?.find((m) => m.key === 'content');
|
|
394
|
+
if (!contentMatch || contentMatch.indices.length === 0) {
|
|
395
|
+
const s = content.slice(0, 120).replace(/\n/g, ' ').trim();
|
|
396
|
+
return content.length > 120 ? s + '...' : s;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
let bestStart = 0, bestEnd = 0, bestLen = 0;
|
|
400
|
+
for (const [ms, me] of contentMatch.indices) {
|
|
401
|
+
const len = me - ms;
|
|
402
|
+
if (len > bestLen) {
|
|
403
|
+
bestStart = ms;
|
|
404
|
+
bestEnd = me;
|
|
405
|
+
bestLen = len;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const snippetStart = Math.max(0, bestStart - 60);
|
|
410
|
+
const snippetEnd = Math.min(content.length, bestEnd + 61);
|
|
411
|
+
|
|
412
|
+
let start = snippetStart;
|
|
413
|
+
if (start > 0) {
|
|
414
|
+
const spaceIdx = content.indexOf(' ', start);
|
|
415
|
+
if (spaceIdx !== -1 && spaceIdx < bestStart) start = spaceIdx + 1;
|
|
416
|
+
}
|
|
417
|
+
let end = snippetEnd;
|
|
418
|
+
if (end < content.length) {
|
|
419
|
+
const spaceIdx = content.lastIndexOf(' ', end);
|
|
420
|
+
if (spaceIdx > bestEnd) end = spaceIdx;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
let snippet = content.slice(start, end).replace(/\n/g, ' ').trim();
|
|
424
|
+
if (start > 0) snippet = '...' + snippet;
|
|
425
|
+
if (end < content.length) snippet = snippet + '...';
|
|
426
|
+
return snippet;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ─── Public API: CSV (delegated to @mindos/core) ────────────────────────────
|
|
430
|
+
|
|
431
|
+
export function appendCsvRow(filePath: string, row: string[]): { newRowCount: number } {
|
|
432
|
+
const result = coreAppendCsvRow(getMindRoot(), filePath, row);
|
|
433
|
+
invalidateCache();
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ─── Public API: Move file (delegated to @mindos/core) ──────────────────────
|
|
438
|
+
|
|
439
|
+
export function moveFile(fromPath: string, toPath: string): { newPath: string; affectedFiles: string[] } {
|
|
440
|
+
const result = coreMoveFile(getMindRoot(), fromPath, toPath, coreFindBacklinks);
|
|
441
|
+
invalidateCache();
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// ─── Public API: Git operations (delegated to @mindos/core) ─────────────────
|
|
446
|
+
|
|
447
|
+
export function isGitRepo(): boolean {
|
|
448
|
+
return coreIsGitRepo(getMindRoot());
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export function gitLog(filePath: string, limit = 10): Array<{ hash: string; date: string; message: string; author: string }> {
|
|
452
|
+
return coreGitLog(getMindRoot(), filePath, limit);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export function gitShowFile(filePath: string, commit: string): string {
|
|
456
|
+
return coreGitShowFile(getMindRoot(), filePath, commit);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ─── Public API: Backlinks (delegated to @mindos/core) ──────────────────────
|
|
460
|
+
|
|
461
|
+
import type { BacklinkEntry } from './core/types';
|
|
462
|
+
export type { BacklinkEntry } from './core/types';
|
|
463
|
+
|
|
464
|
+
export function findBacklinks(targetPath: string): BacklinkEntry[] {
|
|
465
|
+
return coreFindBacklinks(getMindRoot(), targetPath);
|
|
466
|
+
}
|
|
467
|
+
|