idea-manager 0.7.7 → 0.7.9
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.ja.md +1 -1
- package/README.ko.md +127 -0
- package/README.md +57 -57
- package/README.zh.md +1 -1
- package/package.json +1 -1
- package/src/app/api/projects/[id]/git-sync/route.ts +118 -0
- package/src/components/dashboard/GitSyncResultsModal.tsx +90 -0
- package/src/components/workspace/WorkspacePanel.tsx +58 -2
- package/src/types/index.ts +8 -0
- package/README.en.md +0 -127
package/README.ja.md
CHANGED
package/README.ko.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# IM (Idea Manager)
|
|
2
|
+
|
|
3
|
+
[English](README.md) | **한국어** | [日本語](README.ja.md) | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
> 아이디어에서 실행 가능한 프롬프트까지, 멀티 프로젝트 워크플로우 매니저
|
|
6
|
+
|
|
7
|
+
여러 프로젝트를 동시에 진행하는 개발자를 위한 태스크 관리 도구입니다. 아이디어를 서브 프로젝트와 태스크로 조직화하고, 각 태스크별 프롬프트를 정제하여 Claude Code 등 AI 에이전트에게 전달할 수 있습니다. MCP Server를 내장하고 있어 AI 에이전트가 자율적으로 태스크를 가져가 실행할 수 있습니다.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## 핵심 워크플로우
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
브레인스토밍 → 서브 프로젝트/태스크 조직화 → 프롬프트 정제 → MCP로 AI 실행
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 계층 구조
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
프로젝트
|
|
21
|
+
├── 서브 프로젝트 A
|
|
22
|
+
│ ├── 태스크 1 → 프롬프트
|
|
23
|
+
│ ├── 태스크 2 → 프롬프트
|
|
24
|
+
│ └── 태스크 3 → 프롬프트
|
|
25
|
+
└── 서브 프로젝트 B
|
|
26
|
+
├── 태스크 4 → 프롬프트
|
|
27
|
+
└── 태스크 5 → 프롬프트
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 태스크 상태 흐름
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
💡 Idea → ✏️ Writing → 🚀 Submitted → 🧪 Testing → ✅ Done
|
|
34
|
+
🔴 Problem
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 설치
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g idea-manager
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 사용법
|
|
44
|
+
|
|
45
|
+
### 웹 UI 실행
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
im start
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`http://localhost:3456`에서 웹 UI가 열립니다.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# 포트 변경
|
|
55
|
+
im start -p 4000
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### MCP Server 실행
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
im mcp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Claude Desktop 설정 (claude_desktop_config.json)
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"idea-manager": {
|
|
70
|
+
"command": "npx",
|
|
71
|
+
"args": ["-y", "idea-manager", "mcp"]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Claude Code 설정
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
claude mcp add idea-manager -- npx -y idea-manager mcp
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### MCP 제공 도구
|
|
84
|
+
|
|
85
|
+
| 도구 | 설명 |
|
|
86
|
+
|------|------|
|
|
87
|
+
| `list-projects` | 프로젝트 목록 조회 |
|
|
88
|
+
| `get-project-context` | 서브 프로젝트 + 태스크 트리 전체 조회 |
|
|
89
|
+
| `get-next-task` | 다음 실행할 태스크와 프롬프트 조회 (status=submitted) |
|
|
90
|
+
| `get-task-prompt` | 특정 태스크의 프롬프트 조회 |
|
|
91
|
+
| `update-status` | 태스크 상태 변경 (idea/writing/submitted/testing/done/problem) |
|
|
92
|
+
| `report-completion` | 태스크 완료 보고 |
|
|
93
|
+
|
|
94
|
+
## 주요 기능
|
|
95
|
+
|
|
96
|
+
- **탭 기반 멀티 프로젝트** — 브라우저/IDE처럼 여러 프로젝트를 탭으로 동시에 열기, 탭 전환 시 상태 보존
|
|
97
|
+
- **3-패널 워크스페이스** — 브레인스토밍 | 프로젝트 트리 | 태스크 상세, 패널 간 드래그로 크기 조절
|
|
98
|
+
- **트리형 프로젝트 구조** — 서브 프로젝트 아래 태스크가 계층적으로 표시
|
|
99
|
+
- **브레인스토밍 패널** — 자유 형식 메모, 접기/펼치기 가능
|
|
100
|
+
- **프롬프트 에디터** — 태스크별 프롬프트 작성/편집/복사, AI 다듬기
|
|
101
|
+
- **AI 채팅** — 태스크별 AI 대화로 프롬프트 구체화
|
|
102
|
+
- **3탭 대시보드** — 진행 중 / 전체 / 오늘 할 일
|
|
103
|
+
- **키보드 단축키** — Ctrl+Tab/Ctrl+Shift+Tab으로 탭 이동, B: 브레인스토밍 토글, N: 서브 프로젝트 추가, T: 태스크 추가, Cmd+1~6: 상태 변경 (한영 전환 상관없이 동작)
|
|
104
|
+
- **PWA 지원** — 앱으로 설치하여 독립 창에서 사용 가능
|
|
105
|
+
- **Watch 모드** — submitted 태스크를 Claude CLI로 자동 실행, 실시간 진행 표시
|
|
106
|
+
- **MCP Server 내장** — AI 에이전트 자율 실행 지원
|
|
107
|
+
- **로컬 우선** — SQLite 기반, 데이터는 `~/.idea-manager/`에 저장
|
|
108
|
+
|
|
109
|
+
## 기술 스택
|
|
110
|
+
|
|
111
|
+
| 영역 | 기술 |
|
|
112
|
+
|------|------|
|
|
113
|
+
| Frontend | Next.js 15, React 19, TypeScript, Tailwind CSS 4 |
|
|
114
|
+
| Backend | Next.js API Routes |
|
|
115
|
+
| Database | SQLite (better-sqlite3) |
|
|
116
|
+
| AI | Claude CLI (구독 기반, API 키 불필요) |
|
|
117
|
+
| MCP | Model Context Protocol (stdio) |
|
|
118
|
+
| CLI | Commander.js |
|
|
119
|
+
|
|
120
|
+
## 요구 사항
|
|
121
|
+
|
|
122
|
+
- **Node.js** 18+
|
|
123
|
+
- **Claude CLI** — AI 채팅/다듬기 기능 사용 시 필요 (Claude 구독 필요). 없어도 태스크 관리, 프롬프트 작성 등 기본 기능은 정상 동작합니다.
|
|
124
|
+
|
|
125
|
+
## 라이선스
|
|
126
|
+
|
|
127
|
+
MIT
|
package/README.md
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
1
|
# IM (Idea Manager)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**English** | [한국어](README.ko.md) | [日本語](README.ja.md) | [中文](README.zh.md)
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> From ideas to executable prompts — a multi-project workflow manager
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A task management tool for developers juggling multiple projects simultaneously. Organize ideas into sub-projects and tasks, refine prompts for each task, and hand them off to AI agents like Claude Code. With a built-in MCP Server, AI agents can autonomously pick up and execute tasks.
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Core Workflow
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
|
|
14
|
+
Brainstorming → Organize into Sub-projects/Tasks → Refine Prompts → Execute via MCP
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
###
|
|
17
|
+
### Hierarchy
|
|
18
18
|
|
|
19
19
|
```
|
|
20
|
-
|
|
21
|
-
├──
|
|
22
|
-
│ ├──
|
|
23
|
-
│ ├──
|
|
24
|
-
│ └──
|
|
25
|
-
└──
|
|
26
|
-
├──
|
|
27
|
-
└──
|
|
20
|
+
Project
|
|
21
|
+
├── Sub-project A
|
|
22
|
+
│ ├── Task 1 → Prompt
|
|
23
|
+
│ ├── Task 2 → Prompt
|
|
24
|
+
│ └── Task 3 → Prompt
|
|
25
|
+
└── Sub-project B
|
|
26
|
+
├── Task 4 → Prompt
|
|
27
|
+
└── Task 5 → Prompt
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
###
|
|
30
|
+
### Task Status Flow
|
|
31
31
|
|
|
32
32
|
```
|
|
33
33
|
💡 Idea → ✏️ Writing → 🚀 Submitted → 🧪 Testing → ✅ Done
|
|
34
34
|
🔴 Problem
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## Installation
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
npm install -g idea-manager
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## Usage
|
|
44
44
|
|
|
45
|
-
###
|
|
45
|
+
### Start Web UI
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
im start
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
`http://localhost:3456
|
|
51
|
+
Opens the web UI at `http://localhost:3456`.
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
#
|
|
54
|
+
# Custom port
|
|
55
55
|
im start -p 4000
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
### MCP Server
|
|
58
|
+
### Start MCP Server
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
im mcp
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
#### Claude Desktop
|
|
64
|
+
#### Claude Desktop Configuration (claude_desktop_config.json)
|
|
65
65
|
|
|
66
66
|
```json
|
|
67
67
|
{
|
|
@@ -74,54 +74,54 @@ im mcp
|
|
|
74
74
|
}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
#### Claude Code
|
|
77
|
+
#### Claude Code Configuration
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
claude mcp add idea-manager -- npx -y idea-manager mcp
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
### MCP
|
|
84
|
-
|
|
85
|
-
|
|
|
86
|
-
|
|
87
|
-
| `list-projects` |
|
|
88
|
-
| `get-project-context` |
|
|
89
|
-
| `get-next-task` |
|
|
90
|
-
| `get-task-prompt` |
|
|
91
|
-
| `update-status` |
|
|
92
|
-
| `report-completion` |
|
|
93
|
-
|
|
94
|
-
##
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
- **3
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
- **AI
|
|
102
|
-
- **3
|
|
103
|
-
-
|
|
104
|
-
- **PWA
|
|
105
|
-
- **Watch
|
|
106
|
-
- **MCP Server
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
##
|
|
110
|
-
|
|
111
|
-
|
|
|
112
|
-
|
|
83
|
+
### MCP Tools
|
|
84
|
+
|
|
85
|
+
| Tool | Description |
|
|
86
|
+
|------|-------------|
|
|
87
|
+
| `list-projects` | List all projects |
|
|
88
|
+
| `get-project-context` | Get full sub-project + task tree |
|
|
89
|
+
| `get-next-task` | Get next task to execute (status=submitted) |
|
|
90
|
+
| `get-task-prompt` | Get prompt for a specific task |
|
|
91
|
+
| `update-status` | Change task status (idea/writing/submitted/testing/done/problem) |
|
|
92
|
+
| `report-completion` | Report task completion |
|
|
93
|
+
|
|
94
|
+
## Key Features
|
|
95
|
+
|
|
96
|
+
- **Tab-based Multi-project** — Open multiple projects in tabs like a browser/IDE, state preserved on tab switch
|
|
97
|
+
- **3-Panel Workspace** — Brainstorming | Project Tree | Task Detail, drag to resize panels
|
|
98
|
+
- **Tree-structured Projects** — Tasks displayed hierarchically under sub-projects
|
|
99
|
+
- **Brainstorming Panel** — Free-form notes, collapsible
|
|
100
|
+
- **Prompt Editor** — Write/edit/copy prompts per task, AI refinement
|
|
101
|
+
- **AI Chat** — Per-task AI conversations to refine prompts
|
|
102
|
+
- **3-Tab Dashboard** — Active / All / Today
|
|
103
|
+
- **Keyboard Shortcuts** — Ctrl+Tab/Ctrl+Shift+Tab for tab navigation, B: toggle brainstorm, N: add sub-project, T: add task, Cmd+1~6: change status
|
|
104
|
+
- **PWA Support** — Install as an app for a standalone window experience
|
|
105
|
+
- **Watch Mode** — Auto-execute submitted tasks via Claude CLI with real-time progress
|
|
106
|
+
- **Built-in MCP Server** — Supports autonomous AI agent execution
|
|
107
|
+
- **Local-first** — SQLite-based, data stored in `~/.idea-manager/`
|
|
108
|
+
|
|
109
|
+
## Tech Stack
|
|
110
|
+
|
|
111
|
+
| Area | Technology |
|
|
112
|
+
|------|------------|
|
|
113
113
|
| Frontend | Next.js 15, React 19, TypeScript, Tailwind CSS 4 |
|
|
114
114
|
| Backend | Next.js API Routes |
|
|
115
115
|
| Database | SQLite (better-sqlite3) |
|
|
116
|
-
| AI | Claude CLI (
|
|
116
|
+
| AI | Claude CLI (subscription-based, no API key needed) |
|
|
117
117
|
| MCP | Model Context Protocol (stdio) |
|
|
118
118
|
| CLI | Commander.js |
|
|
119
119
|
|
|
120
|
-
##
|
|
120
|
+
## Requirements
|
|
121
121
|
|
|
122
122
|
- **Node.js** 18+
|
|
123
|
-
- **Claude CLI** —
|
|
123
|
+
- **Claude CLI** — Required for AI chat/refinement features (Claude subscription needed). Core features like task management and prompt editing work without it.
|
|
124
124
|
|
|
125
|
-
##
|
|
125
|
+
## License
|
|
126
126
|
|
|
127
127
|
MIT
|
package/README.zh.md
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getProject } from '@/lib/db/queries/projects';
|
|
3
|
+
import { execFile } from 'child_process';
|
|
4
|
+
import { existsSync, readdirSync, statSync } from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import type { IGitSyncResult } from '@/types';
|
|
7
|
+
|
|
8
|
+
function gitPull(cwd: string): Promise<{ stdout: string; stderr: string }> {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
execFile('git', ['pull'], { cwd, timeout: 15000 }, (err, stdout, stderr) => {
|
|
11
|
+
if (err) reject(err);
|
|
12
|
+
else resolve({ stdout, stderr });
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function syncOneRepo(dirPath: string, name: string): Promise<IGitSyncResult> {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout, stderr } = await gitPull(dirPath);
|
|
20
|
+
const message = (stdout || stderr || '').trim().slice(0, 500);
|
|
21
|
+
return {
|
|
22
|
+
projectId: name,
|
|
23
|
+
projectName: name,
|
|
24
|
+
projectPath: dirPath,
|
|
25
|
+
status: 'success',
|
|
26
|
+
message: message || 'Already up to date.',
|
|
27
|
+
};
|
|
28
|
+
} catch (err: unknown) {
|
|
29
|
+
const message = err instanceof Error ? err.message.slice(0, 500) : 'Unknown error';
|
|
30
|
+
return {
|
|
31
|
+
projectId: name,
|
|
32
|
+
projectName: name,
|
|
33
|
+
projectPath: dirPath,
|
|
34
|
+
status: 'error',
|
|
35
|
+
message,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function findGitRepos(rootPath: string): { name: string; path: string }[] {
|
|
41
|
+
// If root itself is a git repo
|
|
42
|
+
if (existsSync(path.join(rootPath, '.git'))) {
|
|
43
|
+
return [{ name: path.basename(rootPath), path: rootPath }];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Otherwise scan immediate subdirectories
|
|
47
|
+
const repos: { name: string; path: string }[] = [];
|
|
48
|
+
try {
|
|
49
|
+
const entries = readdirSync(rootPath);
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (entry.startsWith('.')) continue;
|
|
52
|
+
const fullPath = path.join(rootPath, entry);
|
|
53
|
+
try {
|
|
54
|
+
if (statSync(fullPath).isDirectory() && existsSync(path.join(fullPath, '.git'))) {
|
|
55
|
+
repos.push({ name: entry, path: fullPath });
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// skip inaccessible dirs
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
// skip
|
|
63
|
+
}
|
|
64
|
+
return repos;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function POST(
|
|
68
|
+
_request: NextRequest,
|
|
69
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
70
|
+
) {
|
|
71
|
+
const { id } = await params;
|
|
72
|
+
const project = getProject(id);
|
|
73
|
+
|
|
74
|
+
if (!project) {
|
|
75
|
+
return NextResponse.json({ error: 'Project not found' }, { status: 404 });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!project.project_path) {
|
|
79
|
+
return NextResponse.json([{
|
|
80
|
+
projectId: project.id,
|
|
81
|
+
projectName: project.name,
|
|
82
|
+
projectPath: '',
|
|
83
|
+
status: 'no-path',
|
|
84
|
+
message: 'No folder linked',
|
|
85
|
+
}] satisfies IGitSyncResult[]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const projectPath = project.project_path;
|
|
89
|
+
|
|
90
|
+
if (!existsSync(projectPath)) {
|
|
91
|
+
return NextResponse.json([{
|
|
92
|
+
projectId: project.id,
|
|
93
|
+
projectName: project.name,
|
|
94
|
+
projectPath,
|
|
95
|
+
status: 'error',
|
|
96
|
+
message: 'Directory not found',
|
|
97
|
+
}] satisfies IGitSyncResult[]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const repos = findGitRepos(projectPath);
|
|
101
|
+
|
|
102
|
+
if (repos.length === 0) {
|
|
103
|
+
return NextResponse.json([{
|
|
104
|
+
projectId: project.id,
|
|
105
|
+
projectName: project.name,
|
|
106
|
+
projectPath,
|
|
107
|
+
status: 'no-git',
|
|
108
|
+
message: 'No git repositories found',
|
|
109
|
+
}] satisfies IGitSyncResult[]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const results: IGitSyncResult[] = [];
|
|
113
|
+
for (const repo of repos) {
|
|
114
|
+
results.push(await syncOneRepo(repo.path, repo.name));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return NextResponse.json(results);
|
|
118
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import type { IGitSyncResult } from '@/types';
|
|
5
|
+
|
|
6
|
+
const STATUS_STYLE: Record<IGitSyncResult['status'], { icon: string; color: string }> = {
|
|
7
|
+
success: { icon: '\u2705', color: 'text-success' },
|
|
8
|
+
error: { icon: '\u274C', color: 'text-destructive' },
|
|
9
|
+
'no-git': { icon: '\u2796', color: 'text-muted-foreground' },
|
|
10
|
+
'no-path': { icon: '\u2796', color: 'text-muted-foreground' },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default function GitSyncResultsModal({
|
|
14
|
+
open,
|
|
15
|
+
results,
|
|
16
|
+
onClose,
|
|
17
|
+
}: {
|
|
18
|
+
open: boolean;
|
|
19
|
+
results: IGitSyncResult[];
|
|
20
|
+
onClose: () => void;
|
|
21
|
+
}) {
|
|
22
|
+
const overlayRef = useRef<HTMLDivElement>(null);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!open) return;
|
|
26
|
+
const handler = (e: KeyboardEvent) => {
|
|
27
|
+
if (e.key === 'Escape') onClose();
|
|
28
|
+
};
|
|
29
|
+
window.addEventListener('keydown', handler);
|
|
30
|
+
return () => window.removeEventListener('keydown', handler);
|
|
31
|
+
}, [open, onClose]);
|
|
32
|
+
|
|
33
|
+
if (!open) return null;
|
|
34
|
+
|
|
35
|
+
const successCount = results.filter(r => r.status === 'success').length;
|
|
36
|
+
const errorCount = results.filter(r => r.status === 'error').length;
|
|
37
|
+
const skipCount = results.filter(r => r.status === 'no-git' || r.status === 'no-path').length;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
ref={overlayRef}
|
|
42
|
+
onClick={(e) => { if (e.target === overlayRef.current) onClose(); }}
|
|
43
|
+
className="fixed inset-0 z-50 flex items-center justify-center"
|
|
44
|
+
style={{ background: 'rgba(0,0,0,0.5)', backdropFilter: 'blur(2px)' }}
|
|
45
|
+
>
|
|
46
|
+
<div className="bg-card border border-border rounded-xl shadow-2xl shadow-black/40
|
|
47
|
+
w-full max-w-md mx-4 animate-dialog-in">
|
|
48
|
+
<div className="p-5 border-b border-border">
|
|
49
|
+
<h3 className="text-sm font-semibold text-foreground">Git Sync Results</h3>
|
|
50
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
51
|
+
{successCount} synced, {errorCount} failed, {skipCount} skipped
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div className="max-h-64 overflow-y-auto p-3 space-y-1.5">
|
|
56
|
+
{results.length === 0 ? (
|
|
57
|
+
<p className="text-xs text-muted-foreground text-center py-4">
|
|
58
|
+
No projects with linked folders
|
|
59
|
+
</p>
|
|
60
|
+
) : (
|
|
61
|
+
results.map((r) => {
|
|
62
|
+
const style = STATUS_STYLE[r.status];
|
|
63
|
+
return (
|
|
64
|
+
<div key={r.projectId} className="flex items-start gap-2 p-2 rounded-lg bg-muted/50">
|
|
65
|
+
<span className="text-sm flex-shrink-0">{style.icon}</span>
|
|
66
|
+
<div className="flex-1 min-w-0">
|
|
67
|
+
<div className="text-xs font-medium truncate">{r.projectName}</div>
|
|
68
|
+
<div className={`text-xs ${style.color} truncate`} title={r.message}>
|
|
69
|
+
{r.message}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
})
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div className="flex justify-end px-5 pb-4 pt-2">
|
|
79
|
+
<button
|
|
80
|
+
onClick={onClose}
|
|
81
|
+
className="px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground
|
|
82
|
+
bg-muted hover:bg-card-hover border border-border rounded-md transition-colors"
|
|
83
|
+
>
|
|
84
|
+
Close
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -8,7 +8,8 @@ import TaskDetail from '@/components/task/TaskDetail';
|
|
|
8
8
|
import DirectoryPicker from '@/components/DirectoryPicker';
|
|
9
9
|
import ConfirmDialog from '@/components/ui/ConfirmDialog';
|
|
10
10
|
import AiPolicyModal from '@/components/ui/AiPolicyModal';
|
|
11
|
-
import
|
|
11
|
+
import GitSyncResultsModal from '@/components/dashboard/GitSyncResultsModal';
|
|
12
|
+
import type { ISubProject, ITask, TaskStatus, ISubProjectWithStats, IGitSyncResult } from '@/types';
|
|
12
13
|
|
|
13
14
|
interface IProject {
|
|
14
15
|
id: string;
|
|
@@ -58,6 +59,10 @@ export default function WorkspacePanel({
|
|
|
58
59
|
const [showBrainstorm, setShowBrainstorm] = useState(true);
|
|
59
60
|
const [newSubName, setNewSubName] = useState('');
|
|
60
61
|
const [showAiPolicy, setShowAiPolicy] = useState(false);
|
|
62
|
+
const [syncing, setSyncing] = useState(false);
|
|
63
|
+
const [syncResults, setSyncResults] = useState<IGitSyncResult[] | null>(null);
|
|
64
|
+
const [lastSyncTime, setLastSyncTime] = useState<Date | null>(null);
|
|
65
|
+
const syncingRef = useRef(false);
|
|
61
66
|
|
|
62
67
|
// Resizable panel widths
|
|
63
68
|
const [leftWidth, setLeftWidth] = useState(500);
|
|
@@ -279,6 +284,35 @@ export default function WorkspacePanel({
|
|
|
279
284
|
}
|
|
280
285
|
};
|
|
281
286
|
|
|
287
|
+
const handleGitSync = useCallback(async (silent = false) => {
|
|
288
|
+
if (syncingRef.current) return;
|
|
289
|
+
syncingRef.current = true;
|
|
290
|
+
if (!silent) setSyncing(true);
|
|
291
|
+
try {
|
|
292
|
+
const res = await fetch(`/api/projects/${id}/git-sync`, { method: 'POST' });
|
|
293
|
+
if (res.ok) {
|
|
294
|
+
const results: IGitSyncResult[] = await res.json();
|
|
295
|
+
setLastSyncTime(new Date());
|
|
296
|
+
if (!silent) {
|
|
297
|
+
setSyncResults(results);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
301
|
+
// silent fail
|
|
302
|
+
} finally {
|
|
303
|
+
syncingRef.current = false;
|
|
304
|
+
if (!silent) setSyncing(false);
|
|
305
|
+
}
|
|
306
|
+
}, [id]);
|
|
307
|
+
|
|
308
|
+
// Auto git-sync every 30 minutes
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
if (!project?.project_path) return;
|
|
311
|
+
const INTERVAL_MS = 30 * 60 * 1000;
|
|
312
|
+
const timer = setInterval(() => handleGitSync(true), INTERVAL_MS);
|
|
313
|
+
return () => clearInterval(timer);
|
|
314
|
+
}, [project?.project_path, handleGitSync]);
|
|
315
|
+
|
|
282
316
|
const handleToggleWatch = async () => {
|
|
283
317
|
if (!project) return;
|
|
284
318
|
const res = await fetch(`/api/projects/${id}`, {
|
|
@@ -370,7 +404,24 @@ export default function WorkspacePanel({
|
|
|
370
404
|
}`}>
|
|
371
405
|
AI Policy{project.ai_context ? ' *' : ''}
|
|
372
406
|
</button>
|
|
373
|
-
{
|
|
407
|
+
{project.project_path ? (
|
|
408
|
+
<div className="flex items-center gap-1.5">
|
|
409
|
+
<button
|
|
410
|
+
onClick={() => handleGitSync(false)}
|
|
411
|
+
disabled={syncing}
|
|
412
|
+
className="px-3 py-1.5 text-xs bg-muted hover:bg-card-hover text-foreground border border-border rounded-md transition-colors disabled:opacity-50 flex items-center gap-1.5"
|
|
413
|
+
title={lastSyncTime ? `Last sync: ${lastSyncTime.toLocaleTimeString()}` : 'Git pull'}
|
|
414
|
+
>
|
|
415
|
+
<span className={syncing ? 'animate-spin' : ''}>↻</span>
|
|
416
|
+
{syncing ? 'Syncing...' : 'Git Sync'}
|
|
417
|
+
</button>
|
|
418
|
+
{lastSyncTime && (
|
|
419
|
+
<span className="text-xs text-muted-foreground">
|
|
420
|
+
{lastSyncTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
|
421
|
+
</span>
|
|
422
|
+
)}
|
|
423
|
+
</div>
|
|
424
|
+
) : (
|
|
374
425
|
<button onClick={() => setShowDirPicker(true)}
|
|
375
426
|
className="px-3 py-1.5 text-xs bg-muted hover:bg-card-hover text-foreground border border-border rounded-md transition-colors">
|
|
376
427
|
Link folder
|
|
@@ -450,6 +501,11 @@ export default function WorkspacePanel({
|
|
|
450
501
|
onConfirm={handleConfirmAction} onCancel={() => setConfirmAction(null)} />
|
|
451
502
|
<AiPolicyModal open={showAiPolicy} content={project.ai_context || ''}
|
|
452
503
|
onSave={handleSaveAiPolicy} onClose={() => setShowAiPolicy(false)} />
|
|
504
|
+
<GitSyncResultsModal
|
|
505
|
+
open={!!syncResults}
|
|
506
|
+
results={syncResults || []}
|
|
507
|
+
onClose={() => setSyncResults(null)}
|
|
508
|
+
/>
|
|
453
509
|
</div>
|
|
454
510
|
);
|
|
455
511
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -63,6 +63,14 @@ export interface ITaskConversation {
|
|
|
63
63
|
created_at: string;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
export interface IGitSyncResult {
|
|
67
|
+
projectId: string;
|
|
68
|
+
projectName: string;
|
|
69
|
+
projectPath: string;
|
|
70
|
+
status: 'success' | 'error' | 'no-git' | 'no-path';
|
|
71
|
+
message: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
66
74
|
export interface ISubProjectWithStats extends ISubProject {
|
|
67
75
|
task_count: number;
|
|
68
76
|
active_count: number;
|
package/README.en.md
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
# IM (Idea Manager)
|
|
2
|
-
|
|
3
|
-
[한국어](README.md) | **English** | [日本語](README.ja.md) | [中文](README.zh.md)
|
|
4
|
-
|
|
5
|
-
> From ideas to executable prompts — a multi-project workflow manager
|
|
6
|
-
|
|
7
|
-
A task management tool for developers juggling multiple projects simultaneously. Organize ideas into sub-projects and tasks, refine prompts for each task, and hand them off to AI agents like Claude Code. With a built-in MCP Server, AI agents can autonomously pick up and execute tasks.
|
|
8
|
-
|
|
9
|
-

|
|
10
|
-
|
|
11
|
-
## Core Workflow
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
Brainstorming → Organize into Sub-projects/Tasks → Refine Prompts → Execute via MCP
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
### Hierarchy
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
Project
|
|
21
|
-
├── Sub-project A
|
|
22
|
-
│ ├── Task 1 → Prompt
|
|
23
|
-
│ ├── Task 2 → Prompt
|
|
24
|
-
│ └── Task 3 → Prompt
|
|
25
|
-
└── Sub-project B
|
|
26
|
-
├── Task 4 → Prompt
|
|
27
|
-
└── Task 5 → Prompt
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Task Status Flow
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
💡 Idea → ✏️ Writing → 🚀 Submitted → 🧪 Testing → ✅ Done
|
|
34
|
-
🔴 Problem
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Installation
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
npm install -g idea-manager
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Usage
|
|
44
|
-
|
|
45
|
-
### Start Web UI
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
im start
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Opens the web UI at `http://localhost:3456`.
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
# Custom port
|
|
55
|
-
im start -p 4000
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Start MCP Server
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
im mcp
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
#### Claude Desktop Configuration (claude_desktop_config.json)
|
|
65
|
-
|
|
66
|
-
```json
|
|
67
|
-
{
|
|
68
|
-
"mcpServers": {
|
|
69
|
-
"idea-manager": {
|
|
70
|
-
"command": "npx",
|
|
71
|
-
"args": ["-y", "idea-manager", "mcp"]
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
#### Claude Code Configuration
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
claude mcp add idea-manager -- npx -y idea-manager mcp
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### MCP Tools
|
|
84
|
-
|
|
85
|
-
| Tool | Description |
|
|
86
|
-
|------|-------------|
|
|
87
|
-
| `list-projects` | List all projects |
|
|
88
|
-
| `get-project-context` | Get full sub-project + task tree |
|
|
89
|
-
| `get-next-task` | Get next task to execute (status=submitted) |
|
|
90
|
-
| `get-task-prompt` | Get prompt for a specific task |
|
|
91
|
-
| `update-status` | Change task status (idea/writing/submitted/testing/done/problem) |
|
|
92
|
-
| `report-completion` | Report task completion |
|
|
93
|
-
|
|
94
|
-
## Key Features
|
|
95
|
-
|
|
96
|
-
- **Tab-based Multi-project** — Open multiple projects in tabs like a browser/IDE, state preserved on tab switch
|
|
97
|
-
- **3-Panel Workspace** — Brainstorming | Project Tree | Task Detail, drag to resize panels
|
|
98
|
-
- **Tree-structured Projects** — Tasks displayed hierarchically under sub-projects
|
|
99
|
-
- **Brainstorming Panel** — Free-form notes, collapsible
|
|
100
|
-
- **Prompt Editor** — Write/edit/copy prompts per task, AI refinement
|
|
101
|
-
- **AI Chat** — Per-task AI conversations to refine prompts
|
|
102
|
-
- **3-Tab Dashboard** — Active / All / Today
|
|
103
|
-
- **Keyboard Shortcuts** — Ctrl+Tab/Ctrl+Shift+Tab for tab navigation, B: toggle brainstorm, N: add sub-project, T: add task, Cmd+1~6: change status
|
|
104
|
-
- **PWA Support** — Install as an app for a standalone window experience
|
|
105
|
-
- **Watch Mode** — Auto-execute submitted tasks via Claude CLI with real-time progress
|
|
106
|
-
- **Built-in MCP Server** — Supports autonomous AI agent execution
|
|
107
|
-
- **Local-first** — SQLite-based, data stored in `~/.idea-manager/`
|
|
108
|
-
|
|
109
|
-
## Tech Stack
|
|
110
|
-
|
|
111
|
-
| Area | Technology |
|
|
112
|
-
|------|------------|
|
|
113
|
-
| Frontend | Next.js 15, React 19, TypeScript, Tailwind CSS 4 |
|
|
114
|
-
| Backend | Next.js API Routes |
|
|
115
|
-
| Database | SQLite (better-sqlite3) |
|
|
116
|
-
| AI | Claude CLI (subscription-based, no API key needed) |
|
|
117
|
-
| MCP | Model Context Protocol (stdio) |
|
|
118
|
-
| CLI | Commander.js |
|
|
119
|
-
|
|
120
|
-
## Requirements
|
|
121
|
-
|
|
122
|
-
- **Node.js** 18+
|
|
123
|
-
- **Claude CLI** — Required for AI chat/refinement features (Claude subscription needed). Core features like task management and prompt editing work without it.
|
|
124
|
-
|
|
125
|
-
## License
|
|
126
|
-
|
|
127
|
-
MIT
|