feat: add mcp server, reorganize repo

This commit is contained in:
2026-09-03 17:20:08 +05:00
parent c1dda4d257
commit 1fd8ad6b50
48 changed files with 1691 additions and 12467 deletions
+28 -1
View File
@@ -1 +1,28 @@
/node_modules/ # deps
node_modules/
# keep_local - локальные файлы, не для git
/_keep_local/
# build output
dist/
.next/
out/
# vite
# (frontend)
# misc
.DS_Store
*.pem
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# env files - начало
.env*
# env files - конец
# typescript - начало
*.tsbuildinfo
next-env.d.ts
# typescript - конец
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

View File
+33
View File
@@ -0,0 +1,33 @@
{
"name": "rubber-duck",
"version": "0.0.1",
"description": "Best debug tool - rubber duck",
"keywords": [
"rubber",
"duck",
"debug",
"tool",
"fun"
],
"homepage": "https://github.com/Ku6epXBOCTuK/rubber-duck#readme",
"bugs": {
"url": "https://github.com/Ku6epXBOCTuK/rubber-duck/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Ku6epXBOCTuK/rubber-duck.git"
},
"license": "MIT",
"author": "Ku6epXBOCTuK",
"type": "module",
"main": "index.js",
"scripts": {
"dev": "vite",
"test": "echo \"Error: no test specified\" && exit 1",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^8.0.11"
}
}
+41
View File
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+9
View File
@@ -0,0 +1,9 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
<!-- END:nextjs-agent-rules -->
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+45
View File
@@ -0,0 +1,45 @@
# Rubber Duck MCP
MCP-сервер для техники «резиновая уточка»: когда ИИ застревает в логическом цикле или склонен к
галлюцинациям, он вызывает инструмент `quack()`, чтобы сформулировать мысль и продолжить.
Сервер работает в режиме **Streamable HTTP** (stateless), развёртывается на Vercel, использует
`mcp-handler` v2 + `@modelcontextprotocol/server` v2.
## Эндпоинт
```
POST /api/mcp
```
Подключите этот URL как MCP-сервер (тип Streamable HTTP) в Cursor, Claude Desktop и других клиентах.
## Инструменты
- `quack` — возвращает кряк. Опциональный параметр `mood`: `happy | confused | excited | sleepy`.
## Запуск
```bash
pnpm install
pnpm dev # http://localhost:3000
pnpm build
pnpm start
```
## Проверка
```bash
curl -X POST http://localhost:3000/api/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"quack","arguments":{"mood":"happy"}}}'
```
## Деплой на Vercel
```bash
vercel --prod
```
(`rootDirectory` проекта — `apps/mcp`.)
+21
View File
@@ -0,0 +1,21 @@
import { createMcpHandler } from "mcp-handler";
import { registerDuckTools } from "@/lib/mcp";
export const runtime = "nodejs";
const post = createMcpHandler(
async (server) => {
registerDuckTools(server);
},
{
serverInfo: { name: "rubber-duck-mcp", version: "1.0.0" },
},
);
export async function GET(req: Request) {
return post(req);
}
export async function POST(req: Request) {
return post(req);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+49
View File
@@ -0,0 +1,49 @@
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
html {
height: 100%;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
min-height: 100%;
display: flex;
flex-direction: column;
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}
+15
View File
@@ -0,0 +1,15 @@
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "Rubber Duck MCP",
description: "MCP server for rubber duck debugging — quack() to articulate your thinking.",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
+23
View File
@@ -0,0 +1,23 @@
export default function Home() {
return (
<main style={{ fontFamily: "system-ui, sans-serif", maxWidth: 640, margin: "0 auto", padding: "3rem 1rem" }}>
<h1>Rubber Duck MCP</h1>
<p>
MCP-сервер для техники «резиновая уточка»: когда ИИ застревает, он вызывает инструмент{" "}
<code>quack()</code>, чтобы сформулировать мысль и продолжить. Сервер работает в режиме
Streamable HTTP и предназначен для подключения в Cursor, Claude Desktop и других MCP-клиентах.
</p>
<h2>Эндпоинт</h2>
<p>
<code>/api/mcp</code> подключите этот URL как MCP-сервер (тип Streamable HTTP).
</p>
<h2>Инструменты</h2>
<ul>
<li>
<code>quack</code> возвращает кряк. Опциональный параметр <code>mood</code>:{" "}
<code>happy | confused | excited | sleepy</code>.
</li>
</ul>
</main>
);
}
+38
View File
@@ -0,0 +1,38 @@
import type { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";
const QUACKS = {
happy: ["QUACK! 🎉", "Quack quack! 😄", "QUAAACK! 🦆✨"],
confused: ["Qu...ack?", "Quack? 🤔", "Quaaack...?"],
excited: ["QUACK QUACK QUACK!", "QUAAACK!! 🔥", "Quack! Quack! Quack!"],
sleepy: ["quack... 😴", "quaack... 💤", "quack. *yawn*"],
default: ["Quack!", "Quack quack!", "QUACK!", "Quaaack!", "Quack. 🦆"],
} as const;
const MOODS = ["happy", "confused", "excited", "sleepy"] as const;
function randomFrom<T>(arr: readonly T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
export function registerDuckTools(server: McpServer): void {
server.registerTool(
"quack",
{
title: "Quack",
description:
"Rubber duck quacks. Use when you're stuck, looping, or hallucinating. " +
"The act of calling forces you to articulate your current thinking out loud.",
inputSchema: z.object({
mood: z
.enum(MOODS)
.optional()
.describe("Mood of the duck. Omit for a random quack."),
}),
},
async ({ mood }) => {
const text = mood ? randomFrom(QUACKS[mood]) : randomFrom(QUACKS.default);
return { content: [{ type: "text", text }] };
},
);
}
+7
View File
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
+25
View File
@@ -0,0 +1,25 @@
{
"name": "mcp",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@modelcontextprotocol/server": "^2.0.0",
"mcp-handler": "^2.1.1",
"next": "16.3.3",
"react": "19.2.8",
"react-dom": "19.2.8",
"zod": "^4.5.4"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"typescript": "^5"
},
"packageManager": "pnpm@11.20.0"
}
+34
View File
@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}
-860
View File
@@ -1,860 +0,0 @@
{
"name": "rubber-duck",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "rubber-duck",
"version": "0.0.1",
"license": "MIT",
"devDependencies": {
"vite": "^8.0.11"
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@tybys/wasm-util": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
},
"peerDependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1"
}
},
"node_modules/@oxc-project/types": {
"version": "0.128.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz",
"integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz",
"integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz",
"integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz",
"integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz",
"integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz",
"integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz",
"integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz",
"integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz",
"integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz",
"integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz",
"integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz",
"integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz",
"integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz",
"integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==",
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "1.10.0",
"@emnapi/runtime": "1.10.0",
"@napi-rs/wasm-runtime": "^1.1.4"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz",
"integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz",
"integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz",
"integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==",
"dev": true,
"license": "MIT"
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/lightningcss": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-android-arm64": "1.32.0",
"lightningcss-darwin-arm64": "1.32.0",
"lightningcss-darwin-x64": "1.32.0",
"lightningcss-freebsd-x64": "1.32.0",
"lightningcss-linux-arm-gnueabihf": "1.32.0",
"lightningcss-linux-arm64-gnu": "1.32.0",
"lightningcss-linux-arm64-musl": "1.32.0",
"lightningcss-linux-x64-gnu": "1.32.0",
"lightningcss-linux-x64-musl": "1.32.0",
"lightningcss-win32-arm64-msvc": "1.32.0",
"lightningcss-win32-x64-msvc": "1.32.0"
}
},
"node_modules/lightningcss-android-arm64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
"integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
"integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
"integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-freebsd-x64": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
"integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
"integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
"integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
"integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
"integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
"integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
"integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/nanoid": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.5.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rolldown": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz",
"integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.128.0",
"@rolldown/pluginutils": "1.0.0-rc.18"
},
"bin": {
"rolldown": "bin/cli.mjs"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.0-rc.18",
"@rolldown/binding-darwin-arm64": "1.0.0-rc.18",
"@rolldown/binding-darwin-x64": "1.0.0-rc.18",
"@rolldown/binding-freebsd-x64": "1.0.0-rc.18",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18",
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18",
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18",
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18",
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18",
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.18",
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.18",
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.18",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18",
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tinyglobby": {
"version": "0.2.16",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD",
"optional": true
},
"node_modules/undici-types": {
"version": "7.19.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/vite": {
"version": "8.0.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz",
"integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
"postcss": "^8.5.14",
"rolldown": "1.0.0-rc.18",
"tinyglobby": "^0.2.16"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"@vitejs/devtools": "^0.1.18",
"esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"@vitejs/devtools": {
"optional": true
},
"esbuild": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
}
}
}
+17 -27
View File
@@ -1,32 +1,22 @@
{ {
"name": "rubber-duck", "name": "rubber-duck-monorepo",
"version": "0.0.1", "version": "1.0.0",
"description": "Best debug tool - rubber duck", "description": "Rubber duck debugging - frontend + MCP server monorepo",
"keywords": [
"rubber",
"duck",
"debug",
"tool",
"fun"
],
"homepage": "https://github.com/Ku6epXBOCTuK/rubber-duck#readme",
"bugs": {
"url": "https://github.com/Ku6epXBOCTuK/rubber-duck/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Ku6epXBOCTuK/rubber-duck.git"
},
"license": "MIT",
"author": "Ku6epXBOCTuK",
"type": "module",
"main": "index.js",
"scripts": { "scripts": {
"dev": "vite", "dev": "pnpm --dir apps/frontend dev",
"refs": "npx serve refs", "build-frontend": "pnpm --dir apps/frontend build",
"test": "echo \"Error: no test specified\" && exit 1" "build-mcp": "pnpm --dir apps/mcp build",
"dev-mcp": "pnpm --dir apps/mcp dev",
"typecheck-mcp": "pnpm --dir apps/mcp exec tsc --noEmit"
}, },
"devDependencies": { "license": "ISC",
"vite": "^8.0.11" "devEngines": {
"packageManager": {
"name": "pnpm",
"version": "^11.20.0",
"onFail": "download"
} }
},
"type": "module",
"private": "true"
} }
+1302
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
packages:
- apps/*
-33
View File
@@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Refs</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; display: flex; flex-direction: column; background: #1a1a1a; color: #fff; font-family: sans-serif; }
nav { display: flex; gap: 2px; background: #2a2a2a; padding: 4px 8px; }
nav button { background: #3a3a3a; border: none; color: #aaa; padding: 6px 16px; cursor: pointer; font-size: 13px; border-radius: 3px; transition: background .15s, color .15s; }
nav button:hover { background: #4a4a4a; color: #fff; }
nav button.active { background: #fff; color: #000; }
iframe { flex: 1; border: none; background: #fff; }
</style>
</head>
<body>
<nav>
<button class="active" onclick="show('ref1')">ref1</button>
<button onclick="show('ref2')">ref2</button>
<button onclick="show('ref3')">ref3</button>
<button onclick="show('ref4')">ref4</button>
</nav>
<iframe id="frame" src="ref1/"></iframe>
<script>
function show(name) {
document.getElementById('frame').src = name + '/';
document.querySelectorAll('nav button').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
}
</script>
</body>
</html>
-399
View File
@@ -1,399 +0,0 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DUCKDEBUG.gg — Rubber Duck Debugging as a Service</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" />
<style>
@import url("https://fonts.googleapis.com/css2?family=VT323&amp;display=swap");
:root {
--primary: #00ff00;
}
.terminal-font {
font-family: "VT323", monospace;
}
body {
background: #0a0a0a;
color: #00ff00;
}
.code-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
overflow: hidden;
opacity: 0.15;
}
.code-line {
position: absolute;
color: #00ff00;
font-family: "VT323", monospace;
font-size: 14px;
white-space: nowrap;
animation: flyCode linear infinite;
text-shadow: 0 0 8px #00ff00;
}
@keyframes flyCode {
0% {
transform: translateY(-100px);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(2000px);
opacity: 0;
}
}
.glitch {
animation: glitch 1s linear infinite alternate;
}
@keyframes glitch {
0% {
transform: translate(0);
}
20% {
transform: translate(-2px, 2px);
}
40% {
transform: translate(-2px, -2px);
}
60% {
transform: translate(2px, 2px);
}
80% {
transform: translate(2px, -2px);
}
100% {
transform: translate(0);
}
}
.scanline {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to bottom, transparent 50%, rgba(0, 255, 0, 0.03) 50%);
background-size: 100% 4px;
pointer-events: none;
animation: scan 4s linear infinite;
z-index: -1;
}
@keyframes scan {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(100%);
}
}
.terminal-window {
border: 4px solid #00ff00;
box-shadow: 0 0 30px #00ff00;
}
</style>
</head>
<body class="terminal-font min-h-screen overflow-x-hidden">
<!-- Animated code background -->
<div class="code-bg" id="code-bg"></div>
<div class="scanline"></div>
<!-- Navbar -->
<nav class="border-b border-green-500 bg-black/80 backdrop-blur-md fixed w-full z-50">
<div class="max-w-7xl mx-auto px-6 py-4 flex justify-between items-center">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-green-500 text-black flex items-center justify-center text-3xl font-bold rounded">
🦆
</div>
<div>
<span class="text-3xl tracking-widest">DUCKDEBUG</span>
<span class="text-xs text-green-400 block -mt-1">.gg</span>
</div>
</div>
<div class="flex gap-8 text-xl">
<a href="#features" class="hover:text-white transition-colors">FEATURES</a>
<a href="#roadmap" class="hover:text-white transition-colors">ROADMAP</a>
<a href="#" class="hover:text-white transition-colors">DOCS</a>
</div>
<button
onclick="toggleTheme()"
class="px-6 py-2 border border-green-500 hover:bg-green-500 hover:text-black transition-all"
>
<i class="fas fa-moon"></i>
</button>
</div>
</nav>
<!-- Hero -->
<section class="min-h-screen flex items-center pt-20 relative">
<div class="max-w-5xl mx-auto px-6 text-center">
<div class="inline-block bg-black border-2 border-green-500 px-8 py-2 mb-8 text-2xl tracking-widest glitch">
RUBBER DUCK DEBUGGING AS A SERVICE
</div>
<h1 class="text-8xl leading-none mb-6">
ТВОЙ УТЁНОК<br />
<span class="text-white">ВСЕГДА НА СВЯЗИ</span>
</h1>
<p class="text-3xl text-green-300 max-w-2xl mx-auto mb-12">
Объясни проблему утёнку.<br />
Получи решение. Или хотя бы quack.
</p>
<!-- Prominent Start Now Button -->
<div class="flex justify-center gap-6">
<button
onclick="startDebugging()"
class="group relative px-16 py-8 text-5xl font-bold border-4 border-green-500 bg-black hover:bg-green-500 hover:text-black transition-all duration-300 transform hover:scale-110 active:scale-95 shadow-2xl shadow-green-500/50"
>
<span class="relative z-10">START NOW</span>
<div
class="absolute inset-0 bg-green-400 scale-x-0 group-active:scale-x-100 transition-transform origin-left"
></div>
<div
class="absolute -inset-2 border border-green-400 opacity-30 group-hover:opacity-70 animate-pulse"
></div>
</button>
</div>
<div class="mt-12 text-green-400 text-xl flex justify-center gap-8">
<div class="flex items-center gap-2">
<i class="fas fa-check"></i>
<span>Бесплатный tier</span>
</div>
<div class="flex items-center gap-2">
<i class="fas fa-check"></i>
<span>24/7 онлайн</span>
</div>
<div class="flex items-center gap-2">
<i class="fas fa-check"></i>
<span>Voice + Text</span>
</div>
</div>
</div>
<!-- Floating duck -->
<div class="absolute bottom-10 right-10 text-8xl animate-bounce" style="animation-duration: 3s">🦆</div>
</section>
<!-- Features -->
<section id="features" class="py-24 bg-black/70 border-t border-b border-green-500">
<div class="max-w-6xl mx-auto px-6">
<h2 class="text-6xl text-center mb-16">WHY DUCKDEBUG?</h2>
<div class="grid md:grid-cols-3 gap-8">
<div class="terminal-window bg-black p-8 border border-green-500">
<div class="text-4xl mb-6">🦆</div>
<h3 class="text-3xl mb-4">Классический Rubber Duck</h3>
<p class="text-green-300 text-xl">Просто расскажи проблему. Утёнок слушает внимательно.</p>
</div>
<div class="terminal-window bg-black p-8 border border-green-500">
<div class="text-4xl mb-6">🤖</div>
<h3 class="text-3xl mb-4">AI + Duck Hybrid</h3>
<p class="text-green-300 text-xl">Иногда утёнок даёт реальные подсказки (на базе AI).</p>
</div>
<div class="terminal-window bg-black p-8 border border-green-500">
<div class="text-4xl mb-6">📡</div>
<h3 class="text-3xl mb-4">Multiplayer Debug</h3>
<p class="text-green-300 text-xl">Делитесь сессией с командой. Все смотрят на одного утёнка.</p>
</div>
</div>
</div>
</section>
<!-- Roadmap -->
<section id="roadmap" class="py-24">
<div class="max-w-5xl mx-auto px-6">
<div class="terminal-window bg-black p-10 border-4 border-green-500">
<h2 class="text-6xl mb-12 text-center border-b border-green-500 pb-6">ROADMAP v0.1 → v1.0</h2>
<div class="space-y-12 text-2xl">
<div class="flex gap-8 items-start">
<div class="w-8 h-8 bg-green-500 text-black flex-shrink-0 flex items-center justify-center font-bold">
01
</div>
<div class="flex-1">
<div class="font-bold text-green-400">MVP (Сейчас)</div>
<div class="text-green-300">Базовый веб-утёнок + чат + voice-to-text</div>
</div>
<div class="text-green-400">LIVE</div>
</div>
<div class="flex gap-8 items-start">
<div class="w-8 h-8 bg-green-500 text-black flex-shrink-0 flex items-center justify-center font-bold">
02
</div>
<div class="flex-1">
<div class="font-bold text-green-400">MCP Сервер</div>
<div class="text-green-300">
Подключай свой Minecraft сервер — утёнок будет дебажить моды и плагины в реальном времени
</div>
</div>
<div class="text-amber-400">Q3 2026</div>
</div>
<div class="flex gap-8 items-start">
<div class="w-8 h-8 bg-green-500 text-black flex-shrink-0 flex items-center justify-center font-bold">
03
</div>
<div class="flex-1">
<div class="font-bold text-green-400">Скины и Кастомизация</div>
<div class="text-green-300">Cyberduck, Steampunk Duck, Matrix Duck, Gentoo Duck и др.</div>
</div>
<div class="text-amber-400">Q4 2026</div>
</div>
<div class="flex gap-8 items-start">
<div class="w-8 h-8 bg-green-500 text-black flex-shrink-0 flex items-center justify-center font-bold">
04
</div>
<div class="flex-1">
<div class="font-bold text-green-400">Battle Pass</div>
<div class="text-green-300">Еженедельные челленджи: "Debug 50 bugs", "Объясни legacy код без мата"</div>
</div>
<div class="text-amber-400">Q1 2027</div>
</div>
<div class="flex gap-8 items-start">
<div class="w-8 h-8 bg-green-500 text-black flex-shrink-0 flex items-center justify-center font-bold">
05
</div>
<div class="flex-1">
<div class="font-bold text-green-400">Лутбоксы</div>
<div class="text-green-300">
Rare скины, voice packs ("Quack but Russian", "Quack but Rust"), эксклюзивные ошибки
</div>
</div>
<div class="text-amber-400">Q2 2027</div>
</div>
</div>
</div>
</div>
</section>
<!-- Footer CTA -->
<footer class="border-t border-green-500 py-12 bg-black">
<div class="max-w-4xl mx-auto text-center px-6">
<button
onclick="startDebugging()"
class="w-full max-w-md mx-auto block px-12 py-6 text-4xl border-4 border-green-500 hover:bg-green-500 hover:text-black transition-all"
>
НАЧАТЬ ДЕБАГ СЕССИЮ →
</button>
<p class="mt-8 text-green-400">Made with ❤️ and too much coffee • 2026</p>
</div>
</footer>
<script>
// Tailwind script already included via CDN
function toggleTheme() {
if (document.documentElement.classList.contains("light")) {
document.documentElement.classList.remove("light");
document.body.style.background = "#0a0a0a";
document.body.style.color = "#00ff00";
} else {
document.documentElement.classList.add("light");
document.body.style.background = "#f0f0f0";
document.body.style.color = "#006600";
}
}
// Flying code lines
function createCodeLine() {
const lines = [
"const bug = null;",
"if (user.isConfused) quack();",
"git commit -m 'fixed everything'",
"SELECT * FROM problems WHERE solved = false;",
"try { explainToDuck() } catch(e) {}",
"sudo rm -rf /problems",
"console.log('why is this not working');",
"def debug(): return 'quack'",
];
const line = document.createElement("div");
line.className = "code-line";
line.textContent = lines[Math.floor(Math.random() * lines.length)];
line.style.left = Math.random() * 100 + "vw";
line.style.animationDuration = Math.random() * 8 + 12 + "s";
line.style.fontSize = Math.random() * 12 + 13 + "px";
line.style.opacity = Math.random() * 0.6 + 0.2;
document.getElementById("code-bg").appendChild(line);
setTimeout(() => {
line.remove();
}, 25000);
}
// Start debugging simulation
function startDebugging() {
const messages = [
"🦆 Соединяемся с утёнком...",
"🦆 Утёнок слушает...",
"🦆 Quack... Quack... Решение найдено!",
];
let i = 0;
const notif = document.createElement("div");
notif.style.position = "fixed";
notif.style.bottom = "40px";
notif.style.left = "50%";
notif.style.transform = "translateX(-50%)";
notif.style.background = "black";
notif.style.border = "3px solid #00ff00";
notif.style.padding = "20px 40px";
notif.style.fontSize = "28px";
notif.style.zIndex = "10000";
document.body.appendChild(notif);
const interval = setInterval(() => {
notif.textContent = messages[i];
i++;
if (i >= messages.length) {
clearInterval(interval);
setTimeout(() => notif.remove(), 2000);
}
}, 800);
}
// Initialize everything
window.onload = function () {
// Generate flying code
setInterval(createCodeLine, 180);
// Initial lines
for (let i = 0; i < 12; i++) {
setTimeout(createCodeLine, i * 120);
}
console.log(
"%c🦆 DUCKDEBUG.gg initialized successfully",
"color: #00ff00; font-family: monospace; font-size: 18px",
);
};
</script>
</body>
</html>
-1371
View File
File diff suppressed because it is too large Load Diff
-367
View File
@@ -1,367 +0,0 @@
<html lang="en" class="dark" data-theme="dark">
<head>
<meta charset="utf-8" />
<title>Rubber Duck Debugging as a service</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<link rel="stylesheet" href="./duck.css" />
</head>
<body>
<div id="app">
<div class="relative min-h-screen overflow-hidden bg-background text-foreground">
<div aria-hidden="true" class="pointer-events-none fixed inset-0 z-0 overflow-hidden">
<span
class="code-line absolute font-mono text-code-float"
style="
top: 1.25%;
left: -8%;
font-size: 10px;
animation-delay: 0s;
animation-duration: 20s;
letter-spacing: 0.03em;
user-select: none;
"
>const duck = new RubberDuck();</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 9.58333%;
left: -10%;
font-size: 11.5px;
animation-delay: 2.7s;
animation-duration: 21.9s;
letter-spacing: 0.03em;
user-select: none;
"
>duck.listen(problem);</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 17.9167%;
left: -12%;
font-size: 13px;
animation-delay: 5.4s;
animation-duration: 23.8s;
letter-spacing: 0.03em;
user-select: none;
"
>trace.stack().filter(signal);</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 26.25%;
left: -14%;
font-size: 10px;
animation-delay: 8.1s;
animation-duration: 25.7s;
letter-spacing: 0.03em;
user-select: none;
"
>if (confused) explain(out_loud);</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 34.5833%;
left: -16%;
font-size: 11.5px;
animation-delay: 10.8s;
animation-duration: 27.6s;
letter-spacing: 0.03em;
user-select: none;
"
>return plan.nextStep();</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 42.9167%;
left: -8%;
font-size: 13px;
animation-delay: 13.5s;
animation-duration: 29.5s;
letter-spacing: 0.03em;
user-select: none;
"
>debugger;</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 51.25%;
left: -10%;
font-size: 10px;
animation-delay: 16.2s;
animation-duration: 31.4s;
letter-spacing: 0.03em;
user-select: none;
"
>console.log('aha moment...');</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 59.5833%;
left: -12%;
font-size: 11.5px;
animation-delay: 18.9s;
animation-duration: 33.3s;
letter-spacing: 0.03em;
user-select: none;
"
>assert.equal(got, expected);</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 67.9167%;
left: -14%;
font-size: 13px;
animation-delay: 21.6s;
animation-duration: 21.2s;
letter-spacing: 0.03em;
user-select: none;
"
>catch (e) { log(e.stack); }</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 76.25%;
left: -16%;
font-size: 10px;
animation-delay: 0.3s;
animation-duration: 23.1s;
letter-spacing: 0.03em;
user-select: none;
"
>process.env.DEBUG = '*'</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 84.5833%;
left: -8%;
font-size: 11.5px;
animation-delay: 3s;
animation-duration: 25s;
letter-spacing: 0.03em;
user-select: none;
"
>heap.snapshot().analyze();</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 92.9167%;
left: -10%;
font-size: 13px;
animation-delay: 5.7s;
animation-duration: 26.9s;
letter-spacing: 0.03em;
user-select: none;
"
>git diff HEAD~1 --stat</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 1.25%;
left: -12%;
font-size: 10px;
animation-delay: 8.4s;
animation-duration: 28.8s;
letter-spacing: 0.03em;
user-select: none;
"
>npm run debug:verbose</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 9.58333%;
left: -14%;
font-size: 11.5px;
animation-delay: 11.1s;
animation-duration: 30.7s;
letter-spacing: 0.03em;
user-select: none;
"
>grep -r 'undefined' ./src</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 17.9167%;
left: -16%;
font-size: 13px;
animation-delay: 13.8s;
animation-duration: 32.6s;
letter-spacing: 0.03em;
user-select: none;
"
>curl -v http://localhost:3000</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 26.25%;
left: -8%;
font-size: 10px;
animation-delay: 16.5s;
animation-duration: 20.5s;
letter-spacing: 0.03em;
user-select: none;
"
>SELECT * FROM logs LIMIT 50;</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 34.5833%;
left: -10%;
font-size: 11.5px;
animation-delay: 19.2s;
animation-duration: 22.4s;
letter-spacing: 0.03em;
user-select: none;
"
>docker logs --tail 200 app</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 42.9167%;
left: -12%;
font-size: 13px;
animation-delay: 21.9s;
animation-duration: 24.3s;
letter-spacing: 0.03em;
user-select: none;
"
>strace -e trace=network -p $PID</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 51.25%;
left: -14%;
font-size: 10px;
animation-delay: 0.6s;
animation-duration: 26.2s;
letter-spacing: 0.03em;
user-select: none;
"
>tcpdump -i any port 8080</span
><span
class="code-line absolute font-mono text-code-float"
style="
top: 59.5833%;
left: -16%;
font-size: 11.5px;
animation-delay: 3.3s;
animation-duration: 28.1s;
letter-spacing: 0.03em;
user-select: none;
"
>breakpoint.set(line, 42);</span
>
</div>
<div class="relative z-20 p-4">
<a
href="/"
class="inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 shadow h-9 px-4 py-2 min-h-10 gap-2 bg-muted/80 text-foreground hover:bg-muted backdrop-blur-sm font-mono text-sm"
type="button"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 256 256">
<path
d="M224,128a8,8,0,0,1-8,8H59.31l58.35,58.34a8,8,0,0,1-11.32,11.32l-72-72a8,8,0,0,1,0-11.32l72-72a8,8,0,0,1,11.32,11.32L59.31,120H216A8,8,0,0,1,224,128Z"
></path></svg
>duck://home
</a>
</div>
<div
class="relative z-10 flex min-h-[calc(100vh-64px)] flex-col items-center justify-center gap-8 px-4 py-8 lg:flex-row lg:gap-12 lg:px-12"
>
<div class="flex flex-col items-center gap-4 lg:w-[40%]">
<div class="relative">
<div class="absolute inset-0 rounded-full blur-3xl opacity-40 bg-primary scale-125"></div>
<div class="relative rounded-3xl border border-border bg-card/60 backdrop-blur-md p-6 shadow-2xl">
<img
src="./img/ai_1.png"
alt="Rubber debug duck"
class="h-64 w-64 rounded-2xl object-cover md:h-80 md:w-80"
/>
<div
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-border bg-panel/80 px-4 py-2"
>
<span class="relative flex h-2.5 w-2.5"
><span
class="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75"
></span
><span class="relative inline-flex h-2.5 w-2.5 rounded-full bg-primary"></span></span
><span class="font-mono text-xs text-primary">booting session...</span>
</div>
</div>
</div>
<div class="text-center space-y-1">
<h1 class="font-mono text-2xl font-medium text-foreground">
<span class="text-primary">&gt;</span> RubberDuck.debug()
</h1>
<p class="text-sm text-muted-foreground font-light">Explain the bug. Follow the path. Ship the fix.</p>
</div>
</div>
<div class="w-full lg:w-[55%] max-w-2xl">
<div class="rounded-2xl border border-border bg-card/70 backdrop-blur-md shadow-2xl overflow-hidden">
<div class="flex items-center gap-2 border-b border-border bg-muted/80 px-4 py-3">
<div class="flex gap-1.5">
<span class="h-3 w-3 rounded-full bg-destructive/70"></span
><span class="h-3 w-3 rounded-full bg-warning/70"></span
><span class="h-3 w-3 rounded-full bg-success/70"></span>
</div>
<div class="flex items-center gap-2 ml-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
fill="currentColor"
viewBox="0 0 256 256"
class="text-muted-foreground"
>
<path
d="M117.31,134l-72,64a8,8,0,1,1-10.63-12L100,128,34.69,70A8,8,0,1,1,45.32,58l72,64a8,8,0,0,1,0,12ZM216,184H120a8,8,0,0,0,0,16h96a8,8,0,0,0,0-16Z"
></path></svg
><span class="font-mono text-xs text-muted-foreground">duck-session — bash</span>
</div>
</div>
<div
class="h-64 overflow-y-auto p-4 font-mono text-sm space-y-1 scrollbar-thin"
style="scrollbar-width: none"
>
<p class="text-muted-foreground"><span class="text-primary">duck@debug:~$</span> ./start-session.sh</p>
<p class="text-foreground">$ describe issue --env production</p>
<p class="text-tertiary pl-2">&gt; analysing stack trace...</p>
<p class="text-foreground">$ git log --oneline -10</p>
<p class="text-tertiary pl-2">&gt; narrowing root cause...</p>
<p class="text-foreground">$ strace -p $PID</p>
<p class="text-tertiary pl-2">&gt; check<span class="animate-pulse text-primary"></span></p>
</div>
<div class="border-t border-border bg-muted/40 p-4">
<form class="flex gap-2">
<div
class="flex flex-1 items-center gap-2 rounded-xl border border-border bg-input/80 px-4 py-2 focus-within:border-primary focus-within:ring-1 focus-within:ring-primary transition-all"
>
<span class="font-mono text-sm text-primary shrink-0">$</span
><input
type="text"
disabled=""
placeholder="initializing..."
class="flex-1 bg-transparent font-mono text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50"
value=""
/>
</div>
<button
class="inline-flex items-center justify-center gap-2 whitespace-nowrap focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 h-9 py-2 min-h-10 rounded-xl bg-primary px-5 font-mono text-sm font-medium text-primary-foreground shadow-[0_0_18px_-4px_var(--color-primary)] hover:bg-primary-hover hover:shadow-[0_0_28px_-2px_var(--color-primary)] disabled:opacity-40 transition-shadow duration-300"
type="submit"
disabled=""
>
send
</button>
</form>
<p class="mt-2 font-mono text-xs text-muted-foreground">
rubber duck debugging as a service · v0.1.0-mvp
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
-2258
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

-1333
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

-3514
View File
File diff suppressed because it is too large Load Diff
-73
View File
@@ -1,73 +0,0 @@
.rfm-marquee-container {
overflow-x: hidden;
display: flex;
flex-direction: row;
position: relative;
width: var(--width);
transform: var(--transform);
}
.rfm-marquee-container:hover div {
animation-play-state: var(--pause-on-hover);
}
.rfm-marquee-container:active div {
animation-play-state: var(--pause-on-click);
}
.rfm-overlay {
position: absolute;
width: 100%;
height: 100%;
}
.rfm-overlay::before,
.rfm-overlay::after {
background: linear-gradient(to right, var(--gradient-color), rgba(255, 255, 255, 0));
content: "";
height: 100%;
position: absolute;
width: var(--gradient-width);
z-index: 2;
pointer-events: none;
touch-action: none;
}
.rfm-overlay::after {
right: 0;
top: 0;
transform: rotateZ(180deg);
}
.rfm-overlay::before {
left: 0;
top: 0;
}
.rfm-marquee {
flex: 0 0 auto;
min-width: var(--min-width);
z-index: 1;
display: flex;
flex-direction: row;
align-items: center;
animation: scroll var(--duration) linear var(--delay) var(--iteration-count);
animation-play-state: var(--play);
animation-delay: var(--delay);
animation-direction: var(--direction);
}
@keyframes scroll {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-100%);
}
}
.rfm-initial-child-container {
flex: 0 0 auto;
display: flex;
min-width: auto;
flex-direction: row;
align-items: center;
}
.rfm-child {
transform: var(--transform);
}
-2230
View File
File diff suppressed because it is too large Load Diff