idea-manager 0.2.0 → 0.3.1
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 +33 -41
- package/next.config.ts +0 -1
- package/package.json +2 -2
- package/{src/app/icon.svg → public/favicon.svg} +2 -2
- package/src/app/api/filesystem/route.ts +49 -0
- package/src/app/api/projects/[id]/cleanup/route.ts +32 -0
- package/src/app/api/projects/[id]/items/[itemId]/refine/route.ts +36 -0
- package/src/app/api/projects/[id]/items/[itemId]/route.ts +23 -1
- package/src/app/api/projects/[id]/items/route.ts +51 -1
- package/src/app/api/projects/[id]/scan/route.ts +73 -0
- package/src/app/api/projects/[id]/scan/stream/route.ts +112 -0
- package/src/app/api/projects/[id]/structure/route.ts +34 -3
- package/src/app/api/projects/[id]/structure/stream/route.ts +157 -0
- package/src/app/api/projects/[id]/sub-projects/[subId]/route.ts +39 -0
- package/src/app/api/projects/[id]/sub-projects/[subId]/tasks/[taskId]/chat/route.ts +60 -0
- package/src/app/api/projects/[id]/sub-projects/[subId]/tasks/[taskId]/prompt/route.ts +26 -0
- package/src/app/api/projects/[id]/sub-projects/[subId]/tasks/[taskId]/route.ts +39 -0
- package/src/app/api/projects/[id]/sub-projects/[subId]/tasks/route.ts +33 -0
- package/src/app/api/projects/[id]/sub-projects/route.ts +31 -0
- package/src/app/api/projects/route.ts +1 -1
- package/src/app/globals.css +465 -5
- package/src/app/layout.tsx +3 -0
- package/src/app/page.tsx +260 -88
- package/src/app/projects/[id]/page.tsx +366 -183
- package/src/cli.ts +10 -10
- package/src/components/DirectoryPicker.tsx +137 -0
- package/src/components/ScanPanel.tsx +743 -0
- package/src/components/brainstorm/Editor.tsx +20 -4
- package/src/components/brainstorm/MemoPin.tsx +91 -5
- package/src/components/dashboard/SubProjectCard.tsx +76 -0
- package/src/components/dashboard/TabBar.tsx +42 -0
- package/src/components/task/ProjectTree.tsx +223 -0
- package/src/components/task/PromptEditor.tsx +107 -0
- package/src/components/task/StatusFlow.tsx +43 -0
- package/src/components/task/TaskChat.tsx +134 -0
- package/src/components/task/TaskDetail.tsx +205 -0
- package/src/components/task/TaskList.tsx +119 -0
- package/src/components/tree/CardView.tsx +206 -0
- package/src/components/tree/RefinePopover.tsx +157 -0
- package/src/components/tree/TreeNode.tsx +147 -38
- package/src/components/tree/TreeView.tsx +270 -26
- package/src/components/ui/ConfirmDialog.tsx +88 -0
- package/src/lib/ai/chat-responder.ts +4 -2
- package/src/lib/ai/cleanup.ts +87 -0
- package/src/lib/ai/client.ts +175 -58
- package/src/lib/ai/prompter.ts +19 -24
- package/src/lib/ai/refiner.ts +128 -0
- package/src/lib/ai/structurer.ts +340 -11
- package/src/lib/db/queries/context.ts +76 -0
- package/src/lib/db/queries/items.ts +133 -12
- package/src/lib/db/queries/projects.ts +12 -8
- package/src/lib/db/queries/sub-projects.ts +122 -0
- package/src/lib/db/queries/task-conversations.ts +27 -0
- package/src/lib/db/queries/task-prompts.ts +32 -0
- package/src/lib/db/queries/tasks.ts +133 -0
- package/src/lib/db/schema.ts +75 -0
- package/src/lib/mcp/server.ts +38 -39
- package/src/lib/mcp/tools.ts +47 -45
- package/src/lib/scanner.ts +573 -0
- package/src/lib/task-store.ts +97 -0
- package/src/types/index.ts +65 -0
package/src/cli.ts
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { startMcpServer } from '@/lib/mcp/server';
|
|
5
5
|
import { listProjects, getProject } from '@/lib/db/queries/projects';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { getSubProjects } from '@/lib/db/queries/sub-projects';
|
|
7
|
+
import { getTasksByProject, updateTask } from '@/lib/db/queries/tasks';
|
|
8
|
+
import { getTaskPrompt } from '@/lib/db/queries/task-prompts';
|
|
8
9
|
import type { McpToolContext } from '@/lib/mcp/tools';
|
|
9
10
|
import { spawn } from 'child_process';
|
|
10
11
|
import path from 'path';
|
|
@@ -18,8 +19,8 @@ const program = new Command();
|
|
|
18
19
|
|
|
19
20
|
program
|
|
20
21
|
.name('im')
|
|
21
|
-
.description('Idea Manager -
|
|
22
|
-
.version('0.
|
|
22
|
+
.description('Idea Manager v2 - Brainstorming to structured tasks with prompts')
|
|
23
|
+
.version('0.2.0');
|
|
23
24
|
|
|
24
25
|
program
|
|
25
26
|
.command('mcp')
|
|
@@ -28,10 +29,10 @@ program
|
|
|
28
29
|
const ctx: McpToolContext = {
|
|
29
30
|
listProjects,
|
|
30
31
|
getProject,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
getSubProjects,
|
|
33
|
+
getTasksByProject,
|
|
34
|
+
getTaskPrompt,
|
|
35
|
+
updateTask: (id, data) => updateTask(id, data as Parameters<typeof updateTask>[1]),
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
await startMcpServer(ctx);
|
|
@@ -43,7 +44,7 @@ program
|
|
|
43
44
|
.option('-p, --port <port>', 'Port number', '3456')
|
|
44
45
|
.action(async (opts) => {
|
|
45
46
|
const port = opts.port;
|
|
46
|
-
console.log(`\n IM -
|
|
47
|
+
console.log(`\n IM - Idea Manager v2`);
|
|
47
48
|
console.log(` Starting on http://localhost:${port}\n`);
|
|
48
49
|
|
|
49
50
|
const nextBin = path.join(PKG_ROOT, 'node_modules', '.bin', 'next');
|
|
@@ -58,7 +59,6 @@ program
|
|
|
58
59
|
process.exit(1);
|
|
59
60
|
});
|
|
60
61
|
|
|
61
|
-
// 서버 시작 후 브라우저 오픈
|
|
62
62
|
setTimeout(async () => {
|
|
63
63
|
try {
|
|
64
64
|
const open = (await import('open')).default;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
interface DirEntry {
|
|
6
|
+
name: string;
|
|
7
|
+
path: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface DirInfo {
|
|
11
|
+
current: string;
|
|
12
|
+
parent: string | null;
|
|
13
|
+
dirs: DirEntry[];
|
|
14
|
+
isProject: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface DirectoryPickerProps {
|
|
18
|
+
onSelect: (path: string) => void;
|
|
19
|
+
onCancel: () => void;
|
|
20
|
+
initialPath?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function DirectoryPicker({ onSelect, onCancel, initialPath }: DirectoryPickerProps) {
|
|
24
|
+
const [dirInfo, setDirInfo] = useState<DirInfo | null>(null);
|
|
25
|
+
const [loading, setLoading] = useState(true);
|
|
26
|
+
const [error, setError] = useState<string | null>(null);
|
|
27
|
+
|
|
28
|
+
const loadDir = useCallback(async (dirPath?: string) => {
|
|
29
|
+
setLoading(true);
|
|
30
|
+
setError(null);
|
|
31
|
+
try {
|
|
32
|
+
const params = dirPath ? `?path=${encodeURIComponent(dirPath)}` : '';
|
|
33
|
+
const res = await fetch(`/api/filesystem${params}`);
|
|
34
|
+
if (res.ok) {
|
|
35
|
+
setDirInfo(await res.json());
|
|
36
|
+
} else {
|
|
37
|
+
const data = await res.json();
|
|
38
|
+
setError(data.error || '불러오기 실패');
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
setError('불러오기 실패');
|
|
42
|
+
} finally {
|
|
43
|
+
setLoading(false);
|
|
44
|
+
}
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
loadDir(initialPath);
|
|
49
|
+
}, [loadDir, initialPath]);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={onCancel}>
|
|
53
|
+
<div
|
|
54
|
+
className="bg-card border border-border rounded-lg shadow-xl w-[520px] max-h-[70vh] flex flex-col"
|
|
55
|
+
onClick={e => e.stopPropagation()}
|
|
56
|
+
>
|
|
57
|
+
{/* Header */}
|
|
58
|
+
<div className="flex items-center justify-between px-4 py-3 border-b border-border">
|
|
59
|
+
<h3 className="text-sm font-semibold">프로젝트 폴더 선택</h3>
|
|
60
|
+
<button onClick={onCancel} className="text-muted-foreground hover:text-foreground text-lg leading-none">×</button>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
{/* Current path */}
|
|
64
|
+
<div className="px-4 py-2 border-b border-border bg-muted">
|
|
65
|
+
<div className="flex items-center gap-2">
|
|
66
|
+
<span className="text-xs text-muted-foreground shrink-0">경로:</span>
|
|
67
|
+
<span className="text-xs font-mono truncate flex-1" title={dirInfo?.current}>
|
|
68
|
+
{dirInfo?.current || '...'}
|
|
69
|
+
</span>
|
|
70
|
+
{dirInfo?.isProject && (
|
|
71
|
+
<span className="text-xs text-success shrink-0 font-medium">프로젝트 감지</span>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{/* Directory list */}
|
|
77
|
+
<div className="flex-1 overflow-y-auto min-h-0">
|
|
78
|
+
{loading ? (
|
|
79
|
+
<div className="p-8 text-center text-muted-foreground text-sm">불러오는 중...</div>
|
|
80
|
+
) : error ? (
|
|
81
|
+
<div className="p-8 text-center text-destructive text-sm">{error}</div>
|
|
82
|
+
) : (
|
|
83
|
+
<div className="py-1">
|
|
84
|
+
{/* Parent directory */}
|
|
85
|
+
{dirInfo?.parent && (
|
|
86
|
+
<button
|
|
87
|
+
onClick={() => loadDir(dirInfo.parent!)}
|
|
88
|
+
className="w-full text-left px-4 py-2 text-sm hover:bg-muted transition-colors
|
|
89
|
+
flex items-center gap-2 text-muted-foreground"
|
|
90
|
+
>
|
|
91
|
+
<span>↑</span>
|
|
92
|
+
<span>..</span>
|
|
93
|
+
</button>
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
{/* Subdirectories */}
|
|
97
|
+
{dirInfo?.dirs.length === 0 && (
|
|
98
|
+
<div className="px-4 py-6 text-center text-muted-foreground text-xs">
|
|
99
|
+
하위 폴더가 없습니다
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
{dirInfo?.dirs.map(dir => (
|
|
103
|
+
<button
|
|
104
|
+
key={dir.path}
|
|
105
|
+
onClick={() => loadDir(dir.path)}
|
|
106
|
+
className="w-full text-left px-4 py-2 text-sm hover:bg-muted transition-colors
|
|
107
|
+
flex items-center gap-2"
|
|
108
|
+
>
|
|
109
|
+
<span className="text-muted-foreground">📁</span>
|
|
110
|
+
<span>{dir.name}</span>
|
|
111
|
+
</button>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
{/* Footer */}
|
|
118
|
+
<div className="flex items-center justify-end gap-2 px-4 py-3 border-t border-border">
|
|
119
|
+
<button
|
|
120
|
+
onClick={onCancel}
|
|
121
|
+
className="px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
122
|
+
>
|
|
123
|
+
취소
|
|
124
|
+
</button>
|
|
125
|
+
<button
|
|
126
|
+
onClick={() => dirInfo && onSelect(dirInfo.current)}
|
|
127
|
+
disabled={!dirInfo}
|
|
128
|
+
className="px-4 py-1.5 text-xs bg-primary hover:bg-primary-hover text-white
|
|
129
|
+
rounded-md transition-colors disabled:opacity-50"
|
|
130
|
+
>
|
|
131
|
+
이 폴더 선택
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|