oh-my-til 0.11.2 → 0.12.2

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.ko.md CHANGED
@@ -15,7 +15,8 @@ AI 기반 TIL(Today I Learned) 학습 워크플로우를 위한 Claude Code 플
15
15
  - **터미널 임베딩** — Obsidian 사이드바에서 Claude Code 터미널 실행 (xterm.js + node-pty)
16
16
  - **MCP 서버 내장** — Claude Code가 HTTP로 vault에 직접 접근 (별도 플러그인 불필요)
17
17
  - **학습 대시보드** — TIL 통계, 카테고리별 학습 현황을 한눈에
18
- - **스킬 자동 설치** — `/til`, `/research`, `/backlog`, `/save`, `/migrate-links` 명령을 바로 사용 가능
18
+ - **스킬 자동 설치** — `/til`, `/research`, `/backlog`, `/save`, `/til-review`, `/dashboard`, `/migrate-links` 명령을 바로 사용 가능
19
+ - **간격 반복 복습 (SRS)** — SM-2 알고리즘 기반 TIL 복습 스케줄링
19
20
  - **마크다운 링크 감지** — 터미널의 `[텍스트](경로)` 링크를 클릭하면 노트 열기 (CJK 문자 지원)
20
21
  - **백로그 → TIL 연동** — 빈 백로그 링크 클릭 시 TIL 학습 세션 시작 제안
21
22
  - **파일 자동 열기** — 새로 생성된 TIL 파일을 에디터에서 자동으로 열기
@@ -24,7 +25,7 @@ AI 기반 TIL(Today I Learned) 학습 워크플로우를 위한 Claude Code 플
24
25
 
25
26
  ```
26
27
  커맨드 팔레트 → 터미널 열기 → Claude Code 자동 시작
27
- → /til, /backlog, /research, /save, /dashboard, /migrate-links 스킬 실행
28
+ → /til, /backlog, /research, /save, /til-review, /dashboard, /migrate-links 스킬 실행
28
29
  → Claude가 리서치 → 대화형 학습 → TIL 마크다운 저장
29
30
  → 새 파일 감지 → 에디터에서 자동 열기
30
31
  ```
@@ -136,6 +137,9 @@ MCP 서버 연결 시 Claude Code에서 사용할 수 있는 도구:
136
137
  | `til_backlog_status` | 백로그 진행률 요약 (체크박스 카운트) |
137
138
  | `til_get_context` | 주제 관련 기존 학습 컨텍스트 (파일, 링크 관계, 미작성 주제) |
138
139
  | `til_recent_context` | 최근 학습 활동을 날짜별로 조회 |
140
+ | `til_dashboard` | 학습 통계 요약 |
141
+ | `til_review_list` | 복습 대상 카드 목록 + 통계 (SRS) |
142
+ | `til_review_update` | 복습 결과 기록 또는 복습 해제 |
139
143
 
140
144
  ## Claude 스킬
141
145
 
@@ -147,6 +151,7 @@ MCP 서버 연결 시 Claude Code에서 사용할 수 있는 도구:
147
151
  | **research** | `/research <주제> [카테고리]` | 주제를 리서치하여 학습 백로그 생성 |
148
152
  | **backlog** | `/backlog [카테고리]` | 학습 백로그 조회 및 진행 상황 요약 |
149
153
  | **save** | *(/til에서 자동 호출)* | TIL 마크다운 저장 + Daily 노트, MOC, 백로그 연동 |
154
+ | **til-review** | `/til-review [카테고리]` | SRS 기반 간격 반복 복습 세션 (SM-2 알고리즘) |
150
155
  | **migrate-links** | `/migrate-links` | `[[wikilink]]`를 표준 마크다운 링크로 일괄 변환 |
151
156
 
152
157
  ## 개발
@@ -167,6 +172,7 @@ src/
167
172
  │ ├── backlog.ts # 백로그 파싱/포맷 순수 함수
168
173
  │ ├── context.ts # 학습 컨텍스트 헬퍼 (순수 함수)
169
174
  │ ├── stats.ts # TIL 통계 순수 함수
175
+ │ ├── srs.ts # SRS 간격 반복 (SM-2 알고리즘, 복습 카드/통계)
170
176
  │ ├── migrate-links.ts # Wikilink [[]] → [](path) 변환
171
177
  │ ├── keyboard.ts # Shift+Enter → \n (멀티라인 지원)
172
178
  │ ├── env.ts # macOS PATH 보정 (Homebrew)
@@ -213,6 +219,7 @@ src/
213
219
  - [x] MCP 서버 내장
214
220
  - [x] 학습 대시보드 (기본 통계)
215
221
  - [x] 독립 CLI (`npx oh-my-til`) — Obsidian 없이 사용
222
+ - [x] 간격 반복 복습 (SRS) — SM-2 기반 복습 스케줄링
216
223
  - [ ] 대시보드 백로그 진행률 바
217
224
  - [ ] TIL 폴더 경로 커스터마이즈
218
225
  - [ ] 리치 대시보드 — 최근 TIL 목록, 학습 스트릭, 주간 요약
package/README.md CHANGED
@@ -15,7 +15,8 @@ A Claude Code plugin for AI-powered TIL (Today I Learned) learning workflow. Wor
15
15
  - **Embedded Terminal** — Claude Code terminal in Obsidian sidebar (xterm.js + node-pty)
16
16
  - **Built-in MCP Server** — Claude Code can directly access your vault via HTTP
17
17
  - **Learning Dashboard** — TIL statistics and category breakdown at a glance
18
- - **Auto-installed Skills** — `/til`, `/research`, `/backlog`, `/save`, `/migrate-links` commands ready out of the box
18
+ - **Auto-installed Skills** — `/til`, `/research`, `/backlog`, `/save`, `/til-review`, `/dashboard`, `/migrate-links` commands ready out of the box
19
+ - **Spaced Repetition (SRS)** — SM-2 algorithm-based review scheduling for TIL notes
19
20
  - **Markdown Link Detection** — `[text](path)` links in terminal are clickable and open notes (CJK-aware)
20
21
  - **Backlog-to-TIL Trigger** — Click an empty backlog link to start a TIL session
21
22
  - **File Watcher** — Newly created TIL files open automatically in the editor
@@ -24,7 +25,7 @@ A Claude Code plugin for AI-powered TIL (Today I Learned) learning workflow. Wor
24
25
 
25
26
  ```
26
27
  Command Palette → Open Terminal → Claude Code starts
27
- → Run /til, /backlog, /research, /save, /dashboard, /migrate-links skills
28
+ → Run /til, /backlog, /research, /save, /til-review, /dashboard, /migrate-links skills
28
29
  → Claude researches → interactive learning → saves TIL markdown
29
30
  → New file detected → opens in editor
30
31
  ```
@@ -136,6 +137,9 @@ When the MCP server is connected, Claude Code can use these tools:
136
137
  | `til_backlog_status` | Backlog progress summary with checkbox counts |
137
138
  | `til_get_context` | Get existing knowledge context for a topic (files, links, unresolved mentions) |
138
139
  | `til_recent_context` | Recent learning activity grouped by date |
140
+ | `til_dashboard` | Learning statistics summary |
141
+ | `til_review_list` | Due review cards list + stats (SRS) |
142
+ | `til_review_update` | Record review result or remove from review |
139
143
 
140
144
  ## Claude Skills
141
145
 
@@ -147,6 +151,7 @@ The plugin auto-installs these skills to `.claude/skills/`:
147
151
  | **research** | `/research <topic> [category]` | Research a topic and create a learning backlog |
148
152
  | **backlog** | `/backlog [category]` | View learning backlog and progress |
149
153
  | **save** | *(auto-invoked by /til)* | Save TIL markdown with Daily note, MOC, and backlog updates |
154
+ | **til-review** | `/til-review [category]` | SRS-based spaced repetition review session (SM-2 algorithm) |
150
155
  | **migrate-links** | `/migrate-links` | Batch-convert `[[wikilinks]]` to standard markdown links |
151
156
 
152
157
  ## Development
@@ -167,6 +172,7 @@ src/
167
172
  │ ├── backlog.ts # Backlog parsing/formatting (pure functions)
168
173
  │ ├── context.ts # Learning context helpers (pure functions)
169
174
  │ ├── stats.ts # TIL statistics (pure functions)
175
+ │ ├── srs.ts # Spaced repetition (SM-2 algorithm, review cards/stats)
170
176
  │ ├── migrate-links.ts # Wikilink [[]] → [](path) conversion
171
177
  │ ├── keyboard.ts # Shift+Enter → \n (multiline support)
172
178
  │ ├── env.ts # macOS PATH resolution (Homebrew)
@@ -213,6 +219,7 @@ src/
213
219
  - [x] Built-in MCP server
214
220
  - [x] Learning dashboard (basic stats)
215
221
  - [x] Standalone CLI (`npx oh-my-til`) — use without Obsidian
222
+ - [x] Spaced Repetition (SRS) — SM-2 based review scheduling
216
223
  - [ ] Backlog progress bars in dashboard
217
224
  - [ ] Configurable TIL folder path
218
225
  - [ ] Rich dashboard — recent TILs, streaks, weekly summary
package/dist/cli.js CHANGED
@@ -39006,14 +39006,15 @@ function computeCategoryDistribution(files, tilPath, _prefilteredFiles) {
39006
39006
  percentage: Math.round(count / total * 100)
39007
39007
  })).sort((a, b) => b.count - a.count);
39008
39008
  }
39009
- function computeEnhancedStats(files, tilPath, backlogEntries, now) {
39009
+ function computeEnhancedStats(files, tilPath, backlogEntries, now, reviewDueCount) {
39010
39010
  const tilFiles = filterTilFiles(files, tilPath);
39011
39011
  const categories = computeEnhancedCategories(files, tilPath, tilFiles);
39012
39012
  const summary = {
39013
39013
  totalTils: tilFiles.length,
39014
39014
  categoryCount: categories.length,
39015
39015
  thisWeekCount: computeWeeklyCount(files, tilPath, now, tilFiles),
39016
- streak: computeStreak(files, tilPath, now, tilFiles)
39016
+ streak: computeStreak(files, tilPath, now, tilFiles),
39017
+ ...reviewDueCount !== void 0 && reviewDueCount > 0 ? { reviewDueCount } : {}
39017
39018
  };
39018
39019
  return {
39019
39020
  summary,
@@ -39025,6 +39026,251 @@ function computeEnhancedStats(files, tilPath, backlogEntries, now) {
39025
39026
  };
39026
39027
  }
39027
39028
 
39029
+ // src/core/srs.ts
39030
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
39031
+ function formatDate2(ts) {
39032
+ const d = new Date(ts);
39033
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
39034
+ }
39035
+ function startOfDay2(ts) {
39036
+ const d = new Date(ts);
39037
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
39038
+ }
39039
+ function parseDateStr2(dateStr) {
39040
+ if (dateStr.includes("T")) {
39041
+ return new Date(dateStr).getTime();
39042
+ }
39043
+ const [y, m, d] = dateStr.split("-").map(Number);
39044
+ return new Date(y, m - 1, d).getTime();
39045
+ }
39046
+ function createDefaultSrsMetadata(now) {
39047
+ const currentTime = now ?? Date.now();
39048
+ const today = formatDate2(currentTime);
39049
+ const tomorrow = formatDate2(currentTime + DAY_MS2);
39050
+ return {
39051
+ next_review: tomorrow,
39052
+ interval: 1,
39053
+ ease_factor: 2.5,
39054
+ repetitions: 0,
39055
+ last_review: today
39056
+ };
39057
+ }
39058
+ function computeNextReview(current, grade, now) {
39059
+ const currentTime = now ?? Date.now();
39060
+ const today = formatDate2(currentTime);
39061
+ let { interval, ease_factor, repetitions } = current;
39062
+ if (grade < 3) {
39063
+ repetitions = 0;
39064
+ interval = 1;
39065
+ } else {
39066
+ if (repetitions === 0) {
39067
+ interval = 1;
39068
+ } else if (repetitions === 1) {
39069
+ interval = 6;
39070
+ } else {
39071
+ interval = Math.round(interval * ease_factor);
39072
+ }
39073
+ repetitions += 1;
39074
+ }
39075
+ ease_factor += 0.1 - (5 - grade) * (0.08 + (5 - grade) * 0.02);
39076
+ ease_factor = Math.max(ease_factor, 1.3);
39077
+ const next_review = formatDate2(currentTime + interval * DAY_MS2);
39078
+ return {
39079
+ next_review,
39080
+ interval,
39081
+ ease_factor: Math.round(ease_factor * 100) / 100,
39082
+ repetitions,
39083
+ last_review: today
39084
+ };
39085
+ }
39086
+ function isDueForReview(nextReview, now) {
39087
+ const currentTime = now ?? Date.now();
39088
+ const todayStart = startOfDay2(currentTime);
39089
+ const dueStart = startOfDay2(parseDateStr2(nextReview));
39090
+ return dueStart <= todayStart;
39091
+ }
39092
+ function computeOverdueDays(nextReview, now) {
39093
+ const currentTime = now ?? Date.now();
39094
+ const todayStart = startOfDay2(currentTime);
39095
+ const dueStart = startOfDay2(parseDateStr2(nextReview));
39096
+ return Math.round((todayStart - dueStart) / DAY_MS2);
39097
+ }
39098
+ function parseSrsMetadata(frontmatter) {
39099
+ const nextReview = frontmatter.next_review;
39100
+ const interval = frontmatter.interval;
39101
+ const easeFactor = frontmatter.ease_factor;
39102
+ const repetitions = frontmatter.repetitions;
39103
+ const lastReview = frontmatter.last_review;
39104
+ if (typeof nextReview !== "string" || typeof interval !== "number" || typeof easeFactor !== "number" || typeof repetitions !== "number" || typeof lastReview !== "string") {
39105
+ return null;
39106
+ }
39107
+ return {
39108
+ next_review: nextReview,
39109
+ interval,
39110
+ ease_factor: easeFactor,
39111
+ repetitions,
39112
+ last_review: lastReview
39113
+ };
39114
+ }
39115
+ function updateFrontmatterSrs(fileContent, srs) {
39116
+ const srsFields = [
39117
+ `next_review: "${srs.next_review}"`,
39118
+ `interval: ${srs.interval}`,
39119
+ `ease_factor: ${srs.ease_factor}`,
39120
+ `repetitions: ${srs.repetitions}`,
39121
+ `last_review: "${srs.last_review}"`
39122
+ ];
39123
+ const SRS_KEYS = ["next_review", "interval", "ease_factor", "repetitions", "last_review"];
39124
+ if (!fileContent.startsWith("---")) {
39125
+ return `---
39126
+ ${srsFields.join("\n")}
39127
+ ---
39128
+ ${fileContent}`;
39129
+ }
39130
+ const endIdx = fileContent.indexOf("---", 3);
39131
+ if (endIdx === -1) {
39132
+ return fileContent;
39133
+ }
39134
+ const fmContent = fileContent.slice(4, endIdx);
39135
+ const afterFm = fileContent.slice(endIdx + 3);
39136
+ const existingLines = fmContent.split("\n").filter((line) => {
39137
+ const key = line.split(":")[0]?.trim();
39138
+ return !SRS_KEYS.includes(key ?? "");
39139
+ });
39140
+ const cleanedLines = existingLines.filter((l) => l.trim() !== "");
39141
+ const newFmLines = [...cleanedLines, ...srsFields];
39142
+ return `---
39143
+ ${newFmLines.join("\n")}
39144
+ ---${afterFm}`;
39145
+ }
39146
+ function removeFrontmatterSrs(fileContent) {
39147
+ const SRS_KEYS = ["next_review", "interval", "ease_factor", "repetitions", "last_review"];
39148
+ if (!fileContent.startsWith("---")) {
39149
+ return fileContent;
39150
+ }
39151
+ const endIdx = fileContent.indexOf("---", 3);
39152
+ if (endIdx === -1) {
39153
+ return fileContent;
39154
+ }
39155
+ const fmContent = fileContent.slice(4, endIdx);
39156
+ const afterFm = fileContent.slice(endIdx + 3);
39157
+ const filteredLines = fmContent.split("\n").filter((line) => {
39158
+ const key = line.split(":")[0]?.trim();
39159
+ return !SRS_KEYS.includes(key ?? "");
39160
+ });
39161
+ const cleanedLines = filteredLines.filter((l) => l.trim() !== "");
39162
+ if (cleanedLines.length === 0) {
39163
+ return afterFm.replace(/^\n/, "");
39164
+ }
39165
+ return `---
39166
+ ${cleanedLines.join("\n")}
39167
+ ---${afterFm}`;
39168
+ }
39169
+ function filterDueCards(files, tilPath, now, limit = 20) {
39170
+ const currentTime = now ?? Date.now();
39171
+ const cards = [];
39172
+ for (const file2 of files) {
39173
+ if (!file2.path.startsWith(tilPath + "/"))
39174
+ continue;
39175
+ if (file2.extension !== "md")
39176
+ continue;
39177
+ if (file2.path.split("/").pop() === "backlog.md")
39178
+ continue;
39179
+ const srs = parseSrsMetadata(file2.frontmatter);
39180
+ if (!srs)
39181
+ continue;
39182
+ if (!isDueForReview(srs.next_review, currentTime))
39183
+ continue;
39184
+ cards.push({
39185
+ path: file2.path,
39186
+ category: extractCategory(file2.path, tilPath),
39187
+ title: file2.title,
39188
+ dueDate: srs.next_review,
39189
+ overdueDays: computeOverdueDays(srs.next_review, currentTime),
39190
+ interval: srs.interval,
39191
+ repetitions: srs.repetitions,
39192
+ ease_factor: srs.ease_factor
39193
+ });
39194
+ }
39195
+ cards.sort((a, b) => b.overdueDays - a.overdueDays);
39196
+ return cards.slice(0, limit);
39197
+ }
39198
+ function computeReviewStats(files, tilPath, now) {
39199
+ const currentTime = now ?? Date.now();
39200
+ const today = formatDate2(currentTime);
39201
+ let dueToday = 0;
39202
+ let overdueCount = 0;
39203
+ let totalScheduled = 0;
39204
+ let totalReviewed = 0;
39205
+ let easeSum = 0;
39206
+ for (const file2 of files) {
39207
+ if (!file2.path.startsWith(tilPath + "/"))
39208
+ continue;
39209
+ if (file2.extension !== "md")
39210
+ continue;
39211
+ if (file2.path.split("/").pop() === "backlog.md")
39212
+ continue;
39213
+ const srs = parseSrsMetadata(file2.frontmatter);
39214
+ if (!srs)
39215
+ continue;
39216
+ totalScheduled++;
39217
+ easeSum += srs.ease_factor;
39218
+ if (srs.last_review === today) {
39219
+ totalReviewed++;
39220
+ }
39221
+ const overdue = computeOverdueDays(srs.next_review, currentTime);
39222
+ if (overdue > 0) {
39223
+ overdueCount++;
39224
+ dueToday++;
39225
+ } else if (overdue === 0) {
39226
+ dueToday++;
39227
+ }
39228
+ }
39229
+ return {
39230
+ dueToday,
39231
+ overdueCount,
39232
+ totalReviewed,
39233
+ totalScheduled,
39234
+ averageEase: totalScheduled > 0 ? Math.round(easeSum / totalScheduled * 100) / 100 : 0,
39235
+ reviewStreak: computeReviewStreak(files, tilPath, currentTime)
39236
+ };
39237
+ }
39238
+ function computeReviewStreak(files, tilPath, now) {
39239
+ const currentTime = now ?? Date.now();
39240
+ const reviewDays = /* @__PURE__ */ new Set();
39241
+ for (const file2 of files) {
39242
+ if (!file2.path.startsWith(tilPath + "/"))
39243
+ continue;
39244
+ if (file2.extension !== "md")
39245
+ continue;
39246
+ if (file2.path.split("/").pop() === "backlog.md")
39247
+ continue;
39248
+ const srs = parseSrsMetadata(file2.frontmatter);
39249
+ if (!srs)
39250
+ continue;
39251
+ reviewDays.add(srs.last_review);
39252
+ }
39253
+ if (reviewDays.size === 0)
39254
+ return 0;
39255
+ const todayStart = startOfDay2(currentTime);
39256
+ const cursor = new Date(todayStart);
39257
+ let streak = 0;
39258
+ for (let i = 0; i < 365; i++) {
39259
+ const dayStr = formatDate2(cursor.getTime());
39260
+ if (reviewDays.has(dayStr)) {
39261
+ streak++;
39262
+ } else {
39263
+ if (i === 0) {
39264
+ cursor.setDate(cursor.getDate() - 1);
39265
+ continue;
39266
+ }
39267
+ break;
39268
+ }
39269
+ cursor.setDate(cursor.getDate() - 1);
39270
+ }
39271
+ return streak;
39272
+ }
39273
+
39028
39274
  // src/mcp/tools.ts
39029
39275
  function registerTools(server, storage, metadata, tilPath) {
39030
39276
  server.registerTool(
@@ -39277,11 +39523,12 @@ ${text}` }] };
39277
39523
  "til_dashboard",
39278
39524
  {
39279
39525
  title: "Dashboard Stats",
39280
- description: "\uD559\uC2B5 \uB300\uC2DC\uBCF4\uB4DC \uD1B5\uACC4\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4 (\uC694\uC57D, \uD788\uD2B8\uB9F5, \uCE74\uD14C\uACE0\uB9AC, \uBC31\uB85C\uADF8)",
39526
+ description: "\uD559\uC2B5 \uB300\uC2DC\uBCF4\uB4DC \uD1B5\uACC4\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4 (\uC694\uC57D, \uD788\uD2B8\uB9F5, \uCE74\uD14C\uACE0\uB9AC, \uBC31\uB85C\uADF8, \uBCF5\uC2B5)",
39281
39527
  inputSchema: external_exports3.object({})
39282
39528
  },
39283
39529
  async () => {
39284
39530
  const allFiles = await storage.listFiles();
39531
+ let reviewDueCount = 0;
39285
39532
  const files = await Promise.all(
39286
39533
  allFiles.filter((f) => f.extension === "md").map(async (f) => {
39287
39534
  const fileMeta = await metadata.getFileMetadata(f.path);
@@ -39289,6 +39536,12 @@ ${text}` }] };
39289
39536
  const createdDate = typeof fmDate === "string" ? fmDate : void 0;
39290
39537
  const fmTags = fileMeta?.frontmatter?.tags;
39291
39538
  const tags = Array.isArray(fmTags) ? fmTags.filter((t) => typeof t === "string") : void 0;
39539
+ if (f.path.startsWith(tilPath + "/") && f.name !== "backlog.md") {
39540
+ const nextReview = fileMeta?.frontmatter?.next_review;
39541
+ if (typeof nextReview === "string" && isDueForReview(nextReview)) {
39542
+ reviewDueCount++;
39543
+ }
39544
+ }
39292
39545
  return {
39293
39546
  path: f.path,
39294
39547
  extension: f.extension,
@@ -39322,7 +39575,7 @@ ${text}` }] };
39322
39575
  });
39323
39576
  }
39324
39577
  }
39325
- const stats = computeEnhancedStats(files, tilPath, backlogEntries);
39578
+ const stats = computeEnhancedStats(files, tilPath, backlogEntries, void 0, reviewDueCount);
39326
39579
  return {
39327
39580
  content: [
39328
39581
  { type: "text", text: JSON.stringify(stats) }
@@ -39330,6 +39583,97 @@ ${text}` }] };
39330
39583
  };
