doccupine 0.0.90 → 0.0.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,12 +42,27 @@ doccupine config --reset # Re-prompt for configuration
42
42
 
43
43
  ### Options
44
44
 
45
+ `watch` (default):
46
+
45
47
  | Flag | Description |
46
48
  | --------------- | -------------------------------------------------------------------- |
47
49
  | `--port <port>` | Port for the dev server (default: `3000`). Auto-increments if taken. |
48
50
  | `--verbose` | Show all Next.js output including compilation details |
49
51
  | `--reset` | Re-prompt for watch/output directories |
50
52
 
53
+ `build`:
54
+
55
+ | Flag | Description |
56
+ | --------- | -------------------------------------- |
57
+ | `--reset` | Re-prompt for watch/output directories |
58
+
59
+ `config`:
60
+
61
+ | Flag | Description |
62
+ | --------- | ---------------------------------------- |
63
+ | `--show` | Print the current configuration and exit |
64
+ | `--reset` | Re-prompt for watch/output directories |
65
+
51
66
  ## MDX Frontmatter
52
67
 
53
68
  Each MDX file supports these frontmatter fields:
@@ -154,6 +169,20 @@ Doccupine generates `robots.ts` automatically for every site. When you set a `ur
154
169
 
155
170
  You can override the URL at deploy time by setting the `NEXT_PUBLIC_SITE_URL` environment variable. When no URL is configured (neither in `config.json` nor via env), the sitemap is skipped and `robots.txt` is emitted without a sitemap reference.
156
171
 
172
+ ## llms.txt
173
+
174
+ Doccupine generates [llms.txt](https://llmstxt.org) artifacts so AI agents can discover and ingest your docs. On every regeneration, the following files are written to the generated app's `public/` directory:
175
+
176
+ | File | Contents |
177
+ | ------------------ | --------------------------------------------------------------------------------------- |
178
+ | `llms.txt` | Index of every page (title, description, URL), grouped by section and category |
179
+ | `llms-full.txt` | Full-text bundle: every page's body concatenated for one-shot ingestion |
180
+ | `public/<slug>.md` | Per-page Markdown mirror of each MDX page, suitable for direct fetching at `/<slug>.md` |
181
+
182
+ The site name and description used in `llms.txt` come from `config.json` (`name`, `description`). Page URLs are absolute when `url` is set in `config.json` (or via `NEXT_PUBLIC_SITE_URL`), and root-relative otherwise.
183
+
184
+ A `.doccupine-llms-manifest.json` file in the generated app tracks which per-page mirrors were emitted so renamed or deleted pages get cleaned up on the next regeneration. Don't commit this file — it's regenerated automatically.
185
+
157
186
  ## AI Chat Setup
158
187
 
159
188
  The generated app includes an AI chat assistant. To enable it, create a `.env` file in the generated app directory:
@@ -1 +1 @@
1
- export declare const clientThemeProviderTemplate = "\"use client\";\nimport React, { useCallback, useContext, useEffect, useState } from \"react\";\nimport { ThemeProvider as StyledThemeProvider } from \"styled-components\";\nimport { Theme } from \"@/app/theme\";\nimport { GlobalStyles } from \"@/components/layout/GlobalStyles\";\n\ntype ThemeMode = \"light\" | \"dark\";\n\ntype ThemeContextValue = {\n /** The styled-components theme object \u2014 same reference in both modes;\n the values it points to are CSS variables that flip per active class. */\n theme: Theme;\n /** Active mode, derived from the \"dark\" class on <html>. */\n mode: ThemeMode;\n /** Update the active mode: flips the class and persists the cookie. */\n setMode: (m: ThemeMode) => void;\n};\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\nfunction useThemeOverride() {\n const ctx = useContext(ThemeContext);\n if (!ctx) {\n throw new Error(\"useThemeOverride must be used within ClientThemeProvider\");\n }\n return ctx;\n}\n\nfunction readMode(): ThemeMode {\n if (typeof document === \"undefined\") return \"light\";\n return document.documentElement.classList.contains(\"dark\") ? \"dark\" : \"light\";\n}\n\nfunction ClientThemeProvider({\n children,\n theme,\n}: {\n children: React.ReactNode;\n theme: Theme;\n}) {\n // Mode lives in React state so consumers (e.g. ThemeToggle) re-render when\n // the user toggles. The visual swap itself is driven entirely by the\n // \"dark\" class on <html> + CSS variables \u2014 React doesn't drive paint.\n const [mode, setModeState] = useState<ThemeMode>(\"light\");\n\n useEffect(() => {\n // Sync with the class set by the theme-init blocking script. Also\n // backfill the cookie if the script didn't get to write one (rare).\n setModeState(readMode());\n try {\n const has = document.cookie\n .split(\";\")\n .some((c) => c.trim().startsWith(\"theme=\"));\n if (!has) {\n const m = readMode();\n document.cookie = `theme=${m};path=/;max-age=31536000;SameSite=Lax`;\n }\n } catch {}\n }, []);\n\n const setMode = useCallback((next: ThemeMode) => {\n if (typeof document !== \"undefined\") {\n document.documentElement.classList.toggle(\"dark\", next === \"dark\");\n document.documentElement.style.colorScheme = next;\n try {\n document.cookie = `theme=${next};path=/;max-age=31536000;SameSite=Lax`;\n } catch {}\n }\n setModeState(next);\n }, []);\n\n return (\n <ThemeContext.Provider value={{ theme, mode, setMode }}>\n <StyledThemeProvider theme={theme}>\n <GlobalStyles />\n {children}\n </StyledThemeProvider>\n </ThemeContext.Provider>\n );\n}\n\nexport { ClientThemeProvider, useThemeOverride };\n";
1
+ export declare const clientThemeProviderTemplate = "\"use client\";\nimport React, { useCallback, useContext, useEffect, useState } from \"react\";\nimport { ThemeProvider as StyledThemeProvider } from \"styled-components\";\nimport { Theme } from \"@/app/theme\";\nimport { GlobalStyles } from \"@/components/layout/GlobalStyles\";\n\ntype ThemeMode = \"light\" | \"dark\";\n\ntype ThemeContextValue = {\n /** The styled-components theme object \u2014 same reference in both modes;\n the values it points to are CSS variables that flip per active class. */\n theme: Theme;\n /** Active mode, derived from the \"dark\" class on <html>. */\n mode: ThemeMode;\n /** Update the active mode: flips the class and persists the cookie. */\n setMode: (m: ThemeMode) => void;\n};\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\nfunction useThemeOverride() {\n const ctx = useContext(ThemeContext);\n if (!ctx) {\n throw new Error(\"useThemeOverride must be used within ClientThemeProvider\");\n }\n return ctx;\n}\n\nfunction readMode(): ThemeMode {\n if (typeof document === \"undefined\") return \"light\";\n return document.documentElement.classList.contains(\"dark\") ? \"dark\" : \"light\";\n}\n\nfunction ClientThemeProvider({\n children,\n theme,\n}: {\n children: React.ReactNode;\n theme: Theme;\n}) {\n // Mode lives in React state so consumers (e.g. ThemeToggle) re-render when\n // the user toggles. The visual swap itself is driven entirely by the\n // \"dark\" class on <html> + CSS variables \u2014 React doesn't drive paint.\n // The lazy initializer reads the class set by the theme-init blocking\n // script (SSR returns \"light\"); no rendered markup branches on mode, so\n // reading the DOM during the hydration render can't cause a mismatch.\n const [mode, setModeState] = useState<ThemeMode>(readMode);\n\n useEffect(() => {\n // Backfill the cookie if the theme-init script didn't get to write one\n // (rare \u2014 e.g. cookies disabled at first paint).\n try {\n const has = document.cookie\n .split(\";\")\n .some((c) => c.trim().startsWith(\"theme=\"));\n if (!has) {\n const m = readMode();\n document.cookie = `theme=${m};path=/;max-age=31536000;SameSite=Lax`;\n }\n } catch {}\n }, []);\n\n const setMode = useCallback((next: ThemeMode) => {\n if (typeof document !== \"undefined\") {\n document.documentElement.classList.toggle(\"dark\", next === \"dark\");\n document.documentElement.style.colorScheme = next;\n try {\n document.cookie = `theme=${next};path=/;max-age=31536000;SameSite=Lax`;\n } catch {}\n }\n setModeState(next);\n }, []);\n\n return (\n <ThemeContext.Provider value={{ theme, mode, setMode }}>\n <StyledThemeProvider theme={theme}>\n <GlobalStyles />\n {children}\n </StyledThemeProvider>\n </ThemeContext.Provider>\n );\n}\n\nexport { ClientThemeProvider, useThemeOverride };\n";
@@ -41,12 +41,14 @@ function ClientThemeProvider({
41
41
  // Mode lives in React state so consumers (e.g. ThemeToggle) re-render when
42
42
  // the user toggles. The visual swap itself is driven entirely by the
43
43
  // "dark" class on <html> + CSS variables — React doesn't drive paint.
44
- const [mode, setModeState] = useState<ThemeMode>("light");
44
+ // The lazy initializer reads the class set by the theme-init blocking
45
+ // script (SSR returns "light"); no rendered markup branches on mode, so
46
+ // reading the DOM during the hydration render can't cause a mismatch.
47
+ const [mode, setModeState] = useState<ThemeMode>(readMode);
45
48
 
46
49
  useEffect(() => {
47
- // Sync with the class set by the theme-init blocking script. Also
48
- // backfill the cookie if the script didn't get to write one (rare).
49
- setModeState(readMode());
50
+ // Backfill the cookie if the theme-init script didn't get to write one
51
+ // (rare e.g. cookies disabled at first paint).
50
52
  try {
51
53
  const has = document.cookie
52
54
  .split(";")
@@ -1 +1 @@
1
- export declare const updateMdxTemplate = "---\ntitle: \"Update\"\ndescription: \"Easily manage and present change history.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\norder: 12\n---\n\n# Update\n\nEasily manage and present change history.\n\nThe `Update` component helps you display release notes, version details, and changelogs in a standardized format.\n\n<Update label=\"Example\" description=\"v0.0.1\">\n ## Example entry\n\nYou can include anything here\u2014images, code snippets, or a bullet list of modifications.\n\n![Demo Image](https://docs.doccupine.com/demo.png)\n\n### Key additions\n\n- Fully responsive layout\n- Individual anchor for each update\n- Automatic RSS feed entry generation\n </Update>\n\n## Update Usage\n\nYou can combine multiple `Update` components to build complete changelogs.\n\n```mdx\n<Update label=\"Example\" description=\"v0.0.1\">\n ## Example entry\n\nYou can include anything here\u2014images, code snippets, or a bullet list of modifications.\n\n![Demo Image](https://docs.doccupine.com/demo.png)\n\n### Key additions\n\n- Fully responsive layout\n- Individual anchor for each update\n- Automatic RSS feed entry generation\n </Update>\n```\n\n## Properties\n\n<Field value=\"label\" type=\"string\" required>\n The label of the update.\n</Field>\n\n<Field value=\"description\" type=\"string\" required>\n The description of the update.\n</Field>\n\n<Field value=\"children\" type=\"node\" required>\n The content of the update.\n</Field>";
1
+ export declare const updateMdxTemplate = "---\ntitle: \"Update\"\ndescription: \"Easily manage and present change history.\"\ndate: \"2026-02-19\"\ncategory: \"Components\"\ncategoryOrder: 1\norder: 12\n---\n\n# Update\n\nEasily manage and present change history.\n\nThe `Update` component helps you display release notes, version details, and changelogs in a standardized format.\n\n<Update label=\"Example\" description=\"v0.0.1\">\n ## Example entry\n\nYou can include anything here\u2014images, code snippets, or a bullet list of modifications.\n\n![Demo Image](https://docs.doccupine.com/demo.png)\n\n### Key additions\n\n- Fully responsive layout\n- Individual anchor for each update\n- Automatic RSS feed entry generation\n\n</Update>\n\n## Update Usage\n\nYou can combine multiple `Update` components to build complete changelogs.\n\n```mdx\n<Update label=\"Example\" description=\"v0.0.1\">\n ## Example entry\n\nYou can include anything here\u2014images, code snippets, or a bullet list of modifications.\n\n![Demo Image](https://docs.doccupine.com/demo.png)\n\n### Key additions\n\n- Fully responsive layout\n- Individual anchor for each update\n- Automatic RSS feed entry generation\n\n</Update>\n```\n\n## Properties\n\n<Field value=\"label\" type=\"string\" required>\n The label of the update.\n</Field>\n\n<Field value=\"description\" type=\"string\" required>\n The description of the update.\n</Field>\n\n<Field value=\"children\" type=\"node\" required>\n The content of the update.\n</Field>";
@@ -25,7 +25,8 @@ You can include anything here—images, code snippets, or a bullet list of modif
25
25
  - Fully responsive layout
