---
name: recall
description: "메모리 검색 스킬. temporal 모드(날짜별 세션 히스토리)와 topic 모드(키워드 전체 검색)를 지원합니다."
user_invocable: true
---

# /recall — 메모리 검색

사용자의 입력을 분석하여 적절한 모드를 선택하고 메모리를 검색합니다.

## 모드 판별

입력에서 모드를 자동 판별합니다:

1. **temporal** — 날짜/시간 관련 키워드가 있을 때
   - "어제", "오늘", "지난주", "3월 5일", "2026-03-07", "최근", "일주일"
   - 날짜 패턴: YYYY-MM-DD, MM/DD, N일 전

2. **topic** — 그 외 모든 키워드 검색
   - 키워드, 기술명, 프로젝트명, 에이전트명 등

사용자가 명시적으로 `temporal` 또는 `topic`을 지정한 경우 해당 모드를 사용합니다.

## 인자 파싱

`$ARGUMENTS`에서 모드와 쿼리를 추출합니다:
- `/recall 어제 무슨 작업했어?` → temporal, 어제
- `/recall 배포 설정` → topic, "배포 설정"
- `/recall temporal 2026-03-07` → temporal, 2026-03-07
- `/recall topic webhook` → topic, "webhook"

## temporal 모드 실행

날짜별 세션 로그를 검색합니다.

### Step 1: 날짜 해석
- "오늘" → 오늘 날짜
- "어제" → 어제 날짜
- "N일 전" → N일 전 날짜
- "지난주" → 최근 7일
- "YYYY-MM-DD" → 해당 날짜
- 날짜 미지정 → 최근 3일

### Step 2: JSONL 파싱 (최신 데이터 보장)

daily 파일이 없거나 빈 경우, JSONL에서 직접 파싱:
```bash
cd /home/llm/starian && node .claude/hooks/memory/parse-jsonl.mjs --date {YYYY-MM-DD}
```

### Step 3: 파일 검색
```bash
ls -la /home/llm/starian/memory/daily/
```
해당 날짜의 `YYYY-MM-DD.md` 파일을 Read 도구로 읽습니다.

### Step 4: 결과 포맷

날짜별 세션 블록을 서술형으로 요약하여 보고한다. 파일 위치(memory/daily/{date}.md)를 함께 명시한다.

## topic 모드 실행

키워드로 전체 메모리를 검색합니다. BM25 + TF-IDF 스코어링으로 관련도 우선 정렬.

### Step 1: 키워드 추출
사용자 입력에서 핵심 키워드를 추출합니다.

### Step 2: DB 검색 (3가지 병렬)

**a) BM25/TF-IDF 랭킹 검색:**
```bash
cd /home/llm/starian && node -e "
  import { getDb } from './.claude/hooks/memory/lib/db.mjs';
  import { expandQuery, tokenize, filterStopwords } from './.claude/hooks/memory/lib/expand.mjs';
  import { bm25Score, tfidfScore, computeBm25Stats } from './.claude/hooks/memory/lib/rank.mjs';

  const query = '$QUERY';
  const { ftsQuery, keywords, expanded } = expandQuery(query);
  const db = getDb();

  // FTS5로 후보 수집
  let rows = [];
  try { rows = db.prepare('SELECT id, path, text FROM chunks_fts WHERE chunks_fts MATCH ? LIMIT 30').all(ftsQuery); } catch {}

  if (rows.length === 0) { console.log('[]'); process.exit(0); }

  // BM25 스코어링
  const { avgDocLen, docFreqs, totalDocs } = computeBm25Stats(rows, expanded);
  const scored = rows.map(r => ({
    id: r.id, path: r.path,
    text: r.text.slice(0, 300),
    bm25: bm25Score(r.text, expanded, avgDocLen, docFreqs, totalDocs),
    tfidf: tfidfScore(r.text, expanded, docFreqs, totalDocs),
  }));

  scored.sort((a, b) => (b.bm25 + b.tfidf) - (a.bm25 + a.tfidf));
  console.log(JSON.stringify(scored.slice(0, 10), null, 2));
"
```

**b) 파일 grep 검색:**
memory/ 디렉토리 전체를 Grep 도구로 검색합니다 (daily/ + long-term/).

**c) 동의어 확장 검색:**
키워드의 동의어/관련어로 추가 검색합니다. expand.mjs의 SYNONYMS 맵을 활용합니다.

### Step 3: 결과 통합 및 랭킹

검색 결과를 관련도순으로 정렬합니다:
- BM25 + TF-IDF 복합 스코어가 높은 결과 우선
- importance가 높은 결과 우선
- 최신 결과 우선 (동점 시)

### Step 4: 결과 포맷

검색 결과를 관련도순으로 서술형 보고한다. 각 결과에 출처(daily/long-term, 날짜), 내용 요약(300자 이내), 관련도(높음/중간)를 포함한다.

## 결과 없음 처리

검색 결과가 없으면 검색 범위(파일 수, DB 청크 수)를 명시하고, 다른 키워드나 날짜 지정을 안내한다.