39331
39584
  }
39332
39585
  );
39586
+ server.registerTool(
39587
+ "til_review_list",
39588
+ {
39589
+ title: "Review List",
39590
+ description: "\uC624\uB298 \uBCF5\uC2B5\uD560 TIL \uCE74\uB4DC \uBAA9\uB85D\uACFC \uD1B5\uACC4\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4",
39591
+ inputSchema: external_exports3.object({
39592
+ category: external_exports3.string().optional().describe("\uD2B9\uC815 \uCE74\uD14C\uACE0\uB9AC\uB9CC \uD544\uD130\uB9C1"),
39593
+ limit: external_exports3.number().min(1).max(100).optional().describe("\uCD5C\uB300 \uCE74\uB4DC \uC218 (\uAE30\uBCF8 20)")
39594
+ })
39595
+ },
39596
+ async ({ category, limit }) => {
39597
+ const allFiles = await storage.listFiles();
39598
+ const srsFiles = [];
39599
+ for (const f of allFiles) {
39600
+ if (!f.path.startsWith(tilPath + "/"))
39601
+ continue;
39602
+ if (f.extension !== "md")
39603
+ continue;
39604
+ if (f.name === "backlog.md")
39605
+ continue;
39606
+ if (category) {
39607
+ const cat = extractCategory(f.path, tilPath);
39608
+ if (cat !== category)
39609
+ continue;
39610
+ }
39611
+ const fileMeta = await metadata.getFileMetadata(f.path);
39612
+ if (!fileMeta)
39613
+ continue;
39614
+ const headings = fileMeta.headings ?? [];
39615
+ const title = headings.length > 0 ? headings[0] : f.name.replace(/\.md$/, "");
39616
+ srsFiles.push({
39617
+ path: f.path,
39618
+ extension: f.extension,
39619
+ title,
39620
+ frontmatter: fileMeta.frontmatter ?? {}
39621
+ });
39622
+ }
39623
+ const effectiveLimit = limit ?? 20;
39624
+ const cards = filterDueCards(srsFiles, tilPath, void 0, effectiveLimit);
39625
+ const stats = computeReviewStats(srsFiles, tilPath);
39626
+ const remaining = Math.max(0, stats.dueToday - cards.length);
39627
+ const data = { cards, stats, remaining };
39628
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
39629
+ }
39630
+ );
39631
+ server.registerTool(
39632
+ "til_review_update",
39633
+ {
39634
+ title: "Update Review",
39635
+ description: "TIL \uD30C\uC77C\uC758 \uBCF5\uC2B5 \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uAC70\uB098 \uBCF5\uC2B5 \uB300\uC0C1\uC5D0\uC11C \uC81C\uAC70\uD569\uB2C8\uB2E4",
39636
+ inputSchema: external_exports3.object({
39637
+ path: external_exports3.string().describe("TIL \uD30C\uC77C \uACBD\uB85C"),
39638
+ grade: external_exports3.number().min(0).max(5).optional().describe("SM-2 \uB4F1\uAE09 (0-5, action=review \uC2DC \uD544\uC218)"),
39639
+ action: external_exports3.enum(["review", "remove"]).optional().describe("review(\uAE30\uBCF8): \uBCF5\uC2B5 \uAE30\uB85D, remove: \uBCF5\uC2B5 \uD574\uC81C")
39640
+ })
39641
+ },
39642
+ async ({ path: path6, grade, action }) => {
39643
+ const effectiveAction = action ?? "review";
39644
+ const content = await storage.readFile(path6);
39645
+ if (content === null) {
39646
+ return { content: [{ type: "text", text: `Error: \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 ${path6}` }], isError: true };
39647
+ }
39648
+ if (effectiveAction === "remove") {
39649
+ const updated2 = removeFrontmatterSrs(content);
39650
+ await storage.writeFile(path6, updated2);
39651
+ return { content: [{ type: "text", text: JSON.stringify({ path: path6, removed: true }) }] };
39652
+ }
39653
+ if (grade === void 0) {
39654
+ return { content: [{ type: "text", text: "Error: action=review \uC2DC grade(0-5)\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4" }], isError: true };
39655
+ }
39656
+ const fileMeta = await metadata.getFileMetadata(path6);
39657
+ const fm = fileMeta?.frontmatter ?? {};
39658
+ const currentSrs = parseSrsMetadata(fm) ?? createDefaultSrsMetadata();
39659
+ const newSrs = computeNextReview(currentSrs, grade);
39660
+ const updated = updateFrontmatterSrs(content, newSrs);
39661
+ await storage.writeFile(path6, updated);
39662
+ return {
39663
+ content: [{
39664
+ type: "text",
39665
+ text: JSON.stringify({
39666
+ path: path6,
39667
+ grade,
39668
+ next_review: newSrs.next_review,
39669
+ interval: newSrs.interval,
39670
+ ease_factor: newSrs.ease_factor,
39671
+ repetitions: newSrs.repetitions
39672
+ })
39673
+ }]
39674
+ };
39675
+ }
39676
+ );
39333
39677
  }
39334
39678
 
39335
39679
  // src/mcp/server.ts
@@ -39429,7 +39773,7 @@ var SKILL_default2 = '---\nname: backlog\ndescription: "\uD559\uC2B5 \uBC31\uB85
39429
39773
  var SKILL_default3 = '---\nname: research\ndescription: "\uC8FC\uC81C\uB97C \uB9AC\uC11C\uCE58\uD558\uC5EC \uD559\uC2B5\uC5D0 \uD544\uC694\uD55C \uAC1C\uB150/\uC6A9\uC5B4\uB97C \uD30C\uC545\uD558\uACE0, \uBC31\uB85C\uADF8\uB85C \uC815\uB9AC"\nargument-hint: "<\uC8FC\uC81C> [\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Research Skill\n\n\uC8FC\uC81C \uB9AC\uC11C\uCE58 \u2192 \uAC1C\uB150/\uC758\uC874 \uAD00\uACC4 \uD30C\uC545 \u2192 \uBC31\uB85C\uADF8 \uD30C\uC77C \uC800\uC7A5.\n\n## Phase 1: \uC8FC\uC81C \uB9AC\uC11C\uCE58\n\n1. \uC6F9 \uAC80\uC0C9\uC73C\uB85C \uC8FC\uC81C \uC870\uC0AC, \uD544\uC694 \uAC1C\uB150\xB7\uC6A9\uC5B4\xB7\uC120\uD589 \uC9C0\uC2DD \uD30C\uC545\n2. \uC18C\uC8FC\uC81C \uBD84\uD574 \uD6C4 \uC9C1\uC811 \uB9AC\uC11C\uCE58\n3. \uC18C\uC8FC\uC81C \uAC04 \uC758\uC874 \uAD00\uACC4 \uBD84\uC11D\n\n## Phase 2: \uBC31\uB85C\uADF8 \uC815\uB9AC\n\n1. \uD559\uC2B5 \uC21C\uC11C \uC815\uB82C: \uC120\uD589 \uC9C0\uC2DD \u2192 \uD575\uC2EC \uAC1C\uB150 \u2192 \uC2EC\uD654\n2. \uAC01 \uD56D\uBAA9\uC5D0 1\uC904 \uC124\uBA85\n3. \uC0AC\uC6A9\uC790 \uD53C\uB4DC\uBC31 (\uCD94\uAC00/\uC81C\uAC70/\uC21C\uC11C \uBCC0\uACBD)\n\n## Phase 3: \uC800\uC7A5\n\n1. `./til/{\uCE74\uD14C\uACE0\uB9AC}/backlog.md`\uC5D0 \uC800\uC7A5 (\uD3F4\uB354 \uC790\uB3D9 \uC0DD\uC131)\n2. \uAE30\uC874 backlog.md \uC788\uC73C\uBA74 \uBCD1\uD569:\n - `[x]` \uC644\uB8CC \uD56D\uBAA9 \uBCF4\uC874\n - \uB3D9\uC77C \uD56D\uBAA9 \uCCB4\uD06C \uC0C1\uD0DC \uC720\uC9C0\n - \uAE30\uC874 sources \uBCF4\uC874, \uC0C8 \uD56D\uBAA9\uB9CC \uCD94\uAC00\n3. TIL MOC\uC5D0 \uBC31\uB85C\uADF8 \uB9C1\uD06C \uCD94\uAC00\n4. atomic commit: `\u{1F4CB} research: {\uC8FC\uC81C} \uD559\uC2B5 \uBC31\uB85C\uADF8 - {\uCE74\uD14C\uACE0\uB9AC}` (push \uC548 \uD568)\n\n## \uBC31\uB85C\uADF8 \uD15C\uD50C\uB9BF\n\n```markdown\n---\ntags: [backlog, {\uCE74\uD14C\uACE0\uB9AC}]\naliases: ["Backlog - {\uC8FC\uC81C}"]\nupdated: YYYY-MM-DD\nsources:\n slug-a: [https://url-1]\n---\n\n# {\uC8FC\uC81C} \uD559\uC2B5 \uBC31\uB85C\uADF8\n\n## \uC120\uD589 \uC9C0\uC2DD\n- [ ] [\uAC1C\uB150A](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug-a}.md) - \uC124\uBA85\n\n## \uD575\uC2EC \uAC1C\uB150\n- [ ] [\uAC1C\uB150C](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug-c}.md) - \uC124\uBA85\n\n## \uC2EC\uD654\n- [ ] [\uAC1C\uB150E](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug-e}.md) - \uC124\uBA85\n```\n\n## \uC778\uC218\n\n- \uCCAB \uBC88\uC9F8: \uB9AC\uC11C\uCE58 \uC8FC\uC81C (\uD544\uC218)\n- \uB450 \uBC88\uC9F8: \uCE74\uD14C\uACE0\uB9AC (\uC120\uD0DD, \uBBF8\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uCD94\uB860)\n\n## \uADDC\uCE59\n\n- \uD56D\uBAA9\uB2F9 1\uC904 \uC124\uBA85, 20\uAC1C \uCD08\uACFC \uC2DC \uBD84\uB9AC\n- \uD55C\uAD6D\uC5B4 \uC791\uC131, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n- \uB9C1\uD06C: `[\uD45C\uC2DC\uBA85](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)`\n';
