claude-session-continuity-mcp 1.6.4 → 1.6.6

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.
@@ -134,73 +134,40 @@ function install() {
134
134
  // ===== 1. Hooks 설치 (npm exec 방식 - 경로 독립적) =====
135
135
  console.log('📌 Step 1: Installing Hooks (npm exec mode)...');
136
136
  const settings = loadSettings();
137
- // 기존 hooks 유지하면서 추가
137
+ // 기존 hooks 유지하면서 우리 훅만 추가/교체
138
138
  const hooks = settings.hooks || {};
139
- // SessionStart Hook - 세션 시작 컨텍스트 로드
140
- hooks.SessionStart = [
141
- {
142
- hooks: [
143
- {
144
- type: 'command',
145
- command: 'npm exec -- claude-hook-session-start'
146
- }
147
- ]
148
- }
149
- ];
150
- // UserPromptSubmit Hook - 프롬프트 제출 시 관련 컨텍스트 주입
151
- hooks.UserPromptSubmit = [
152
- {
153
- hooks: [
154
- {
155
- type: 'command',
156
- command: 'npm exec -- claude-hook-user-prompt'
157
- }
158
- ]
159
- }
160
- ];
161
- // PostToolUse Hook - 파일 변경 시 자동 기록 (Edit, Write)
162
- hooks.PostToolUse = [
163
- {
164
- matcher: 'Edit',
165
- hooks: [
166
- {
167
- type: 'command',
168
- command: 'npm exec -- claude-hook-post-tool'
169
- }
170
- ]
171
- },
172
- {
173
- matcher: 'Write',
174
- hooks: [
175
- {
176
- type: 'command',
177
- command: 'npm exec -- claude-hook-post-tool'
178
- }
179
- ]
180
- }
181
- ];
182
- // PreCompact Hook - 컨텍스트 압축 전 중요 정보 저장
183
- hooks.PreCompact = [
184
- {
185
- hooks: [
186
- {
187
- type: 'command',
188
- command: 'npm exec -- claude-hook-pre-compact'
189
- }
190
- ]
191
- }
192
- ];
193
- // Stop Hook - 세션 종료 시 자동 저장
194
- hooks.Stop = [
195
- {
196
- hooks: [
197
- {
198
- type: 'command',
199
- command: 'npm exec -- claude-hook-session-end'
200
- }
201
- ]
202
- }
203
- ];
139
+ // 우리 명령어 prefix (이걸로 우리 훅인지 판별)
140
+ const OUR_PREFIX = 'claude-hook-';
141
+ /**
142
+ * 기존 훅 배열에서 우리 훅만 제거하고, 새 훅을 추가
143
+ * 사용자 커스텀 훅은 보존됨
144
+ */
145
+ function mergeHooks(event, ourEntries) {
146
+ const existing = (hooks[event] || []);
147
+ // 기존 항목 중 우리 훅이 아닌 것만 보존
148
+ const userEntries = existing.filter(entry => {
149
+ const cmds = entry.hooks || [];
150
+ return !cmds.some(h => h.command && h.command.includes(OUR_PREFIX));
151
+ });
152
+ // 사용자 훅 먼저, 우리 훅 뒤에 추가
153
+ hooks[event] = [...userEntries, ...ourEntries];
154
+ }
155
+ mergeHooks('SessionStart', [
156
+ { hooks: [{ type: 'command', command: 'npm exec -- claude-hook-session-start' }] }
157
+ ]);
158
+ mergeHooks('UserPromptSubmit', [
159
+ { hooks: [{ type: 'command', command: 'npm exec -- claude-hook-user-prompt' }] }
160
+ ]);
161
+ mergeHooks('PostToolUse', [
162
+ { matcher: 'Edit', hooks: [{ type: 'command', command: 'npm exec -- claude-hook-post-tool' }] },
163
+ { matcher: 'Write', hooks: [{ type: 'command', command: 'npm exec -- claude-hook-post-tool' }] }
164
+ ]);
165
+ mergeHooks('PreCompact', [
166
+ { hooks: [{ type: 'command', command: 'npm exec -- claude-hook-pre-compact' }] }
167
+ ]);
168
+ mergeHooks('Stop', [
169
+ { hooks: [{ type: 'command', command: 'npm exec -- claude-hook-session-end' }] }
170
+ ]);
204
171
  settings.hooks = hooks;
205
172
  saveSettings(settings);
206
173
  console.log('✅ Hooks installed (npm exec mode - works with local or global install!)');
@@ -235,12 +202,21 @@ function uninstall() {
235
202
  console.log('🔧 Removing Claude Code Hooks...');
236
203
  const settings = loadSettings();
237
204
  const hooks = settings.hooks || {};
238
- // session-continuity 관련 Hook만 제거
239
- delete hooks.SessionStart;
240
- delete hooks.UserPromptSubmit;
241
- delete hooks.PostToolUse;
242
- delete hooks.PreCompact;
243
- delete hooks.Stop;
205
+ const OUR_PREFIX = 'claude-hook-';
206
+ // 각 이벤트에서 우리 훅만 제거, 사용자 훅은 보존
207
+ for (const event of ['SessionStart', 'UserPromptSubmit', 'PostToolUse', 'PreCompact', 'Stop']) {
208
+ const existing = (hooks[event] || []);
209
+ const remaining = existing.filter(entry => {
210
+ const cmds = entry.hooks || [];
211
+ return !cmds.some(h => h.command && h.command.includes(OUR_PREFIX));
212
+ });
213
+ if (remaining.length === 0) {
214
+ delete hooks[event];
215
+ }
216
+ else {
217
+ hooks[event] = remaining;
218
+ }
219
+ }
244
220
  if (Object.keys(hooks).length === 0) {
245
221
  delete settings.hooks;
246
222
  }
@@ -6,10 +6,22 @@
6
6
  */
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
- import * as os from 'os';
10
9
  import Database from 'better-sqlite3';
11
- function getDbPath() {
12
- const claudeDir = path.join(os.homedir(), '.claude');
10
+ function detectWorkspaceRoot(cwd) {
11
+ let current = cwd;
12
+ const root = path.parse(current).root;
13
+ while (current !== root) {
14
+ if (fs.existsSync(path.join(current, 'apps')))
15
+ return current;
16
+ if (fs.existsSync(path.join(current, '.claude', 'sessions.db')))
17
+ return current;
18
+ current = path.dirname(current);
19
+ }
20
+ return cwd;
21
+ }
22
+ function getDbPath(cwd) {
23
+ const workspaceRoot = detectWorkspaceRoot(cwd);
24
+ const claudeDir = path.join(workspaceRoot, '.claude');
13
25
  if (!fs.existsSync(claudeDir)) {
14
26
  fs.mkdirSync(claudeDir, { recursive: true });
15
27
  }
@@ -100,7 +112,7 @@ async function main() {
100
112
  const input = inputData ? JSON.parse(inputData) : {};
101
113
  const cwd = input.cwd || process.cwd();
102
114
  const project = detectProject(cwd);
103
- const dbPath = getDbPath();
115
+ const dbPath = getDbPath(cwd);
104
116
  if (!fs.existsSync(dbPath)) {
105
117
  console.log('[SessionEnd] No DB found, skipping');
106
118
  process.exit(0);
@@ -13,10 +13,6 @@ function detectWorkspaceRoot(cwd) {
13
13
  return current;
14
14
  if (fs.existsSync(path.join(current, '.claude', 'sessions.db')))
15
15
  return current;
16
- if (fs.existsSync(path.join(current, 'package.json'))) {
17
- // package.json이 있으면 여기가 프로젝트 루트일 가능성 높음
18
- return current;
19
- }
20
16
  current = path.dirname(current);
21
17
  }
22
18
  return cwd;
@@ -28,9 +24,24 @@ function getProject(cwd, workspaceRoot) {
28
24
  const relative = path.relative(appsDir, cwd);
29
25
  return relative.split(path.sep)[0];
30
26
  }
31
- // 단일 프로젝트 모드
32
- if (!fs.existsSync(appsDir)) {
33
- return path.basename(workspaceRoot);
27
+ // 워크스페이스 루트 자체에서 실행
28
+ if (cwd === workspaceRoot) {
29
+ return null;
30
+ }
31
+ // apps/ 외부 하위 프로젝트 (hackathons/ 등) - package.json에서 이름 추출
32
+ let current = cwd;
33
+ while (current !== workspaceRoot && current !== path.parse(current).root) {
34
+ const pkgPath = path.join(current, 'package.json');
35
+ if (fs.existsSync(pkgPath)) {
36
+ try {
37
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
38
+ return pkg.name || path.basename(current);
39
+ }
40
+ catch {
41
+ return path.basename(current);
42
+ }
43
+ }
44
+ current = path.dirname(current);
34
45
  }
35
46
  return null;
36
47
  }
package/dist/index.js CHANGED
@@ -692,7 +692,9 @@ async function handleTool(name, args) {
692
692
  const projectPath = getProjectPath(project);
693
693
  if (!await fileExists(projectPath)) {
694
694
  // DB에 컨텍스트가 있는지 확인 (디렉토리 없어도 컨텍스트는 있을 수 있음)
695
- const hasContext = db.prepare('SELECT 1 FROM project_context WHERE project = ?').get(project);
695
+ const hasContext = db.prepare('SELECT 1 FROM project_context WHERE project = ?').get(project)
696
+ || db.prepare('SELECT 1 FROM active_context WHERE project = ?').get(project)
697
+ || db.prepare('SELECT 1 FROM sessions WHERE project = ? LIMIT 1').get(project);
696
698
  if (!hasContext) {
697
699
  return { content: [{ type: 'text', text: `Project not found: ${project}` }] };
698
700
  }
@@ -847,7 +849,11 @@ async function handleTool(name, args) {
847
849
  const project = args.project;
848
850
  const projectPath = getProjectPath(project);
849
851
  if (!await fileExists(projectPath)) {
850
- return { content: [{ type: 'text', text: `Project not found: ${project}` }] };
852
+ const hasData = db.prepare('SELECT 1 FROM active_context WHERE project = ?').get(project)
853
+ || db.prepare('SELECT 1 FROM sessions WHERE project = ? LIMIT 1').get(project);
854
+ if (!hasData) {
855
+ return { content: [{ type: 'text', text: `Project not found: ${project}` }] };
856
+ }
851
857
  }
852
858
  // 태스크 통계
853
859
  const taskStats = db.prepare(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-session-continuity-mcp",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "description": "Session Continuity for Claude Code - Never re-explain your project again",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",