doccupine 0.0.29 → 0.0.31
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/LICENSE +8 -0
- package/README.md +3 -1
- package/dist/index.js +113 -9
- package/dist/templates/app/api/mcp/route.d.ts +1 -0
- package/dist/templates/app/api/mcp/route.js +179 -0
- package/dist/templates/app/api/rag/route.d.ts +1 -0
- package/dist/templates/app/api/rag/route.js +125 -0
- package/dist/templates/app/api/theme/routes.d.ts +1 -1
- package/dist/templates/app/api/theme/routes.js +2 -1
- package/dist/templates/app/layout.js +33 -22
- package/dist/templates/app/not-found.d.ts +1 -1
- package/dist/templates/app/not-found.js +7 -7
- package/dist/templates/components/Chat.d.ts +1 -0
- package/dist/templates/components/Chat.js +932 -0
- package/dist/templates/components/Docs.d.ts +1 -1
- package/dist/templates/components/Docs.js +5 -3
- package/dist/templates/components/MDXComponents.d.ts +1 -1
- package/dist/templates/components/MDXComponents.js +4 -4
- package/dist/templates/components/SideBar.d.ts +1 -1
- package/dist/templates/components/SideBar.js +1 -1
- package/dist/templates/components/layout/ActionBar.d.ts +1 -1
- package/dist/templates/components/layout/ActionBar.js +120 -8
- package/dist/templates/components/layout/Callout.d.ts +1 -1
- package/dist/templates/components/layout/Callout.js +1 -1
- package/dist/templates/components/layout/CherryThemeProvider.d.ts +1 -1
- package/dist/templates/components/layout/CherryThemeProvider.js +2 -1
- package/dist/templates/components/layout/ClientThemeProvider.d.ts +1 -1
- package/dist/templates/components/layout/ClientThemeProvider.js +2 -1
- package/dist/templates/components/layout/Code.d.ts +1 -1
- package/dist/templates/components/layout/Code.js +19 -9
- package/dist/templates/components/layout/Columns.d.ts +1 -1
- package/dist/templates/components/layout/Columns.js +2 -1
- package/dist/templates/components/layout/DemoTheme.d.ts +1 -1
- package/dist/templates/components/layout/DemoTheme.js +0 -1
- package/dist/templates/components/layout/DocsComponents.d.ts +1 -1
- package/dist/templates/components/layout/DocsComponents.js +24 -104
- package/dist/templates/components/layout/DocsNavigation.d.ts +1 -1
- package/dist/templates/components/layout/DocsNavigation.js +16 -28
- package/dist/templates/components/layout/Field.d.ts +1 -1
- package/dist/templates/components/layout/Field.js +2 -1
- package/dist/templates/components/layout/Footer.d.ts +1 -1
- package/dist/templates/components/layout/Footer.js +71 -2
- package/dist/templates/components/layout/GlobalStyles.d.ts +1 -1
- package/dist/templates/components/layout/GlobalStyles.js +2 -1
- package/dist/templates/components/layout/Pictograms.d.ts +1 -1
- package/dist/templates/components/layout/Pictograms.js +26 -1
- package/dist/templates/components/layout/SharedStyles.d.ts +1 -1
- package/dist/templates/components/layout/SharedStyles.js +103 -0
- package/dist/templates/components/layout/StaticLinks.d.ts +1 -0
- package/dist/templates/components/layout/StaticLinks.js +141 -0
- package/dist/templates/components/layout/Stepts.d.ts +1 -1
- package/dist/templates/components/layout/Stepts.js +9 -3
- package/dist/templates/components/layout/Tabs.d.ts +1 -1
- package/dist/templates/components/layout/Tabs.js +8 -25
- package/dist/templates/components/layout/ThemeToggle.d.ts +1 -1
- package/dist/templates/components/layout/ThemeToggle.js +4 -6
- package/dist/templates/components/layout/Typography.d.ts +1 -1
- package/dist/templates/components/layout/Typography.js +15 -15
- package/dist/templates/env.example.d.ts +1 -0
- package/dist/templates/env.example.js +24 -0
- package/dist/templates/layout.js +10 -10
- package/dist/templates/mdx/ai-assistant.mdx.d.ts +1 -0
- package/dist/templates/mdx/ai-assistant.mdx.js +106 -0
- package/dist/templates/mdx/deployment.mdx.d.ts +1 -1
- package/dist/templates/mdx/deployment.mdx.js +1 -1
- package/dist/templates/mdx/fonts.mdx.d.ts +1 -1
- package/dist/templates/mdx/fonts.mdx.js +1 -1
- package/dist/templates/mdx/headers-and-text.mdx.d.ts +1 -1
- package/dist/templates/mdx/headers-and-text.mdx.js +1 -1
- package/dist/templates/mdx/index.mdx.d.ts +1 -1
- package/dist/templates/mdx/index.mdx.js +1 -1
- package/dist/templates/mdx/links.mdx.d.ts +1 -0
- package/dist/templates/mdx/links.mdx.js +45 -0
- package/dist/templates/mdx/model-context-protocol.mdx.d.ts +1 -0
- package/dist/templates/mdx/model-context-protocol.mdx.js +331 -0
- package/dist/templates/mdx/navigation.mdx.d.ts +1 -1
- package/dist/templates/mdx/navigation.mdx.js +3 -0
- package/dist/templates/mdx/theme.mdx.d.ts +1 -1
- package/dist/templates/mdx/theme.mdx.js +1 -1
- package/dist/templates/package.js +16 -7
- package/dist/templates/services/llm/config.d.ts +1 -0
- package/dist/templates/services/llm/config.js +72 -0
- package/dist/templates/services/llm/factory.d.ts +1 -0
- package/dist/templates/services/llm/factory.js +69 -0
- package/dist/templates/services/llm/index.d.ts +1 -0
- package/dist/templates/services/llm/index.js +4 -0
- package/dist/templates/services/llm/types.d.ts +1 -0
- package/dist/templates/services/llm/types.js +26 -0
- package/dist/templates/services/mcp/index.d.ts +1 -0
- package/dist/templates/services/mcp/index.js +4 -0
- package/dist/templates/services/mcp/server.d.ts +1 -0
- package/dist/templates/services/mcp/server.js +246 -0
- package/dist/templates/services/mcp/tools.d.ts +1 -0
- package/dist/templates/services/mcp/tools.js +244 -0
- package/dist/templates/services/mcp/types.d.ts +1 -0
- package/dist/templates/services/mcp/types.js +39 -0
- package/dist/templates/utils/orderNavItems.d.ts +1 -1
- package/dist/templates/utils/orderNavItems.js +12 -9
- package/package.json +5 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright © 2026, Doccupine.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
No licensee or downstream recipient may use the Software (including any modified or derivative versions) to directly compete with the original Licensor by offering it to third parties as a hosted, managed, or Software-as-a-Service (SaaS) product or cloud service where the primary value of the service is the functionality of the Software itself.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Doccupine is built on open standards, enabling customization and extensibility f
|
|
|
12
12
|
- 📦 Built-in file structure
|
|
13
13
|
- ⚡ Live Preview & Auto-Update
|
|
14
14
|
- 🚀 Easy Deployment
|
|
15
|
+
- 🤖 AI-powered
|
|
16
|
+
- 🔌 Model Context Protocol (MCP)
|
|
15
17
|
|
|
16
18
|
## Getting Started
|
|
17
19
|
|
|
@@ -32,6 +34,6 @@ This will start the development server on port 3000. Open your browser and navig
|
|
|
32
34
|
|
|
33
35
|
## Start documenting
|
|
34
36
|
|
|
35
|
-
Start documenting your project by editing the
|
|
37
|
+
Start documenting your project by editing the `index.mdx` file in the choosen MDX directory, this is the content for the home page of your documentation website.
|
|
36
38
|
|
|
37
39
|
In your MDX directory, you can structure your content using folders and files. Doccupine will automatically generate a navigation menu based on the configured categories and order.
|
package/dist/index.js
CHANGED
|
@@ -3,18 +3,23 @@ import { program } from "commander";
|
|
|
3
3
|
import chokidar from "chokidar";
|
|
4
4
|
import fs from "fs-extra";
|
|
5
5
|
import path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
6
7
|
import matter from "gray-matter";
|
|
7
8
|
import chalk from "chalk";
|
|
8
9
|
import prompts from "prompts";
|
|
10
|
+
import { envExampleTemplate } from "./templates/env.example.js";
|
|
9
11
|
import { gitignoreTemplate } from "./templates/gitignore.js";
|
|
10
12
|
import { nextConfigTemplate } from "./templates/next.config.js";
|
|
11
13
|
import { packageJsonTemplate } from "./templates/package.js";
|
|
12
14
|
import { proxyTemplate } from "./templates/proxy.js";
|
|
13
15
|
import { tsconfigTemplate } from "./templates/tsconfig.js";
|
|
16
|
+
import { mcpRoutesTemplate } from "./templates/app/api/mcp/route.js";
|
|
17
|
+
import { ragRoutesTemplate } from "./templates/app/api/rag/route.js";
|
|
14
18
|
import { routesTemplate } from "./templates/app/api/theme/routes.js";
|
|
15
19
|
import { layoutTemplate } from "./templates/app/layout.js";
|
|
16
20
|
import { notFoundTemplate } from "./templates/app/not-found.js";
|
|
17
21
|
import { themeTemplate } from "./templates/app/theme.js";
|
|
22
|
+
import { chatTemplate } from "./templates/components/Chat.js";
|
|
18
23
|
import { clickOutsideTemplate } from "./templates/components/ClickOutside.js";
|
|
19
24
|
import { docsTemplate } from "./templates/components/Docs.js";
|
|
20
25
|
import { docsSideBarTemplate } from "./templates/components/DocsSideBar.js";
|
|
@@ -39,14 +44,24 @@ import { headerTemplate } from "./templates/components/layout/Header.js";
|
|
|
39
44
|
import { iconTemplate } from "./templates/components/layout/Icon.js";
|
|
40
45
|
import { pictogramsTemplate } from "./templates/components/layout/Pictograms.js";
|
|
41
46
|
import { sharedStyledTemplate } from "./templates/components/layout/SharedStyles.js";
|
|
47
|
+
import { staticLinksTemplate } from "./templates/components/layout/StaticLinks.js";
|
|
42
48
|
import { stepsTemplate } from "./templates/components/layout/Stepts.js";
|
|
43
49
|
import { tabsTemplate } from "./templates/components/layout/Tabs.js";
|
|
44
50
|
import { themeToggleTemplate } from "./templates/components/layout/ThemeToggle.js";
|
|
45
51
|
import { typographyTemplate } from "./templates/components/layout/Typography.js";
|
|
46
52
|
import { updateTemplate } from "./templates/components/layout/Update.js";
|
|
53
|
+
import { mcpIndexTemplate } from "./templates/services/mcp/index.js";
|
|
54
|
+
import { mcpServerTemplate } from "./templates/services/mcp/server.js";
|
|
55
|
+
import { mcpToolsTemplate } from "./templates/services/mcp/tools.js";
|
|
56
|
+
import { mcpTypesTemplate } from "./templates/services/mcp/types.js";
|
|
57
|
+
import { llmConfigTemplate } from "./templates/services/llm/config.js";
|
|
58
|
+
import { llmFactoryTemplate } from "./templates/services/llm/factory.js";
|
|
59
|
+
import { llmIndexTemplate } from "./templates/services/llm/index.js";
|
|
60
|
+
import { llmTypesTemplate } from "./templates/services/llm/types.js";
|
|
47
61
|
import { styledDTemplate } from "./templates/types/styled.js";
|
|
48
62
|
import { orderNavItemsTemplate } from "./templates/utils/orderNavItems.js";
|
|
49
63
|
import { accordionMdxTemplate } from "./templates/mdx/accordion.mdx.js";
|
|
64
|
+
import { aiAssistantMdxTemplate } from "./templates/mdx/ai-assistant.mdx.js";
|
|
50
65
|
import { buttonsMdxTemplate } from "./templates/mdx/buttons.mdx.js";
|
|
51
66
|
import { calloutsMdxTemplate } from "./templates/mdx/callouts.mdx.js";
|
|
52
67
|
import { cardsMdxTemplate } from "./templates/mdx/cards.mdx.js";
|
|
@@ -61,12 +76,18 @@ import { headersAndTextMdxTemplate } from "./templates/mdx/headers-and-text.mdx.
|
|
|
61
76
|
import { iconsMdxTemplate } from "./templates/mdx/icons.mdx.js";
|
|
62
77
|
import { imageAndEmbedsMdxTemplate } from "./templates/mdx/image-and-embeds.mdx.js";
|
|
63
78
|
import { indexMdxTemplate } from "./templates/mdx/index.mdx.js";
|
|
79
|
+
import { linksMdxTemplate } from "./templates/mdx/links.mdx.js";
|
|
80
|
+
import { mcpMdxTemplate } from "./templates/mdx/model-context-protocol.mdx.js";
|
|
64
81
|
import { listAndTablesMdxTemplate } from "./templates/mdx/list-and-tables.mdx.js";
|
|
65
82
|
import { navigationMdxTemplate } from "./templates/mdx/navigation.mdx.js";
|
|
66
83
|
import { stepsMdxTemplate } from "./templates/mdx/steps.mdx.js";
|
|
67
84
|
import { tabsMdxTemplate } from "./templates/mdx/tabs.mdx.js";
|
|
68
85
|
import { themeMdxTemplate } from "./templates/mdx/theme.mdx.js";
|
|
69
86
|
import { updateMdxTemplate } from "./templates/mdx/update.mdx.js";
|
|
87
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
88
|
+
const __dirname = path.dirname(__filename);
|
|
89
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
|
|
90
|
+
const version = packageJson.version;
|
|
70
91
|
class ConfigManager {
|
|
71
92
|
configPath;
|
|
72
93
|
constructor(configPath = "doccupine.json") {
|
|
@@ -141,7 +162,12 @@ class MDXToNextJSGenerator {
|
|
|
141
162
|
watcher = null;
|
|
142
163
|
configWatcher = null;
|
|
143
164
|
fontWatcher = null;
|
|
144
|
-
configFiles = [
|
|
165
|
+
configFiles = [
|
|
166
|
+
"theme.json",
|
|
167
|
+
"navigation.json",
|
|
168
|
+
"config.json",
|
|
169
|
+
"links.json",
|
|
170
|
+
];
|
|
145
171
|
fontConfigFile = "fonts.json";
|
|
146
172
|
constructor(watchDir, outputDir) {
|
|
147
173
|
this.watchDir = path.resolve(watchDir);
|
|
@@ -164,8 +190,10 @@ class MDXToNextJSGenerator {
|
|
|
164
190
|
}
|
|
165
191
|
async createNextJSStructure() {
|
|
166
192
|
const structure = {
|
|
193
|
+
".env.example": this.generateEnvExample(),
|
|
167
194
|
".gitignore": this.generateGitIgnore(),
|
|
168
195
|
"config.json": this.generateConfig(),
|
|
196
|
+
"links.json": this.generateLinksConfig(),
|
|
169
197
|
"navigation.json": this.generateNavigationConfig(),
|
|
170
198
|
"next.config.ts": this.generateNextConfig(),
|
|
171
199
|
"package.json": this.generatePackageJson(),
|
|
@@ -175,9 +203,20 @@ class MDXToNextJSGenerator {
|
|
|
175
203
|
"app/layout.tsx": await this.generateRootLayout(),
|
|
176
204
|
"app/not-found.tsx": this.generateNotFoundPage(),
|
|
177
205
|
"app/theme.ts": this.generateTheme(),
|
|
206
|
+
"app/api/mcp/route.ts": this.generateMCPRoutes(),
|
|
207
|
+
"app/api/rag/route.ts": this.generateRagRoutes(),
|
|
178
208
|
"app/api/theme/route.ts": this.generateRoutes(),
|
|
209
|
+
"services/mcp/index.ts": this.generateMCPIndex(),
|
|
210
|
+
"services/mcp/server.ts": this.generateMCPServer(),
|
|
211
|
+
"services/mcp/tools.ts": this.generateMCPTools(),
|
|
212
|
+
"services/mcp/types.ts": this.generateMCPTypes(),
|
|
213
|
+
"services/llm/config.ts": this.generateLLMConfig(),
|
|
214
|
+
"services/llm/factory.ts": this.generateLLMFactory(),
|
|
215
|
+
"services/llm/index.ts": this.generateLLMIndex(),
|
|
216
|
+
"services/llm/types.ts": this.generateLLMTypes(),
|
|
179
217
|
"types/styled.d.ts": this.generateStyledDTypes(),
|
|
180
218
|
"utils/orderNavItems.ts": this.generateOrderNavItems(),
|
|
219
|
+
"components/Chat.tsx": this.generateChat(),
|
|
181
220
|
"components/ClickOutside.ts": this.generateClickOutside(),
|
|
182
221
|
"components/Docs.tsx": this.generateDocs(),
|
|
183
222
|
"components/DocsSideBar.tsx": this.generateDocsSideBar(),
|
|
@@ -202,6 +241,7 @@ class MDXToNextJSGenerator {
|
|
|
202
241
|
"components/layout/Icon.tsx": this.generateIcon(),
|
|
203
242
|
"components/layout/Pictograms.tsx": this.generatePictograms(),
|
|
204
243
|
"components/layout/SharedStyled.ts": this.generateSharedStyled(),
|
|
244
|
+
"components/layout/StaticLinks.tsx": this.generateStaticLinks(),
|
|
205
245
|
"components/layout/Steps.tsx": this.generateSteps(),
|
|
206
246
|
"components/layout/Tabs.tsx": this.generateTabs(),
|
|
207
247
|
"components/layout/ThemeToggle.tsx": this.generateThemeToggle(),
|
|
@@ -217,6 +257,7 @@ class MDXToNextJSGenerator {
|
|
|
217
257
|
async createStartingDocs() {
|
|
218
258
|
const structure = {
|
|
219
259
|
"accordion.mdx": this.generateAccordionMdx(),
|
|
260
|
+
"ai-assistant.mdx": this.generateAiAssistantMdx(),
|
|
220
261
|
"buttons.mdx": this.generateButtonsMdx(),
|
|
221
262
|
"callouts.mdx": this.generateCalloutsMdx(),
|
|
222
263
|
"cards.mdx": this.generateCardsMdx(),
|
|
@@ -231,6 +272,8 @@ class MDXToNextJSGenerator {
|
|
|
231
272
|
"icons.mdx": this.generateIconsMdx(),
|
|
232
273
|
"image-and-embeds.mdx": this.generateImagesAndEmbedsMdx(),
|
|
233
274
|
"index.mdx": this.generateIndexMdx(),
|
|
275
|
+
"links.mdx": this.generateLinksMdx(),
|
|
276
|
+
"model-context-protocol.mdx": this.generateMCPMdx(),
|
|
234
277
|
"lists-and-tables.mdx": this.generateListsAndTablesMdx(),
|
|
235
278
|
"navigation.mdx": this.generateNavigationMdx(),
|
|
236
279
|
"steps.mdx": this.generateStepsMdx(),
|
|
@@ -507,12 +550,18 @@ class MDXToNextJSGenerator {
|
|
|
507
550
|
.replace(/[^a-zA-Z0-9\/\-_]/g, "-")
|
|
508
551
|
.toLowerCase();
|
|
509
552
|
}
|
|
553
|
+
generateEnvExample() {
|
|
554
|
+
return envExampleTemplate;
|
|
555
|
+
}
|
|
510
556
|
generateGitIgnore() {
|
|
511
557
|
return gitignoreTemplate;
|
|
512
558
|
}
|
|
513
559
|
generateConfig() {
|
|
514
560
|
return `{}`;
|
|
515
561
|
}
|
|
562
|
+
generateLinksConfig() {
|
|
563
|
+
return `[]`;
|
|
564
|
+
}
|
|
516
565
|
generateNavigationConfig() {
|
|
517
566
|
return `[]`;
|
|
518
567
|
}
|
|
@@ -590,11 +639,11 @@ const content = \`${mdxFile.content.replace(/`/g, "\\`")}\`;
|
|
|
590
639
|
export const metadata: Metadata = {
|
|
591
640
|
title: \`${mdxFile.frontmatter.title || "Generated with Doccupine"} \${config.name ? "- " + config.name : "- Doccupine"}\`,
|
|
592
641
|
description: \`${mdxFile.frontmatter.description ? mdxFile.frontmatter.description : '${config.description ? config.description : "Generated with Doccupine"}'}\`,
|
|
593
|
-
icons: \`${mdxFile.frontmatter.icon ? mdxFile.frontmatter.icon :
|
|
642
|
+
icons: \`${mdxFile.frontmatter.icon ? mdxFile.frontmatter.icon : '\${config.icon || "https://doccupine.com/favicon.ico"}'}\`,
|
|
594
643
|
openGraph: {
|
|
595
644
|
title: \`${mdxFile.frontmatter.title || "Generated with Doccupine"} \${config.name ? "- " + config.name : "- Doccupine"}\`,
|
|
596
645
|
description: \`${mdxFile.frontmatter.description ? mdxFile.frontmatter.description : '${config.description ? config.description : "Generated with Doccupine"}'}\`,
|
|
597
|
-
images: \`${mdxFile.frontmatter.image ? mdxFile.frontmatter.image :
|
|
646
|
+
images: \`${mdxFile.frontmatter.image ? mdxFile.frontmatter.image : '\${config.preview || "https://doccupine.com/preview.png"}'}\`,
|
|
598
647
|
},
|
|
599
648
|
};
|
|
600
649
|
|
|
@@ -639,7 +688,7 @@ interface Config {
|
|
|
639
688
|
|
|
640
689
|
const config = configData as Config;
|
|
641
690
|
|
|
642
|
-
${indexMDX ? `const
|
|
691
|
+
${indexMDX ? `const content = \`${indexMDX.content.replace(/`/g, "\\`")}\`;` : `const content = null;`}
|
|
643
692
|
|
|
644
693
|
${indexMDX
|
|
645
694
|
? `export const metadata: Metadata = {
|
|
@@ -649,7 +698,7 @@ ${indexMDX
|
|
|
649
698
|
openGraph: {
|
|
650
699
|
title: \`\${config.name ? config.name + " -" : "Doccupine -"} ${indexMDX.title}\`,
|
|
651
700
|
description: \`${indexMDX.description ? indexMDX.description : '${config.description ? config.description : "Generated with Doccupine"}'}\`,
|
|
652
|
-
images: \`${indexMDX.image ? indexMDX.image :
|
|
701
|
+
images: \`${indexMDX.image ? indexMDX.image : '\${config.preview || "https://doccupine.com/preview.png"}'}\`,
|
|
653
702
|
},
|
|
654
703
|
};`
|
|
655
704
|
: `export const metadata: Metadata = {
|
|
@@ -664,7 +713,7 @@ ${indexMDX
|
|
|
664
713
|
};`}
|
|
665
714
|
|
|
666
715
|
export default function Home() {
|
|
667
|
-
return <Docs content={
|
|
716
|
+
return <Docs content={content} />;
|
|
668
717
|
}
|
|
669
718
|
`;
|
|
670
719
|
await fs.writeFile(path.join(this.outputDir, "app", "page.tsx"), indexContent, "utf8");
|
|
@@ -676,15 +725,48 @@ export default function Home() {
|
|
|
676
725
|
generateTheme() {
|
|
677
726
|
return themeTemplate;
|
|
678
727
|
}
|
|
728
|
+
generateMCPRoutes() {
|
|
729
|
+
return mcpRoutesTemplate;
|
|
730
|
+
}
|
|
731
|
+
generateRagRoutes() {
|
|
732
|
+
return ragRoutesTemplate;
|
|
733
|
+
}
|
|
679
734
|
generateRoutes() {
|
|
680
735
|
return routesTemplate;
|
|
681
736
|
}
|
|
737
|
+
generateMCPIndex() {
|
|
738
|
+
return mcpIndexTemplate;
|
|
739
|
+
}
|
|
740
|
+
generateMCPServer() {
|
|
741
|
+
return mcpServerTemplate;
|
|
742
|
+
}
|
|
743
|
+
generateMCPTools() {
|
|
744
|
+
return mcpToolsTemplate;
|
|
745
|
+
}
|
|
746
|
+
generateMCPTypes() {
|
|
747
|
+
return mcpTypesTemplate;
|
|
748
|
+
}
|
|
749
|
+
generateLLMConfig() {
|
|
750
|
+
return llmConfigTemplate;
|
|
751
|
+
}
|
|
752
|
+
generateLLMFactory() {
|
|
753
|
+
return llmFactoryTemplate;
|
|
754
|
+
}
|
|
755
|
+
generateLLMIndex() {
|
|
756
|
+
return llmIndexTemplate;
|
|
757
|
+
}
|
|
758
|
+
generateLLMTypes() {
|
|
759
|
+
return llmTypesTemplate;
|
|
760
|
+
}
|
|
682
761
|
generateStyledDTypes() {
|
|
683
762
|
return styledDTemplate;
|
|
684
763
|
}
|
|
685
764
|
generateOrderNavItems() {
|
|
686
765
|
return orderNavItemsTemplate;
|
|
687
766
|
}
|
|
767
|
+
generateChat() {
|
|
768
|
+
return chatTemplate;
|
|
769
|
+
}
|
|
688
770
|
generateClickOutside() {
|
|
689
771
|
return clickOutsideTemplate;
|
|
690
772
|
}
|
|
@@ -757,6 +839,9 @@ export default function Home() {
|
|
|
757
839
|
generateSharedStyled() {
|
|
758
840
|
return sharedStyledTemplate;
|
|
759
841
|
}
|
|
842
|
+
generateStaticLinks() {
|
|
843
|
+
return staticLinksTemplate;
|
|
844
|
+
}
|
|
760
845
|
generateSteps() {
|
|
761
846
|
return stepsTemplate;
|
|
762
847
|
}
|
|
@@ -775,6 +860,9 @@ export default function Home() {
|
|
|
775
860
|
generateAccordionMdx() {
|
|
776
861
|
return accordionMdxTemplate;
|
|
777
862
|
}
|
|
863
|
+
generateAiAssistantMdx() {
|
|
864
|
+
return aiAssistantMdxTemplate;
|
|
865
|
+
}
|
|
778
866
|
generateButtonsMdx() {
|
|
779
867
|
return buttonsMdxTemplate;
|
|
780
868
|
}
|
|
@@ -814,9 +902,15 @@ export default function Home() {
|
|
|
814
902
|
generateImagesAndEmbedsMdx() {
|
|
815
903
|
return imageAndEmbedsMdxTemplate;
|
|
816
904
|
}
|
|
905
|
+
generateMCPMdx() {
|
|
906
|
+
return mcpMdxTemplate;
|
|
907
|
+
}
|
|
817
908
|
generateIndexMdx() {
|
|
818
909
|
return indexMdxTemplate;
|
|
819
910
|
}
|
|
911
|
+
generateLinksMdx() {
|
|
912
|
+
return linksMdxTemplate;
|
|
913
|
+
}
|
|
820
914
|
generateListsAndTablesMdx() {
|
|
821
915
|
return listAndTablesMdxTemplate;
|
|
822
916
|
}
|
|
@@ -853,7 +947,7 @@ export default function Home() {
|
|
|
853
947
|
program
|
|
854
948
|
.name("doccupine")
|
|
855
949
|
.description("Watch MDX files and generate Next.js documentation pages automatically")
|
|
856
|
-
.version(
|
|
950
|
+
.version(version);
|
|
857
951
|
program
|
|
858
952
|
.command("watch", { isDefault: true })
|
|
859
953
|
.description("Watch a directory for MDX changes and generate Next.js app")
|
|
@@ -870,8 +964,18 @@ program
|
|
|
870
964
|
await generator.init();
|
|
871
965
|
let devServer = null;
|
|
872
966
|
console.log(chalk.blue("📦 Installing dependencies..."));
|
|
873
|
-
const { spawn } = await import("child_process");
|
|
874
|
-
|
|
967
|
+
const { spawn, execSync } = await import("child_process");
|
|
968
|
+
// Check if pnpm is available, fallback to npm
|
|
969
|
+
let packageManager = "npm";
|
|
970
|
+
try {
|
|
971
|
+
execSync("pnpm --version", { stdio: "ignore" });
|
|
972
|
+
packageManager = "pnpm";
|
|
973
|
+
}
|
|
974
|
+
catch {
|
|
975
|
+
// pnpm not available, use npm
|
|
976
|
+
}
|
|
977
|
+
console.log(chalk.blue(`📦 Using ${packageManager}...`));
|
|
978
|
+
const install = spawn(packageManager, ["install"], {
|
|
875
979
|
cwd: config.outputDir,
|
|
876
980
|
stdio: "pipe",
|
|
877
981
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const mcpRoutesTemplate = "import { NextResponse } from \"next/server\";\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport { createMCPServer } from \"@/services/mcp/server\";\nimport {\n searchDocs,\n ensureDocsIndex,\n getIndexStatus,\n DOCS_TOOLS,\n listDocs,\n getDoc,\n} from \"@/services/mcp\";\nimport type { MCPToolName } from \"@/services/mcp\";\n\n// Create a stateless transport for serverless environment\nfunction createTransport() {\n return new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: undefined, // Stateless mode for serverless\n enableJsonResponse: true,\n });\n}\n\n// Handle MCP protocol requests\nasync function handleMCPRequest(req: Request) {\n const transport = createTransport();\n const server = createMCPServer();\n\n try {\n await server.connect(transport);\n const response = await transport.handleRequest(req);\n\n // Clean up after response is done\n response\n .clone()\n .body?.pipeTo(\n new WritableStream({\n close() {\n transport.close();\n server.close();\n },\n }),\n )\n .catch(() => {});\n\n return response;\n } catch (error) {\n console.error(\"MCP request error:\", error);\n return new Response(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32603,\n message: \"Internal server error\",\n },\n id: null,\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n}\n\n// Handle REST API requests (original JSON format)\ninterface ToolCallRequest {\n tool: MCPToolName;\n params: Record<string, unknown>;\n}\n\nasync function handleRESTRequest(body: ToolCallRequest) {\n try {\n const { tool, params } = body;\n\n if (!tool) {\n return NextResponse.json(\n { error: \"Missing 'tool' parameter\" },\n { status: 400 },\n );\n }\n\n switch (tool) {\n case \"search_docs\": {\n const query = String(params?.query || \"\");\n const limit = typeof params?.limit === \"number\" ? params.limit : 6;\n await ensureDocsIndex();\n const results = await searchDocs(query, limit);\n return NextResponse.json({\n content: results.map(({ chunk, score }) => ({\n path: chunk.path,\n uri: chunk.uri,\n score: score.toFixed(3),\n text: chunk.text,\n })),\n });\n }\n\n case \"get_doc\": {\n const path = String(params?.path || \"\");\n const doc = await getDoc({ path });\n if (!doc) {\n return NextResponse.json(\n { error: \"Document not found\" },\n { status: 404 },\n );\n }\n return NextResponse.json({ content: doc });\n }\n\n case \"list_docs\": {\n const directory =\n typeof params?.directory === \"string\" ? params.directory : undefined;\n const docs = await listDocs({ directory });\n return NextResponse.json({\n content: docs.map((d) => ({\n name: d.name,\n path: d.path,\n uri: d.uri,\n })),\n });\n }\n\n default:\n return NextResponse.json(\n { error: `Unknown tool: ${tool}` },\n { status: 400 },\n );\n }\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : \"Unknown error\";\n return NextResponse.json({ error: message }, { status: 500 });\n }\n}\n\nexport async function POST(req: Request) {\n // Clone the request to read body twice if needed\n const clonedReq = req.clone();\n\n try {\n const body = await clonedReq.json();\n\n // Check if this is an MCP protocol request (has jsonrpc field)\n // or a REST API request (has tool field)\n if (\"jsonrpc\" in body) {\n // MCP protocol request - use the original request\n return handleMCPRequest(req);\n } else if (\"tool\" in body) {\n // REST API request\n return handleRESTRequest(body as ToolCallRequest);\n } else {\n return NextResponse.json(\n {\n error:\n \"Invalid request format. Expected 'jsonrpc' (MCP) or 'tool' (REST) field.\",\n },\n { status: 400 },\n );\n }\n } catch {\n return NextResponse.json({ error: \"Invalid JSON body\" }, { status: 400 });\n }\n}\n\nexport async function GET() {\n // GET always returns REST API format (tools list and index status)\n const status = getIndexStatus();\n return NextResponse.json({\n tools: DOCS_TOOLS,\n index: {\n ready: status.ready,\n chunkCount: status.chunkCount,\n },\n });\n}\n\nexport async function DELETE(req: Request) {\n // DELETE is only used by MCP protocol\n return handleMCPRequest(req);\n}\n";
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
export const mcpRoutesTemplate = `import { NextResponse } from "next/server";
|
|
2
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
3
|
+
import { createMCPServer } from "@/services/mcp/server";
|
|
4
|
+
import {
|
|
5
|
+
searchDocs,
|
|
6
|
+
ensureDocsIndex,
|
|
7
|
+
getIndexStatus,
|
|
8
|
+
DOCS_TOOLS,
|
|
9
|
+
listDocs,
|
|
10
|
+
getDoc,
|
|
11
|
+
} from "@/services/mcp";
|
|
12
|
+
import type { MCPToolName } from "@/services/mcp";
|
|
13
|
+
|
|
14
|
+
// Create a stateless transport for serverless environment
|
|
15
|
+
function createTransport() {
|
|
16
|
+
return new WebStandardStreamableHTTPServerTransport({
|
|
17
|
+
sessionIdGenerator: undefined, // Stateless mode for serverless
|
|
18
|
+
enableJsonResponse: true,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Handle MCP protocol requests
|
|
23
|
+
async function handleMCPRequest(req: Request) {
|
|
24
|
+
const transport = createTransport();
|
|
25
|
+
const server = createMCPServer();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await server.connect(transport);
|
|
29
|
+
const response = await transport.handleRequest(req);
|
|
30
|
+
|
|
31
|
+
// Clean up after response is done
|
|
32
|
+
response
|
|
33
|
+
.clone()
|
|
34
|
+
.body?.pipeTo(
|
|
35
|
+
new WritableStream({
|
|
36
|
+
close() {
|
|
37
|
+
transport.close();
|
|
38
|
+
server.close();
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
)
|
|
42
|
+
.catch(() => {});
|
|
43
|
+
|
|
44
|
+
return response;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error("MCP request error:", error);
|
|
47
|
+
return new Response(
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
jsonrpc: "2.0",
|
|
50
|
+
error: {
|
|
51
|
+
code: -32603,
|
|
52
|
+
message: "Internal server error",
|
|
53
|
+
},
|
|
54
|
+
id: null,
|
|
55
|
+
}),
|
|
56
|
+
{
|
|
57
|
+
status: 500,
|
|
58
|
+
headers: { "Content-Type": "application/json" },
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Handle REST API requests (original JSON format)
|
|
65
|
+
interface ToolCallRequest {
|
|
66
|
+
tool: MCPToolName;
|
|
67
|
+
params: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function handleRESTRequest(body: ToolCallRequest) {
|
|
71
|
+
try {
|
|
72
|
+
const { tool, params } = body;
|
|
73
|
+
|
|
74
|
+
if (!tool) {
|
|
75
|
+
return NextResponse.json(
|
|
76
|
+
{ error: "Missing 'tool' parameter" },
|
|
77
|
+
{ status: 400 },
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
switch (tool) {
|
|
82
|
+
case "search_docs": {
|
|
83
|
+
const query = String(params?.query || "");
|
|
84
|
+
const limit = typeof params?.limit === "number" ? params.limit : 6;
|
|
85
|
+
await ensureDocsIndex();
|
|
86
|
+
const results = await searchDocs(query, limit);
|
|
87
|
+
return NextResponse.json({
|
|
88
|
+
content: results.map(({ chunk, score }) => ({
|
|
89
|
+
path: chunk.path,
|
|
90
|
+
uri: chunk.uri,
|
|
91
|
+
score: score.toFixed(3),
|
|
92
|
+
text: chunk.text,
|
|
93
|
+
})),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case "get_doc": {
|
|
98
|
+
const path = String(params?.path || "");
|
|
99
|
+
const doc = await getDoc({ path });
|
|
100
|
+
if (!doc) {
|
|
101
|
+
return NextResponse.json(
|
|
102
|
+
{ error: "Document not found" },
|
|
103
|
+
{ status: 404 },
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return NextResponse.json({ content: doc });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case "list_docs": {
|
|
110
|
+
const directory =
|
|
111
|
+
typeof params?.directory === "string" ? params.directory : undefined;
|
|
112
|
+
const docs = await listDocs({ directory });
|
|
113
|
+
return NextResponse.json({
|
|
114
|
+
content: docs.map((d) => ({
|
|
115
|
+
name: d.name,
|
|
116
|
+
path: d.path,
|
|
117
|
+
uri: d.uri,
|
|
118
|
+
})),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
default:
|
|
123
|
+
return NextResponse.json(
|
|
124
|
+
{ error: \`Unknown tool: \${tool}\` },
|
|
125
|
+
{ status: 400 },
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
} catch (e: unknown) {
|
|
129
|
+
const message = e instanceof Error ? e.message : "Unknown error";
|
|
130
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function POST(req: Request) {
|
|
135
|
+
// Clone the request to read body twice if needed
|
|
136
|
+
const clonedReq = req.clone();
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const body = await clonedReq.json();
|
|
140
|
+
|
|
141
|
+
// Check if this is an MCP protocol request (has jsonrpc field)
|
|
142
|
+
// or a REST API request (has tool field)
|
|
143
|
+
if ("jsonrpc" in body) {
|
|
144
|
+
// MCP protocol request - use the original request
|
|
145
|
+
return handleMCPRequest(req);
|
|
146
|
+
} else if ("tool" in body) {
|
|
147
|
+
// REST API request
|
|
148
|
+
return handleRESTRequest(body as ToolCallRequest);
|
|
149
|
+
} else {
|
|
150
|
+
return NextResponse.json(
|
|
151
|
+
{
|
|
152
|
+
error:
|
|
153
|
+
"Invalid request format. Expected 'jsonrpc' (MCP) or 'tool' (REST) field.",
|
|
154
|
+
},
|
|
155
|
+
{ status: 400 },
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function GET() {
|
|
164
|
+
// GET always returns REST API format (tools list and index status)
|
|
165
|
+
const status = getIndexStatus();
|
|
166
|
+
return NextResponse.json({
|
|
167
|
+
tools: DOCS_TOOLS,
|
|
168
|
+
index: {
|
|
169
|
+
ready: status.ready,
|
|
170
|
+
chunkCount: status.chunkCount,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function DELETE(req: Request) {
|
|
176
|
+
// DELETE is only used by MCP protocol
|
|
177
|
+
return handleMCPRequest(req);
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ragRoutesTemplate = "import { NextResponse } from \"next/server\";\nimport path from \"node:path\";\nimport { getLLMConfig, createChatModel } from \"@/services/llm\";\nimport {\n searchDocs,\n ensureDocsIndex,\n getIndexStatus,\n} from \"@/services/mcp/server\";\n\nconst PROJECT_ROOT = process.cwd();\n\nexport async function POST(req: Request) {\n try {\n const { question, refresh } = await req.json();\n\n let config;\n try {\n config = getLLMConfig();\n } catch (error: unknown) {\n const message =\n error instanceof Error ? error.message : \"LLM configuration error\";\n return NextResponse.json({ error: message }, { status: 500 });\n }\n\n // Use MCP service to ensure docs are indexed\n await ensureDocsIndex(Boolean(refresh));\n\n // Use MCP search_docs tool to find relevant documentation\n const searchResults = await searchDocs(String(question || \"\"), 6);\n\n // Build context from search results\n const context = searchResults\n .map(\n ({ chunk, score }) =>\n `File: ${path.relative(PROJECT_ROOT, chunk.path)}\\nScore: ${score.toFixed(3)}\\n----\\n${chunk.text}`,\n )\n .join(\"\\n\\n================\\n\\n\");\n\n // Create chat model and stream response\n const llm = createChatModel(config);\n const prompt = [\n {\n role: \"system\" as const,\n content:\n \"You are a helpful documentation assistant. Answer strictly from the provided context. If the answer is not in the context, say you don't know and suggest where to look. Make sure the mdx can be nested properly if you show code components within nested ```\",\n },\n {\n role: \"user\" as const,\n content: `Question: ${question}\\n\\nContext:\\n${context}`,\n },\n ];\n\n const stream = await llm.stream(prompt);\n\n // Build metadata from MCP search results\n const indexStatus = getIndexStatus();\n const metadata = {\n sources: searchResults.map(({ chunk, score }) => ({\n id: chunk.id,\n path: path.relative(PROJECT_ROOT, chunk.path),\n uri: chunk.uri,\n score,\n })),\n chunkCount: indexStatus.chunkCount,\n };\n\n const encoder = new TextEncoder();\n const readableStream = new ReadableStream({\n async start(controller) {\n try {\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: \"metadata\", data: metadata })}\\n\\n`,\n ),\n );\n\n for await (const chunk of stream) {\n const content = chunk?.content || \"\";\n if (content) {\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: \"content\", data: content })}\\n\\n`,\n ),\n );\n }\n }\n\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify({ type: \"done\" })}\\n\\n`),\n );\n controller.close();\n } catch (error: unknown) {\n const message =\n error instanceof Error ? error.message : \"Stream error\";\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({ type: \"error\", data: message })}\\n\\n`,\n ),\n );\n controller.close();\n }\n },\n });\n\n return new Response(readableStream, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : \"Unknown error\";\n return NextResponse.json({ error: message }, { status: 500 });\n }\n}\n\nexport async function GET() {\n const status = getIndexStatus();\n return NextResponse.json({\n ready: status.ready,\n chunks: status.chunkCount,\n });\n}\n";
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export const ragRoutesTemplate = `import { NextResponse } from "next/server";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getLLMConfig, createChatModel } from "@/services/llm";
|
|
4
|
+
import {
|
|
5
|
+
searchDocs,
|
|
6
|
+
ensureDocsIndex,
|
|
7
|
+
getIndexStatus,
|
|
8
|
+
} from "@/services/mcp/server";
|
|
9
|
+
|
|
10
|
+
const PROJECT_ROOT = process.cwd();
|
|
11
|
+
|
|
12
|
+
export async function POST(req: Request) {
|
|
13
|
+
try {
|
|
14
|
+
const { question, refresh } = await req.json();
|
|
15
|
+
|
|
16
|
+
let config;
|
|
17
|
+
try {
|
|
18
|
+
config = getLLMConfig();
|
|
19
|
+
} catch (error: unknown) {
|
|
20
|
+
const message =
|
|
21
|
+
error instanceof Error ? error.message : "LLM configuration error";
|
|
22
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Use MCP service to ensure docs are indexed
|
|
26
|
+
await ensureDocsIndex(Boolean(refresh));
|
|
27
|
+
|
|
28
|
+
// Use MCP search_docs tool to find relevant documentation
|
|
29
|
+
const searchResults = await searchDocs(String(question || ""), 6);
|
|
30
|
+
|
|
31
|
+
// Build context from search results
|
|
32
|
+
const context = searchResults
|
|
33
|
+
.map(
|
|
34
|
+
({ chunk, score }) =>
|
|
35
|
+
\`File: \${path.relative(PROJECT_ROOT, chunk.path)}\\nScore: \${score.toFixed(3)}\\n----\\n\${chunk.text}\`,
|
|
36
|
+
)
|
|
37
|
+
.join("\\n\\n================\\n\\n");
|
|
38
|
+
|
|
39
|
+
// Create chat model and stream response
|
|
40
|
+
const llm = createChatModel(config);
|
|
41
|
+
const prompt = [
|
|
42
|
+
{
|
|
43
|
+
role: "system" as const,
|
|
44
|
+
content:
|
|
45
|
+
"You are a helpful documentation assistant. Answer strictly from the provided context. If the answer is not in the context, say you don't know and suggest where to look. Make sure the mdx can be nested properly if you show code components within nested \`\`\`",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
role: "user" as const,
|
|
49
|
+
content: \`Question: \${question}\\n\\nContext:\\n\${context}\`,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const stream = await llm.stream(prompt);
|
|
54
|
+
|
|
55
|
+
// Build metadata from MCP search results
|
|
56
|
+
const indexStatus = getIndexStatus();
|
|
57
|
+
const metadata = {
|
|
58
|
+
sources: searchResults.map(({ chunk, score }) => ({
|
|
59
|
+
id: chunk.id,
|
|
60
|
+
path: path.relative(PROJECT_ROOT, chunk.path),
|
|
61
|
+
uri: chunk.uri,
|
|
62
|
+
score,
|
|
63
|
+
})),
|
|
64
|
+
chunkCount: indexStatus.chunkCount,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const encoder = new TextEncoder();
|
|
68
|
+
const readableStream = new ReadableStream({
|
|
69
|
+
async start(controller) {
|
|
70
|
+
try {
|
|
71
|
+
controller.enqueue(
|
|
72
|
+
encoder.encode(
|
|
73
|
+
\`data: \${JSON.stringify({ type: "metadata", data: metadata })}\\n\\n\`,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
for await (const chunk of stream) {
|
|
78
|
+
const content = chunk?.content || "";
|
|
79
|
+
if (content) {
|
|
80
|
+
controller.enqueue(
|
|
81
|
+
encoder.encode(
|
|
82
|
+
\`data: \${JSON.stringify({ type: "content", data: content })}\\n\\n\`,
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
controller.enqueue(
|
|
89
|
+
encoder.encode(\`data: \${JSON.stringify({ type: "done" })}\\n\\n\`),
|
|
90
|
+
);
|
|
91
|
+
controller.close();
|
|
92
|
+
} catch (error: unknown) {
|
|
93
|
+
const message =
|
|
94
|
+
error instanceof Error ? error.message : "Stream error";
|
|
95
|
+
controller.enqueue(
|
|
96
|
+
encoder.encode(
|
|
97
|
+
\`data: \${JSON.stringify({ type: "error", data: message })}\\n\\n\`,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
controller.close();
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return new Response(readableStream, {
|
|
106
|
+
headers: {
|
|
107
|
+
"Content-Type": "text/event-stream",
|
|
108
|
+
"Cache-Control": "no-cache",
|
|
109
|
+
Connection: "keep-alive",
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
} catch (e: unknown) {
|
|
113
|
+
const message = e instanceof Error ? e.message : "Unknown error";
|
|
114
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function GET() {
|
|
119
|
+
const status = getIndexStatus();
|
|
120
|
+
return NextResponse.json({
|
|
121
|
+
ready: status.ready,
|
|
122
|
+
chunks: status.chunkCount,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const routesTemplate = "import { NextResponse } from \"next/server\";\nimport { cookies } from \"next/headers\";\n\nexport async function POST(request: Request) {\n try {\n const { theme } = await request.json();\n if (theme !== \"light\" && theme !== \"dark\") {\n return NextResponse.json(\n { ok: false, error: \"Invalid theme\" },\n { status: 400 },\n );\n }\n\n const cookieStore = await cookies();\n cookieStore.set(\"theme\", theme, {\n path: \"/\",\n maxAge: 60 * 60 * 24 * 365,\n sameSite: \"lax\",\n });\n\n return NextResponse.json({ ok: true });\n } catch (e) {\n return NextResponse.json(\n { ok: false, error: \"Bad Request\" },\n { status: 400 },\n );\n }\n}";
|
|
1
|
+
export declare const routesTemplate = "import { NextResponse } from \"next/server\";\nimport { cookies } from \"next/headers\";\n\nexport async function POST(request: Request) {\n try {\n const { theme } = await request.json();\n if (theme !== \"light\" && theme !== \"dark\") {\n return NextResponse.json(\n { ok: false, error: \"Invalid theme\" },\n { status: 400 },\n );\n }\n\n const cookieStore = await cookies();\n cookieStore.set(\"theme\", theme, {\n path: \"/\",\n maxAge: 60 * 60 * 24 * 365,\n sameSite: \"lax\",\n });\n\n return NextResponse.json({ ok: true });\n } catch (e) {\n return NextResponse.json(\n { ok: false, error: \"Bad Request\" },\n { status: 400 },\n );\n }\n}\n";
|