39430
39774
 
39431
39775
  // vault-assets/skills/save/SKILL.md
39432
- var SKILL_default4 = '---\nname: save\ndescription: "\uD559\uC2B5 \uB0B4\uC6A9\uC744 TIL \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 Daily \uB178\uD2B8, MOC, \uBC31\uB85C\uADF8\uB97C \uC77C\uAD04 \uC5C5\uB370\uC774\uD2B8"\nargument-hint: "[\uC8FC\uC81C] [\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Save Skill\n\n\uD559\uC2B5 \uB300\uD654 \u2192 TIL \uD30C\uC77C \uC800\uC7A5 \u2192 Daily/MOC/\uBC31\uB85C\uADF8 \uC5C5\uB370\uC774\uD2B8 \u2192 \uBB38\uC11C \uB9AC\uBDF0 \u2192 \uCEE4\uBC0B.\n\n## MCP \uB3C4\uAD6C\n\n- `til_get_context`: \uAD00\uB828 TIL\xB7\uBC31\uB85C\uADF8 \uD30C\uC545\n- `til_list`: \uAE30\uC874 TIL \uC911\uBCF5 \uD655\uC778\n- `vault_get_active_file`: \uC0AC\uC6A9\uC790 \uD30C\uC77C \uCEE8\uD14D\uC2A4\uD2B8\n\n## Step 1: \uCEE8\uD14D\uC2A4\uD2B8 \uD655\uC778\n\n\uC8FC\uC81C\xB7\uCE74\uD14C\uACE0\uB9AC \uD30C\uC545. \uBD88\uBA85\uD655\uD558\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC9C8\uBB38.\n\n## Step 2: \uB9C1\uD06C \uD6C4\uBCF4 \uD30C\uC545\n\n1. `til_get_context` \uB610\uB294 MOC/\uBC31\uB85C\uADF8\uB85C \uAE30\uC874 TIL\xB7\uBC31\uB85C\uADF8 \uD56D\uBAA9 \uD30C\uC545\n2. \uAE30\uC874 TIL/\uBC31\uB85C\uADF8 \uD56D\uBAA9 \u2192 \uBCF8\uBB38\uC5D0\uC11C \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C \uC0AC\uC6A9\n3. \uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uAC1C\uB150 \u2192 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778 \uD6C4 \uAD00\uB828 \uB178\uD2B8\uC5D0\uB9CC \uCD94\uAC00\n\n## Step 3: TIL \uD30C\uC77C \uC800\uC7A5\n\n\uACBD\uB85C: `./til/{\uCE74\uD14C\uACE0\uB9AC}/{\uC8FC\uC81C\uC2AC\uB7EC\uADF8}.md` (\uC2AC\uB7EC\uADF8: \uC601\uBB38 \uC18C\uBB38\uC790, \uD558\uC774\uD508)\n\n\uB3D9\uC77C \uC2AC\uB7EC\uADF8 \uD30C\uC77C \uC788\uC744 \uB54C:\n- `/til` \uC2EC\uD654 \uD559\uC2B5\uC774 \uC5F0\uC18D\uB41C \uACBD\uC6B0\uB9CC \uC790\uB3D9 \uBCD1\uD569 (\uAE30\uC874 \uB0B4\uC6A9 \uC720\uC9C0 + \uBCF4\uAC15, `updated` \uCD94\uAC00)\n- \uADF8 \uC678: \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCD1\uD569/\uB36E\uC5B4\uC4F0\uAE30 \uD655\uC778\n\n### TIL \uD15C\uD50C\uB9BF\n\n```markdown\n---\ndate: YYYY-MM-DDTHH:mm:ss\ncategory: \uCE74\uD14C\uACE0\uB9AC\ntags: [til, \uD0DC\uADF81]\naliases: ["\uD55C\uAE00 \uC81C\uBAA9", "\uC601\uBB38 \uC81C\uBAA9"]\n---\n\n# \uC81C\uBAA9\n\n> [!tldr] \uD55C\uC904 \uC694\uC57D\n\n## \uD575\uC2EC \uB0B4\uC6A9\n## \uC608\uC2DC\n## \uCC38\uACE0 \uC790\uB8CC\n- [\uC81C\uBAA9](URL)\n## \uAD00\uB828 \uB178\uD2B8\n- [TIL](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)\n```\n\n- `date`: `date +%Y-%m-%dT%H:%M:%S` \uBA85\uB839\uC73C\uB85C \uB85C\uCEEC \uC2DC\uAC01 \uC870\uD68C\n- \uB9C1\uD06C: `[\uD45C\uC2DC\uBA85](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)` \u2014 `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0\n\n## Step 4: \uC5F0\uAD00 \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8\n\n\uC544\uB798 3\uAC1C \uD30C\uC77C\uC744 **\uC9C1\uC811** \uC21C\uCC28 \uC5C5\uB370\uC774\uD2B8\uD55C\uB2E4 (subagent \uC0AC\uC6A9 \uAE08\uC9C0):\n\n1. Daily \uB178\uD2B8 (`./Daily/YYYY-MM-DD.md`): \uCE74\uD14C\uACE0\uB9AC\uBCC4 TIL \uB9C1\uD06C \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n2. TIL MOC (`./til/TIL MOC.md`): \uCE74\uD14C\uACE0\uB9AC \uC139\uC158\uC5D0 \uD56D\uBAA9 \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n3. \uBC31\uB85C\uADF8 (`./til/{\uCE74\uD14C\uACE0\uB9AC}/backlog.md`): \uD574\uB2F9 \uD56D\uBAA9 `[ ]` \u2192 `[x]`\n\n\uAC01 \uD30C\uC77C: Read \u2192 \uC704\uCE58 \uD655\uC778 \u2192 Edit. \uD30C\uC77C \uC5C6\uC73C\uBA74 \uC0DD\uC131.\n\n## Step 5: \uBB38\uC11C \uB9AC\uBDF0\n\n\uC800\uC7A5\uB41C TIL \uC804\uCCB4 \uB0B4\uC6A9 \uD45C\uC2DC \u2192 `AskUserQuestion`\uC73C\uB85C \uD655\uC778 ("\uD655\uC778 \uC644\uB8CC" / "\uC218\uC815 \uD544\uC694").\n\n## Step 6: git commit\n\n`\u{1F4DD} til: {\uD55C\uAE00 \uC81C\uBAA9}({\uC601\uBB38 \uC81C\uBAA9}) - {\uCE74\uD14C\uACE0\uB9AC}` (push \uC548 \uD568)\n\n## \uADDC\uCE59\n\n- frontmatter \uD544\uC218: date, category, tags, aliases (\uB204\uB77D \uC2DC \uC800\uC7A5 \uC804 \uBCF4\uC644)\n- tags\uC5D0 \uBC18\uB4DC\uC2DC "til" \uD3EC\uD568 (\uC815\uC801 \uC0AC\uC774\uD2B8 \uD544\uD130 \uAE30\uC900)\n- `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0 \u2014 `[\uD45C\uC2DC\uBA85](\uACBD\uB85C)` \uD615\uC2DD\uB9CC \uC0AC\uC6A9\n- TIL\uB9CC \uC800\uC7A5\uD558\uACE0 Daily/MOC/\uBC31\uB85C\uADF8 \uB204\uB77D\uD558\uC9C0 \uC54A\uB294\uB2E4\n- Callout \uD65C\uC6A9: `> [!tldr]`, `> [!example]`, `> [!warning]`, `> [!tip]`\n- \uBCF5\uC7A1\uD55C \uAC1C\uB150\uC740 Mermaid \uB2E4\uC774\uC5B4\uADF8\uB7A8\uC73C\uB85C \uC2DC\uAC01\uD654 (TIL\uB2F9 \uCD5C\uB300 1\uAC1C)\n- \uBBFC\uAC10 \uC815\uBCF4 \uB300\uCCB4\uAC12 \uC0AC\uC6A9\n- \uD55C\uAD6D\uC5B4 \uC791\uC131, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n';
39776
+ var SKILL_default4 = '---\nname: save\ndescription: "\uD559\uC2B5 \uB0B4\uC6A9\uC744 TIL \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 Daily \uB178\uD2B8, MOC, \uBC31\uB85C\uADF8\uB97C \uC77C\uAD04 \uC5C5\uB370\uC774\uD2B8"\nargument-hint: "[\uC8FC\uC81C] [\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Save Skill\n\n\uD559\uC2B5 \uB300\uD654 \u2192 TIL \uD30C\uC77C \uC800\uC7A5 \u2192 Daily/MOC/\uBC31\uB85C\uADF8 \uC5C5\uB370\uC774\uD2B8 \u2192 \uBB38\uC11C \uB9AC\uBDF0 \u2192 \uCEE4\uBC0B.\n\n## MCP \uB3C4\uAD6C\n\n- `til_get_context`: \uAD00\uB828 TIL\xB7\uBC31\uB85C\uADF8 \uD30C\uC545\n- `til_list`: \uAE30\uC874 TIL \uC911\uBCF5 \uD655\uC778\n- `vault_get_active_file`: \uC0AC\uC6A9\uC790 \uD30C\uC77C \uCEE8\uD14D\uC2A4\uD2B8\n\n## Step 1: \uCEE8\uD14D\uC2A4\uD2B8 \uD655\uC778\n\n\uC8FC\uC81C\xB7\uCE74\uD14C\uACE0\uB9AC \uD30C\uC545. \uBD88\uBA85\uD655\uD558\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC9C8\uBB38.\n\n## Step 2: \uB9C1\uD06C \uD6C4\uBCF4 \uD30C\uC545\n\n1. `til_get_context` \uB610\uB294 MOC/\uBC31\uB85C\uADF8\uB85C \uAE30\uC874 TIL\xB7\uBC31\uB85C\uADF8 \uD56D\uBAA9 \uD30C\uC545\n2. \uAE30\uC874 TIL/\uBC31\uB85C\uADF8 \uD56D\uBAA9 \u2192 \uBCF8\uBB38\uC5D0\uC11C \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C \uC0AC\uC6A9\n3. \uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uAC1C\uB150 \u2192 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778 \uD6C4 \uAD00\uB828 \uB178\uD2B8\uC5D0\uB9CC \uCD94\uAC00\n\n## Step 3: TIL \uD30C\uC77C \uC800\uC7A5\n\n\uACBD\uB85C: `./til/{\uCE74\uD14C\uACE0\uB9AC}/{\uC8FC\uC81C\uC2AC\uB7EC\uADF8}.md` (\uC2AC\uB7EC\uADF8: \uC601\uBB38 \uC18C\uBB38\uC790, \uD558\uC774\uD508)\n\n\uB3D9\uC77C \uC2AC\uB7EC\uADF8 \uD30C\uC77C \uC788\uC744 \uB54C:\n- `/til` \uC2EC\uD654 \uD559\uC2B5\uC774 \uC5F0\uC18D\uB41C \uACBD\uC6B0\uB9CC \uC790\uB3D9 \uBCD1\uD569 (\uAE30\uC874 \uB0B4\uC6A9 \uC720\uC9C0 + \uBCF4\uAC15, `updated` \uCD94\uAC00)\n- \uADF8 \uC678: \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCD1\uD569/\uB36E\uC5B4\uC4F0\uAE30 \uD655\uC778\n\n### TIL \uD15C\uD50C\uB9BF\n\n```markdown\n---\ndate: YYYY-MM-DDTHH:mm:ss\ncategory: \uCE74\uD14C\uACE0\uB9AC\ntags: [til, \uD0DC\uADF81]\naliases: ["\uD55C\uAE00 \uC81C\uBAA9", "\uC601\uBB38 \uC81C\uBAA9"]\n---\n\n# \uC81C\uBAA9\n\n> [!tldr] \uD55C\uC904 \uC694\uC57D\n\n## \uD575\uC2EC \uB0B4\uC6A9\n## \uC608\uC2DC\n## \uCC38\uACE0 \uC790\uB8CC\n- [\uC81C\uBAA9](URL)\n## \uAD00\uB828 \uB178\uD2B8\n- [TIL](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)\n```\n\n- `date`: `date +%Y-%m-%dT%H:%M:%S` \uBA85\uB839\uC73C\uB85C \uB85C\uCEEC \uC2DC\uAC01 \uC870\uD68C\n- \uB9C1\uD06C: `[\uD45C\uC2DC\uBA85](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)` \u2014 `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0\n\n## Step 4: \uC5F0\uAD00 \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8\n\n\uC544\uB798 3\uAC1C \uD30C\uC77C\uC744 **\uC9C1\uC811** \uC21C\uCC28 \uC5C5\uB370\uC774\uD2B8\uD55C\uB2E4 (subagent \uC0AC\uC6A9 \uAE08\uC9C0):\n\n1. Daily \uB178\uD2B8 (`./Daily/YYYY-MM-DD.md`): \uCE74\uD14C\uACE0\uB9AC\uBCC4 TIL \uB9C1\uD06C \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n2. TIL MOC (`./til/TIL MOC.md`): \uCE74\uD14C\uACE0\uB9AC \uC139\uC158\uC5D0 \uD56D\uBAA9 \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n3. \uBC31\uB85C\uADF8 (`./til/{\uCE74\uD14C\uACE0\uB9AC}/backlog.md`): \uD574\uB2F9 \uD56D\uBAA9 `[ ]` \u2192 `[x]`\n\n\uAC01 \uD30C\uC77C: Read \u2192 \uC704\uCE58 \uD655\uC778 \u2192 Edit. \uD30C\uC77C \uC5C6\uC73C\uBA74 \uC0DD\uC131.\n\n## Step 5: \uBB38\uC11C \uB9AC\uBDF0\n\n\uC800\uC7A5\uB41C TIL \uC804\uCCB4 \uB0B4\uC6A9 \uD45C\uC2DC \u2192 `AskUserQuestion`\uC73C\uB85C \uD655\uC778 ("\uD655\uC778 \uC644\uB8CC" / "\uC218\uC815 \uD544\uC694").\n\n## Step 6: \uBCF5\uC2B5 \uB4F1\uB85D\n\n`AskUserQuestion`\uC73C\uB85C "\uC774 TIL\uC744 \uBCF5\uC2B5 \uB300\uC0C1\uC5D0 \uCD94\uAC00\uD560\uAE4C\uC694?" \uC9C8\uBB38.\n\uC0AC\uC6A9\uC790 \uB3D9\uC758 \uC2DC `til_review_update` (action: "review", grade: 4) \uD638\uCD9C\uD558\uC5EC SRS \uBA54\uD0C0\uB370\uC774\uD130 \uC0DD\uC131.\n\n## Step 7: git commit\n\n`\u{1F4DD} til: {\uD55C\uAE00 \uC81C\uBAA9}({\uC601\uBB38 \uC81C\uBAA9}) - {\uCE74\uD14C\uACE0\uB9AC}` (push \uC548 \uD568)\n\n## \uADDC\uCE59\n\n- frontmatter \uD544\uC218: date, category, tags, aliases (\uB204\uB77D \uC2DC \uC800\uC7A5 \uC804 \uBCF4\uC644)\n- tags\uC5D0 \uBC18\uB4DC\uC2DC "til" \uD3EC\uD568 (\uC815\uC801 \uC0AC\uC774\uD2B8 \uD544\uD130 \uAE30\uC900)\n- `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0 \u2014 `[\uD45C\uC2DC\uBA85](\uACBD\uB85C)` \uD615\uC2DD\uB9CC \uC0AC\uC6A9\n- TIL\uB9CC \uC800\uC7A5\uD558\uACE0 Daily/MOC/\uBC31\uB85C\uADF8 \uB204\uB77D\uD558\uC9C0 \uC54A\uB294\uB2E4\n- Callout \uD65C\uC6A9: `> [!tldr]`, `> [!example]`, `> [!warning]`, `> [!tip]`\n- \uBCF5\uC7A1\uD55C \uAC1C\uB150\uC740 Mermaid \uB2E4\uC774\uC5B4\uADF8\uB7A8\uC73C\uB85C \uC2DC\uAC01\uD654 (TIL\uB2F9 \uCD5C\uB300 1\uAC1C)\n- \uBBFC\uAC10 \uC815\uBCF4 \uB300\uCCB4\uAC12 \uC0AC\uC6A9\n- \uD55C\uAD6D\uC5B4 \uC791\uC131, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n';
39433
39777
 
39434
39778
  // vault-assets/skills/migrate-links/SKILL.md
