@haema/cli 0.8.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 +258 -0
- package/dist/auth.js +30 -0
- package/dist/auth.js.map +1 -0
- package/dist/discoverClaudeFiles.js +132 -0
- package/dist/discoverClaudeFiles.js.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/install.js +353 -0
- package/dist/mcp/install.js.map +1 -0
- package/dist/mcp/mcpClient.js +55 -0
- package/dist/mcp/mcpClient.js.map +1 -0
- package/dist/mcp/resolveProjectId.js +27 -0
- package/dist/mcp/resolveProjectId.js.map +1 -0
- package/dist/mcp/server.js +344 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/addTask.js +15 -0
- package/dist/mcp/tools/addTask.js.map +1 -0
- package/dist/mcp/tools/applyHooks.js +110 -0
- package/dist/mcp/tools/applyHooks.js.map +1 -0
- package/dist/mcp/tools/brief.js +166 -0
- package/dist/mcp/tools/brief.js.map +1 -0
- package/dist/mcp/tools/createFolder.js +11 -0
- package/dist/mcp/tools/createFolder.js.map +1 -0
- package/dist/mcp/tools/finishTask.js +16 -0
- package/dist/mcp/tools/finishTask.js.map +1 -0
- package/dist/mcp/tools/getTask.js +31 -0
- package/dist/mcp/tools/getTask.js.map +1 -0
- package/dist/mcp/tools/listTasks.js +27 -0
- package/dist/mcp/tools/listTasks.js.map +1 -0
- package/dist/mcp/tools/loadSkill.js +8 -0
- package/dist/mcp/tools/loadSkill.js.map +1 -0
- package/dist/mcp/tools/logSession.js +13 -0
- package/dist/mcp/tools/logSession.js.map +1 -0
- package/dist/mcp/tools/recall.js +23 -0
- package/dist/mcp/tools/recall.js.map +1 -0
- package/dist/mcp/tools/signin.js +111 -0
- package/dist/mcp/tools/signin.js.map +1 -0
- package/dist/mcp/tools/signout.js +11 -0
- package/dist/mcp/tools/signout.js.map +1 -0
- package/dist/mcp/tools/startTask.js +21 -0
- package/dist/mcp/tools/startTask.js.map +1 -0
- package/dist/mcp/tools/updateTask.js +9 -0
- package/dist/mcp/tools/updateTask.js.map +1 -0
- package/dist/mcp/tools/uploadPrompt.js +32 -0
- package/dist/mcp/tools/uploadPrompt.js.map +1 -0
- package/dist/mcp/tools/whoami.js +13 -0
- package/dist/mcp/tools/whoami.js.map +1 -0
- package/dist/openBrowser.js +15 -0
- package/dist/openBrowser.js.map +1 -0
- package/dist/readClaudeFile.js +16 -0
- package/dist/readClaudeFile.js.map +1 -0
- package/dist/scanLimits.js +15 -0
- package/dist/scanLimits.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
const HAEMA_MARKER = "<!-- haema-memory -->";
|
|
6
|
+
function resolveMcpServerBlock() {
|
|
7
|
+
const isWindows = process.platform === "win32";
|
|
8
|
+
try {
|
|
9
|
+
const cmd = isWindows ? "where haema" : "which haema";
|
|
10
|
+
const result = execSync(cmd, { encoding: "utf8" }).trim();
|
|
11
|
+
const full = result.split("\n")[0].trim();
|
|
12
|
+
if (full)
|
|
13
|
+
return { command: full, args: ["--stdio"] };
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// PATH에서 못 찾으면 폴백
|
|
17
|
+
}
|
|
18
|
+
return { command: "haema", args: ["--stdio"] };
|
|
19
|
+
}
|
|
20
|
+
const WORKFLOW_INSTRUCTION = `
|
|
21
|
+
${HAEMA_MARKER}
|
|
22
|
+
## Haema Memory MCP
|
|
23
|
+
|
|
24
|
+
haema-memory MCP 서버가 연결되어 있어요. 아래 툴을 활용하세요.
|
|
25
|
+
|
|
26
|
+
### 사용 가능한 툴
|
|
27
|
+
- \`brief\` — 현재 프로젝트의 태스크·커밋·추천을 한번에 조회
|
|
28
|
+
- \`recall\` — 과거 결정 의미 검색
|
|
29
|
+
- \`add_task\` — 태스크 미리 등록 (나중에 시작할 때만)
|
|
30
|
+
- \`start_task\` — 태스크 생성 + 즉시 IN_PROGRESS 시작
|
|
31
|
+
- \`update_task\` — 태스크 상태·내용 변경
|
|
32
|
+
- \`finish_task\` — 태스크 완료 (DONE)
|
|
33
|
+
- \`list_tasks\` — 태스크 목록 조회
|
|
34
|
+
- \`create_folder\` — 태스크 폴더 생성
|
|
35
|
+
- \`load_skill\` — 스킬 전체 내용 로드
|
|
36
|
+
- \`propose_skill\` — 반복 패턴을 커스텀 스킬로 등록
|
|
37
|
+
- \`upload_prompt\` — CLAUDE.md/AGENTS.md/SKILL.md 업로드
|
|
38
|
+
- \`signin\` / \`whoami\` / \`signout\` — 계정 관리
|
|
39
|
+
|
|
40
|
+
### 세션 시작 시 (필수)
|
|
41
|
+
\`brief\` 호출 후 아래 형식으로 현황 정리:
|
|
42
|
+
|
|
43
|
+
\`\`\`
|
|
44
|
+
최근 작업 흐름:
|
|
45
|
+
- [커밋·태스크 기반 흐름 1]
|
|
46
|
+
- [커밋·태스크 기반 흐름 2]
|
|
47
|
+
- [커밋·태스크 기반 흐름 3]
|
|
48
|
+
|
|
49
|
+
추천 태스크:
|
|
50
|
+
1) [AI 추천 1개]
|
|
51
|
+
2) [기존 태스크·최근 커밋 분석 추천]
|
|
52
|
+
3) [기존 태스크·최근 커밋 분석 추천]
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
추천 태스크는 반드시 3개를 채워서 출력해요.
|
|
56
|
+
|
|
57
|
+
### 태스크 사이클 (매 태스크마다 반복)
|
|
58
|
+
|
|
59
|
+
**1단계 — 제안 및 유저 확인**
|
|
60
|
+
수행할 태스크를 유저에게 먼저 제안하고 승낙을 받아요. 유저가 승낙하기 전에 구현을 시작하지 마세요.
|
|
61
|
+
|
|
62
|
+
**2단계 — 폴더·모듈 선택**
|
|
63
|
+
- **폴더**: brief의 폴더 목록에서 적합한 폴더를 선택해요. 맞는 폴더가 없으면 \`create_folder\`로 생성하고 유저에게 확인받아요.
|
|
64
|
+
- **모듈**: \`backend · frontend · database · designer · integration · devops · planner · testing\` 중 하나를 반드시 선택해요.
|
|
65
|
+
|
|
66
|
+
**3단계 — 시작**
|
|
67
|
+
\`start_task(title, module, folderId)\`로 태스크를 생성하고 즉시 IN_PROGRESS로 시작해요.
|
|
68
|
+
응답에 매칭된 스킬이 있으면 **반드시** \`load_skill(slug)\`를 호출해 스킬을 로드한 후 구현을 시작해요.
|
|
69
|
+
|
|
70
|
+
**4단계 — 탐색**
|
|
71
|
+
\`recall(태스크명)\`으로 관련 과거 결정을 검색해요.
|
|
72
|
+
|
|
73
|
+
**5단계 — 구현**
|
|
74
|
+
코드 작업을 수행해요.
|
|
75
|
+
|
|
76
|
+
**6단계 — 완료**
|
|
77
|
+
\`finish_task(taskSeq, summary, keyDecisions, outcome)\`로 태스크를 완료해요.
|
|
78
|
+
- \`outcome\`: 수정한 파일 경로 포함
|
|
79
|
+
- \`keyDecisions\`: 아키텍처 선택, 버그 원인, 방향 변경 등 다음 세션에서 recall로 찾을 결정만
|
|
80
|
+
- 완료 후 스킬 제안 알림이 표시되면 \`propose_skill\`로 등록하세요.
|
|
81
|
+
|
|
82
|
+
**7단계 — 다음 태스크 제안**
|
|
83
|
+
완료 후 brief의 대기 태스크 또는 현재 맥락을 바탕으로 다음 태스크 3개를 제안하고 1단계부터 반복해요.
|
|
84
|
+
`;
|
|
85
|
+
const TOOL_CONFIGS = {
|
|
86
|
+
claude: {
|
|
87
|
+
name: "Claude Code",
|
|
88
|
+
mcpConfigPath: path.join(homedir(), ".claude.json"),
|
|
89
|
+
instructionPath: path.join(homedir(), ".claude", "CLAUDE.md"),
|
|
90
|
+
},
|
|
91
|
+
cursor: {
|
|
92
|
+
name: "Cursor",
|
|
93
|
+
mcpConfigPath: path.join(homedir(), ".cursor", "mcp.json"),
|
|
94
|
+
instructionPath: path.join(homedir(), ".cursor", "rules", "haema.mdc"),
|
|
95
|
+
},
|
|
96
|
+
gemini: {
|
|
97
|
+
name: "Gemini CLI",
|
|
98
|
+
mcpConfigPath: path.join(homedir(), ".gemini", "settings.json"),
|
|
99
|
+
instructionPath: path.join(homedir(), ".gemini", "GEMINI.md"),
|
|
100
|
+
},
|
|
101
|
+
codex: {
|
|
102
|
+
name: "Codex CLI",
|
|
103
|
+
mcpConfigPath: path.join(homedir(), ".codex", "config.yaml"),
|
|
104
|
+
instructionPath: path.join(homedir(), ".codex", "AGENTS.md"),
|
|
105
|
+
},
|
|
106
|
+
antigravity: {
|
|
107
|
+
name: "Antigravity",
|
|
108
|
+
mcpConfigPath: path.join(homedir(), ".gemini", "antigravity", "mcp_config.json"),
|
|
109
|
+
instructionPath: path.join(homedir(), ".gemini", "antigravity", "AGENTS.md"),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
export async function installCommand(forFlag) {
|
|
113
|
+
const targets = parseTargets(forFlag ?? "claude");
|
|
114
|
+
const mcpServerBlock = resolveMcpServerBlock();
|
|
115
|
+
console.log(`\n=== haema-memory MCP 설치 (${targets.map((t) => TOOL_CONFIGS[t].name).join(", ")}) ===\n`);
|
|
116
|
+
for (const tool of targets) {
|
|
117
|
+
await configureTool(tool, mcpServerBlock);
|
|
118
|
+
}
|
|
119
|
+
console.log("\n✅ 설치 완료! 새 대화창을 열어 'brief' 라고 말하면 현재 프로젝트 상태를 바로 확인할 수 있어요.");
|
|
120
|
+
}
|
|
121
|
+
function parseTargets(flag) {
|
|
122
|
+
if (flag === "all")
|
|
123
|
+
return ["claude", "cursor", "gemini", "codex", "antigravity"];
|
|
124
|
+
const parts = flag.split(",").map((s) => s.trim());
|
|
125
|
+
const valid = ["claude", "cursor", "gemini", "codex", "antigravity"];
|
|
126
|
+
const invalid = parts.filter((p) => !valid.includes(p));
|
|
127
|
+
if (invalid.length > 0) {
|
|
128
|
+
console.error(`알 수 없는 도구: ${invalid.join(", ")}. 사용 가능: claude, cursor, gemini, codex, antigravity, all`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
return parts;
|
|
132
|
+
}
|
|
133
|
+
const SUGGEST_SKILL_HOOK = `#!/bin/bash
|
|
134
|
+
# PostToolUse hook: finish_task 완료 후 패턴 기반 스킬 제안 확인
|
|
135
|
+
AUTH="$HOME/.haema/auth.json"
|
|
136
|
+
[ -f "$AUTH" ] || exit 0
|
|
137
|
+
APP_URL=\$(jq -r '.appUrl // empty' "$AUTH" 2>/dev/null)
|
|
138
|
+
API_KEY=\$(jq -r '.apiKey // empty' "$AUTH" 2>/dev/null)
|
|
139
|
+
[ -z "$APP_URL" ] || [ -z "$API_KEY" ] && exit 0
|
|
140
|
+
TOOL_DATA=\$(cat)
|
|
141
|
+
PROJECT_ID=\$(echo "$TOOL_DATA" | jq -r '.tool_response.projectId // .tool_input.projectId // empty' 2>/dev/null)
|
|
142
|
+
[ -z "$PROJECT_ID" ] && exit 0
|
|
143
|
+
RESP=\$(curl -sf --max-time 5 -H "Authorization: Bearer $API_KEY" "\${APP_URL}/api/memory/skill-suggestions?projectId=\${PROJECT_ID}" 2>/dev/null)
|
|
144
|
+
[ $? -ne 0 ] && exit 0
|
|
145
|
+
COUNT=\$(echo "$RESP" | jq -r '.count // 0' 2>/dev/null)
|
|
146
|
+
[ "\${COUNT}" -le 0 ] && exit 0
|
|
147
|
+
NAMES=\$(echo "$RESP" | jq -r '[.suggestions[].name] | join(", ")' 2>/dev/null)
|
|
148
|
+
echo ""
|
|
149
|
+
echo "💡 반복 패턴 \${COUNT}개 감지: \${NAMES}"
|
|
150
|
+
echo "propose_skill 툴로 등록하면 다음 세션부터 load_skill로 자동 로드돼요."
|
|
151
|
+
`;
|
|
152
|
+
async function deploySkillHook() {
|
|
153
|
+
const hooksDir = path.join(homedir(), ".haema", "hooks");
|
|
154
|
+
const hookPath = path.join(hooksDir, "suggest-skill.sh");
|
|
155
|
+
try {
|
|
156
|
+
await fs.mkdir(hooksDir, { recursive: true });
|
|
157
|
+
await fs.writeFile(hookPath, SUGGEST_SKILL_HOOK, { encoding: "utf8", mode: 0o755 });
|
|
158
|
+
console.log(` [완료] ${hookPath} 훅 스크립트를 배포했어요.`);
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
162
|
+
console.warn(` [실패] 훅 스크립트 배포 실패: ${msg}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function injectClaudePostToolHook() {
|
|
166
|
+
const settingsPath = path.join(homedir(), ".claude", "settings.json");
|
|
167
|
+
const hookEntry = {
|
|
168
|
+
matcher: "mcp__haema-memory__finish_task",
|
|
169
|
+
hooks: [
|
|
170
|
+
{
|
|
171
|
+
type: "command",
|
|
172
|
+
command: `bash ${path.join(homedir(), ".haema", "hooks", "suggest-skill.sh")}`,
|
|
173
|
+
statusMessage: "스킬 제안 확인 중...",
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
try {
|
|
178
|
+
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
179
|
+
let settings = {};
|
|
180
|
+
try {
|
|
181
|
+
const raw = await fs.readFile(settingsPath, "utf8");
|
|
182
|
+
settings = JSON.parse(raw);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// 없으면 새로 생성
|
|
186
|
+
}
|
|
187
|
+
const hooks = (settings.hooks ?? {});
|
|
188
|
+
const postHooks = (hooks.PostToolUse ?? []);
|
|
189
|
+
if (postHooks.some((h) => h.matcher === hookEntry.matcher)) {
|
|
190
|
+
console.log(` [건너뜀] ${settingsPath} 에 이미 스킬 제안 훅이 있어요.`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
hooks.PostToolUse = [...postHooks, hookEntry];
|
|
194
|
+
settings.hooks = hooks;
|
|
195
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
196
|
+
console.log(` [완료] ${settingsPath} 에 PostToolUse 훅을 추가했어요.`);
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
200
|
+
console.warn(` [실패] Claude settings.json 수정 실패: ${msg}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function configureTool(tool, mcpServerBlock) {
|
|
204
|
+
const cfg = TOOL_CONFIGS[tool];
|
|
205
|
+
console.log(`\n── ${cfg.name} ──`);
|
|
206
|
+
if (tool === "claude") {
|
|
207
|
+
await injectMcpJson(cfg.mcpConfigPath, "Claude Code", mcpServerBlock);
|
|
208
|
+
await injectInstruction(cfg.instructionPath, WORKFLOW_INSTRUCTION, "CLAUDE.md");
|
|
209
|
+
await deploySkillHook();
|
|
210
|
+
await injectClaudePostToolHook();
|
|
211
|
+
}
|
|
212
|
+
else if (tool === "cursor") {
|
|
213
|
+
await injectMcpJson(cfg.mcpConfigPath, "Cursor", mcpServerBlock);
|
|
214
|
+
await injectCursorRule(cfg.instructionPath);
|
|
215
|
+
}
|
|
216
|
+
else if (tool === "gemini") {
|
|
217
|
+
await injectMcpJson(cfg.mcpConfigPath, "Gemini settings.json", mcpServerBlock);
|
|
218
|
+
await injectInstruction(cfg.instructionPath, WORKFLOW_INSTRUCTION, "GEMINI.md");
|
|
219
|
+
}
|
|
220
|
+
else if (tool === "codex") {
|
|
221
|
+
await injectCodexConfig(cfg.mcpConfigPath, mcpServerBlock);
|
|
222
|
+
await injectInstruction(cfg.instructionPath, WORKFLOW_INSTRUCTION, "AGENTS.md");
|
|
223
|
+
}
|
|
224
|
+
else if (tool === "antigravity") {
|
|
225
|
+
await injectMcpJson(cfg.mcpConfigPath, "Antigravity", mcpServerBlock);
|
|
226
|
+
await injectInstruction(cfg.instructionPath, WORKFLOW_INSTRUCTION, "AGENTS.md");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function injectMcpJson(filePath, label, mcpServerBlock) {
|
|
230
|
+
try {
|
|
231
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
232
|
+
let existing = {};
|
|
233
|
+
try {
|
|
234
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
235
|
+
existing = JSON.parse(raw);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// 없거나 파싱 실패 → 새로 생성
|
|
239
|
+
}
|
|
240
|
+
const servers = (existing.mcpServers ?? {});
|
|
241
|
+
if ("haema-memory" in servers) {
|
|
242
|
+
const current = servers["haema-memory"];
|
|
243
|
+
if (current?.command === mcpServerBlock.command) {
|
|
244
|
+
console.log(` [건너뜀] ${label} 에 이미 haema-memory 설정이 있어요.`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
servers["haema-memory"] = mcpServerBlock;
|
|
248
|
+
existing.mcpServers = servers;
|
|
249
|
+
await fs.writeFile(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
250
|
+
console.log(` [업데이트] ${filePath} 의 haema-memory command 경로를 업데이트했어요.`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
servers["haema-memory"] = mcpServerBlock;
|
|
254
|
+
existing.mcpServers = servers;
|
|
255
|
+
await fs.writeFile(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
256
|
+
console.log(` [완료] ${filePath} 에 haema-memory 서버를 추가했어요.`);
|
|
257
|
+
}
|
|
258
|
+
catch (e) {
|
|
259
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
260
|
+
console.warn(` [실패] ${filePath} 수정 실패: ${msg}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async function injectCursorRule(filePath) {
|
|
264
|
+
try {
|
|
265
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
266
|
+
const frontmatter = `---\ndescription: Haema Memory MCP 워크플로우\nalwaysApply: true\n---`;
|
|
267
|
+
const fullContent = `${frontmatter}\n${WORKFLOW_INSTRUCTION}`;
|
|
268
|
+
let existing = "";
|
|
269
|
+
try {
|
|
270
|
+
existing = await fs.readFile(filePath, "utf8");
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// 없으면 새로 생성
|
|
274
|
+
}
|
|
275
|
+
if (existing.includes(HAEMA_MARKER)) {
|
|
276
|
+
const markerIdx = existing.indexOf(HAEMA_MARKER);
|
|
277
|
+
// frontmatter는 마커 앞 부분에 있으므로 마커부터 전체 교체
|
|
278
|
+
const updated = existing.slice(0, markerIdx) + WORKFLOW_INSTRUCTION.trimStart();
|
|
279
|
+
await fs.writeFile(filePath, updated, "utf8");
|
|
280
|
+
console.log(` [업데이트] ${filePath} 의 haema-memory 지시문을 최신 버전으로 교체했어요.`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
await fs.writeFile(filePath, fullContent, "utf8");
|
|
284
|
+
console.log(` [완료] ${filePath} 에 haema 규칙을 추가했어요.`);
|
|
285
|
+
}
|
|
286
|
+
catch (e) {
|
|
287
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
288
|
+
console.warn(` [실패] ${filePath} 생성 실패: ${msg}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function injectCodexConfig(filePath, mcpServerBlock) {
|
|
292
|
+
try {
|
|
293
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
294
|
+
let existing = "";
|
|
295
|
+
try {
|
|
296
|
+
existing = await fs.readFile(filePath, "utf8");
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// 없으면 새로 생성
|
|
300
|
+
}
|
|
301
|
+
if (existing.includes("haema-memory")) {
|
|
302
|
+
// haema-memory 섹션 이후의 command 행만 교체 (다른 섹션의 command 행 오염 방지)
|
|
303
|
+
const sectionMatch = existing.match(/(haema-memory:[\s\S]*?command:\s*)(.+)/);
|
|
304
|
+
const currentCommand = sectionMatch?.[2]?.trim();
|
|
305
|
+
if (currentCommand === mcpServerBlock.command) {
|
|
306
|
+
console.log(` [건너뜀] ${filePath} 에 이미 haema-memory 설정이 있어요.`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const updated = existing.replace(/(haema-memory:[\s\S]*?command:\s*).+/, `$1${mcpServerBlock.command}`);
|
|
310
|
+
await fs.writeFile(filePath, updated, "utf8");
|
|
311
|
+
console.log(` [업데이트] ${filePath} 의 haema-memory command 경로를 업데이트했어요.`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const block = `
|
|
315
|
+
mcp_servers:
|
|
316
|
+
haema-memory:
|
|
317
|
+
command: ${mcpServerBlock.command}
|
|
318
|
+
args: [${mcpServerBlock.args.join(", ")}]
|
|
319
|
+
`;
|
|
320
|
+
await fs.appendFile(filePath, block, "utf8");
|
|
321
|
+
console.log(` [완료] ${filePath} 에 haema-memory 서버를 추가했어요.`);
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
325
|
+
console.warn(` [실패] ${filePath} 수정 실패: ${msg}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function injectInstruction(filePath, instruction, label) {
|
|
329
|
+
try {
|
|
330
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
331
|
+
let existing = "";
|
|
332
|
+
try {
|
|
333
|
+
existing = await fs.readFile(filePath, "utf8");
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// 없으면 생성
|
|
337
|
+
}
|
|
338
|
+
if (existing.includes(HAEMA_MARKER)) {
|
|
339
|
+
const markerIdx = existing.indexOf(HAEMA_MARKER);
|
|
340
|
+
const updated = existing.slice(0, markerIdx) + instruction.trimStart();
|
|
341
|
+
await fs.writeFile(filePath, updated, "utf8");
|
|
342
|
+
console.log(` [업데이트] ${filePath} 의 haema-memory 지시문을 최신 버전으로 교체했어요.`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
await fs.appendFile(filePath, instruction, "utf8");
|
|
346
|
+
console.log(` [완료] ${filePath} 에 brief 워크플로우 지시문을 추가했어요.`);
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
350
|
+
console.warn(` [실패] ${filePath} 수정 실패: ${msg}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/mcp/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readAuth } from "../auth.js";
|
|
2
|
+
export async function readMcpConfig() {
|
|
3
|
+
const auth = await readAuth();
|
|
4
|
+
if (!auth)
|
|
5
|
+
throw new Error("로그인이 필요해요. `haema signin` 을 먼저 실행해주세요.");
|
|
6
|
+
return { appUrl: auth.appUrl, apiKey: auth.apiKey };
|
|
7
|
+
}
|
|
8
|
+
const TIMEOUT_MS = 10_000;
|
|
9
|
+
function withTimeout(signal) {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const timer = setTimeout(() => controller.abort(new Error("요청 시간이 초과됐어요 (10초)")), TIMEOUT_MS);
|
|
12
|
+
signal?.addEventListener("abort", () => { clearTimeout(timer); controller.abort(signal.reason); });
|
|
13
|
+
return controller.signal;
|
|
14
|
+
}
|
|
15
|
+
function authHeaders(config) {
|
|
16
|
+
return { "content-type": "application/json", authorization: `Bearer ${config.apiKey}` };
|
|
17
|
+
}
|
|
18
|
+
async function checkResponse(res) {
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const text = await res.text().catch(() => "");
|
|
21
|
+
throw new Error(`HTTP ${res.status}: ${text.slice(0, 500)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function mcpPost(config, path, body) {
|
|
25
|
+
const res = await fetch(`${config.appUrl}${path}`, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: authHeaders(config),
|
|
28
|
+
body: JSON.stringify(body),
|
|
29
|
+
signal: withTimeout(),
|
|
30
|
+
});
|
|
31
|
+
await checkResponse(res);
|
|
32
|
+
return res.json();
|
|
33
|
+
}
|
|
34
|
+
export async function mcpGet(config, path, params) {
|
|
35
|
+
const url = new URL(`${config.appUrl}${path}`);
|
|
36
|
+
for (const [k, v] of Object.entries(params))
|
|
37
|
+
url.searchParams.set(k, v);
|
|
38
|
+
const res = await fetch(url.toString(), {
|
|
39
|
+
headers: authHeaders(config),
|
|
40
|
+
signal: withTimeout(),
|
|
41
|
+
});
|
|
42
|
+
await checkResponse(res);
|
|
43
|
+
return res.json();
|
|
44
|
+
}
|
|
45
|
+
export async function mcpPatch(config, path, body) {
|
|
46
|
+
const res = await fetch(`${config.appUrl}${path}`, {
|
|
47
|
+
method: "PATCH",
|
|
48
|
+
headers: authHeaders(config),
|
|
49
|
+
body: JSON.stringify(body),
|
|
50
|
+
signal: withTimeout(),
|
|
51
|
+
});
|
|
52
|
+
await checkResponse(res);
|
|
53
|
+
return res.json();
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=mcpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../../src/mcp/mcpClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mcpPost } from "./mcpClient.js";
|
|
2
|
+
// cwd로 프로젝트 조회 또는 자동 생성 (upsert).
|
|
3
|
+
export async function resolveOrInitProject(cwd, config) {
|
|
4
|
+
try {
|
|
5
|
+
const res = await mcpPost(config, "/api/memory/init-project", { cwd });
|
|
6
|
+
if (res.ok)
|
|
7
|
+
return res.projectId;
|
|
8
|
+
console.error(`[haema-memory] 프로젝트 초기화 실패: ${res.error}`);
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12
|
+
if (msg.includes("401") || msg.includes("로그인")) {
|
|
13
|
+
console.error("[haema-memory] 인증 오류 — `signin` 툴로 로그인해주세요.");
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
console.error(`[haema-memory] 프로젝트 연결 실패: ${msg}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
// tool 파라미터에서 projectId 결정: cwd 있으면 동적 resolve/init, 없으면 기본값 사용
|
|
22
|
+
export async function resolveProject(args, config) {
|
|
23
|
+
if (!args.cwd)
|
|
24
|
+
return args.defaultProjectId;
|
|
25
|
+
return resolveOrInitProject(args.cwd, config);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=resolveProjectId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveProjectId.js","sourceRoot":"","sources":["../../src/mcp/resolveProjectId.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAMzC,kCAAkC;AAClC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,GAAW,EAAE,MAAiB;IACvE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAe,MAAM,EAAE,0BAA0B,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACrF,IAAI,GAAG,CAAC,EAAE;YAAE,OAAO,GAAG,CAAC,SAAS,CAAC;QACjC,OAAO,CAAC,KAAK,CAAC,+BAA+B,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAuD,EACvD,MAAiB;IAEjB,IAAI,CAAC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC5C,OAAO,oBAAoB,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAChD,CAAC"}
|