idea-manager 0.4.0 → 0.5.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.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/src/app/projects/[id]/page.tsx +0 -4
  3. package/src/components/brainstorm/Editor.tsx +1 -84
  4. package/src/lib/ai/client.ts +0 -123
  5. package/src/lib/db/schema.ts +0 -70
  6. package/src/types/index.ts +0 -90
  7. package/src/app/api/projects/[id]/cleanup/route.ts +0 -32
  8. package/src/app/api/projects/[id]/conversations/route.ts +0 -50
  9. package/src/app/api/projects/[id]/items/[itemId]/prompt/route.ts +0 -51
  10. package/src/app/api/projects/[id]/items/[itemId]/refine/route.ts +0 -36
  11. package/src/app/api/projects/[id]/items/[itemId]/route.ts +0 -95
  12. package/src/app/api/projects/[id]/items/route.ts +0 -67
  13. package/src/app/api/projects/[id]/memos/route.ts +0 -18
  14. package/src/app/api/projects/[id]/scan/route.ts +0 -73
  15. package/src/app/api/projects/[id]/scan/stream/route.ts +0 -112
  16. package/src/app/api/projects/[id]/structure/route.ts +0 -59
  17. package/src/app/api/projects/[id]/structure/stream/route.ts +0 -157
  18. package/src/components/ScanPanel.tsx +0 -743
  19. package/src/components/brainstorm/MemoPin.tsx +0 -117
  20. package/src/components/tree/CardView.tsx +0 -206
  21. package/src/components/tree/ItemDetail.tsx +0 -196
  22. package/src/components/tree/LockToggle.tsx +0 -23
  23. package/src/components/tree/RefinePopover.tsx +0 -157
  24. package/src/components/tree/StatusBadge.tsx +0 -32
  25. package/src/components/tree/TreeNode.tsx +0 -227
  26. package/src/components/tree/TreeView.tsx +0 -304
  27. package/src/lib/ai/chat-responder.ts +0 -71
  28. package/src/lib/ai/cleanup.ts +0 -87
  29. package/src/lib/ai/prompter.ts +0 -78
  30. package/src/lib/ai/refiner.ts +0 -128
  31. package/src/lib/ai/structurer.ts +0 -403
  32. package/src/lib/db/queries/context.ts +0 -76
  33. package/src/lib/db/queries/conversations.ts +0 -46
  34. package/src/lib/db/queries/items.ts +0 -268
  35. package/src/lib/db/queries/memos.ts +0 -66
  36. package/src/lib/db/queries/prompts.ts +0 -68
  37. package/src/lib/scanner.ts +0 -573
  38. package/src/lib/task-store.ts +0 -97
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "idea-manager",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "AI 기반 브레인스토밍 → 구조화 → 프롬프트 생성 도구. MCP Server 내장.",
5
5
  "keywords": [
6
6
  "brainstorm",
@@ -334,10 +334,6 @@ function WorkspaceInner({ id }: { id: string }) {
334
334
  <div style={{ width: leftWidth }} className="border-r border-border flex flex-col flex-shrink-0">
335
335
  <Editor
336
336
  projectId={id}
337
- onContentChange={() => {}}
338
- onSendMessage={() => {}}
339
- memos={[]}
340
- chatLoading={false}
341
337
  onCollapse={() => setShowBrainstorm(false)}
342
338
  />
343
339
  </div>
@@ -1,39 +1,18 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useRef, useCallback } from 'react';
4
- import MemoPin from './MemoPin';
5
-
6
- interface Memo {
7
- id: string;
8
- anchor_text: string;
9
- question: string;
10
- is_resolved: boolean;
11
- }
12
4
 
13
5
  interface EditorProps {
14
6
  projectId: string;
15
- onContentChange: (content: string) => void;
16
- onSendMessage: (message: string) => void;
17
- memos?: Memo[];
18
- chatLoading?: boolean;
19
7
  onCollapse?: () => void;
20
8
  }
21
9
 
22
- interface PinPosition {
23
- memo: Memo;
24
- top: number;
25
- left: number;
26
- }
27
-
28
- export default function Editor({ projectId, onContentChange, onSendMessage, memos = [], chatLoading, onCollapse }: EditorProps) {
10
+ export default function Editor({ projectId, onCollapse }: EditorProps) {
29
11
  const [content, setContent] = useState('');
30
12
  const [saving, setSaving] = useState(false);
31
13
  const [loaded, setLoaded] = useState(false);
32
- const [pinPositions, setPinPositions] = useState<PinPosition[]>([]);
33
14
  const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
34
- const structureTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
35
15
  const textareaRef = useRef<HTMLTextAreaElement>(null);
36
- const overlayRef = useRef<HTMLDivElement>(null);
37
16
 
38
17
  // Load brainstorm content
39
18
  useEffect(() => {
@@ -46,37 +25,6 @@ export default function Editor({ projectId, onContentChange, onSendMessage, memo
46
25
  load();
47
26
  }, [projectId]);
48
27
 
49
- // Calculate pin positions when memos or content change
50
- useEffect(() => {
51
- if (!textareaRef.current || !content) {
52
- setPinPositions([]);
53
- return;
54
- }
55
-
56
- const textarea = textareaRef.current;
57
- const unresolvedMemos = memos.filter(m => !m.is_resolved);
58
- const positions: PinPosition[] = [];
59
-
60
- for (const memo of unresolvedMemos) {
61
- const idx = content.indexOf(memo.anchor_text);
62
- if (idx === -1) continue;
63
-
64
- // Calculate approximate position based on character index
65
- const textBefore = content.substring(0, idx);
66
- const lines = textBefore.split('\n');
67
- const lineNumber = lines.length - 1;
68
- const lineHeight = parseFloat(getComputedStyle(textarea).lineHeight) || 22;
69
- const paddingTop = parseFloat(getComputedStyle(textarea).paddingTop) || 16;
70
-
71
- const top = paddingTop + lineNumber * lineHeight;
72
- const left = textarea.clientWidth - 28;
73
-
74
- positions.push({ memo, top, left });
75
- }
76
-
77
- setPinPositions(positions);
78
- }, [memos, content]);
79
-
80
28
  const saveContent = useCallback(async (text: string) => {
81
29
  setSaving(true);
82
30
  await fetch(`/api/projects/${projectId}/brainstorm`, {
@@ -96,21 +44,6 @@ export default function Editor({ projectId, onContentChange, onSendMessage, memo
96
44
  saveTimerRef.current = setTimeout(() => {
97
45
  saveContent(newContent);
98
46
  }, 1000);
99
-
100
- // Trigger AI structuring with 3s debounce
101
- if (structureTimerRef.current) clearTimeout(structureTimerRef.current);
102
- if (newContent.trim()) {
103
- structureTimerRef.current = setTimeout(() => {
104
- onContentChange(newContent);
105
- }, 3000);
106
- }
107
- };
108
-
109
- // Sync scroll between textarea and overlay
110
- const handleScroll = () => {
111
- if (textareaRef.current && overlayRef.current) {
112
- overlayRef.current.scrollTop = textareaRef.current.scrollTop;
113
- }
114
47
  };
115
48
 
116
49
  if (!loaded) {
@@ -145,7 +78,6 @@ export default function Editor({ projectId, onContentChange, onSendMessage, memo
145
78
  ref={textareaRef}
146
79
  value={content}
147
80
  onChange={handleChange}
148
- onScroll={handleScroll}
149
81
  placeholder={`자유롭게 아이디어를 적어보세요...
150
82
 
151
83
  예시:
@@ -158,21 +90,6 @@ export default function Editor({ projectId, onContentChange, onSendMessage, memo
158
90
  placeholder:text-muted-foreground/40 font-mono text-sm leading-relaxed"
159
91
  spellCheck={false}
160
92
  />
161
- {pinPositions.length > 0 && (
162
- <div ref={overlayRef} className="memo-overlay">
163
- {pinPositions.map((pin) => (
164
- <MemoPin
165
- key={pin.memo.id}
166
- question={pin.memo.question}
167
- anchorText={pin.memo.anchor_text}
168
- top={pin.top}
169
- left={pin.left}
170
- loading={chatLoading}
171
- onSendMessage={onSendMessage}
172
- />
173
- ))}
174
- </div>
175
- )}
176
93
  </div>
177
94
  </div>
178
95
  );
@@ -1,14 +1,4 @@
1
1
  import { spawn } from 'node:child_process';
2
- import type { IStructureWithQuestions } from '@/types';
3
-
4
- export interface IStructuredItem {
5
- title: string;
6
- description: string;
7
- item_type: 'feature' | 'task' | 'bug' | 'idea' | 'note';
8
- priority: 'high' | 'medium' | 'low';
9
- status?: 'pending' | 'in_progress' | 'done';
10
- children?: IStructuredItem[];
11
- }
12
2
 
13
3
  const CLI_PATH = 'claude';
14
4
  const DEFAULT_ARGS = ['--dangerously-skip-permissions'];
@@ -122,116 +112,3 @@ export function runClaude(prompt: string, onText?: OnTextChunk, onRawEvent?: OnR
122
112
  });
123
113
  });
124
114
  }
125
-
126
- /**
127
- * Run Claude for free-form markdown analysis (not JSON).
128
- * Used for building the hub document in multi-agent analysis.
129
- */
130
- export function runAnalysis(prompt: string, onText?: OnTextChunk, onRawEvent?: OnRawEvent): Promise<string> {
131
- return runClaude(prompt, onText, onRawEvent);
132
- }
133
-
134
- export function extractJson(text: string, type: 'array' | 'object'): string {
135
- // Strip markdown fences
136
- text = text.replace(/```(?:json)?\s*/g, '').replace(/```\s*/g, '').trim();
137
- const pattern = type === 'array' ? /\[[\s\S]*\]/ : /\{[\s\S]*\}/;
138
- const match = text.match(pattern);
139
- if (!match) {
140
- throw new Error(`AI did not return valid JSON ${type}`);
141
- }
142
- return match[0];
143
- }
144
-
145
- export async function runStructure(brainstormContent: string, projectContext?: string, onText?: OnTextChunk, onRawEvent?: OnRawEvent): Promise<IStructuredItem[]> {
146
- const systemPrompt = `You are a JSON-only structuring machine. You NEVER respond with text, explanations, or conversation.
147
- You ALWAYS output ONLY a raw JSON array, nothing else.
148
-
149
- Your job: convert ANY input text into a structured JSON array of items.
150
- Even if the input seems like a greeting or conversation, extract the implicit intent and structure it.
151
-
152
- Schema per item:
153
- { "title": string, "description": string, "item_type": "feature"|"task"|"bug"|"idea"|"note", "priority": "high"|"medium"|"low", "status": "pending"|"in_progress"|"done", "children": [same schema] }
154
-
155
- Rules:
156
- - Output MUST start with [ and end with ]
157
- - No markdown fences, no explanation, no text before or after the JSON
158
- - Keep titles concise (under 50 chars)
159
- - Group related ideas under parent items
160
- - If input is vague, interpret it as best you can and create at least 1 item
161
- - IMPORTANT: When project source code context is provided, judge the status based on the actual code:
162
- - "done": feature/task is fully implemented in the source code
163
- - "in_progress": partially implemented or has TODOs
164
- - "pending": not yet started or only planned
165
- - If no source code context is provided, default status to "pending"`;
166
-
167
- const ctxBlock = projectContext ? `\n\n프로젝트 문서 컨텍스트:\n${projectContext}` : '';
168
- const prompt = `${systemPrompt}\n\nAnalyze this brainstorming content and structure it into a JSON tree:\n\n${brainstormContent}${ctxBlock}`;
169
-
170
- const resultText = await runClaude(prompt, onText, onRawEvent);
171
- const json = extractJson(resultText, 'array');
172
- return JSON.parse(json) as IStructuredItem[];
173
- }
174
-
175
- export async function runStructureWithQuestions(
176
- brainstormContent: string,
177
- conversationHistory: { role: 'assistant' | 'user'; content: string }[],
178
- projectContext?: string,
179
- onText?: OnTextChunk,
180
- onRawEvent?: OnRawEvent,
181
- existingStructure?: string,
182
- ): Promise<IStructureWithQuestions> {
183
- const systemPrompt = `You are an AI assistant that structures brainstorming content AND identifies ambiguous areas.
184
- You ALWAYS output ONLY a raw JSON object (not an array), nothing else.
185
-
186
- Your job:
187
- 1. Convert the brainstorming text into a structured JSON tree of items
188
- 2. Identify 0-5 areas where the brainstorming is ambiguous or could benefit from clarification
189
- 3. Consider the conversation history to avoid repeating questions already answered
190
- 4. If existing structured items are provided, UPDATE them rather than creating duplicates
191
-
192
- Output schema:
193
- {
194
- "items": [{ "title": string, "description": string, "item_type": "feature"|"task"|"bug"|"idea"|"note", "priority": "high"|"medium"|"low", "status": "pending"|"in_progress"|"done", "children": [same] }],
195
- "questions": [{ "anchor_text": string, "question": string }]
196
- }
197
-
198
- Rules:
199
- - Output MUST be a JSON object with "items" and "questions" keys
200
- - No markdown fences, no explanation, no text before or after the JSON
201
- - Keep titles concise (under 50 chars)
202
- - Group related ideas under parent items
203
- - NEVER create duplicate items. If the existing structure already has an item for a concept, update it instead of adding a new one
204
- - Merge similar items together. The output should be a clean, deduplicated structure
205
- - Preserve the status of existing items unless the brainstorming text explicitly changes them
206
- - questions[].anchor_text MUST be an exact substring from the brainstorming content (5-20 chars)
207
- - questions[].question should be a helpful Korean question asking for clarification
208
- - Generate 0-5 questions. Skip questions already answered in conversation history.
209
- - If the brainstorming is clear enough, return an empty questions array
210
- - All questions MUST be in Korean
211
- - If project documentation context is provided, use it to make more informed structuring decisions (e.g., matching tech stack, conventions, existing patterns)
212
- - IMPORTANT: When project source code context is provided, judge the status based on the actual code:
213
- - "done": feature/task is fully implemented in the source code
214
- - "in_progress": partially implemented or has TODOs
215
- - "pending": not yet started or only planned
216
- - If no source code context is provided, default status to "pending"`;
217
-
218
- let historyContext = '';
219
- if (conversationHistory.length > 0) {
220
- historyContext = '\n\n이전 대화:\n' + conversationHistory
221
- .map(m => `${m.role === 'user' ? '사용자' : 'AI'}: ${m.content}`)
222
- .join('\n');
223
- }
224
-
225
- const ctxBlock = projectContext ? `\n\n프로젝트 문서 컨텍스트:\n${projectContext}` : '';
226
- const existingBlock = existingStructure ? `\n\n현재 구조화된 항목 (중복 생성하지 말고 업데이트하세요):\n${existingStructure}` : '';
227
- const prompt = `${systemPrompt}\n\n다음 브레인스토밍 내용을 분석하고 구조화하세요:\n\n${brainstormContent}${historyContext}${existingBlock}${ctxBlock}`;
228
-
229
- const resultText = await runClaude(prompt, onText, onRawEvent);
230
- const json = extractJson(resultText, 'object');
231
- const parsed = JSON.parse(json);
232
-
233
- return {
234
- items: parsed.items || [],
235
- questions: parsed.questions || [],
236
- } as IStructureWithQuestions;
237
- }
@@ -21,78 +21,8 @@ export function initSchema(db: Database.Database): void {
21
21
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
22
22
  );
23
23
 
24
- CREATE TABLE IF NOT EXISTS items (
25
- id TEXT PRIMARY KEY,
26
- project_id TEXT NOT NULL,
27
- brainstorm_id TEXT,
28
- parent_id TEXT,
29
- title TEXT NOT NULL,
30
- description TEXT NOT NULL DEFAULT '',
31
- item_type TEXT NOT NULL DEFAULT 'feature',
32
- priority TEXT NOT NULL DEFAULT 'medium',
33
- status TEXT NOT NULL DEFAULT 'pending',
34
- is_locked INTEGER NOT NULL DEFAULT 1,
35
- is_pinned INTEGER NOT NULL DEFAULT 1,
36
- sort_order INTEGER NOT NULL DEFAULT 0,
37
- metadata TEXT,
38
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
39
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
40
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
41
- FOREIGN KEY (brainstorm_id) REFERENCES brainstorms(id),
42
- FOREIGN KEY (parent_id) REFERENCES items(id)
43
- );
44
-
45
- CREATE TABLE IF NOT EXISTS conversations (
46
- id TEXT PRIMARY KEY,
47
- project_id TEXT NOT NULL,
48
- role TEXT NOT NULL CHECK(role IN ('assistant', 'user')),
49
- content TEXT NOT NULL,
50
- metadata TEXT,
51
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
52
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
53
- );
54
-
55
- CREATE TABLE IF NOT EXISTS memos (
56
- id TEXT PRIMARY KEY,
57
- project_id TEXT NOT NULL,
58
- conversation_id TEXT,
59
- anchor_text TEXT NOT NULL,
60
- question TEXT NOT NULL,
61
- is_resolved INTEGER NOT NULL DEFAULT 0,
62
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
63
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
64
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
65
- FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE SET NULL
66
- );
67
-
68
- CREATE TABLE IF NOT EXISTS prompts (
69
- id TEXT PRIMARY KEY,
70
- project_id TEXT NOT NULL,
71
- item_id TEXT NOT NULL,
72
- content TEXT NOT NULL,
73
- prompt_type TEXT NOT NULL DEFAULT 'auto',
74
- version INTEGER NOT NULL DEFAULT 1,
75
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
76
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
77
- FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
78
- );
79
-
80
- CREATE TABLE IF NOT EXISTS project_context (
81
- id TEXT PRIMARY KEY,
82
- project_id TEXT NOT NULL,
83
- file_path TEXT NOT NULL,
84
- content TEXT NOT NULL,
85
- scanned_at TEXT NOT NULL DEFAULT (datetime('now')),
86
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
87
- );
88
24
  `);
89
25
 
90
- // Migrations for existing DBs
91
- const itemCols = db.prepare("PRAGMA table_info(items)").all() as { name: string }[];
92
- if (!itemCols.some(c => c.name === 'is_pinned')) {
93
- db.exec("ALTER TABLE items ADD COLUMN is_pinned INTEGER NOT NULL DEFAULT 1");
94
- }
95
-
96
26
  const projCols = db.prepare("PRAGMA table_info(projects)").all() as { name: string }[];
97
27
  if (!projCols.some(c => c.name === 'project_path')) {
98
28
  db.exec("ALTER TABLE projects ADD COLUMN project_path TEXT");
@@ -8,14 +8,6 @@ export interface IProject {
8
8
  updated_at: string;
9
9
  }
10
10
 
11
- export interface IProjectContext {
12
- id: string;
13
- project_id: string;
14
- file_path: string;
15
- content: string;
16
- scanned_at: string;
17
- }
18
-
19
11
  export interface IBrainstorm {
20
12
  id: string;
21
13
  project_id: string;
@@ -25,75 +17,7 @@ export interface IBrainstorm {
25
17
  updated_at: string;
26
18
  }
27
19
 
28
- export type ItemType = 'feature' | 'task' | 'bug' | 'idea' | 'note';
29
- export type ItemStatus = 'pending' | 'in_progress' | 'done';
30
20
  export type ItemPriority = 'high' | 'medium' | 'low';
31
-
32
- export interface IItem {
33
- id: string;
34
- project_id: string;
35
- brainstorm_id: string | null;
36
- parent_id: string | null;
37
- title: string;
38
- description: string;
39
- item_type: ItemType;
40
- priority: ItemPriority;
41
- status: ItemStatus;
42
- is_locked: boolean;
43
- is_pinned: boolean;
44
- sort_order: number;
45
- metadata: string | null;
46
- created_at: string;
47
- updated_at: string;
48
- }
49
-
50
- export interface IItemTree extends IItem {
51
- children: IItemTree[];
52
- }
53
-
54
- export interface IStructureResult {
55
- items: {
56
- id?: string;
57
- parent_id: string | null;
58
- title: string;
59
- description: string;
60
- item_type: ItemType;
61
- priority: ItemPriority;
62
- children?: IStructureResult['items'];
63
- }[];
64
- }
65
-
66
- export interface IConversation {
67
- id: string;
68
- project_id: string;
69
- role: 'assistant' | 'user';
70
- content: string;
71
- metadata: string | null;
72
- created_at: string;
73
- }
74
-
75
- export interface IMemo {
76
- id: string;
77
- project_id: string;
78
- conversation_id: string | null;
79
- anchor_text: string;
80
- question: string;
81
- is_resolved: boolean;
82
- created_at: string;
83
- updated_at: string;
84
- }
85
-
86
- export interface IPrompt {
87
- id: string;
88
- project_id: string;
89
- item_id: string;
90
- content: string;
91
- prompt_type: 'auto' | 'manual';
92
- version: number;
93
- created_at: string;
94
- }
95
-
96
- // v2 types
97
21
  export type TaskStatus = 'idea' | 'writing' | 'submitted' | 'testing' | 'done' | 'problem';
98
22
 
99
23
  export interface ISubProject {
@@ -147,17 +71,3 @@ export interface ISubProjectWithStats extends ISubProject {
147
71
  last_activity: string | null;
148
72
  preview_tasks: { title: string; status: TaskStatus }[];
149
73
  }
150
-
151
- export interface IStructureWithQuestions {
152
- items: {
153
- title: string;
154
- description: string;
155
- item_type: ItemType;
156
- priority: ItemPriority;
157
- children?: IStructureWithQuestions['items'];
158
- }[];
159
- questions: {
160
- anchor_text: string;
161
- question: string;
162
- }[];
163
- }
@@ -1,32 +0,0 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { getProject } from '@/lib/db/queries/projects';
3
- import { getItemTree } from '@/lib/db/queries/items';
4
- import { getBrainstorm } from '@/lib/db/queries/brainstorms';
5
- import { cleanupItems } from '@/lib/ai/cleanup';
6
-
7
- export async function POST(
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 items = getItemTree(id);
18
- if (items.length === 0) {
19
- return NextResponse.json({ items: [], changed: false });
20
- }
21
-
22
- const brainstorm = getBrainstorm(id);
23
- const brainstormContent = brainstorm?.content || '';
24
-
25
- try {
26
- const result = await cleanupItems(id, brainstorm?.id || '', items, brainstormContent);
27
- return NextResponse.json(result);
28
- } catch (error) {
29
- const message = error instanceof Error ? error.message : 'Cleanup failed';
30
- return NextResponse.json({ error: message }, { status: 500 });
31
- }
32
- }
@@ -1,50 +0,0 @@
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
- }
@@ -1,51 +0,0 @@
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
- }
@@ -1,36 +0,0 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { getItem } from '@/lib/db/queries/items';
3
- import { getProject } from '@/lib/db/queries/projects';
4
- import { refineItem } from '@/lib/ai/refiner';
5
-
6
- export async function POST(
7
- request: NextRequest,
8
- { params }: { params: Promise<{ id: string; itemId: string }> },
9
- ) {
10
- const { id: projectId, itemId } = await params;
11
-
12
- const project = getProject(projectId);
13
- if (!project) {
14
- return NextResponse.json({ error: 'Project not found' }, { status: 404 });
15
- }
16
-
17
- const item = getItem(itemId);
18
- if (!item || item.project_id !== projectId) {
19
- return NextResponse.json({ error: 'Item not found' }, { status: 404 });
20
- }
21
-
22
- const body = await request.json();
23
- const { message } = body;
24
-
25
- if (!message || typeof message !== 'string' || !message.trim()) {
26
- return NextResponse.json({ error: 'Message is required' }, { status: 400 });
27
- }
28
-
29
- try {
30
- const result = await refineItem(item, message.trim());
31
- return NextResponse.json(result);
32
- } catch (error) {
33
- const msg = error instanceof Error ? error.message : 'Refine failed';
34
- return NextResponse.json({ error: msg }, { status: 500 });
35
- }
36
- }