39435
39779
  var SKILL_default5 = '---\nname: migrate-links\ndescription: "vault\uC758 [[wikilink]]\uB97C \uD45C\uC900 \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C\uB85C \uC77C\uAD04 \uBCC0\uD658"\nargument-hint: ""\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Migrate Links Skill\n\n`[[wikilink]]` \u2192 `[text](path.md)` \uC77C\uAD04 \uBCC0\uD658.\n\n## CLI\n\n```bash\nnode .obsidian/plugins/oh-my-til/migrate-links.mjs . scan\nnode .obsidian/plugins/oh-my-til/migrate-links.mjs . migrate\nnode .obsidian/plugins/oh-my-til/migrate-links.mjs . verify\n```\n\n## \uC6CC\uD06C\uD50C\uB85C\uC6B0\n\n1. **\uC2A4\uCE94**: `scan` \uC2E4\uD589 \u2192 wikilink \uC5C6\uC73C\uBA74 \uC885\uB8CC, \uC788\uC73C\uBA74 `AskUserQuestion`\uC73C\uB85C \uD655\uC778\n2. **\uBCC0\uD658**: `migrate` \uC2E4\uD589\n3. **\uAC80\uC99D**: `verify` \uC2E4\uD589 \u2192 \uC794\uC5EC wikilink \uC548\uB0B4\n4. **\uCEE4\uBC0B**: `\u267B\uFE0F refactor: [[wikilink]] \u2192 \uD45C\uC900 \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C \uC77C\uAD04 \uBCC0\uD658` (push \uC548 \uD568)\n\n## \uBCC0\uD658 \uADDC\uCE59\n\n- `[[path|Display]]` \u2192 `[Display](path.md)`\n- `[[path]]` \u2192 `[path](path.md)`\n- \uCF54\uB4DC \uBE14\uB85D \uB0B4\uBD80 \uC81C\uC678, \uD14C\uC774\uBE14 `\\|` \uC774\uC2A4\uCF00\uC774\uD504 \uCC98\uB9AC\n';
@@ -39443,8 +39787,11 @@ var SKILL_default7 = '---\nname: setup-pages\ndescription: "(deprecated) /omt-se
39443
39787
  // vault-assets/skills/omt-setup/SKILL.md
39444
39788
  var SKILL_default8 = '---\nname: omt-setup\ndescription: "oh-my-til \uD1B5\uD569 \uC124\uC815 \u2014 \uBC30\uD3EC \uC124\uC815"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# OMT Setup Skill\n\noh-my-til \uC124\uC815\uC744 \uD55C \uACF3\uC5D0\uC11C \uAD00\uB9AC. \uC11C\uBE0C\uCEE4\uB9E8\uB4DC\uB85C \uB3D9\uC791.\n\n## \uC11C\uBE0C\uCEE4\uB9E8\uB4DC\n\n### `/omt-setup` (\uC778\uC218 \uC5C6\uC74C)\n\n`oh-my-til.json` \uC77D\uC5B4\uC11C \uD604\uC7AC \uC124\uC815 \uD45C\uC2DC + \uC11C\uBE0C\uCEE4\uB9E8\uB4DC \uC548\uB0B4:\n- `deploy` \u2014 GitHub Pages \uBC30\uD3EC \uC124\uC815\n\n### `/omt-setup deploy`\n\nGitHub Pages \uBC30\uD3EC \uC124\uC815:\n1. `.git/` \uD655\uC778 (\uC5C6\uC73C\uBA74 \uC548\uB0B4 \uD6C4 \uC885\uB8CC)\n2. `.github/workflows/deploy-til.yml` \uD655\uC778 (\uC788\uC73C\uBA74 \uC218\uC815 \uD544\uC694 \uC5EC\uBD80 \uC9C8\uBB38)\n3. `oh-my-til.json` deploy \uC139\uC158 \uC124\uC815 (\uC81C\uBAA9, \uBD80\uC81C\uBAA9, GitHub URL)\n4. \uC6CC\uD06C\uD50C\uB85C\uC6B0 YAML \uC0DD\uC131\n5. \uC644\uB8CC \uC548\uB0B4 (Settings \u2192 Pages \u2192 GitHub Actions \uC120\uD0DD, \uCEE4\uBC0B\xB7push \uBA85\uB839\uC5B4)\n\n## \uADDC\uCE59\n\n- \uD55C\uAD6D\uC5B4 \uCD9C\uB825\n- `oh-my-til.json`\uC758 \uAE30\uC874 \uC124\uC815 \uBCF4\uC874, \uD574\uB2F9 \uC139\uC158\uB9CC \uCD94\uAC00/\uC218\uC815\n- \uCEE4\uBC0B\uC740 \uD558\uC9C0 \uC54A\uC74C (\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBA85\uB839\uC5B4 \uC548\uB0B4\uB9CC)\n';
39445
39789
 
39790
+ // vault-assets/skills/til-review/SKILL.md
39791
+ var SKILL_default9 = '---\nname: til-review\ndescription: "SRS \uAE30\uBC18 TIL \uBCF5\uC2B5 \uC138\uC158 (\uAC04\uACA9 \uBC18\uBCF5 \uD559\uC2B5)"\nargument-hint: "[\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Review Skill\n\nSRS(\uAC04\uACA9 \uBC18\uBCF5) \uAE30\uBC18 TIL \uBCF5\uC2B5 \uC138\uC158. SM-2 \uC54C\uACE0\uB9AC\uC998\uC73C\uB85C \uBCF5\uC2B5 \uC77C\uC815\uC744 \uAD00\uB9AC\uD55C\uB2E4.\n\n## MCP \uB3C4\uAD6C\n\n- `til_review_list`: \uC624\uB298 \uBCF5\uC2B5 \uB300\uC0C1 \uCE74\uB4DC \uBAA9\uB85D + \uD1B5\uACC4\n- `til_review_update`: \uBCF5\uC2B5 \uACB0\uACFC \uAE30\uB85D (grade 0-5) \uB610\uB294 \uBCF5\uC2B5 \uD574\uC81C\n- `vault_read_note`: \uCE74\uB4DC \uB0B4\uC6A9 \uC77D\uAE30\n\n## Step 1: \uBCF5\uC2B5 \uCE74\uB4DC \uB85C\uB4DC\n\n`til_review_list` \uD638\uCD9C (\uCE74\uD14C\uACE0\uB9AC \uC778\uC790 \uC788\uC73C\uBA74 \uC804\uB2EC).\n\n- \uCE74\uB4DC 0\uAC1C \u2192 "\uC624\uB298 \uBCF5\uC2B5 \uC5C6\uC74C" \uC548\uB0B4 + \uBBF8\uB4F1\uB85D TIL \uB4F1\uB85D \uC81C\uC548 (Step 5\uB85C)\n- \uCE74\uB4DC \uC788\uC74C \u2192 \uBAA9\uB85D \uD45C\uC2DC + Step 2\uB85C\n\n## Step 2: \uD3C9\uAC00 \uBAA8\uB4DC \uC120\uD0DD\n\n`AskUserQuestion`\uC73C\uB85C \uC120\uD0DD:\n- **\uAC04\uB2E8 \uBAA8\uB4DC**: "\uAE30\uC5B5\uB0A8 / \uBAA8\uB984" 2\uB2E8\uACC4 (\uB0B4\uBD80\uC801\uC73C\uB85C grade 4 / 1)\n- **\uC0C1\uC138 \uBAA8\uB4DC**: 0~5\uC810 \uC790\uAE30 \uD3C9\uAC00\n\n## Step 3: \uCE74\uB4DC\uBCC4 \uBCF5\uC2B5 \uB8E8\uD504\n\n\uAC01 \uCE74\uB4DC\uC5D0 \uB300\uD574:\n\n1. \uC81C\uBAA9\xB7\uCE74\uD14C\uACE0\uB9AC\xB7\uBCF5\uC2B5 \uC815\uBCF4(\uBC18\uBCF5 \uD69F\uC218, EF, \uC5F0\uCCB4\uC77C) \uD45C\uC2DC\n2. `vault_read_note`\uB85C \uB0B4\uC6A9 \uC77D\uAE30\n3. \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC9C8\uBB38 \uD615\uC2DD\uC73C\uB85C \uC81C\uC2DC (\uB0B4\uC6A9 \uAE30\uBC18\uC73C\uB85C 1~2\uAC1C \uC9C8\uBB38 \uC0DD\uC131)\n4. \uC0AC\uC6A9\uC790 \uB2F5\uBCC0 \uB300\uAE30\n5. \uD53C\uB4DC\uBC31 \uC81C\uACF5 (\uC815\uB2F5/\uBCF4\uCDA9 \uC124\uBA85)\n6. \uD3C9\uAC00 \uC785\uB825:\n - \uAC04\uB2E8 \uBAA8\uB4DC: "\uAE30\uC5B5\uB0A8" / "\uBAA8\uB984" \u2192 grade 4 / 1\n - \uC0C1\uC138 \uBAA8\uB4DC: 0~5\uC810\n7. `til_review_update` (action: "review", grade) \uD638\uCD9C\n8. \uACB0\uACFC \uC694\uC57D (\uB2E4\uC74C \uBCF5\uC2B5\uC77C, interval) \uD45C\uC2DC\n\n## Step 4: \uC644\uB8CC \uD1B5\uACC4\n\n\uBAA8\uB4E0 \uCE74\uB4DC \uC644\uB8CC \uD6C4:\n- \uBCF5\uC2B5\uD55C \uCE74\uB4DC \uC218, \uD3C9\uADE0 grade\n- remaining > 0\uC774\uBA74 "N\uAC1C \uB354 \uB0A8\uC74C, \uB0B4\uC77C \uC774\uC5B4\uC11C" \uC548\uB0B4\n- `til_review_list` \uC7AC\uD638\uCD9C\uD558\uC5EC \uCD5C\uC2E0 \uD1B5\uACC4 \uD45C\uC2DC\n\n## Step 5: TIL \uB4F1\uB85D (\uC120\uD0DD)\n\n\uCE74\uB4DC\uAC00 \uC5C6\uC744 \uB54C \uB610\uB294 \uC0AC\uC6A9\uC790 \uC694\uCCAD \uC2DC:\n- `til_list`\uB85C \uC804\uCCB4 TIL \uBAA9\uB85D \uD45C\uC2DC\n- \uC0AC\uC6A9\uC790\uAC00 \uBCF5\uC2B5 \uB300\uC0C1\uC5D0 \uCD94\uAC00\uD560 \uD30C\uC77C \uC120\uD0DD\n- \uC120\uD0DD\uB41C \uD30C\uC77C\uB9C8\uB2E4 `til_review_update` (action: "review", grade: 4) \uD638\uCD9C\n\n## \uADDC\uCE59\n\n- \uD55C \uC138\uC158 \uCD5C\uB300 20\uAC1C (\uACFC\uBD80\uD558 \uBC29\uC9C0)\n- \uC5F0\uCCB4 \uCE74\uB4DC \uC6B0\uC120 (\uAC00\uC7A5 \uAE09\uD55C \uAC83 \uBA3C\uC800)\n- \uBCF5\uC2B5 \uD574\uC81C: \uC0AC\uC6A9\uC790\uAC00 "\uC774 \uCE74\uB4DC \uC81C\uAC70"\uD558\uBA74 `til_review_update` (action: "remove") \uD638\uCD9C\n- \uD55C\uAD6D\uC5B4 \uC9C4\uD589, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n';
39792
+
39446
39793
  // vault-assets/claude-md-section.md
39447
- var claude_md_section_default = "## \uD559\uC2B5 \uC6CC\uD06C\uD50C\uB85C\uC6B0\n\n1. `/research <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uBC31\uB85C\uADF8 \uC0DD\uC131\n2. `/backlog [\uCE74\uD14C\uACE0\uB9AC]` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589 \uD655\uC778\n3. `/til <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uB300\uD654\uD615 \uD559\uC2B5 \u2192 \uC800\uC7A5\n4. `/save` \u2014 TIL \uC800\uC7A5 (Daily/MOC/\uBC31\uB85C\uADF8 \uC790\uB3D9 \uC5C5\uB370\uC774\uD2B8)\n\n## MCP \uB3C4\uAD6C\n\n- `vault_read_note` \u2014 \uB178\uD2B8 \uC77D\uAE30\n- `vault_list_files` \u2014 \uD30C\uC77C \uBAA9\uB85D\n- `vault_search` \u2014 \uD14D\uC2A4\uD2B8 \uAC80\uC0C9\n- `vault_get_active_file` \u2014 \uD604\uC7AC \uD30C\uC77C\n- `til_list` \u2014 TIL \uBAA9\uB85D + \uCE74\uD14C\uACE0\uB9AC \uBD84\uB958\n- `til_get_context` \u2014 \uC8FC\uC81C \uAD00\uB828 \uCEE8\uD14D\uC2A4\uD2B8\n- `til_recent_context` \u2014 \uCD5C\uADFC \uD559\uC2B5 \uD750\uB984\n- `til_backlog_status` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589\uB960\n- `til_dashboard` \u2014 \uD559\uC2B5 \uD1B5\uACC4\n\n### \uC5F0\uACB0\n\n```bash\nclaude mcp add --transport http oh-my-til http://localhost:22360/mcp\n```\n";
39794
+ var claude_md_section_default = "## \uD559\uC2B5 \uC6CC\uD06C\uD50C\uB85C\uC6B0\n\n1. `/research <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uBC31\uB85C\uADF8 \uC0DD\uC131\n2. `/backlog [\uCE74\uD14C\uACE0\uB9AC]` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589 \uD655\uC778\n3. `/til <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uB300\uD654\uD615 \uD559\uC2B5 \u2192 \uC800\uC7A5\n4. `/save` \u2014 TIL \uC800\uC7A5 (Daily/MOC/\uBC31\uB85C\uADF8 \uC790\uB3D9 \uC5C5\uB370\uC774\uD2B8)\n5. `/til-review [\uCE74\uD14C\uACE0\uB9AC]` \u2014 SRS \uAE30\uBC18 \uAC04\uACA9 \uBC18\uBCF5 \uBCF5\uC2B5\n\n## MCP \uB3C4\uAD6C\n\n- `vault_read_note` \u2014 \uB178\uD2B8 \uC77D\uAE30\n- `vault_list_files` \u2014 \uD30C\uC77C \uBAA9\uB85D\n- `vault_search` \u2014 \uD14D\uC2A4\uD2B8 \uAC80\uC0C9\n- `vault_get_active_file` \u2014 \uD604\uC7AC \uD30C\uC77C\n- `til_list` \u2014 TIL \uBAA9\uB85D + \uCE74\uD14C\uACE0\uB9AC \uBD84\uB958\n- `til_get_context` \u2014 \uC8FC\uC81C \uAD00\uB828 \uCEE8\uD14D\uC2A4\uD2B8\n- `til_recent_context` \u2014 \uCD5C\uADFC \uD559\uC2B5 \uD750\uB984\n- `til_backlog_status` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589\uB960\n- `til_dashboard` \u2014 \uD559\uC2B5 \uD1B5\uACC4\n- `til_review_list` \u2014 \uBCF5\uC2B5 \uB300\uC0C1 \uCE74\uB4DC \uBAA9\uB85D + \uD1B5\uACC4\n- `til_review_update` \u2014 \uBCF5\uC2B5 \uACB0\uACFC \uAE30\uB85D \uB610\uB294 \uBCF5\uC2B5 \uD574\uC81C\n\n### \uC5F0\uACB0\n\n```bash\nclaude mcp add --transport http oh-my-til http://localhost:22360/mcp\n```\n";
39448
39795
 
39449
39796
  // vault-assets/agents/til-fetcher.md
39450
39797
  var til_fetcher_default = '---\nname: til-fetcher\ndescription: \uC18C\uC2A4 URL \uCF58\uD150\uCE20\uB97C \uD328\uCE58\uD558\uC5EC \uD559\uC2B5 \uC790\uB8CC\uB85C \uC694\uC57D\uD558\uB294 \uC804\uC6A9 \uC5D0\uC774\uC804\uD2B8\ntools: Read, WebFetch\nmodel: haiku\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# til-fetcher\n\n\uC18C\uC2A4 URL\uC758 \uCF58\uD150\uCE20\uB97C \uD328\uCE58\uD558\uACE0 \uD559\uC2B5\uC5D0 \uD544\uC694\uD55C \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC694\uC57D\uD558\uB294 \uC804\uC6A9 \uC5D0\uC774\uC804\uD2B8.\n\n## \uC5ED\uD560\n\n- `/til` \uC2A4\uD0AC\uC758 Phase 1\uC5D0\uC11C sourceUrls \uD328\uCE58 subagent\uB85C \uC0AC\uC6A9\uB41C\uB2E4\n- \uC8FC\uC5B4\uC9C4 URL(1\uAC1C \uB610\uB294 \uC5EC\uB7EC \uAC1C)\uC744 WebFetch\uB85C \uC21C\uCC28 \uC77D\uACE0 \uD559\uC2B5\uC5D0 \uD544\uC694\uD55C \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC694\uC57D\uD55C\uB2E4\n- \uC5EC\uB7EC URL\uC774 \uC804\uB2EC\uB418\uBA74 \uAC01\uAC01 \uD328\uCE58 \uD6C4 \uD1B5\uD569 \uC694\uC57D\uC744 \uBC18\uD658\uD55C\uB2E4\n\n## \uCD9C\uB825 \uD615\uC2DD\n\n- \uD575\uC2EC \uB0B4\uC6A9 \uC694\uC57D (\uD55C\uAD6D\uC5B4, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30)\n- \uCF54\uB4DC \uC608\uC2DC\uAC00 \uC788\uC73C\uBA74 \uD3EC\uD568\n';
