idea-manager 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/bin/im.js +4 -0
- package/next.config.ts +8 -0
- package/package.json +55 -0
- package/postcss.config.mjs +7 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/src/app/api/health/route.ts +5 -0
- package/src/app/api/projects/[id]/brainstorm/route.ts +37 -0
- package/src/app/api/projects/[id]/conversations/route.ts +50 -0
- package/src/app/api/projects/[id]/items/[itemId]/prompt/route.ts +51 -0
- package/src/app/api/projects/[id]/items/[itemId]/route.ts +73 -0
- package/src/app/api/projects/[id]/items/route.ts +17 -0
- package/src/app/api/projects/[id]/memos/route.ts +18 -0
- package/src/app/api/projects/[id]/route.ts +39 -0
- package/src/app/api/projects/[id]/structure/route.ts +28 -0
- package/src/app/api/projects/route.ts +19 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +437 -0
- package/src/app/layout.tsx +42 -0
- package/src/app/page.tsx +175 -0
- package/src/app/projects/[id]/page.tsx +249 -0
- package/src/cli.ts +41 -0
- package/src/components/brainstorm/Editor.tsx +163 -0
- package/src/components/brainstorm/MemoPin.tsx +31 -0
- package/src/components/brainstorm/ResizeHandle.tsx +45 -0
- package/src/components/chat/ChatMessage.tsx +28 -0
- package/src/components/chat/ChatPanel.tsx +100 -0
- package/src/components/tree/ItemDetail.tsx +196 -0
- package/src/components/tree/LockToggle.tsx +23 -0
- package/src/components/tree/StatusBadge.tsx +32 -0
- package/src/components/tree/TreeNode.tsx +118 -0
- package/src/components/tree/TreeView.tsx +60 -0
- package/src/lib/ai/chat-responder.ts +69 -0
- package/src/lib/ai/client.ts +124 -0
- package/src/lib/ai/prompter.ts +83 -0
- package/src/lib/ai/structurer.ts +74 -0
- package/src/lib/db/index.ts +16 -0
- package/src/lib/db/queries/brainstorms.ts +26 -0
- package/src/lib/db/queries/conversations.ts +46 -0
- package/src/lib/db/queries/items.ts +147 -0
- package/src/lib/db/queries/memos.ts +66 -0
- package/src/lib/db/queries/projects.ts +53 -0
- package/src/lib/db/queries/prompts.ts +68 -0
- package/src/lib/db/schema.ts +78 -0
- package/src/lib/mcp/server.ts +117 -0
- package/src/lib/mcp/tools.ts +83 -0
- package/src/lib/utils/id.ts +5 -0
- package/src/lib/utils/paths.ts +16 -0
- package/src/types/index.ts +97 -0
- package/tsconfig.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
# or
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
+
|
|
19
|
+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
20
|
+
|
|
21
|
+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
+
|
|
23
|
+
## Learn More
|
|
24
|
+
|
|
25
|
+
To learn more about Next.js, take a look at the following resources:
|
|
26
|
+
|
|
27
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
+
|
|
30
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
+
|
|
32
|
+
## Deploy on Vercel
|
|
33
|
+
|
|
34
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
+
|
|
36
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
package/bin/im.js
ADDED
package/next.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "idea-manager",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI 기반 브레인스토밍 → 구조화 → 프롬프트 생성 도구. MCP Server 내장.",
|
|
5
|
+
"keywords": ["brainstorm", "ai", "mcp", "idea", "structuring", "prompt"],
|
|
6
|
+
"author": "navskh",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/navskh/IM.git"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"im": "./bin/im.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin/",
|
|
17
|
+
"src/",
|
|
18
|
+
"public/",
|
|
19
|
+
"next.config.ts",
|
|
20
|
+
"tsconfig.json",
|
|
21
|
+
"postcss.config.mjs",
|
|
22
|
+
"tailwind.config.ts",
|
|
23
|
+
"package.json"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "next dev -p 3456",
|
|
27
|
+
"build": "next build",
|
|
28
|
+
"start": "next start",
|
|
29
|
+
"lint": "eslint",
|
|
30
|
+
"mcp": "tsx src/cli.ts mcp"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.66",
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
35
|
+
"better-sqlite3": "^12.6.2",
|
|
36
|
+
"commander": "^14.0.3",
|
|
37
|
+
"nanoid": "^5.1.6",
|
|
38
|
+
"next": "16.1.6",
|
|
39
|
+
"open": "^11.0.0",
|
|
40
|
+
"react": "19.2.3",
|
|
41
|
+
"react-dom": "19.2.3",
|
|
42
|
+
"tsx": "^4.21.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@tailwindcss/postcss": "^4",
|
|
46
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
47
|
+
"@types/node": "^20",
|
|
48
|
+
"@types/react": "^19",
|
|
49
|
+
"@types/react-dom": "^19",
|
|
50
|
+
"eslint": "^9",
|
|
51
|
+
"eslint-config-next": "16.1.6",
|
|
52
|
+
"tailwindcss": "^4",
|
|
53
|
+
"typescript": "^5"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/public/file.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
package/public/globe.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
package/public/next.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getBrainstorm, updateBrainstorm } from '@/lib/db/queries/brainstorms';
|
|
3
|
+
import { getProject } from '@/lib/db/queries/projects';
|
|
4
|
+
|
|
5
|
+
export async function GET(
|
|
6
|
+
_request: NextRequest,
|
|
7
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
8
|
+
) {
|
|
9
|
+
const { id } = await params;
|
|
10
|
+
const project = getProject(id);
|
|
11
|
+
if (!project) {
|
|
12
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const brainstorm = getBrainstorm(id);
|
|
16
|
+
return NextResponse.json(brainstorm ?? { content: '', version: 0 });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function PUT(
|
|
20
|
+
request: NextRequest,
|
|
21
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
22
|
+
) {
|
|
23
|
+
const { id } = await params;
|
|
24
|
+
const body = await request.json();
|
|
25
|
+
const { content } = body;
|
|
26
|
+
|
|
27
|
+
if (typeof content !== 'string') {
|
|
28
|
+
return NextResponse.json({ error: 'content is required' }, { status: 400 });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const brainstorm = updateBrainstorm(id, content);
|
|
32
|
+
if (!brainstorm) {
|
|
33
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return NextResponse.json(brainstorm);
|
|
37
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getProject } from '@/lib/db/queries/projects';
|
|
3
|
+
import { getConversations } from '@/lib/db/queries/conversations';
|
|
4
|
+
import { getBrainstorm } from '@/lib/db/queries/brainstorms';
|
|
5
|
+
import { handleChatResponse } from '@/lib/ai/chat-responder';
|
|
6
|
+
|
|
7
|
+
export async function GET(
|
|
8
|
+
_request: NextRequest,
|
|
9
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
10
|
+
) {
|
|
11
|
+
const { id } = await params;
|
|
12
|
+
const project = getProject(id);
|
|
13
|
+
if (!project) {
|
|
14
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const conversations = getConversations(id);
|
|
18
|
+
return NextResponse.json(conversations);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function POST(
|
|
22
|
+
request: NextRequest,
|
|
23
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
24
|
+
) {
|
|
25
|
+
const { id } = await params;
|
|
26
|
+
const project = getProject(id);
|
|
27
|
+
if (!project) {
|
|
28
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const brainstorm = getBrainstorm(id);
|
|
32
|
+
if (!brainstorm) {
|
|
33
|
+
return NextResponse.json({ error: 'No brainstorm found' }, { status: 400 });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const body = await request.json();
|
|
37
|
+
const { message } = body;
|
|
38
|
+
|
|
39
|
+
if (!message || typeof message !== 'string' || !message.trim()) {
|
|
40
|
+
return NextResponse.json({ error: 'Message is required' }, { status: 400 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const result = await handleChatResponse(id, brainstorm.id, message.trim());
|
|
45
|
+
return NextResponse.json(result);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const msg = error instanceof Error ? error.message : 'Chat response failed';
|
|
48
|
+
return NextResponse.json({ error: msg }, { status: 500 });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getDb } from '@/lib/db/index';
|
|
3
|
+
import { getPrompt, updatePromptContent } from '@/lib/db/queries/prompts';
|
|
4
|
+
import { generatePromptForItem } from '@/lib/ai/prompter';
|
|
5
|
+
import type { IItem } from '@/types';
|
|
6
|
+
|
|
7
|
+
export async function GET(
|
|
8
|
+
_request: NextRequest,
|
|
9
|
+
{ params }: { params: Promise<{ id: string; itemId: string }> },
|
|
10
|
+
) {
|
|
11
|
+
const { itemId } = await params;
|
|
12
|
+
const prompt = getPrompt(itemId);
|
|
13
|
+
|
|
14
|
+
if (!prompt) {
|
|
15
|
+
return NextResponse.json({ error: 'No prompt found' }, { status: 404 });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return NextResponse.json(prompt);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function POST(
|
|
22
|
+
request: NextRequest,
|
|
23
|
+
{ params }: { params: Promise<{ id: string; itemId: string }> },
|
|
24
|
+
) {
|
|
25
|
+
const { id: projectId, itemId } = await params;
|
|
26
|
+
const db = getDb();
|
|
27
|
+
|
|
28
|
+
const item = db.prepare('SELECT * FROM items WHERE id = ? AND project_id = ?')
|
|
29
|
+
.get(itemId, projectId) as IItem | undefined;
|
|
30
|
+
|
|
31
|
+
if (!item) {
|
|
32
|
+
return NextResponse.json({ error: 'Item not found' }, { status: 404 });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const body = await request.json().catch(() => ({}));
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// If manual content provided, save it directly
|
|
39
|
+
if (body.content && typeof body.content === 'string') {
|
|
40
|
+
const prompt = updatePromptContent(itemId, body.content);
|
|
41
|
+
return NextResponse.json(prompt);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Otherwise, generate with AI
|
|
45
|
+
const prompt = await generatePromptForItem(item);
|
|
46
|
+
return NextResponse.json(prompt);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
const message = error instanceof Error ? error.message : 'Prompt generation failed';
|
|
49
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getDb } from '@/lib/db/index';
|
|
3
|
+
import { updateItem } from '@/lib/db/queries/items';
|
|
4
|
+
import type { IItem, ItemStatus } from '@/types';
|
|
5
|
+
|
|
6
|
+
export async function GET(
|
|
7
|
+
_request: NextRequest,
|
|
8
|
+
{ params }: { params: Promise<{ id: string; itemId: string }> },
|
|
9
|
+
) {
|
|
10
|
+
const { itemId } = await params;
|
|
11
|
+
const db = getDb();
|
|
12
|
+
const item = db.prepare('SELECT * FROM items WHERE id = ?').get(itemId) as IItem | undefined;
|
|
13
|
+
|
|
14
|
+
if (!item) {
|
|
15
|
+
return NextResponse.json({ error: 'Item not found' }, { status: 404 });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return NextResponse.json(item);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function PUT(
|
|
22
|
+
request: NextRequest,
|
|
23
|
+
{ params }: { params: Promise<{ id: string; itemId: string }> },
|
|
24
|
+
) {
|
|
25
|
+
const { id: projectId, itemId } = await params;
|
|
26
|
+
const body = await request.json();
|
|
27
|
+
const db = getDb();
|
|
28
|
+
|
|
29
|
+
const item = db.prepare('SELECT * FROM items WHERE id = ? AND project_id = ?')
|
|
30
|
+
.get(itemId, projectId) as IItem | undefined;
|
|
31
|
+
|
|
32
|
+
if (!item) {
|
|
33
|
+
return NextResponse.json({ error: 'Item not found' }, { status: 404 });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const updates: Parameters<typeof updateItem>[1] = {};
|
|
37
|
+
|
|
38
|
+
if (body.status !== undefined) {
|
|
39
|
+
updates.status = body.status as ItemStatus;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (body.is_locked !== undefined) {
|
|
43
|
+
updates.is_locked = Boolean(body.is_locked);
|
|
44
|
+
|
|
45
|
+
// If locking parent, also lock all children recursively
|
|
46
|
+
if (body.is_locked) {
|
|
47
|
+
lockChildrenRecursive(db, itemId, true);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (body.title !== undefined) updates.title = body.title;
|
|
52
|
+
if (body.description !== undefined) updates.description = body.description;
|
|
53
|
+
if (body.priority !== undefined) updates.priority = body.priority;
|
|
54
|
+
|
|
55
|
+
// Auto-lock on completion
|
|
56
|
+
if (body.status === 'done') {
|
|
57
|
+
updates.is_locked = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const updated = updateItem(itemId, updates);
|
|
61
|
+
return NextResponse.json(updated);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function lockChildrenRecursive(db: ReturnType<typeof getDb>, parentId: string, locked: boolean) {
|
|
65
|
+
const now = new Date().toISOString();
|
|
66
|
+
const children = db.prepare('SELECT id FROM items WHERE parent_id = ?').all(parentId) as { id: string }[];
|
|
67
|
+
|
|
68
|
+
for (const child of children) {
|
|
69
|
+
db.prepare('UPDATE items SET is_locked = ?, updated_at = ? WHERE id = ?')
|
|
70
|
+
.run(locked ? 1 : 0, now, child.id);
|
|
71
|
+
lockChildrenRecursive(db, child.id, locked);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getProject } from '@/lib/db/queries/projects';
|
|
3
|
+
import { getItemTree } from '@/lib/db/queries/items';
|
|
4
|
+
|
|
5
|
+
export async function GET(
|
|
6
|
+
_request: NextRequest,
|
|
7
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
8
|
+
) {
|
|
9
|
+
const { id } = await params;
|
|
10
|
+
const project = getProject(id);
|
|
11
|
+
if (!project) {
|
|
12
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const tree = getItemTree(id);
|
|
16
|
+
return NextResponse.json(tree);
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getProject } from '@/lib/db/queries/projects';
|
|
3
|
+
import { getMemos } from '@/lib/db/queries/memos';
|
|
4
|
+
|
|
5
|
+
export async function GET(
|
|
6
|
+
request: NextRequest,
|
|
7
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
8
|
+
) {
|
|
9
|
+
const { id } = await params;
|
|
10
|
+
const project = getProject(id);
|
|
11
|
+
if (!project) {
|
|
12
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const unresolvedOnly = request.nextUrl.searchParams.get('unresolved') === 'true';
|
|
16
|
+
const memos = getMemos(id, unresolvedOnly);
|
|
17
|
+
return NextResponse.json(memos);
|
|
18
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getProject, updateProject, deleteProject } from '@/lib/db/queries/projects';
|
|
3
|
+
|
|
4
|
+
export async function GET(
|
|
5
|
+
_request: NextRequest,
|
|
6
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
7
|
+
) {
|
|
8
|
+
const { id } = await params;
|
|
9
|
+
const project = getProject(id);
|
|
10
|
+
if (!project) {
|
|
11
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
12
|
+
}
|
|
13
|
+
return NextResponse.json(project);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function PUT(
|
|
17
|
+
request: NextRequest,
|
|
18
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
19
|
+
) {
|
|
20
|
+
const { id } = await params;
|
|
21
|
+
const body = await request.json();
|
|
22
|
+
const project = updateProject(id, body);
|
|
23
|
+
if (!project) {
|
|
24
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
25
|
+
}
|
|
26
|
+
return NextResponse.json(project);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function DELETE(
|
|
30
|
+
_request: NextRequest,
|
|
31
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
32
|
+
) {
|
|
33
|
+
const { id } = await params;
|
|
34
|
+
const deleted = deleteProject(id);
|
|
35
|
+
if (!deleted) {
|
|
36
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
37
|
+
}
|
|
38
|
+
return NextResponse.json({ success: true });
|
|
39
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getProject } from '@/lib/db/queries/projects';
|
|
3
|
+
import { getBrainstorm } from '@/lib/db/queries/brainstorms';
|
|
4
|
+
import { structureWithChat } from '@/lib/ai/structurer';
|
|
5
|
+
|
|
6
|
+
export async function POST(
|
|
7
|
+
_request: NextRequest,
|
|
8
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
9
|
+
) {
|
|
10
|
+
const { id } = await params;
|
|
11
|
+
const project = getProject(id);
|
|
12
|
+
if (!project) {
|
|
13
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const brainstorm = getBrainstorm(id);
|
|
17
|
+
if (!brainstorm || !brainstorm.content.trim()) {
|
|
18
|
+
return NextResponse.json({ error: 'No brainstorm content to structure' }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const result = await structureWithChat(id, brainstorm.id, brainstorm.content);
|
|
23
|
+
return NextResponse.json(result);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
const message = error instanceof Error ? error.message : 'AI structuring failed';
|
|
26
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { listProjects, createProject } from '@/lib/db/queries/projects';
|
|
3
|
+
|
|
4
|
+
export async function GET() {
|
|
5
|
+
const projects = listProjects();
|
|
6
|
+
return NextResponse.json(projects);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function POST(request: NextRequest) {
|
|
10
|
+
const body = await request.json();
|
|
11
|
+
const { name, description } = body;
|
|
12
|
+
|
|
13
|
+
if (!name || typeof name !== 'string') {
|
|
14
|
+
return NextResponse.json({ error: 'name is required' }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const project = createProject(name, description || '');
|
|
18
|
+
return NextResponse.json(project, { status: 201 });
|
|
19
|
+
}
|
|
Binary file
|