26
26
  - Individual anchor for each update
27
27
  - Automatic RSS feed entry generation
28
- </Update>
28
+
29
+ </Update>
29
30
 
30
31
  ## Update Usage
31
32
 
@@ -44,7 +45,8 @@ You can include anything here—images, code snippets, or a bullet list of modif
44
45
  - Fully responsive layout
45
46
  - Individual anchor for each update
46
47
  - Automatic RSS feed entry generation
47
- </Update>
48
+
49
+ </Update>
48
50
  \`\`\`
49
51
 
50
52
  ## Properties
@@ -10,28 +10,28 @@ export const packageJsonTemplate = JSON.stringify({
10
10
  format: "prettier --write .",
11
11
  },
12
12
  dependencies: {
13
- "@langchain/anthropic": "^1.3.29",
14
- "@langchain/core": "^1.1.45",
15
- "@langchain/google-genai": "^2.1.30",
16
- "@langchain/openai": "^1.4.5",
13
+ "@langchain/anthropic": "^1.4.0",
14
+ "@langchain/core": "^1.1.48",
15
+ "@langchain/google-genai": "^2.1.31",
16
+ "@langchain/openai": "^1.4.7",
17
17
  "@mdx-js/react": "^3.1.1",
18
18
  "@modelcontextprotocol/sdk": "^1.29.0",
19
- "@posthog/react": "^1.9.0",
19
+ "@posthog/react": "^1.9.1",
20
20
  "cherry-styled-components": "^0.1.17",
21
- langchain: "^1.4.0",
22
- "lucide-react": "^1.14.0",
21
+ langchain: "^1.4.2",
22
+ "lucide-react": "^1.17.0",
23
23
  minisearch: "^7.2.0",
24
24
  next: "16.2.6",
25
25
  "next-mdx-remote": "^6.0.0",
26
- "posthog-js": "^1.372.10",
27
- "posthog-node": "^5.33.4",
26
+ "posthog-js": "^1.376.4",
27
+ "posthog-node": "^5.35.6",
28
28
  react: "19.2.6",
29
29
  "react-dom": "19.2.6",
30
30
  "rehype-highlight": "^7.0.2",
31
31
  "rehype-parse": "^9.0.1",
32
32
  "rehype-stringify": "^10.0.1",
33
33
  "remark-gfm": "^4.0.1",
34
- "styled-components": "^6.4.1",
34
+ "styled-components": "^6.4.2",
35
35
  unified: "^11.0.5",
36
36
  zod: "^4.4.3",
37
37
  },
@@ -39,7 +39,7 @@ export const packageJsonTemplate = JSON.stringify({
39
39
  "@types/node": "^25",
40
40
  "@types/react": "^19",
41
41
  "@types/react-dom": "^19",
42
- "baseline-browser-mapping": "^2.10.28",
42
+ "baseline-browser-mapping": "^2.10.32",
43
43
  eslint: "^9",
44
44
  "eslint-config-next": "16.2.6",
45
45
  prettier: "^3.8.3",
@@ -1 +1 @@
1
- export declare const pnpmWorkspaceTemplate = "allowBuilds:\n core-js: false\n esbuild: false\n protobufjs: false\n sharp: false\n unrs-resolver: false\n";
1
+ export declare const pnpmWorkspaceTemplate = "allowBuilds:\n core-js: false\n protobufjs: false\n sharp: false\n unrs-resolver: false\n";
@@ -1,6 +1,5 @@
1
1
  export const pnpmWorkspaceTemplate = `allowBuilds:
2
2
  core-js: false
3
- esbuild: false
4
3
  protobufjs: false
5
4
  sharp: false
6
5
  unrs-resolver: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doccupine",
3
- "version": "0.0.90",
3
+ "version": "0.0.92",
4
4
  "description": "Free and open-source documentation platform. Write MDX, get a production-ready site with AI chat, built-in components, and an MCP server - in one command.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -36,7 +36,7 @@
36
36
  "dependencies": {
37
37
  "chalk": "^5.6.2",
38
38
  "chokidar": "^5.0.0",
39
- "commander": "^14.0.3",
39
+ "commander": "^15.0.0",
40
40
  "fs-extra": "^11.3.5",
41
41
  "gray-matter": "^4.0.3",
42
42
  "next": "^16.2.6",
@@ -46,11 +46,11 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/fs-extra": "^11.0.4",
49
- "@types/node": "^25.6.2",
49
+ "@types/node": "^25.9.1",
50
50
  "@types/prompts": "^2.4.9",
51
51
  "prettier": "^3.8.3",
52
52
  "typescript": "^6.0.3",
53
- "vitest": "^4.1.5"
53
+ "vitest": "^4.1.7"
54
54
  },
55
55
  "files": [
56
56
  "dist/**/*"