@@ -39495,7 +39842,8 @@ var SKILLS = {
39495
39842
  "migrate-links/SKILL.md": SKILL_default5,
39496
39843
  "dashboard/SKILL.md": SKILL_default6,
39497
39844
  "setup-pages/SKILL.md": SKILL_default7,
39498
- "omt-setup/SKILL.md": SKILL_default8
39845
+ "omt-setup/SKILL.md": SKILL_default8,
39846
+ "til-review/SKILL.md": SKILL_default9
39499
39847
  };
39500
39848
  var RULES = {};
39501
39849
  var AGENTS = {
@@ -40782,7 +41130,7 @@ function generateProfileHtml(config2, summaryCardsHtml, heatmapHtml, recentTilsH
40782
41130
  // src/cli/index.ts
40783
41131
  var path5 = __toESM(require("path"));
40784
41132
  var fs4 = __toESM(require("fs"));
40785
- var VERSION = true ? "0.11.2" : "0.0.0";
41133
+ var VERSION = true ? "0.12.1" : "0.0.0";
40786
41134
  function printUsage() {
40787
41135
  console.log(`oh-my-til v${VERSION}
40788
41136
 
package/main.js CHANGED
@@ -21939,14 +21939,15 @@ function computeCategoryDistribution(files, tilPath, _prefilteredFiles) {
21939
21939
  percentage: Math.round(count / total * 100)
21940
21940
  })).sort((a, b) => b.count - a.count);
21941
21941
  }
21942
- function computeEnhancedStats(files, tilPath, backlogEntries, now) {
21942
+ function computeEnhancedStats(files, tilPath, backlogEntries, now, reviewDueCount) {
21943
21943
  const tilFiles = filterTilFiles(files, tilPath);
21944
21944
  const categories = computeEnhancedCategories(files, tilPath, tilFiles);
21945
21945
  const summary = {
21946
21946
  totalTils: tilFiles.length,
21947
21947
  categoryCount: categories.length,
21948
21948
  thisWeekCount: computeWeeklyCount(files, tilPath, now, tilFiles),
21949
- streak: computeStreak(files, tilPath, now, tilFiles)
21949
+ streak: computeStreak(files, tilPath, now, tilFiles),
21950
+ ...reviewDueCount !== void 0 && reviewDueCount > 0 ? { reviewDueCount } : {}
21950
21951
  };
21951
21952
  return {
21952
21953
  summary,
@@ -21975,6 +21976,253 @@ function pickRandomReviewItems(files, tilPath, incompleteBacklogItems, randomFn
21975
21976
  return result;
21976
21977
  }
21977
21978
 
21979
+ // src/core/srs.ts
21980
+ var DAY_MS2 = 24 * 60 * 60 * 1e3;
21981
+ function formatDate2(ts) {
21982
+ const d = new Date(ts);
21983
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
21984
+ }
21985
+ function startOfDay2(ts) {
21986
+ const d = new Date(ts);
21987
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
21988
+ }
21989
+ function parseDateStr2(dateStr) {
21990
+ if (dateStr.includes("T")) {
21991
+ return new Date(dateStr).getTime();
21992
+ }
21993
+ const [y, m, d] = dateStr.split("-").map(Number);
21994
+ return new Date(y, m - 1, d).getTime();
21995
+ }
21996
+ function createDefaultSrsMetadata(now) {
21997
+ const currentTime = now != null ? now : Date.now();
21998
+ const today = formatDate2(currentTime);
21999
+ const tomorrow = formatDate2(currentTime + DAY_MS2);
22000
+ return {
22001
+ next_review: tomorrow,
22002
+ interval: 1,
22003
+ ease_factor: 2.5,
22004
+ repetitions: 0,
22005
+ last_review: today
22006
+ };
22007
+ }
22008
+ function computeNextReview(current, grade, now) {
22009
+ const currentTime = now != null ? now : Date.now();
22010
+ const today = formatDate2(currentTime);
22011
+ let { interval, ease_factor, repetitions } = current;
22012
+ if (grade < 3) {
22013
+ repetitions = 0;
22014
+ interval = 1;
22015
+ } else {
22016
+ if (repetitions === 0) {
22017
+ interval = 1;
22018
+ } else if (repetitions === 1) {
22019
+ interval = 6;
22020
+ } else {
22021
+ interval = Math.round(interval * ease_factor);
22022
+ }
22023
+ repetitions += 1;
22024
+ }
22025
+ ease_factor += 0.1 - (5 - grade) * (0.08 + (5 - grade) * 0.02);
22026
+ ease_factor = Math.max(ease_factor, 1.3);
22027
+ const next_review = formatDate2(currentTime + interval * DAY_MS2);
22028
+ return {
22029
+ next_review,
22030
+ interval,
22031
+ ease_factor: Math.round(ease_factor * 100) / 100,
22032
+ repetitions,
22033
+ last_review: today
22034
+ };
22035
+ }
22036
+ function isDueForReview(nextReview, now) {
22037
+ const currentTime = now != null ? now : Date.now();
22038
+ const todayStart = startOfDay2(currentTime);
22039
+ const dueStart = startOfDay2(parseDateStr2(nextReview));
22040
+ return dueStart <= todayStart;
22041
+ }
22042
+ function computeOverdueDays(nextReview, now) {
22043
+ const currentTime = now != null ? now : Date.now();
22044
+ const todayStart = startOfDay2(currentTime);
22045
+ const dueStart = startOfDay2(parseDateStr2(nextReview));
22046
+ return Math.round((todayStart - dueStart) / DAY_MS2);
22047
+ }
22048
+ function parseSrsMetadata(frontmatter) {
22049
+ const nextReview = frontmatter.next_review;
22050
+ const interval = frontmatter.interval;
22051
+ const easeFactor = frontmatter.ease_factor;
22052
+ const repetitions = frontmatter.repetitions;
22053
+ const lastReview = frontmatter.last_review;
22054
+ if (typeof nextReview !== "string" || typeof interval !== "number" || typeof easeFactor !== "number" || typeof repetitions !== "number" || typeof lastReview !== "string") {
22055
+ return null;
22056
+ }
22057
+ return {
22058
+ next_review: nextReview,
22059
+ interval,
22060
+ ease_factor: easeFactor,
22061
+ repetitions,
22062
+ last_review: lastReview
22063
+ };
22064
+ }
22065
+ function updateFrontmatterSrs(fileContent, srs) {
22066
+ const srsFields = [
22067
+ `next_review: "${srs.next_review}"`,
22068
+ `interval: ${srs.interval}`,
22069
+ `ease_factor: ${srs.ease_factor}`,
22070
+ `repetitions: ${srs.repetitions}`,
22071
+ `last_review: "${srs.last_review}"`
22072
+ ];
22073
+ const SRS_KEYS = ["next_review", "interval", "ease_factor", "repetitions", "last_review"];
22074
+ if (!fileContent.startsWith("---")) {
22075
+ return `---
22076
+ ${srsFields.join("\n")}
22077
+ ---
22078
+ ${fileContent}`;
22079
+ }
22080
+ const endIdx = fileContent.indexOf("---", 3);
22081
+ if (endIdx === -1) {
22082
+ return fileContent;
22083
+ }
22084
+ const fmContent = fileContent.slice(4, endIdx);
22085
+ const afterFm = fileContent.slice(endIdx + 3);
22086
+ const existingLines = fmContent.split("\n").filter((line) => {
22087
+ var _a4;
22088
+ const key = (_a4 = line.split(":")[0]) == null ? void 0 : _a4.trim();
22089
+ return !SRS_KEYS.includes(key != null ? key : "");
22090
+ });
22091
+ const cleanedLines = existingLines.filter((l) => l.trim() !== "");
22092
+ const newFmLines = [...cleanedLines, ...srsFields];
22093
+ return `---
22094
+ ${newFmLines.join("\n")}
22095
+ ---${afterFm}`;
22096
+ }
22097
+ function removeFrontmatterSrs(fileContent) {
22098
+ const SRS_KEYS = ["next_review", "interval", "ease_factor", "repetitions", "last_review"];
22099
+ if (!fileContent.startsWith("---")) {
22100
+ return fileContent;
22101
+ }
22102
+ const endIdx = fileContent.indexOf("---", 3);
22103
+ if (endIdx === -1) {
22104
+ return fileContent;
22105
+ }
22106
+ const fmContent = fileContent.slice(4, endIdx);
22107
+ const afterFm = fileContent.slice(endIdx + 3);
22108
+ const filteredLines = fmContent.split("\n").filter((line) => {
22109
+ var _a4;
22110
+ const key = (_a4 = line.split(":")[0]) == null ? void 0 : _a4.trim();
22111
+ return !SRS_KEYS.includes(key != null ? key : "");
22112
+ });
22113
+ const cleanedLines = filteredLines.filter((l) => l.trim() !== "");
22114
+ if (cleanedLines.length === 0) {
22115
+ return afterFm.replace(/^\n/, "");
22116
+ }
22117
+ return `---
22118
+ ${cleanedLines.join("\n")}
22119
+ ---${afterFm}`;
22120
+ }
22121
+ function filterDueCards(files, tilPath, now, limit = 20) {
22122
+ const currentTime = now != null ? now : Date.now();
22123
+ const cards = [];
22124
+ for (const file2 of files) {
22125
+ if (!file2.path.startsWith(tilPath + "/"))
22126
+ continue;
22127
+ if (file2.extension !== "md")
22128
+ continue;
22129
+ if (file2.path.split("/").pop() === "backlog.md")
22130
+ continue;
22131
+ const srs = parseSrsMetadata(file2.frontmatter);
22132
+ if (!srs)
22133
+ continue;
22134
+ if (!isDueForReview(srs.next_review, currentTime))
22135
+ continue;
22136
+ cards.push({
22137
+ path: file2.path,
22138
+ category: extractCategory(file2.path, tilPath),
22139
+ title: file2.title,
22140
+ dueDate: srs.next_review,
22141
+ overdueDays: computeOverdueDays(srs.next_review, currentTime),
22142
+ interval: srs.interval,
22143
+ repetitions: srs.repetitions,
22144
+ ease_factor: srs.ease_factor
22145
+ });
22146
+ }
22147
+ cards.sort((a, b) => b.overdueDays - a.overdueDays);
22148
+ return cards.slice(0, limit);
22149
+ }
22150
+ function computeReviewStats(files, tilPath, now) {
22151
+ const currentTime = now != null ? now : Date.now();
22152
+ const today = formatDate2(currentTime);
22153
+ let dueToday = 0;
22154
+ let overdueCount = 0;
22155
+ let totalScheduled = 0;
22156
+ let totalReviewed = 0;
22157
+ let easeSum = 0;
22158
+ for (const file2 of files) {
22159
+ if (!file2.path.startsWith(tilPath + "/"))
22160
+ continue;
22161
+ if (file2.extension !== "md")
22162
+ continue;
22163
+ if (file2.path.split("/").pop() === "backlog.md")
22164
+ continue;
22165
+ const srs = parseSrsMetadata(file2.frontmatter);
22166
+ if (!srs)
22167
+ continue;
22168
+ totalScheduled++;
22169
+ easeSum += srs.ease_factor;
22170
+ if (srs.last_review === today) {
22171
+ totalReviewed++;
22172
+ }
22173
+ const overdue = computeOverdueDays(srs.next_review, currentTime);
22174
+ if (overdue > 0) {
22175
+ overdueCount++;
22176
+ dueToday++;
22177
+ } else if (overdue === 0) {
22178
+ dueToday++;
22179
+ }
22180
+ }
22181
+ return {
22182
+ dueToday,
22183
+ overdueCount,
22184
+ totalReviewed,
22185
+ totalScheduled,
22186
+ averageEase: totalScheduled > 0 ? Math.round(easeSum / totalScheduled * 100) / 100 : 0,
22187
+ reviewStreak: computeReviewStreak(files, tilPath, currentTime)
22188
+ };
22189
+ }
22190
+ function computeReviewStreak(files, tilPath, now) {
22191
+ const currentTime = now != null ? now : Date.now();
22192
+ const reviewDays = /* @__PURE__ */ new Set();
22193
+ for (const file2 of files) {
22194
+ if (!file2.path.startsWith(tilPath + "/"))
22195
+ continue;
22196
+ if (file2.extension !== "md")
22197
+ continue;
22198
+ if (file2.path.split("/").pop() === "backlog.md")
22199
+ continue;
22200
+ const srs = parseSrsMetadata(file2.frontmatter);
22201
+ if (!srs)
22202
+ continue;
22203
+ reviewDays.add(srs.last_review);
22204
+ }
22205
+ if (reviewDays.size === 0)
22206
+ return 0;
22207
+ const todayStart = startOfDay2(currentTime);
22208
+ const cursor = new Date(todayStart);
22209
+ let streak = 0;
22210
+ for (let i = 0; i < 365; i++) {
22211
+ const dayStr = formatDate2(cursor.getTime());
22212
+ if (reviewDays.has(dayStr)) {
22213
+ streak++;
22214
+ } else {
22215
+ if (i === 0) {
22216
+ cursor.setDate(cursor.getDate() - 1);
22217
+ continue;
22218
+ }
22219
+ break;
22220
+ }
22221
+ cursor.setDate(cursor.getDate() - 1);
22222
+ }
22223
+ return streak;
22224
+ }
22225
+
21978
22226
  // src/obsidian/dashboard/DashboardView.ts
21979
22227
  var VIEW_TYPE_TIL_DASHBOARD = "oh-my-til-dashboard-view";
21980
22228
  var _DashboardView = class _DashboardView extends import_obsidian2.ItemView {
@@ -22042,6 +22290,7 @@ var _DashboardView = class _DashboardView extends import_obsidian2.ItemView {
22042
22290
  }, _DashboardView.DEBOUNCE_MS);
22043
22291
  }
22044
22292
  async render() {
22293
+ var _a4;
22045
22294
  const raw = this.containerEl.children[1];
22046
22295
  if (!raw)
22047
22296
  return;
@@ -22050,9 +22299,9 @@ var _DashboardView = class _DashboardView extends import_obsidian2.ItemView {
22050
22299
  container.addClass("oh-my-til-dashboard");
22051
22300
  const content = container.createDiv({ cls: "oh-my-til-dashboard-content" });
22052
22301
  const files = this.app.vault.getFiles().filter((f) => f.extension === "md").map((f) => {
22053
- var _a4, _b;
22302
+ var _a5, _b;
22054
22303
  const cache = this.app.metadataCache.getFileCache(f);
22055
- const fmDate = (_a4 = cache == null ? void 0 : cache.frontmatter) == null ? void 0 : _a4.date;
22304
+ const fmDate = (_a5 = cache == null ? void 0 : cache.frontmatter) == null ? void 0 : _a5.date;
22056
22305
  const createdDate = typeof fmDate === "string" ? fmDate : void 0;
22057
22306
  const fmTags = (_b = cache == null ? void 0 : cache.frontmatter) == null ? void 0 : _b.tags;
22058
22307
  const tags = Array.isArray(fmTags) ? fmTags.filter((t) => typeof t === "string") : void 0;
@@ -22068,7 +22317,21 @@ var _DashboardView = class _DashboardView extends import_obsidian2.ItemView {
22068
22317
  const backlogEntries = await this.gatherBacklogEntries();
22069
22318
  this.cachedFiles = files;
22070
22319
  this.cachedIncompleteBacklogItems = await this.gatherIncompleteBacklogItems();
22071
- const stats = computeEnhancedStats(files, this.tilPath, backlogEntries);
22320
+ let reviewDueCount = 0;
22321
+ for (const f of this.app.vault.getFiles()) {
22322
+ if (!f.path.startsWith(this.tilPath + "/"))
22323
+ continue;
22324
+ if (f.extension !== "md")
22325
+ continue;
22326
+ if (f.name === "backlog.md")
22327
+ continue;
22328
+ const cache = this.app.metadataCache.getFileCache(f);
22329
+ const nextReview = (_a4 = cache == null ? void 0 : cache.frontmatter) == null ? void 0 : _a4.next_review;
22330
+ if (typeof nextReview === "string" && isDueForReview(nextReview)) {
22331
+ reviewDueCount++;
22332
+ }
22333
+ }
22334
+ const stats = computeEnhancedStats(files, this.tilPath, backlogEntries, void 0, reviewDueCount);
22072
22335
  this.renderHeader(content);
22073
22336
  this.renderSummaryCards(content, stats.summary);
22074
22337
  this.renderRandomReviewSection(content);
@@ -22204,6 +22467,9 @@ var _DashboardView = class _DashboardView extends import_obsidian2.ItemView {
22204
22467
  { value: String(summary.thisWeekCount), label: "This Week" },
22205
22468
  { value: `${summary.streak}d`, label: "Streak" }
22206
22469
  ];
22470
+ if (summary.reviewDueCount !== void 0 && summary.reviewDueCount > 0) {
22471
+ cards.push({ value: String(summary.reviewDueCount), label: "Review Due" });
22472
+ }
22207
22473
  for (const card of cards) {
22208
22474
  const el = row.createDiv({ cls: "oh-my-til-dashboard-card" });
22209
22475
  el.createDiv({ cls: "oh-my-til-dashboard-card-value", text: card.value });
@@ -47440,19 +47706,26 @@ ${text}` }] };
47440
47706
  "til_dashboard",
47441
47707
  {
47442
47708
  title: "Dashboard Stats",
47443
- description: "\uD559\uC2B5 \uB300\uC2DC\uBCF4\uB4DC \uD1B5\uACC4\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4 (\uC694\uC57D, \uD788\uD2B8\uB9F5, \uCE74\uD14C\uACE0\uB9AC, \uBC31\uB85C\uADF8)",
47709
+ description: "\uD559\uC2B5 \uB300\uC2DC\uBCF4\uB4DC \uD1B5\uACC4\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4 (\uC694\uC57D, \uD788\uD2B8\uB9F5, \uCE74\uD14C\uACE0\uB9AC, \uBC31\uB85C\uADF8, \uBCF5\uC2B5)",
47444
47710
  inputSchema: external_exports3.object({})
47445
47711
  },
47446
47712
  async () => {
47447
47713
  const allFiles = await storage.listFiles();
47714
+ let reviewDueCount = 0;
47448
47715
  const files = await Promise.all(
47449
47716
  allFiles.filter((f) => f.extension === "md").map(async (f) => {
47450
- var _a4, _b;
47717
+ var _a4, _b, _c;
47451
47718
  const fileMeta = await metadata.getFileMetadata(f.path);
47452
47719
  const fmDate = (_a4 = fileMeta == null ? void 0 : fileMeta.frontmatter) == null ? void 0 : _a4.date;
47453
47720
  const createdDate = typeof fmDate === "string" ? fmDate : void 0;
47454
47721
  const fmTags = (_b = fileMeta == null ? void 0 : fileMeta.frontmatter) == null ? void 0 : _b.tags;
47455
47722
  const tags = Array.isArray(fmTags) ? fmTags.filter((t) => typeof t === "string") : void 0;
47723
+ if (f.path.startsWith(tilPath + "/") && f.name !== "backlog.md") {
47724
+ const nextReview = (_c = fileMeta == null ? void 0 : fileMeta.frontmatter) == null ? void 0 : _c.next_review;
47725
+ if (typeof nextReview === "string" && isDueForReview(nextReview)) {
47726
+ reviewDueCount++;
47727
+ }
47728
+ }
47456
47729
  return {
47457
47730
  path: f.path,
47458
47731
  extension: f.extension,
@@ -47486,7 +47759,7 @@ ${text}` }] };
47486
47759
  });
47487
47760
  }
47488
47761
  }
47489
- const stats = computeEnhancedStats(files, tilPath, backlogEntries);
47762
+ const stats = computeEnhancedStats(files, tilPath, backlogEntries, void 0, reviewDueCount);
47490
47763
  return {
47491
47764
  content: [
47492
47765
  { type: "text", text: JSON.stringify(stats) }
@@ -47494,6 +47767,99 @@ ${text}` }] };
47494
47767
  };
47495
47768
  }
47496
47769
  );
47770
+ server.registerTool(
47771
+ "til_review_list",
47772
+ {
47773
+ title: "Review List",
47774
+ description: "\uC624\uB298 \uBCF5\uC2B5\uD560 TIL \uCE74\uB4DC \uBAA9\uB85D\uACFC \uD1B5\uACC4\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4",
47775
+ inputSchema: external_exports3.object({
47776
+ category: external_exports3.string().optional().describe("\uD2B9\uC815 \uCE74\uD14C\uACE0\uB9AC\uB9CC \uD544\uD130\uB9C1"),
47777
+ limit: external_exports3.number().min(1).max(100).optional().describe("\uCD5C\uB300 \uCE74\uB4DC \uC218 (\uAE30\uBCF8 20)")
47778
+ })
47779
+ },
47780
+ async ({ category, limit }) => {
47781
+ var _a4, _b;
47782
+ const allFiles = await storage.listFiles();
47783
+ const srsFiles = [];
47784
+ for (const f of allFiles) {
47785
+ if (!f.path.startsWith(tilPath + "/"))
47786
+ continue;
47787
+ if (f.extension !== "md")
47788
+ continue;
47789
+ if (f.name === "backlog.md")
47790
+ continue;
47791
+ if (category) {
47792
+ const cat = extractCategory(f.path, tilPath);
47793
+ if (cat !== category)
47794
+ continue;
47795
+ }
47796
+ const fileMeta = await metadata.getFileMetadata(f.path);
47797
+ if (!fileMeta)
47798
+ continue;
47799
+ const headings = (_a4 = fileMeta.headings) != null ? _a4 : [];
47800
+ const title = headings.length > 0 ? headings[0] : f.name.replace(/\.md$/, "");
47801
+ srsFiles.push({
47802
+ path: f.path,
47803
+ extension: f.extension,
47804
+ title,
47805
+ frontmatter: (_b = fileMeta.frontmatter) != null ? _b : {}
47806
+ });
47807
+ }
47808
+ const effectiveLimit = limit != null ? limit : 20;
47809
+ const cards = filterDueCards(srsFiles, tilPath, void 0, effectiveLimit);
47810
+ const stats = computeReviewStats(srsFiles, tilPath);
47811
+ const remaining = Math.max(0, stats.dueToday - cards.length);
47812
+ const data = { cards, stats, remaining };
47813
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
47814
+ }
47815
+ );
47816
+ server.registerTool(
47817
+ "til_review_update",
47818
+ {
47819
+ title: "Update Review",
47820
+ description: "TIL \uD30C\uC77C\uC758 \uBCF5\uC2B5 \uACB0\uACFC\uB97C \uAE30\uB85D\uD558\uAC70\uB098 \uBCF5\uC2B5 \uB300\uC0C1\uC5D0\uC11C \uC81C\uAC70\uD569\uB2C8\uB2E4",
47821
+ inputSchema: external_exports3.object({
47822
+ path: external_exports3.string().describe("TIL \uD30C\uC77C \uACBD\uB85C"),
47823
+ grade: external_exports3.number().min(0).max(5).optional().describe("SM-2 \uB4F1\uAE09 (0-5, action=review \uC2DC \uD544\uC218)"),
47824
+ action: external_exports3.enum(["review", "remove"]).optional().describe("review(\uAE30\uBCF8): \uBCF5\uC2B5 \uAE30\uB85D, remove: \uBCF5\uC2B5 \uD574\uC81C")
47825
+ })
47826
+ },
47827
+ async ({ path: path3, grade, action }) => {
47828
+ var _a4, _b;
47829
+ const effectiveAction = action != null ? action : "review";
47830
+ const content = await storage.readFile(path3);
47831
+ if (content === null) {
47832
+ return { content: [{ type: "text", text: `Error: \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 ${path3}` }], isError: true };
47833
+ }
47834
+ if (effectiveAction === "remove") {
47835
+ const updated2 = removeFrontmatterSrs(content);
47836
+ await storage.writeFile(path3, updated2);
47837
+ return { content: [{ type: "text", text: JSON.stringify({ path: path3, removed: true }) }] };
47838
+ }
47839
+ if (grade === void 0) {
47840
+ return { content: [{ type: "text", text: "Error: action=review \uC2DC grade(0-5)\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4" }], isError: true };
47841
+ }
47842
+ const fileMeta = await metadata.getFileMetadata(path3);
47843
+ const fm = (_a4 = fileMeta == null ? void 0 : fileMeta.frontmatter) != null ? _a4 : {};
47844
+ const currentSrs = (_b = parseSrsMetadata(fm)) != null ? _b : createDefaultSrsMetadata();
47845
+ const newSrs = computeNextReview(currentSrs, grade);
47846
+ const updated = updateFrontmatterSrs(content, newSrs);
47847
+ await storage.writeFile(path3, updated);
47848
+ return {
47849
+ content: [{
47850
+ type: "text",
47851
+ text: JSON.stringify({
47852
+ path: path3,
47853
+ grade,
47854
+ next_review: newSrs.next_review,
47855
+ interval: newSrs.interval,
47856
+ ease_factor: newSrs.ease_factor,
47857
+ repetitions: newSrs.repetitions
47858
+ })
47859
+ }]
47860
+ };
47861
+ }
47862
+ );
47497
47863
  }
47498
47864
 
47499
47865
  // src/mcp/server.ts
@@ -47595,7 +47961,7 @@ var SKILL_default2 = '---\nname: backlog\ndescription: "\uD559\uC2B5 \uBC31\uB85
47595
47961
  var SKILL_default3 = '---\nname: research\ndescription: "\uC8FC\uC81C\uB97C \uB9AC\uC11C\uCE58\uD558\uC5EC \uD559\uC2B5\uC5D0 \uD544\uC694\uD55C \uAC1C\uB150/\uC6A9\uC5B4\uB97C \uD30C\uC545\uD558\uACE0, \uBC31\uB85C\uADF8\uB85C \uC815\uB9AC"\nargument-hint: "<\uC8FC\uC81C> [\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Research Skill\n\n\uC8FC\uC81C \uB9AC\uC11C\uCE58 \u2192 \uAC1C\uB150/\uC758\uC874 \uAD00\uACC4 \uD30C\uC545 \u2192 \uBC31\uB85C\uADF8 \uD30C\uC77C \uC800\uC7A5.\n\n## Phase 1: \uC8FC\uC81C \uB9AC\uC11C\uCE58\n\n1. \uC6F9 \uAC80\uC0C9\uC73C\uB85C \uC8FC\uC81C \uC870\uC0AC, \uD544\uC694 \uAC1C\uB150\xB7\uC6A9\uC5B4\xB7\uC120\uD589 \uC9C0\uC2DD \uD30C\uC545\n2. \uC18C\uC8FC\uC81C \uBD84\uD574 \uD6C4 \uC9C1\uC811 \uB9AC\uC11C\uCE58\n3. \uC18C\uC8FC\uC81C \uAC04 \uC758\uC874 \uAD00\uACC4 \uBD84\uC11D\n\n## Phase 2: \uBC31\uB85C\uADF8 \uC815\uB9AC\n\n1. \uD559\uC2B5 \uC21C\uC11C \uC815\uB82C: \uC120\uD589 \uC9C0\uC2DD \u2192 \uD575\uC2EC \uAC1C\uB150 \u2192 \uC2EC\uD654\n2. \uAC01 \uD56D\uBAA9\uC5D0 1\uC904 \uC124\uBA85\n3. \uC0AC\uC6A9\uC790 \uD53C\uB4DC\uBC31 (\uCD94\uAC00/\uC81C\uAC70/\uC21C\uC11C \uBCC0\uACBD)\n\n## Phase 3: \uC800\uC7A5\n\n1. `./til/{\uCE74\uD14C\uACE0\uB9AC}/backlog.md`\uC5D0 \uC800\uC7A5 (\uD3F4\uB354 \uC790\uB3D9 \uC0DD\uC131)\n2. \uAE30\uC874 backlog.md \uC788\uC73C\uBA74 \uBCD1\uD569:\n - `[x]` \uC644\uB8CC \uD56D\uBAA9 \uBCF4\uC874\n - \uB3D9\uC77C \uD56D\uBAA9 \uCCB4\uD06C \uC0C1\uD0DC \uC720\uC9C0\n - \uAE30\uC874 sources \uBCF4\uC874, \uC0C8 \uD56D\uBAA9\uB9CC \uCD94\uAC00\n3. TIL MOC\uC5D0 \uBC31\uB85C\uADF8 \uB9C1\uD06C \uCD94\uAC00\n4. atomic commit: `\u{1F4CB} research: {\uC8FC\uC81C} \uD559\uC2B5 \uBC31\uB85C\uADF8 - {\uCE74\uD14C\uACE0\uB9AC}` (push \uC548 \uD568)\n\n## \uBC31\uB85C\uADF8 \uD15C\uD50C\uB9BF\n\n```markdown\n---\ntags: [backlog, {\uCE74\uD14C\uACE0\uB9AC}]\naliases: ["Backlog - {\uC8FC\uC81C}"]\nupdated: YYYY-MM-DD\nsources:\n slug-a: [https://url-1]\n---\n\n# {\uC8FC\uC81C} \uD559\uC2B5 \uBC31\uB85C\uADF8\n\n## \uC120\uD589 \uC9C0\uC2DD\n- [ ] [\uAC1C\uB150A](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug-a}.md) - \uC124\uBA85\n\n## \uD575\uC2EC \uAC1C\uB150\n- [ ] [\uAC1C\uB150C](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug-c}.md) - \uC124\uBA85\n\n## \uC2EC\uD654\n- [ ] [\uAC1C\uB150E](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug-e}.md) - \uC124\uBA85\n```\n\n## \uC778\uC218\n\n- \uCCAB \uBC88\uC9F8: \uB9AC\uC11C\uCE58 \uC8FC\uC81C (\uD544\uC218)\n- \uB450 \uBC88\uC9F8: \uCE74\uD14C\uACE0\uB9AC (\uC120\uD0DD, \uBBF8\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uCD94\uB860)\n\n## \uADDC\uCE59\n\n- \uD56D\uBAA9\uB2F9 1\uC904 \uC124\uBA85, 20\uAC1C \uCD08\uACFC \uC2DC \uBD84\uB9AC\n- \uD55C\uAD6D\uC5B4 \uC791\uC131, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n- \uB9C1\uD06C: `[\uD45C\uC2DC\uBA85](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)`\n';
47596
47962
 
47597
47963
  // vault-assets/skills/save/SKILL.md
47598
- var SKILL_default4 = '---\nname: save\ndescription: "\uD559\uC2B5 \uB0B4\uC6A9\uC744 TIL \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 Daily \uB178\uD2B8, MOC, \uBC31\uB85C\uADF8\uB97C \uC77C\uAD04 \uC5C5\uB370\uC774\uD2B8"\nargument-hint: "[\uC8FC\uC81C] [\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Save Skill\n\n\uD559\uC2B5 \uB300\uD654 \u2192 TIL \uD30C\uC77C \uC800\uC7A5 \u2192 Daily/MOC/\uBC31\uB85C\uADF8 \uC5C5\uB370\uC774\uD2B8 \u2192 \uBB38\uC11C \uB9AC\uBDF0 \u2192 \uCEE4\uBC0B.\n\n## MCP \uB3C4\uAD6C\n\n- `til_get_context`: \uAD00\uB828 TIL\xB7\uBC31\uB85C\uADF8 \uD30C\uC545\n- `til_list`: \uAE30\uC874 TIL \uC911\uBCF5 \uD655\uC778\n- `vault_get_active_file`: \uC0AC\uC6A9\uC790 \uD30C\uC77C \uCEE8\uD14D\uC2A4\uD2B8\n\n## Step 1: \uCEE8\uD14D\uC2A4\uD2B8 \uD655\uC778\n\n\uC8FC\uC81C\xB7\uCE74\uD14C\uACE0\uB9AC \uD30C\uC545. \uBD88\uBA85\uD655\uD558\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC9C8\uBB38.\n\n## Step 2: \uB9C1\uD06C \uD6C4\uBCF4 \uD30C\uC545\n\n1. `til_get_context` \uB610\uB294 MOC/\uBC31\uB85C\uADF8\uB85C \uAE30\uC874 TIL\xB7\uBC31\uB85C\uADF8 \uD56D\uBAA9 \uD30C\uC545\n2. \uAE30\uC874 TIL/\uBC31\uB85C\uADF8 \uD56D\uBAA9 \u2192 \uBCF8\uBB38\uC5D0\uC11C \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C \uC0AC\uC6A9\n3. \uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uAC1C\uB150 \u2192 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778 \uD6C4 \uAD00\uB828 \uB178\uD2B8\uC5D0\uB9CC \uCD94\uAC00\n\n## Step 3: TIL \uD30C\uC77C \uC800\uC7A5\n\n\uACBD\uB85C: `./til/{\uCE74\uD14C\uACE0\uB9AC}/{\uC8FC\uC81C\uC2AC\uB7EC\uADF8}.md` (\uC2AC\uB7EC\uADF8: \uC601\uBB38 \uC18C\uBB38\uC790, \uD558\uC774\uD508)\n\n\uB3D9\uC77C \uC2AC\uB7EC\uADF8 \uD30C\uC77C \uC788\uC744 \uB54C:\n- `/til` \uC2EC\uD654 \uD559\uC2B5\uC774 \uC5F0\uC18D\uB41C \uACBD\uC6B0\uB9CC \uC790\uB3D9 \uBCD1\uD569 (\uAE30\uC874 \uB0B4\uC6A9 \uC720\uC9C0 + \uBCF4\uAC15, `updated` \uCD94\uAC00)\n- \uADF8 \uC678: \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCD1\uD569/\uB36E\uC5B4\uC4F0\uAE30 \uD655\uC778\n\n### TIL \uD15C\uD50C\uB9BF\n\n```markdown\n---\ndate: YYYY-MM-DDTHH:mm:ss\ncategory: \uCE74\uD14C\uACE0\uB9AC\ntags: [til, \uD0DC\uADF81]\naliases: ["\uD55C\uAE00 \uC81C\uBAA9", "\uC601\uBB38 \uC81C\uBAA9"]\n---\n\n# \uC81C\uBAA9\n\n> [!tldr] \uD55C\uC904 \uC694\uC57D\n\n## \uD575\uC2EC \uB0B4\uC6A9\n## \uC608\uC2DC\n## \uCC38\uACE0 \uC790\uB8CC\n- [\uC81C\uBAA9](URL)\n## \uAD00\uB828 \uB178\uD2B8\n- [TIL](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)\n```\n\n- `date`: `date +%Y-%m-%dT%H:%M:%S` \uBA85\uB839\uC73C\uB85C \uB85C\uCEEC \uC2DC\uAC01 \uC870\uD68C\n- \uB9C1\uD06C: `[\uD45C\uC2DC\uBA85](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)` \u2014 `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0\n\n## Step 4: \uC5F0\uAD00 \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8\n\n\uC544\uB798 3\uAC1C \uD30C\uC77C\uC744 **\uC9C1\uC811** \uC21C\uCC28 \uC5C5\uB370\uC774\uD2B8\uD55C\uB2E4 (subagent \uC0AC\uC6A9 \uAE08\uC9C0):\n\n1. Daily \uB178\uD2B8 (`./Daily/YYYY-MM-DD.md`): \uCE74\uD14C\uACE0\uB9AC\uBCC4 TIL \uB9C1\uD06C \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n2. TIL MOC (`./til/TIL MOC.md`): \uCE74\uD14C\uACE0\uB9AC \uC139\uC158\uC5D0 \uD56D\uBAA9 \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n3. \uBC31\uB85C\uADF8 (`./til/{\uCE74\uD14C\uACE0\uB9AC}/backlog.md`): \uD574\uB2F9 \uD56D\uBAA9 `[ ]` \u2192 `[x]`\n\n\uAC01 \uD30C\uC77C: Read \u2192 \uC704\uCE58 \uD655\uC778 \u2192 Edit. \uD30C\uC77C \uC5C6\uC73C\uBA74 \uC0DD\uC131.\n\n## Step 5: \uBB38\uC11C \uB9AC\uBDF0\n\n\uC800\uC7A5\uB41C TIL \uC804\uCCB4 \uB0B4\uC6A9 \uD45C\uC2DC \u2192 `AskUserQuestion`\uC73C\uB85C \uD655\uC778 ("\uD655\uC778 \uC644\uB8CC" / "\uC218\uC815 \uD544\uC694").\n\n## Step 6: git commit\n\n`\u{1F4DD} til: {\uD55C\uAE00 \uC81C\uBAA9}({\uC601\uBB38 \uC81C\uBAA9}) - {\uCE74\uD14C\uACE0\uB9AC}` (push \uC548 \uD568)\n\n## \uADDC\uCE59\n\n- frontmatter \uD544\uC218: date, category, tags, aliases (\uB204\uB77D \uC2DC \uC800\uC7A5 \uC804 \uBCF4\uC644)\n- tags\uC5D0 \uBC18\uB4DC\uC2DC "til" \uD3EC\uD568 (\uC815\uC801 \uC0AC\uC774\uD2B8 \uD544\uD130 \uAE30\uC900)\n- `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0 \u2014 `[\uD45C\uC2DC\uBA85](\uACBD\uB85C)` \uD615\uC2DD\uB9CC \uC0AC\uC6A9\n- TIL\uB9CC \uC800\uC7A5\uD558\uACE0 Daily/MOC/\uBC31\uB85C\uADF8 \uB204\uB77D\uD558\uC9C0 \uC54A\uB294\uB2E4\n- Callout \uD65C\uC6A9: `> [!tldr]`, `> [!example]`, `> [!warning]`, `> [!tip]`\n- \uBCF5\uC7A1\uD55C \uAC1C\uB150\uC740 Mermaid \uB2E4\uC774\uC5B4\uADF8\uB7A8\uC73C\uB85C \uC2DC\uAC01\uD654 (TIL\uB2F9 \uCD5C\uB300 1\uAC1C)\n- \uBBFC\uAC10 \uC815\uBCF4 \uB300\uCCB4\uAC12 \uC0AC\uC6A9\n- \uD55C\uAD6D\uC5B4 \uC791\uC131, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n';
47964
+ var SKILL_default4 = '---\nname: save\ndescription: "\uD559\uC2B5 \uB0B4\uC6A9\uC744 TIL \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 Daily \uB178\uD2B8, MOC, \uBC31\uB85C\uADF8\uB97C \uC77C\uAD04 \uC5C5\uB370\uC774\uD2B8"\nargument-hint: "[\uC8FC\uC81C] [\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Save Skill\n\n\uD559\uC2B5 \uB300\uD654 \u2192 TIL \uD30C\uC77C \uC800\uC7A5 \u2192 Daily/MOC/\uBC31\uB85C\uADF8 \uC5C5\uB370\uC774\uD2B8 \u2192 \uBB38\uC11C \uB9AC\uBDF0 \u2192 \uCEE4\uBC0B.\n\n## MCP \uB3C4\uAD6C\n\n- `til_get_context`: \uAD00\uB828 TIL\xB7\uBC31\uB85C\uADF8 \uD30C\uC545\n- `til_list`: \uAE30\uC874 TIL \uC911\uBCF5 \uD655\uC778\n- `vault_get_active_file`: \uC0AC\uC6A9\uC790 \uD30C\uC77C \uCEE8\uD14D\uC2A4\uD2B8\n\n## Step 1: \uCEE8\uD14D\uC2A4\uD2B8 \uD655\uC778\n\n\uC8FC\uC81C\xB7\uCE74\uD14C\uACE0\uB9AC \uD30C\uC545. \uBD88\uBA85\uD655\uD558\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC9C8\uBB38.\n\n## Step 2: \uB9C1\uD06C \uD6C4\uBCF4 \uD30C\uC545\n\n1. `til_get_context` \uB610\uB294 MOC/\uBC31\uB85C\uADF8\uB85C \uAE30\uC874 TIL\xB7\uBC31\uB85C\uADF8 \uD56D\uBAA9 \uD30C\uC545\n2. \uAE30\uC874 TIL/\uBC31\uB85C\uADF8 \uD56D\uBAA9 \u2192 \uBCF8\uBB38\uC5D0\uC11C \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C \uC0AC\uC6A9\n3. \uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uAC1C\uB150 \u2192 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778 \uD6C4 \uAD00\uB828 \uB178\uD2B8\uC5D0\uB9CC \uCD94\uAC00\n\n## Step 3: TIL \uD30C\uC77C \uC800\uC7A5\n\n\uACBD\uB85C: `./til/{\uCE74\uD14C\uACE0\uB9AC}/{\uC8FC\uC81C\uC2AC\uB7EC\uADF8}.md` (\uC2AC\uB7EC\uADF8: \uC601\uBB38 \uC18C\uBB38\uC790, \uD558\uC774\uD508)\n\n\uB3D9\uC77C \uC2AC\uB7EC\uADF8 \uD30C\uC77C \uC788\uC744 \uB54C:\n- `/til` \uC2EC\uD654 \uD559\uC2B5\uC774 \uC5F0\uC18D\uB41C \uACBD\uC6B0\uB9CC \uC790\uB3D9 \uBCD1\uD569 (\uAE30\uC874 \uB0B4\uC6A9 \uC720\uC9C0 + \uBCF4\uAC15, `updated` \uCD94\uAC00)\n- \uADF8 \uC678: \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCD1\uD569/\uB36E\uC5B4\uC4F0\uAE30 \uD655\uC778\n\n### TIL \uD15C\uD50C\uB9BF\n\n```markdown\n---\ndate: YYYY-MM-DDTHH:mm:ss\ncategory: \uCE74\uD14C\uACE0\uB9AC\ntags: [til, \uD0DC\uADF81]\naliases: ["\uD55C\uAE00 \uC81C\uBAA9", "\uC601\uBB38 \uC81C\uBAA9"]\n---\n\n# \uC81C\uBAA9\n\n> [!tldr] \uD55C\uC904 \uC694\uC57D\n\n## \uD575\uC2EC \uB0B4\uC6A9\n## \uC608\uC2DC\n## \uCC38\uACE0 \uC790\uB8CC\n- [\uC81C\uBAA9](URL)\n## \uAD00\uB828 \uB178\uD2B8\n- [TIL](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)\n```\n\n- `date`: `date +%Y-%m-%dT%H:%M:%S` \uBA85\uB839\uC73C\uB85C \uB85C\uCEEC \uC2DC\uAC01 \uC870\uD68C\n- \uB9C1\uD06C: `[\uD45C\uC2DC\uBA85](til/{\uCE74\uD14C\uACE0\uB9AC}/{slug}.md)` \u2014 `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0\n\n## Step 4: \uC5F0\uAD00 \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8\n\n\uC544\uB798 3\uAC1C \uD30C\uC77C\uC744 **\uC9C1\uC811** \uC21C\uCC28 \uC5C5\uB370\uC774\uD2B8\uD55C\uB2E4 (subagent \uC0AC\uC6A9 \uAE08\uC9C0):\n\n1. Daily \uB178\uD2B8 (`./Daily/YYYY-MM-DD.md`): \uCE74\uD14C\uACE0\uB9AC\uBCC4 TIL \uB9C1\uD06C \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n2. TIL MOC (`./til/TIL MOC.md`): \uCE74\uD14C\uACE0\uB9AC \uC139\uC158\uC5D0 \uD56D\uBAA9 \uCD94\uAC00 (\uC5C6\uC73C\uBA74 \uC0DD\uC131)\n3. \uBC31\uB85C\uADF8 (`./til/{\uCE74\uD14C\uACE0\uB9AC}/backlog.md`): \uD574\uB2F9 \uD56D\uBAA9 `[ ]` \u2192 `[x]`\n\n\uAC01 \uD30C\uC77C: Read \u2192 \uC704\uCE58 \uD655\uC778 \u2192 Edit. \uD30C\uC77C \uC5C6\uC73C\uBA74 \uC0DD\uC131.\n\n## Step 5: \uBB38\uC11C \uB9AC\uBDF0\n\n\uC800\uC7A5\uB41C TIL \uC804\uCCB4 \uB0B4\uC6A9 \uD45C\uC2DC \u2192 `AskUserQuestion`\uC73C\uB85C \uD655\uC778 ("\uD655\uC778 \uC644\uB8CC" / "\uC218\uC815 \uD544\uC694").\n\n## Step 6: \uBCF5\uC2B5 \uB4F1\uB85D\n\n`AskUserQuestion`\uC73C\uB85C "\uC774 TIL\uC744 \uBCF5\uC2B5 \uB300\uC0C1\uC5D0 \uCD94\uAC00\uD560\uAE4C\uC694?" \uC9C8\uBB38.\n\uC0AC\uC6A9\uC790 \uB3D9\uC758 \uC2DC `til_review_update` (action: "review", grade: 4) \uD638\uCD9C\uD558\uC5EC SRS \uBA54\uD0C0\uB370\uC774\uD130 \uC0DD\uC131.\n\n## Step 7: git commit\n\n`\u{1F4DD} til: {\uD55C\uAE00 \uC81C\uBAA9}({\uC601\uBB38 \uC81C\uBAA9}) - {\uCE74\uD14C\uACE0\uB9AC}` (push \uC548 \uD568)\n\n## \uADDC\uCE59\n\n- frontmatter \uD544\uC218: date, category, tags, aliases (\uB204\uB77D \uC2DC \uC800\uC7A5 \uC804 \uBCF4\uC644)\n- tags\uC5D0 \uBC18\uB4DC\uC2DC "til" \uD3EC\uD568 (\uC815\uC801 \uC0AC\uC774\uD2B8 \uD544\uD130 \uAE30\uC900)\n- `[[\uC704\uD0A4\uB9C1\uD06C]]` \uAE08\uC9C0 \u2014 `[\uD45C\uC2DC\uBA85](\uACBD\uB85C)` \uD615\uC2DD\uB9CC \uC0AC\uC6A9\n- TIL\uB9CC \uC800\uC7A5\uD558\uACE0 Daily/MOC/\uBC31\uB85C\uADF8 \uB204\uB77D\uD558\uC9C0 \uC54A\uB294\uB2E4\n- Callout \uD65C\uC6A9: `> [!tldr]`, `> [!example]`, `> [!warning]`, `> [!tip]`\n- \uBCF5\uC7A1\uD55C \uAC1C\uB150\uC740 Mermaid \uB2E4\uC774\uC5B4\uADF8\uB7A8\uC73C\uB85C \uC2DC\uAC01\uD654 (TIL\uB2F9 \uCD5C\uB300 1\uAC1C)\n- \uBBFC\uAC10 \uC815\uBCF4 \uB300\uCCB4\uAC12 \uC0AC\uC6A9\n- \uD55C\uAD6D\uC5B4 \uC791\uC131, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n';
47599
47965
 
47600
47966
  // vault-assets/skills/migrate-links/SKILL.md
47601
47967
  var SKILL_default5 = '---\nname: migrate-links\ndescription: "vault\uC758 [[wikilink]]\uB97C \uD45C\uC900 \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C\uB85C \uC77C\uAD04 \uBCC0\uD658"\nargument-hint: ""\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Migrate Links Skill\n\n`[[wikilink]]` \u2192 `[text](path.md)` \uC77C\uAD04 \uBCC0\uD658.\n\n## CLI\n\n```bash\nnode .obsidian/plugins/oh-my-til/migrate-links.mjs . scan\nnode .obsidian/plugins/oh-my-til/migrate-links.mjs . migrate\nnode .obsidian/plugins/oh-my-til/migrate-links.mjs . verify\n```\n\n## \uC6CC\uD06C\uD50C\uB85C\uC6B0\n\n1. **\uC2A4\uCE94**: `scan` \uC2E4\uD589 \u2192 wikilink \uC5C6\uC73C\uBA74 \uC885\uB8CC, \uC788\uC73C\uBA74 `AskUserQuestion`\uC73C\uB85C \uD655\uC778\n2. **\uBCC0\uD658**: `migrate` \uC2E4\uD589\n3. **\uAC80\uC99D**: `verify` \uC2E4\uD589 \u2192 \uC794\uC5EC wikilink \uC548\uB0B4\n4. **\uCEE4\uBC0B**: `\u267B\uFE0F refactor: [[wikilink]] \u2192 \uD45C\uC900 \uB9C8\uD06C\uB2E4\uC6B4 \uB9C1\uD06C \uC77C\uAD04 \uBCC0\uD658` (push \uC548 \uD568)\n\n## \uBCC0\uD658 \uADDC\uCE59\n\n- `[[path|Display]]` \u2192 `[Display](path.md)`\n- `[[path]]` \u2192 `[path](path.md)`\n- \uCF54\uB4DC \uBE14\uB85D \uB0B4\uBD80 \uC81C\uC678, \uD14C\uC774\uBE14 `\\|` \uC774\uC2A4\uCF00\uC774\uD504 \uCC98\uB9AC\n';
@@ -47609,8 +47975,11 @@ var SKILL_default7 = '---\nname: setup-pages\ndescription: "(deprecated) /omt-se
47609
47975
  // vault-assets/skills/omt-setup/SKILL.md
47610
47976
  var SKILL_default8 = '---\nname: omt-setup\ndescription: "oh-my-til \uD1B5\uD569 \uC124\uC815 \u2014 \uBC30\uD3EC \uC124\uC815"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# OMT Setup Skill\n\noh-my-til \uC124\uC815\uC744 \uD55C \uACF3\uC5D0\uC11C \uAD00\uB9AC. \uC11C\uBE0C\uCEE4\uB9E8\uB4DC\uB85C \uB3D9\uC791.\n\n## \uC11C\uBE0C\uCEE4\uB9E8\uB4DC\n\n### `/omt-setup` (\uC778\uC218 \uC5C6\uC74C)\n\n`oh-my-til.json` \uC77D\uC5B4\uC11C \uD604\uC7AC \uC124\uC815 \uD45C\uC2DC + \uC11C\uBE0C\uCEE4\uB9E8\uB4DC \uC548\uB0B4:\n- `deploy` \u2014 GitHub Pages \uBC30\uD3EC \uC124\uC815\n\n### `/omt-setup deploy`\n\nGitHub Pages \uBC30\uD3EC \uC124\uC815:\n1. `.git/` \uD655\uC778 (\uC5C6\uC73C\uBA74 \uC548\uB0B4 \uD6C4 \uC885\uB8CC)\n2. `.github/workflows/deploy-til.yml` \uD655\uC778 (\uC788\uC73C\uBA74 \uC218\uC815 \uD544\uC694 \uC5EC\uBD80 \uC9C8\uBB38)\n3. `oh-my-til.json` deploy \uC139\uC158 \uC124\uC815 (\uC81C\uBAA9, \uBD80\uC81C\uBAA9, GitHub URL)\n4. \uC6CC\uD06C\uD50C\uB85C\uC6B0 YAML \uC0DD\uC131\n5. \uC644\uB8CC \uC548\uB0B4 (Settings \u2192 Pages \u2192 GitHub Actions \uC120\uD0DD, \uCEE4\uBC0B\xB7push \uBA85\uB839\uC5B4)\n\n## \uADDC\uCE59\n\n- \uD55C\uAD6D\uC5B4 \uCD9C\uB825\n- `oh-my-til.json`\uC758 \uAE30\uC874 \uC124\uC815 \uBCF4\uC874, \uD574\uB2F9 \uC139\uC158\uB9CC \uCD94\uAC00/\uC218\uC815\n- \uCEE4\uBC0B\uC740 \uD558\uC9C0 \uC54A\uC74C (\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBA85\uB839\uC5B4 \uC548\uB0B4\uB9CC)\n';
47611
47977
 
47978
+ // vault-assets/skills/til-review/SKILL.md
47979
+ var SKILL_default9 = '---\nname: til-review\ndescription: "SRS \uAE30\uBC18 TIL \uBCF5\uC2B5 \uC138\uC158 (\uAC04\uACA9 \uBC18\uBCF5 \uD559\uC2B5)"\nargument-hint: "[\uCE74\uD14C\uACE0\uB9AC]"\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# Review Skill\n\nSRS(\uAC04\uACA9 \uBC18\uBCF5) \uAE30\uBC18 TIL \uBCF5\uC2B5 \uC138\uC158. SM-2 \uC54C\uACE0\uB9AC\uC998\uC73C\uB85C \uBCF5\uC2B5 \uC77C\uC815\uC744 \uAD00\uB9AC\uD55C\uB2E4.\n\n## MCP \uB3C4\uAD6C\n\n- `til_review_list`: \uC624\uB298 \uBCF5\uC2B5 \uB300\uC0C1 \uCE74\uB4DC \uBAA9\uB85D + \uD1B5\uACC4\n- `til_review_update`: \uBCF5\uC2B5 \uACB0\uACFC \uAE30\uB85D (grade 0-5) \uB610\uB294 \uBCF5\uC2B5 \uD574\uC81C\n- `vault_read_note`: \uCE74\uB4DC \uB0B4\uC6A9 \uC77D\uAE30\n\n## Step 1: \uBCF5\uC2B5 \uCE74\uB4DC \uB85C\uB4DC\n\n`til_review_list` \uD638\uCD9C (\uCE74\uD14C\uACE0\uB9AC \uC778\uC790 \uC788\uC73C\uBA74 \uC804\uB2EC).\n\n- \uCE74\uB4DC 0\uAC1C \u2192 "\uC624\uB298 \uBCF5\uC2B5 \uC5C6\uC74C" \uC548\uB0B4 + \uBBF8\uB4F1\uB85D TIL \uB4F1\uB85D \uC81C\uC548 (Step 5\uB85C)\n- \uCE74\uB4DC \uC788\uC74C \u2192 \uBAA9\uB85D \uD45C\uC2DC + Step 2\uB85C\n\n## Step 2: \uD3C9\uAC00 \uBAA8\uB4DC \uC120\uD0DD\n\n`AskUserQuestion`\uC73C\uB85C \uC120\uD0DD:\n- **\uAC04\uB2E8 \uBAA8\uB4DC**: "\uAE30\uC5B5\uB0A8 / \uBAA8\uB984" 2\uB2E8\uACC4 (\uB0B4\uBD80\uC801\uC73C\uB85C grade 4 / 1)\n- **\uC0C1\uC138 \uBAA8\uB4DC**: 0~5\uC810 \uC790\uAE30 \uD3C9\uAC00\n\n## Step 3: \uCE74\uB4DC\uBCC4 \uBCF5\uC2B5 \uB8E8\uD504\n\n\uAC01 \uCE74\uB4DC\uC5D0 \uB300\uD574:\n\n1. \uC81C\uBAA9\xB7\uCE74\uD14C\uACE0\uB9AC\xB7\uBCF5\uC2B5 \uC815\uBCF4(\uBC18\uBCF5 \uD69F\uC218, EF, \uC5F0\uCCB4\uC77C) \uD45C\uC2DC\n2. `vault_read_note`\uB85C \uB0B4\uC6A9 \uC77D\uAE30\n3. \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC9C8\uBB38 \uD615\uC2DD\uC73C\uB85C \uC81C\uC2DC (\uB0B4\uC6A9 \uAE30\uBC18\uC73C\uB85C 1~2\uAC1C \uC9C8\uBB38 \uC0DD\uC131)\n4. \uC0AC\uC6A9\uC790 \uB2F5\uBCC0 \uB300\uAE30\n5. \uD53C\uB4DC\uBC31 \uC81C\uACF5 (\uC815\uB2F5/\uBCF4\uCDA9 \uC124\uBA85)\n6. \uD3C9\uAC00 \uC785\uB825:\n - \uAC04\uB2E8 \uBAA8\uB4DC: "\uAE30\uC5B5\uB0A8" / "\uBAA8\uB984" \u2192 grade 4 / 1\n - \uC0C1\uC138 \uBAA8\uB4DC: 0~5\uC810\n7. `til_review_update` (action: "review", grade) \uD638\uCD9C\n8. \uACB0\uACFC \uC694\uC57D (\uB2E4\uC74C \uBCF5\uC2B5\uC77C, interval) \uD45C\uC2DC\n\n## Step 4: \uC644\uB8CC \uD1B5\uACC4\n\n\uBAA8\uB4E0 \uCE74\uB4DC \uC644\uB8CC \uD6C4:\n- \uBCF5\uC2B5\uD55C \uCE74\uB4DC \uC218, \uD3C9\uADE0 grade\n- remaining > 0\uC774\uBA74 "N\uAC1C \uB354 \uB0A8\uC74C, \uB0B4\uC77C \uC774\uC5B4\uC11C" \uC548\uB0B4\n- `til_review_list` \uC7AC\uD638\uCD9C\uD558\uC5EC \uCD5C\uC2E0 \uD1B5\uACC4 \uD45C\uC2DC\n\n## Step 5: TIL \uB4F1\uB85D (\uC120\uD0DD)\n\n\uCE74\uB4DC\uAC00 \uC5C6\uC744 \uB54C \uB610\uB294 \uC0AC\uC6A9\uC790 \uC694\uCCAD \uC2DC:\n- `til_list`\uB85C \uC804\uCCB4 TIL \uBAA9\uB85D \uD45C\uC2DC\n- \uC0AC\uC6A9\uC790\uAC00 \uBCF5\uC2B5 \uB300\uC0C1\uC5D0 \uCD94\uAC00\uD560 \uD30C\uC77C \uC120\uD0DD\n- \uC120\uD0DD\uB41C \uD30C\uC77C\uB9C8\uB2E4 `til_review_update` (action: "review", grade: 4) \uD638\uCD9C\n\n## \uADDC\uCE59\n\n- \uD55C \uC138\uC158 \uCD5C\uB300 20\uAC1C (\uACFC\uBD80\uD558 \uBC29\uC9C0)\n- \uC5F0\uCCB4 \uCE74\uB4DC \uC6B0\uC120 (\uAC00\uC7A5 \uAE09\uD55C \uAC83 \uBA3C\uC800)\n- \uBCF5\uC2B5 \uD574\uC81C: \uC0AC\uC6A9\uC790\uAC00 "\uC774 \uCE74\uB4DC \uC81C\uAC70"\uD558\uBA74 `til_review_update` (action: "remove") \uD638\uCD9C\n- \uD55C\uAD6D\uC5B4 \uC9C4\uD589, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30\n';
47980
+
47612
47981
  // vault-assets/claude-md-section.md
47613
- var claude_md_section_default = "## \uD559\uC2B5 \uC6CC\uD06C\uD50C\uB85C\uC6B0\n\n1. `/research <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uBC31\uB85C\uADF8 \uC0DD\uC131\n2. `/backlog [\uCE74\uD14C\uACE0\uB9AC]` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589 \uD655\uC778\n3. `/til <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uB300\uD654\uD615 \uD559\uC2B5 \u2192 \uC800\uC7A5\n4. `/save` \u2014 TIL \uC800\uC7A5 (Daily/MOC/\uBC31\uB85C\uADF8 \uC790\uB3D9 \uC5C5\uB370\uC774\uD2B8)\n\n## MCP \uB3C4\uAD6C\n\n- `vault_read_note` \u2014 \uB178\uD2B8 \uC77D\uAE30\n- `vault_list_files` \u2014 \uD30C\uC77C \uBAA9\uB85D\n- `vault_search` \u2014 \uD14D\uC2A4\uD2B8 \uAC80\uC0C9\n- `vault_get_active_file` \u2014 \uD604\uC7AC \uD30C\uC77C\n- `til_list` \u2014 TIL \uBAA9\uB85D + \uCE74\uD14C\uACE0\uB9AC \uBD84\uB958\n- `til_get_context` \u2014 \uC8FC\uC81C \uAD00\uB828 \uCEE8\uD14D\uC2A4\uD2B8\n- `til_recent_context` \u2014 \uCD5C\uADFC \uD559\uC2B5 \uD750\uB984\n- `til_backlog_status` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589\uB960\n- `til_dashboard` \u2014 \uD559\uC2B5 \uD1B5\uACC4\n\n### \uC5F0\uACB0\n\n```bash\nclaude mcp add --transport http oh-my-til http://localhost:22360/mcp\n```\n";
47982
+ var claude_md_section_default = "## \uD559\uC2B5 \uC6CC\uD06C\uD50C\uB85C\uC6B0\n\n1. `/research <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uBC31\uB85C\uADF8 \uC0DD\uC131\n2. `/backlog [\uCE74\uD14C\uACE0\uB9AC]` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589 \uD655\uC778\n3. `/til <\uC8FC\uC81C>` \u2014 \uB9AC\uC11C\uCE58 \u2192 \uB300\uD654\uD615 \uD559\uC2B5 \u2192 \uC800\uC7A5\n4. `/save` \u2014 TIL \uC800\uC7A5 (Daily/MOC/\uBC31\uB85C\uADF8 \uC790\uB3D9 \uC5C5\uB370\uC774\uD2B8)\n5. `/til-review [\uCE74\uD14C\uACE0\uB9AC]` \u2014 SRS \uAE30\uBC18 \uAC04\uACA9 \uBC18\uBCF5 \uBCF5\uC2B5\n\n## MCP \uB3C4\uAD6C\n\n- `vault_read_note` \u2014 \uB178\uD2B8 \uC77D\uAE30\n- `vault_list_files` \u2014 \uD30C\uC77C \uBAA9\uB85D\n- `vault_search` \u2014 \uD14D\uC2A4\uD2B8 \uAC80\uC0C9\n- `vault_get_active_file` \u2014 \uD604\uC7AC \uD30C\uC77C\n- `til_list` \u2014 TIL \uBAA9\uB85D + \uCE74\uD14C\uACE0\uB9AC \uBD84\uB958\n- `til_get_context` \u2014 \uC8FC\uC81C \uAD00\uB828 \uCEE8\uD14D\uC2A4\uD2B8\n- `til_recent_context` \u2014 \uCD5C\uADFC \uD559\uC2B5 \uD750\uB984\n- `til_backlog_status` \u2014 \uBC31\uB85C\uADF8 \uC9C4\uD589\uB960\n- `til_dashboard` \u2014 \uD559\uC2B5 \uD1B5\uACC4\n- `til_review_list` \u2014 \uBCF5\uC2B5 \uB300\uC0C1 \uCE74\uB4DC \uBAA9\uB85D + \uD1B5\uACC4\n- `til_review_update` \u2014 \uBCF5\uC2B5 \uACB0\uACFC \uAE30\uB85D \uB610\uB294 \uBCF5\uC2B5 \uD574\uC81C\n\n### \uC5F0\uACB0\n\n```bash\nclaude mcp add --transport http oh-my-til http://localhost:22360/mcp\n```\n";
47614
47983
 
47615
47984
  // vault-assets/agents/til-fetcher.md
47616
47985
  var til_fetcher_default = '---\nname: til-fetcher\ndescription: \uC18C\uC2A4 URL \uCF58\uD150\uCE20\uB97C \uD328\uCE58\uD558\uC5EC \uD559\uC2B5 \uC790\uB8CC\uB85C \uC694\uC57D\uD558\uB294 \uC804\uC6A9 \uC5D0\uC774\uC804\uD2B8\ntools: Read, WebFetch\nmodel: haiku\nplugin-version: "__PLUGIN_VERSION__"\n---\n\n# til-fetcher\n\n\uC18C\uC2A4 URL\uC758 \uCF58\uD150\uCE20\uB97C \uD328\uCE58\uD558\uACE0 \uD559\uC2B5\uC5D0 \uD544\uC694\uD55C \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC694\uC57D\uD558\uB294 \uC804\uC6A9 \uC5D0\uC774\uC804\uD2B8.\n\n## \uC5ED\uD560\n\n- `/til` \uC2A4\uD0AC\uC758 Phase 1\uC5D0\uC11C sourceUrls \uD328\uCE58 subagent\uB85C \uC0AC\uC6A9\uB41C\uB2E4\n- \uC8FC\uC5B4\uC9C4 URL(1\uAC1C \uB610\uB294 \uC5EC\uB7EC \uAC1C)\uC744 WebFetch\uB85C \uC21C\uCC28 \uC77D\uACE0 \uD559\uC2B5\uC5D0 \uD544\uC694\uD55C \uD575\uC2EC \uB0B4\uC6A9\uC744 \uC694\uC57D\uD55C\uB2E4\n- \uC5EC\uB7EC URL\uC774 \uC804\uB2EC\uB418\uBA74 \uAC01\uAC01 \uD328\uCE58 \uD6C4 \uD1B5\uD569 \uC694\uC57D\uC744 \uBC18\uD658\uD55C\uB2E4\n\n## \uCD9C\uB825 \uD615\uC2DD\n\n- \uD575\uC2EC \uB0B4\uC6A9 \uC694\uC57D (\uD55C\uAD6D\uC5B4, \uAE30\uC220 \uC6A9\uC5B4 \uC6D0\uC5B4 \uBCD1\uAE30)\n- \uCF54\uB4DC \uC608\uC2DC\uAC00 \uC788\uC73C\uBA74 \uD3EC\uD568\n';
@@ -47661,7 +48030,8 @@ var SKILLS = {
47661
48030
  "migrate-links/SKILL.md": SKILL_default5,
47662
48031
  "dashboard/SKILL.md": SKILL_default6,
47663
48032
  "setup-pages/SKILL.md": SKILL_default7,
47664
- "omt-setup/SKILL.md": SKILL_default8
48033
+ "omt-setup/SKILL.md": SKILL_default8,
48034
+ "til-review/SKILL.md": SKILL_default9
47665
48035
  };
47666
48036
  var RULES = {};
47667
48037
  var AGENTS = {
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "oh-my-til",
3
3
  "name": "Oh My TIL",
4
- "version": "0.11.2",
4
+ "version": "0.12.2",
5
5
  "minAppVersion": "1.5.0",
6
6
  "description": "Embedded Claude Code terminal for TIL learning workflows",
7
7
  "author": "SongYunSeop",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-til",
3
- "version": "0.11.2",
3
+ "version": "0.12.2",
4
4
  "description": "Oh My TIL — Claude Code plugin for TIL learning workflow, with Obsidian integration",
5
5
  "main": "main.js",
6
6
  "bin": {