oh-my-til 1.3.1-dev.0 → 1.4.0

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,7 @@ 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`, `/til-review`, `/dashboard` 명령을 바로 사용 가능
18
+ - **스킬 자동 설치** — `/til`, `/research`, `/backlog`, `/save`, `/til-review`, `/til-lint`, `/dashboard` 명령을 바로 사용 가능
19
19
  - **간격 반복 복습 (SRS)** — SM-2 알고리즘 기반 TIL 복습 스케줄링
20
20
  - **마크다운 링크 감지** — 터미널의 `[텍스트](경로)` 링크를 클릭하면 노트 열기 (CJK 문자 지원)
21
21
  - **백로그 → TIL 연동** — 빈 백로그 링크 클릭 시 TIL 학습 세션 시작 제안
@@ -25,7 +25,7 @@ AI 기반 TIL(Today I Learned) 학습 워크플로우를 위한 Claude Code 플
25
25
 
26
26
  ```
27
27
  커맨드 팔레트 → 터미널 열기 → Claude Code 자동 시작
28
- → /til, /backlog, /research, /save, /til-review, /dashboard 스킬 실행
28
+ → /til, /backlog, /research, /save, /til-review, /til-lint, /dashboard 스킬 실행
29
29
  → Claude가 리서치 → 대화형 학습 → TIL 마크다운 저장
30
30
  → 새 파일 감지 → 에디터에서 자동 열기
31
31
  ```
@@ -148,6 +148,7 @@ MCP 서버 연결 시 Claude Code에서 사용할 수 있는 도구:
148
148
  | **backlog** | `/backlog [카테고리]` | 학습 백로그 조회 및 진행 상황 요약 |
149
149
  | **save** | *(/til에서 자동 호출)* | TIL 마크다운 저장 + Daily 노트, MOC, 백로그 연동 |
150
150
  | **til-review** | `/til-review [카테고리]` | SRS 기반 간격 반복 복습 세션 (SM-2 알고리즘) |
151
+ | **til-lint** | `/til-lint [카테고리]` | TIL 위키 상태 점검 — frontmatter 누락, 깨진 링크, 고아 TIL, 미처리 raw 자료 |
151
152
  | **dashboard** | `/dashboard` | 학습 대시보드 — 통계, 활동 히트맵, 카테고리, 백로그 진행률 |
152
153
  | **omt-setup** | `/omt-setup [서브커맨드]` | 통합 설정 — init, deploy, oh-my-til 관리 |
153
154
  | **setup-obsidian** | `/setup-obsidian` | 현재 vault에 Obsidian 데스크톱 플러그인 설치 |
package/README.md CHANGED
@@ -15,7 +15,7 @@ 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`, `/til-review`, `/dashboard` commands ready out of the box
18
+ - **Auto-installed Skills** — `/til`, `/research`, `/backlog`, `/save`, `/til-review`, `/til-lint`, `/dashboard` commands ready out of the box
19
19
  - **Spaced Repetition (SRS)** — SM-2 algorithm-based review scheduling for TIL notes
20
20
  - **Markdown Link Detection** — `[text](path)` links in terminal are clickable and open notes (CJK-aware)
21
21
  - **Backlog-to-TIL Trigger** — Click an empty backlog link to start a TIL session
@@ -25,7 +25,7 @@ A Claude Code plugin for AI-powered TIL (Today I Learned) learning workflow. Wor
25
25
 
26
26
  ```
27
27
  Command Palette → Open Terminal → Claude Code starts
28
- → Run /til, /backlog, /research, /save, /til-review, /dashboard skills
28
+ → Run /til, /backlog, /research, /save, /til-review, /til-lint, /dashboard skills
29
29
  → Claude researches → interactive learning → saves TIL markdown
30
30
  → New file detected → opens in editor
31
31
  ```
@@ -148,6 +148,7 @@ The plugin auto-installs these skills to `.claude/skills/`:
148
148
  | **backlog** | `/backlog [category]` | View learning backlog and progress |
149
149
  | **save** | *(auto-invoked by /til)* | Save TIL markdown with Daily note, MOC, and backlog updates |
150
150
  | **til-review** | `/til-review [category]` | SRS-based spaced repetition review session (SM-2 algorithm) |
151
+ | **til-lint** | `/til-lint [category]` | TIL wiki health-check — missing frontmatter, broken links, orphan TILs, unprocessed raw sources |
151
152
  | **dashboard** | `/dashboard` | Learning dashboard — stats, activity heatmap, categories, backlog progress |
152
153
  | **omt-setup** | `/omt-setup [subcommand]` | Unified setup — init, deploy, and manage oh-my-til |
153
154
  | **setup-obsidian** | `/setup-obsidian` | Install the Obsidian desktop plugin in the current vault |
package/dist/cli.js CHANGED
@@ -110,17 +110,17 @@ var require_visit = __commonJS({
110
110
  visit.BREAK = BREAK;
111
111
  visit.SKIP = SKIP;
112
112
  visit.REMOVE = REMOVE;
113
- function visit_(key, node, visitor, path7) {
114
- const ctrl = callVisitor(key, node, visitor, path7);
113
+ function visit_(key, node, visitor, path6) {
114
+ const ctrl = callVisitor(key, node, visitor, path6);
115
115
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
116
- replaceNode(key, path7, ctrl);
117
- return visit_(key, ctrl, visitor, path7);
116
+ replaceNode(key, path6, ctrl);
117
+ return visit_(key, ctrl, visitor, path6);
118
118
  }
119
119
  if (typeof ctrl !== "symbol") {
120
120
  if (identity.isCollection(node)) {
121
- path7 = Object.freeze(path7.concat(node));
121
+ path6 = Object.freeze(path6.concat(node));
122
122
  for (let i = 0; i < node.items.length; ++i) {
123
- const ci = visit_(i, node.items[i], visitor, path7);
123
+ const ci = visit_(i, node.items[i], visitor, path6);
124
124
  if (typeof ci === "number")
125
125
  i = ci - 1;
126
126
  else if (ci === BREAK)
@@ -131,13 +131,13 @@ var require_visit = __commonJS({
131
131
  }
132
132
  }
133
133
  } else if (identity.isPair(node)) {
134
- path7 = Object.freeze(path7.concat(node));
135
- const ck = visit_("key", node.key, visitor, path7);
134
+ path6 = Object.freeze(path6.concat(node));
135
+ const ck = visit_("key", node.key, visitor, path6);
136
136
  if (ck === BREAK)
137
137
  return BREAK;
138
138
  else if (ck === REMOVE)
139
139
  node.key = null;
140
- const cv = visit_("value", node.value, visitor, path7);
140
+ const cv = visit_("value", node.value, visitor, path6);
141
141
  if (cv === BREAK)
142
142
  return BREAK;
143
143
  else if (cv === REMOVE)
@@ -158,17 +158,17 @@ var require_visit = __commonJS({
158
158
  visitAsync.BREAK = BREAK;
159
159
  visitAsync.SKIP = SKIP;
160
160
  visitAsync.REMOVE = REMOVE;
161
- async function visitAsync_(key, node, visitor, path7) {
162
- const ctrl = await callVisitor(key, node, visitor, path7);
161
+ async function visitAsync_(key, node, visitor, path6) {
162
+ const ctrl = await callVisitor(key, node, visitor, path6);
163
163
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
164
- replaceNode(key, path7, ctrl);
165
- return visitAsync_(key, ctrl, visitor, path7);
164
+ replaceNode(key, path6, ctrl);
165
+ return visitAsync_(key, ctrl, visitor, path6);
166
166
  }
167
167
  if (typeof ctrl !== "symbol") {
168
168
  if (identity.isCollection(node)) {
169
- path7 = Object.freeze(path7.concat(node));
169
+ path6 = Object.freeze(path6.concat(node));
170
170
  for (let i = 0; i < node.items.length; ++i) {
171
- const ci = await visitAsync_(i, node.items[i], visitor, path7);
171
+ const ci = await visitAsync_(i, node.items[i], visitor, path6);
172
172
  if (typeof ci === "number")
173
173
  i = ci - 1;
174
174
  else if (ci === BREAK)
@@ -179,13 +179,13 @@ var require_visit = __commonJS({
179
179
  }
180
180
  }
181
181
  } else if (identity.isPair(node)) {
182
- path7 = Object.freeze(path7.concat(node));
183
- const ck = await visitAsync_("key", node.key, visitor, path7);
182
+ path6 = Object.freeze(path6.concat(node));
183
+ const ck = await visitAsync_("key", node.key, visitor, path6);
184
184
  if (ck === BREAK)
185
185
  return BREAK;
186
186
  else if (ck === REMOVE)
187
187
  node.key = null;
188
- const cv = await visitAsync_("value", node.value, visitor, path7);
188
+ const cv = await visitAsync_("value", node.value, visitor, path6);
189
189
  if (cv === BREAK)
190
190
  return BREAK;
191
191
  else if (cv === REMOVE)
@@ -212,23 +212,23 @@ var require_visit = __commonJS({
212
212
  }
213
213
  return visitor;
214
214
  }
215
- function callVisitor(key, node, visitor, path7) {
215
+ function callVisitor(key, node, visitor, path6) {
216
216
  if (typeof visitor === "function")
217
- return visitor(key, node, path7);
217
+ return visitor(key, node, path6);
218
218
  if (identity.isMap(node))
219
- return visitor.Map?.(key, node, path7);
219
+ return visitor.Map?.(key, node, path6);
220
220
  if (identity.isSeq(node))
221
- return visitor.Seq?.(key, node, path7);
221
+ return visitor.Seq?.(key, node, path6);
222
222
  if (identity.isPair(node))
223
- return visitor.Pair?.(key, node, path7);
223
+ return visitor.Pair?.(key, node, path6);
224
224
  if (identity.isScalar(node))
225
- return visitor.Scalar?.(key, node, path7);
225
+ return visitor.Scalar?.(key, node, path6);
226
226
  if (identity.isAlias(node))
227
- return visitor.Alias?.(key, node, path7);
227
+ return visitor.Alias?.(key, node, path6);
228
228
  return void 0;
229
229
  }
230
- function replaceNode(key, path7, node) {
231
- const parent = path7[path7.length - 1];
230
+ function replaceNode(key, path6, node) {
231
+ const parent = path6[path6.length - 1];
232
232
  if (identity.isCollection(parent)) {
233
233
  parent.items[key] = node;
234
234
  } else if (identity.isPair(parent)) {
@@ -836,10 +836,10 @@ var require_Collection = __commonJS({
836
836
  var createNode = require_createNode();
837
837
  var identity = require_identity();
838
838
  var Node = require_Node();
839
- function collectionFromPath(schema, path7, value) {
839
+ function collectionFromPath(schema, path6, value) {
840
840
  let v = value;
841
- for (let i = path7.length - 1; i >= 0; --i) {
842
- const k = path7[i];
841
+ for (let i = path6.length - 1; i >= 0; --i) {
842
+ const k = path6[i];
843
843
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
844
844
  const a = [];
845
845
  a[k] = v;
@@ -858,7 +858,7 @@ var require_Collection = __commonJS({
858
858
  sourceObjects: /* @__PURE__ */ new Map()
859
859
  });
860
860
  }
861
- var isEmptyPath = (path7) => path7 == null || typeof path7 === "object" && !!path7[Symbol.iterator]().next().done;
861
+ var isEmptyPath = (path6) => path6 == null || typeof path6 === "object" && !!path6[Symbol.iterator]().next().done;
862
862
  var Collection = class extends Node.NodeBase {
863
863
  constructor(type, schema) {
864
864
  super(type);
@@ -888,11 +888,11 @@ var require_Collection = __commonJS({
888
888
  * be a Pair instance or a `{ key, value }` object, which may not have a key
889
889
  * that already exists in the map.
890
890
  */
891
- addIn(path7, value) {
892
- if (isEmptyPath(path7))
891
+ addIn(path6, value) {
892
+ if (isEmptyPath(path6))
893
893
  this.add(value);
894
894
  else {
895
- const [key, ...rest] = path7;
895
+ const [key, ...rest] = path6;
896
896
  const node = this.get(key, true);
897
897
  if (identity.isCollection(node))
898
898
  node.addIn(rest, value);
@@ -906,8 +906,8 @@ var require_Collection = __commonJS({
906
906
  * Removes a value from the collection.
907
907
  * @returns `true` if the item was found and removed.
908
908
  */
909
- deleteIn(path7) {
910
- const [key, ...rest] = path7;
909
+ deleteIn(path6) {
910
+ const [key, ...rest] = path6;
911
911
  if (rest.length === 0)
912
912
  return this.delete(key);
913
913
  const node = this.get(key, true);
@@ -921,8 +921,8 @@ var require_Collection = __commonJS({
921
921
  * scalar values from their surrounding node; to disable set `keepScalar` to
922
922
  * `true` (collections are always returned intact).
923
923
  */
924
- getIn(path7, keepScalar) {
925
- const [key, ...rest] = path7;
924
+ getIn(path6, keepScalar) {
925
+ const [key, ...rest] = path6;
926
926
  const node = this.get(key, true);
927
927
  if (rest.length === 0)
928
928
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -940,8 +940,8 @@ var require_Collection = __commonJS({
940
940
  /**
941
941
  * Checks if the collection includes a value with the key `key`.
942
942
  */
943
- hasIn(path7) {
944
- const [key, ...rest] = path7;
943
+ hasIn(path6) {
944
+ const [key, ...rest] = path6;
945
945
  if (rest.length === 0)
946
946
  return this.has(key);
947
947
  const node = this.get(key, true);
@@ -951,8 +951,8 @@ var require_Collection = __commonJS({
951
951
  * Sets a value in this collection. For `!!set`, `value` needs to be a
952
952
  * boolean to add/remove the item from the set.
953
953
  */
954
- setIn(path7, value) {
955
- const [key, ...rest] = path7;
954
+ setIn(path6, value) {
955
+ const [key, ...rest] = path6;
956
956
  if (rest.length === 0) {
957
957
  this.set(key, value);
958
958
  } else {
@@ -3456,9 +3456,9 @@ var require_Document = __commonJS({
3456
3456
  this.contents.add(value);
3457
3457
  }
3458
3458
  /** Adds a value to the document. */
3459
- addIn(path7, value) {
3459
+ addIn(path6, value) {
3460
3460
  if (assertCollection(this.contents))
3461
- this.contents.addIn(path7, value);
3461
+ this.contents.addIn(path6, value);
3462
3462
  }
3463
3463
  /**
3464
3464
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -3533,14 +3533,14 @@ var require_Document = __commonJS({
3533
3533
  * Removes a value from the document.
3534
3534
  * @returns `true` if the item was found and removed.
3535
3535
  */
3536
- deleteIn(path7) {
3537
- if (Collection.isEmptyPath(path7)) {
3536
+ deleteIn(path6) {
3537
+ if (Collection.isEmptyPath(path6)) {
3538
3538
  if (this.contents == null)
3539
3539
  return false;
3540
3540
  this.contents = null;
3541
3541
  return true;
3542
3542
  }
3543
- return assertCollection(this.contents) ? this.contents.deleteIn(path7) : false;
3543
+ return assertCollection(this.contents) ? this.contents.deleteIn(path6) : false;
3544
3544
  }
3545
3545
  /**
3546
3546
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -3555,10 +3555,10 @@ var require_Document = __commonJS({
3555
3555
  * scalar values from their surrounding node; to disable set `keepScalar` to
3556
3556
  * `true` (collections are always returned intact).
3557
3557
  */
3558
- getIn(path7, keepScalar) {
3559
- if (Collection.isEmptyPath(path7))
3558
+ getIn(path6, keepScalar) {
3559
+ if (Collection.isEmptyPath(path6))
3560
3560
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
3561
- return identity.isCollection(this.contents) ? this.contents.getIn(path7, keepScalar) : void 0;
3561
+ return identity.isCollection(this.contents) ? this.contents.getIn(path6, keepScalar) : void 0;
3562
3562
  }
3563
3563
  /**
3564
3564
  * Checks if the document includes a value with the key `key`.
@@ -3569,10 +3569,10 @@ var require_Document = __commonJS({
3569
3569
  /**
3570
3570
  * Checks if the document includes a value at `path`.
3571
3571
  */
3572
- hasIn(path7) {
3573
- if (Collection.isEmptyPath(path7))
3572
+ hasIn(path6) {
3573
+ if (Collection.isEmptyPath(path6))
3574
3574
  return this.contents !== void 0;
3575
- return identity.isCollection(this.contents) ? this.contents.hasIn(path7) : false;
3575
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path6) : false;
3576
3576
  }
3577
3577
  /**
3578
3578
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -3589,13 +3589,13 @@ var require_Document = __commonJS({
3589
3589
  * Sets a value in this document. For `!!set`, `value` needs to be a
3590
3590
  * boolean to add/remove the item from the set.
3591
3591
  */
3592
- setIn(path7, value) {
3593
- if (Collection.isEmptyPath(path7)) {
3592
+ setIn(path6, value) {
3593
+ if (Collection.isEmptyPath(path6)) {
3594
3594
  this.contents = value;
3595
3595
  } else if (this.contents == null) {
3596
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path7), value);
3596
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path6), value);
3597
3597
  } else if (assertCollection(this.contents)) {
3598
- this.contents.setIn(path7, value);
3598
+ this.contents.setIn(path6, value);
3599
3599
  }
3600
3600
  }
3601
3601
  /**
@@ -5543,9 +5543,9 @@ var require_cst_visit = __commonJS({
5543
5543
  visit.BREAK = BREAK;
5544
5544
  visit.SKIP = SKIP;
5545
5545
  visit.REMOVE = REMOVE;
5546
- visit.itemAtPath = (cst, path7) => {
5546
+ visit.itemAtPath = (cst, path6) => {
5547
5547
  let item = cst;
5548
- for (const [field, index] of path7) {
5548
+ for (const [field, index] of path6) {
5549
5549
  const tok = item?.[field];
5550
5550
  if (tok && "items" in tok) {
5551
5551
  item = tok.items[index];
@@ -5554,23 +5554,23 @@ var require_cst_visit = __commonJS({
5554
5554
  }
5555
5555
  return item;
5556
5556
  };
5557
- visit.parentCollection = (cst, path7) => {
5558
- const parent = visit.itemAtPath(cst, path7.slice(0, -1));
5559
- const field = path7[path7.length - 1][0];
5557
+ visit.parentCollection = (cst, path6) => {
5558
+ const parent = visit.itemAtPath(cst, path6.slice(0, -1));
5559
+ const field = path6[path6.length - 1][0];
5560
5560
  const coll = parent?.[field];
5561
5561
  if (coll && "items" in coll)
5562
5562
  return coll;
5563
5563
  throw new Error("Parent collection not found");
5564
5564
  };
5565
- function _visit(path7, item, visitor) {
5566
- let ctrl = visitor(item, path7);
5565
+ function _visit(path6, item, visitor) {
5566
+ let ctrl = visitor(item, path6);
5567
5567
  if (typeof ctrl === "symbol")
5568
5568
  return ctrl;
5569
5569
  for (const field of ["key", "value"]) {
5570
5570
  const token = item[field];
5571
5571
  if (token && "items" in token) {
5572
5572
  for (let i = 0; i < token.items.length; ++i) {
5573
- const ci = _visit(Object.freeze(path7.concat([[field, i]])), token.items[i], visitor);
5573
+ const ci = _visit(Object.freeze(path6.concat([[field, i]])), token.items[i], visitor);
5574
5574
  if (typeof ci === "number")
5575
5575
  i = ci - 1;
5576
5576
  else if (ci === BREAK)
@@ -5581,10 +5581,10 @@ var require_cst_visit = __commonJS({
5581
5581
  }
5582
5582
  }
5583
5583
  if (typeof ctrl === "function" && field === "key")
5584
- ctrl = ctrl(item, path7);
5584
+ ctrl = ctrl(item, path6);
5585
5585
  }
5586
5586
  }
5587
- return typeof ctrl === "function" ? ctrl(item, path7) : ctrl;
5587
+ return typeof ctrl === "function" ? ctrl(item, path6) : ctrl;
5588
5588
  }
5589
5589
  exports2.visit = visit;
5590
5590
  }
@@ -6863,14 +6863,14 @@ var require_parser = __commonJS({
6863
6863
  case "scalar":
6864
6864
  case "single-quoted-scalar":
6865
6865
  case "double-quoted-scalar": {
6866
- const fs6 = this.flowScalar(this.type);
6866
+ const fs5 = this.flowScalar(this.type);
6867
6867
  if (atNextItem || it.value) {
6868
- map2.items.push({ start, key: fs6, sep: [] });
6868
+ map2.items.push({ start, key: fs5, sep: [] });
6869
6869
  this.onKeyLine = true;
6870
6870
  } else if (it.sep) {
6871
- this.stack.push(fs6);
6871
+ this.stack.push(fs5);
6872
6872
  } else {
6873
- Object.assign(it, { key: fs6, sep: [] });
6873
+ Object.assign(it, { key: fs5, sep: [] });
6874
6874
  this.onKeyLine = true;
6875
6875
  }
6876
6876
  return;
@@ -6998,13 +6998,13 @@ var require_parser = __commonJS({
6998
6998
  case "scalar":
6999
6999
  case "single-quoted-scalar":
7000
7000
  case "double-quoted-scalar": {
7001
- const fs6 = this.flowScalar(this.type);
7001
+ const fs5 = this.flowScalar(this.type);
7002
7002
  if (!it || it.value)
7003
- fc.items.push({ start: [], key: fs6, sep: [] });
7003
+ fc.items.push({ start: [], key: fs5, sep: [] });
7004
7004
  else if (it.sep)
7005
- this.stack.push(fs6);
7005
+ this.stack.push(fs5);
7006
7006
  else
7007
- Object.assign(it, { key: fs6, sep: [] });
7007
+ Object.assign(it, { key: fs5, sep: [] });
7008
7008
  return;
7009
7009
  }
7010
7010
  case "flow-map-end":
@@ -10515,8 +10515,8 @@ var require_utils = __commonJS({
10515
10515
  }
10516
10516
  return ind;
10517
10517
  }
10518
- function removeDotSegments(path7) {
10519
- let input = path7;
10518
+ function removeDotSegments(path6) {
10519
+ let input = path6;
10520
10520
  const output = [];
10521
10521
  let nextSlash = -1;
10522
10522
  let len = 0;
@@ -10715,8 +10715,8 @@ var require_schemes = __commonJS({
10715
10715
  wsComponent.secure = void 0;
10716
10716
  }
10717
10717
  if (wsComponent.resourceName) {
10718
- const [path7, query] = wsComponent.resourceName.split("?");
10719
- wsComponent.path = path7 && path7 !== "/" ? path7 : void 0;
10718
+ const [path6, query] = wsComponent.resourceName.split("?");
10719
+ wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
10720
10720
  wsComponent.query = query;
10721
10721
  wsComponent.resourceName = void 0;
10722
10722
  }
@@ -14079,12 +14079,12 @@ var require_dist2 = __commonJS({
14079
14079
  throw new Error(`Unknown format "${name}"`);
14080
14080
  return f;
14081
14081
  };
14082
- function addFormats(ajv, list, fs6, exportName) {
14082
+ function addFormats(ajv, list, fs5, exportName) {
14083
14083
  var _a2;
14084
14084
  var _b;
14085
14085
  (_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
14086
14086
  for (const f of list)
14087
- ajv.addFormat(f, fs6[f]);
14087
+ ajv.addFormat(f, fs5[f]);
14088
14088
  }
14089
14089
  module2.exports = exports2 = formatsPlugin;
14090
14090
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -14099,126 +14099,31 @@ __export(config_exports, {
14099
14099
  loadSiteConfig: () => loadSiteConfig
14100
14100
  });
14101
14101
  function loadOmtConfig(basePath) {
14102
- const configPath = path5.join(basePath, "oh-my-til.json");
14102
+ const configPath = path4.join(basePath, "oh-my-til.json");
14103
14103
  try {
14104
- const raw = fs4.readFileSync(configPath, "utf-8");
14104
+ const raw = fs3.readFileSync(configPath, "utf-8");
14105
14105
  const parsed = JSON.parse(raw);
14106
14106
  return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
14107
14107
  } catch {
14108
14108
  return {};
14109
14109
  }
14110
14110
  }
14111
- var path5, fs4, loadSiteConfig;
14111
+ var path4, fs3, loadSiteConfig;
14112
14112
  var init_config = __esm({
14113
14113
  "src/core/config.ts"() {
14114
- path5 = __toESM(require("path"));
14115
- fs4 = __toESM(require("fs"));
14114
+ path4 = __toESM(require("path"));
14115
+ fs3 = __toESM(require("fs"));
14116
14116
  loadSiteConfig = loadOmtConfig;
14117
14117
  }
14118
14118
  });
14119
14119
 
14120
- // src/cli/global-config.ts
14121
- var fs = __toESM(require("fs"));
14122
- var os = __toESM(require("os"));
14123
- var path = __toESM(require("path"));
14124
- var CONFIG_DIR = path.join(os.homedir(), ".config", "oh-my-til");
14125
- var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
14126
- var VALID_PROVIDERS = ["anthropic", "openai", "ollama"];
14127
- function readConfig() {
14128
- if (!fs.existsSync(CONFIG_FILE))
14129
- return {};
14130
- try {
14131
- const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
14132
- return JSON.parse(raw);
14133
- } catch {
14134
- return {};
14135
- }
14136
- }
14137
- function writeConfig(config2) {
14138
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
14139
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config2, null, 2), { encoding: "utf-8", mode: 384 });
14140
- }
14141
- function maskApiKey(key) {
14142
- if (key.length <= 8)
14143
- return "***";
14144
- return key.slice(0, 4) + "..." + key.slice(-4);
14145
- }
14146
- function runConfigCommand(args) {
14147
- const subcommand = args[0];
14148
- if (!subcommand || subcommand === "get") {
14149
- const config2 = readConfig();
14150
- const display = {};
14151
- if (config2.vault)
14152
- display["vault"] = config2.vault;
14153
- if (config2.ai) {
14154
- display["ai"] = {
14155
- provider: config2.ai.provider,
14156
- ...config2.ai.model ? { model: config2.ai.model } : {},
14157
- ...config2.ai.apiKey ? { apiKey: maskApiKey(config2.ai.apiKey) } : {}
14158
- };
14159
- }
14160
- if (Object.keys(display).length === 0) {
14161
- console.log("No config set. Use `omt config set <key> <value>`");
14162
- } else {
14163
- console.log(JSON.stringify(display, null, 2));
14164
- }
14165
- return;
14166
- }
14167
- if (subcommand === "set") {
14168
- const key = args[1];
14169
- const value = args[2];
14170
- if (!key || value === void 0) {
14171
- console.error("Usage: omt config set <key> <value>");
14172
- console.error("Keys: vault, ai.provider, ai.model, ai.apiKey");
14173
- process.exit(1);
14174
- }
14175
- const config2 = readConfig();
14176
- if (key === "vault") {
14177
- config2.vault = value;
14178
- writeConfig(config2);
14179
- console.log(`vault = ${value}`);
14180
- } else if (key === "ai.provider") {
14181
- if (!VALID_PROVIDERS.includes(value)) {
14182
- console.error(`Invalid provider. Choose from: ${VALID_PROVIDERS.join(", ")}`);
14183
- process.exit(1);
14184
- }
14185
- config2.ai = { ...config2.ai, provider: value };
14186
- writeConfig(config2);
14187
- console.log(`ai.provider = ${value}`);
14188
- } else if (key === "ai.model") {
14189
- if (!config2.ai) {
14190
- console.error("Set ai.provider first.");
14191
- process.exit(1);
14192
- }
14193
- config2.ai.model = value;
14194
- writeConfig(config2);
14195
- console.log(`ai.model = ${value}`);
14196
- } else if (key === "ai.apiKey") {
14197
- if (!config2.ai) {
14198
- console.error("Set ai.provider first.");
14199
- process.exit(1);
14200
- }
14201
- config2.ai.apiKey = value;
14202
- writeConfig(config2);
14203
- console.log(`ai.apiKey = ${maskApiKey(value)}`);
14204
- } else {
14205
- console.error(`Unknown key: ${key}`);
14206
- console.error("Keys: vault, ai.provider, ai.model, ai.apiKey");
14207
- process.exit(1);
14208
- }
14209
- return;
14210
- }
14211
- console.error(`Unknown config subcommand: ${subcommand}`);
14212
- process.exit(1);
14213
- }
14214
-
14215
14120
  // src/adapters/fs-adapter.ts
14216
- var fs2 = __toESM(require("fs/promises"));
14217
- var path2 = __toESM(require("path"));
14121
+ var fs = __toESM(require("fs/promises"));
14122
+ var path = __toESM(require("path"));
14218
14123
  var import_yaml = __toESM(require_dist());
14219
14124
  function resolveSafe(resolvedBase, filePath) {
14220
- const resolved = path2.resolve(resolvedBase, filePath);
14221
- if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + path2.sep)) {
14125
+ const resolved = path.resolve(resolvedBase, filePath);
14126
+ if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + path.sep)) {
14222
14127
  throw new Error(`Path traversal denied: ${filePath}`);
14223
14128
  }
14224
14129
  return resolved;
@@ -14226,11 +14131,11 @@ function resolveSafe(resolvedBase, filePath) {
14226
14131
  var FsStorage = class {
14227
14132
  constructor(basePath) {
14228
14133
  this.basePath = basePath;
14229
- this.resolvedBase = path2.resolve(basePath);
14134
+ this.resolvedBase = path.resolve(basePath);
14230
14135
  }
14231
14136
  async readFile(filePath) {
14232
14137
  try {
14233
- return await fs2.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14138
+ return await fs.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14234
14139
  } catch {
14235
14140
  return null;
14236
14141
  }
@@ -14244,7 +14149,7 @@ var FsStorage = class {
14244
14149
  const fullDir = resolveSafe(this.resolvedBase, dir || ".");
14245
14150
  let dirents;
14246
14151
  try {
14247
- dirents = await fs2.readdir(fullDir, { withFileTypes: true });
14152
+ dirents = await fs.readdir(fullDir, { withFileTypes: true });
14248
14153
  } catch {
14249
14154
  return;
14250
14155
  }
@@ -14257,7 +14162,7 @@ var FsStorage = class {
14257
14162
  } else if (d.isFile()) {
14258
14163
  const ext = d.name.includes(".") ? d.name.split(".").pop() : "";
14259
14164
  try {
14260
- const stat2 = await fs2.stat(resolveSafe(this.resolvedBase, relative));
14165
+ const stat2 = await fs.stat(resolveSafe(this.resolvedBase, relative));
14261
14166
  entries.push({
14262
14167
  path: relative,
14263
14168
  extension: ext,
@@ -14272,7 +14177,7 @@ var FsStorage = class {
14272
14177
  }
14273
14178
  async exists(filePath) {
14274
14179
  try {
14275
- await fs2.access(resolveSafe(this.resolvedBase, filePath));
14180
+ await fs.access(resolveSafe(this.resolvedBase, filePath));
14276
14181
  return true;
14277
14182
  } catch {
14278
14183
  return false;
@@ -14280,15 +14185,15 @@ var FsStorage = class {
14280
14185
  }
14281
14186
  async writeFile(filePath, content) {
14282
14187
  const fullPath = resolveSafe(this.resolvedBase, filePath);
14283
- await fs2.mkdir(path2.dirname(fullPath), { recursive: true });
14284
- await fs2.writeFile(fullPath, content, "utf-8");
14188
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
14189
+ await fs.writeFile(fullPath, content, "utf-8");
14285
14190
  }
14286
14191
  async mkdir(dirPath) {
14287
- await fs2.mkdir(resolveSafe(this.resolvedBase, dirPath), { recursive: true });
14192
+ await fs.mkdir(resolveSafe(this.resolvedBase, dirPath), { recursive: true });
14288
14193
  }
14289
14194
  async remove(filePath) {
14290
14195
  try {
14291
- await fs2.unlink(resolveSafe(this.resolvedBase, filePath));
14196
+ await fs.unlink(resolveSafe(this.resolvedBase, filePath));
14292
14197
  } catch {
14293
14198
  }
14294
14199
  }
@@ -14301,13 +14206,13 @@ var FsMetadata = class {
14301
14206
  constructor(basePath, storage) {
14302
14207
  this.basePath = basePath;
14303
14208
  this.linkScanCache = null;
14304
- this.resolvedBase = path2.resolve(basePath);
14209
+ this.resolvedBase = path.resolve(basePath);
14305
14210
  this.storage = storage ?? new FsStorage(basePath);
14306
14211
  }
14307
14212
  async getFileMetadata(filePath) {
14308
14213
  let content;
14309
14214
  try {
14310
- content = await fs2.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14215
+ content = await fs.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14311
14216
  } catch {
14312
14217
  return null;
14313
14218
  }
@@ -14837,8 +14742,8 @@ function getErrorMap() {
14837
14742
 
14838
14743
  // node_modules/zod/v3/helpers/parseUtil.js
14839
14744
  var makeIssue = (params) => {
14840
- const { data, path: path7, errorMaps, issueData } = params;
14841
- const fullPath = [...path7, ...issueData.path || []];
14745
+ const { data, path: path6, errorMaps, issueData } = params;
14746
+ const fullPath = [...path6, ...issueData.path || []];
14842
14747
  const fullIssue = {
14843
14748
  ...issueData,
14844
14749
  path: fullPath
@@ -14953,11 +14858,11 @@ var errorUtil;
14953
14858
 
14954
14859
  // node_modules/zod/v3/types.js
14955
14860
  var ParseInputLazyPath = class {
14956
- constructor(parent, value, path7, key) {
14861
+ constructor(parent, value, path6, key) {
14957
14862
  this._cachedPath = [];
14958
14863
  this.parent = parent;
14959
14864
  this.data = value;
14960
- this._path = path7;
14865
+ this._path = path6;
14961
14866
  this._key = key;
14962
14867
  }
14963
14868
  get path() {
@@ -18881,10 +18786,10 @@ function mergeDefs(...defs) {
18881
18786
  function cloneDef(schema) {
18882
18787
  return mergeDefs(schema._zod.def);
18883
18788
  }
18884
- function getElementAtPath(obj, path7) {
18885
- if (!path7)
18789
+ function getElementAtPath(obj, path6) {
18790
+ if (!path6)
18886
18791
  return obj;
18887
- return path7.reduce((acc, key) => acc?.[key], obj);
18792
+ return path6.reduce((acc, key) => acc?.[key], obj);
18888
18793
  }
18889
18794
  function promiseAllObject(promisesObj) {
18890
18795
  const keys = Object.keys(promisesObj);
@@ -19267,11 +19172,11 @@ function aborted(x, startIndex = 0) {
19267
19172
  }
19268
19173
  return false;
19269
19174
  }
19270
- function prefixIssues(path7, issues) {
19175
+ function prefixIssues(path6, issues) {
19271
19176
  return issues.map((iss) => {
19272
19177
  var _a2;
19273
19178
  (_a2 = iss).path ?? (_a2.path = []);
19274
- iss.path.unshift(path7);
19179
+ iss.path.unshift(path6);
19275
19180
  return iss;
19276
19181
  });
19277
19182
  }
@@ -19454,7 +19359,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
19454
19359
  }
19455
19360
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
19456
19361
  const result = { errors: [] };
19457
- const processError = (error49, path7 = []) => {
19362
+ const processError = (error49, path6 = []) => {
19458
19363
  var _a2, _b;
19459
19364
  for (const issue2 of error49.issues) {
19460
19365
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -19464,7 +19369,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
19464
19369
  } else if (issue2.code === "invalid_element") {
19465
19370
  processError({ issues: issue2.issues }, issue2.path);
19466
19371
  } else {
19467
- const fullpath = [...path7, ...issue2.path];
19372
+ const fullpath = [...path6, ...issue2.path];
19468
19373
  if (fullpath.length === 0) {
19469
19374
  result.errors.push(mapper(issue2));
19470
19375
  continue;
@@ -19496,8 +19401,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
19496
19401
  }
19497
19402
  function toDotPath(_path) {
19498
19403
  const segs = [];
19499
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19500
- for (const seg of path7) {
19404
+ const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19405
+ for (const seg of path6) {
19501
19406
  if (typeof seg === "number")
19502
19407
  segs.push(`[${seg}]`);
19503
19408
  else if (typeof seg === "symbol")
@@ -31902,13 +31807,13 @@ function resolveRef(ref, ctx) {
31902
31807
  if (!ref.startsWith("#")) {
31903
31808
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
31904
31809
  }
31905
- const path7 = ref.slice(1).split("/").filter(Boolean);
31906
- if (path7.length === 0) {
31810
+ const path6 = ref.slice(1).split("/").filter(Boolean);
31811
+ if (path6.length === 0) {
31907
31812
  return ctx.rootSchema;
31908
31813
  }
31909
31814
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
31910
- if (path7[0] === defsKey) {
31911
- const key = path7[1];
31815
+ if (path6[0] === defsKey) {
31816
+ const key = path6[1];
31912
31817
  if (!key || !ctx.defs[key]) {
31913
31818
  throw new Error(`Reference not found: ${ref}`);
31914
31819
  }
@@ -37783,9 +37688,9 @@ function extractCategory(filePath, tilPath) {
37783
37688
  const parts = relative.split("/");
37784
37689
  return parts.length >= 2 ? parts[0] : "(uncategorized)";
37785
37690
  }
37786
- function buildFileContext(path7, tilPath, matchType, headings, outgoingLinks, backlinks, tags) {
37787
- const category = extractCategory(path7, tilPath);
37788
- return { path: path7, category, headings, outgoingLinks, backlinks, tags, matchType };
37691
+ function buildFileContext(path6, tilPath, matchType, headings, outgoingLinks, backlinks, tags) {
37692
+ const category = extractCategory(path6, tilPath);
37693
+ return { path: path6, category, headings, outgoingLinks, backlinks, tags, matchType };
37789
37694
  }
37790
37695
  function findUnresolvedMentions(unresolvedLinks, topic, tilPath) {
37791
37696
  const lowerTopic = topic.toLowerCase();
@@ -37960,10 +37865,10 @@ function parseBacklogSections(content) {
37960
37865
  if (itemMatch) {
37961
37866
  const done = itemMatch[1] !== " ";
37962
37867
  const rawPath = itemMatch[3].trim();
37963
- const path7 = rawPath.endsWith(".md") ? rawPath : rawPath + ".md";
37964
- const displayName = itemMatch[2]?.trim() || path7.replace(/\.md$/, "");
37965
- const slug = path7.replace(/\.md$/, "").split("/").pop() ?? "";
37966
- const item = { displayName, path: path7, done };
37868
+ const path6 = rawPath.endsWith(".md") ? rawPath : rawPath + ".md";
37869
+ const displayName = itemMatch[2]?.trim() || path6.replace(/\.md$/, "");
37870
+ const slug = path6.replace(/\.md$/, "").split("/").pop() ?? "";
37871
+ const item = { displayName, path: path6, done };
37967
37872
  if (sources[slug] && sources[slug].length > 0) {
37968
37873
  item.sourceUrls = sources[slug];
37969
37874
  }
@@ -38583,7 +38488,7 @@ function registerTools(server, storage, metadata, tilPath) {
38583
38488
  const texts = await Promise.all(batch.map((f) => storage.readFile(f.path)));
38584
38489
  for (let j = 0; j < batch.length; j++) {
38585
38490
  const text = texts[j];
38586
- if (text != null && text.toLowerCase().includes(lowerTopic)) {
38491
+ if (text !== null && text.toLowerCase().includes(lowerTopic)) {
38587
38492
  contentMatches.push(batch[j].path);
38588
38493
  }
38589
38494
  if (pathMatches.length + contentMatches.length >= 20)
@@ -38773,31 +38678,31 @@ function registerTools(server, storage, metadata, tilPath) {
38773
38678
  action: external_exports3.enum(["review", "remove"]).optional().describe("review (default): record review, remove: remove from review schedule")
38774
38679
  })
38775
38680
  },
38776
- async ({ path: path7, grade, action }) => {
38681
+ async ({ path: path6, grade, action }) => {
38777
38682
  const effectiveAction = action ?? "review";
38778
- const content = await storage.readFile(path7);
38683
+ const content = await storage.readFile(path6);
38779
38684
  if (content === null) {
38780
- return { content: [{ type: "text", text: JSON.stringify({ error: `File not found: ${path7}` }) }], isError: true };
38685
+ return { content: [{ type: "text", text: JSON.stringify({ error: `File not found: ${path6}` }) }], isError: true };
38781
38686
  }
38782
38687
  if (effectiveAction === "remove") {
38783
38688
  const updated2 = removeFrontmatterSrs(content);
38784
- await storage.writeFile(path7, updated2);
38785
- return { content: [{ type: "text", text: JSON.stringify({ path: path7, removed: true }) }] };
38689
+ await storage.writeFile(path6, updated2);
38690
+ return { content: [{ type: "text", text: JSON.stringify({ path: path6, removed: true }) }] };
38786
38691
  }
38787
38692
  if (grade === void 0) {
38788
38693
  return { content: [{ type: "text", text: "Error: grade (0-5) is required when action=review" }], isError: true };
38789
38694
  }
38790
- const fileMeta = await metadata.getFileMetadata(path7);
38695
+ const fileMeta = await metadata.getFileMetadata(path6);
38791
38696
  const fm = fileMeta?.frontmatter ?? {};
38792
38697
  const currentSrs = parseSrsMetadata(fm) ?? createDefaultSrsMetadata();
38793
38698
  const newSrs = computeNextReview(currentSrs, grade);
38794
38699
  const updated = updateFrontmatterSrs(content, newSrs);
38795
- await storage.writeFile(path7, updated);
38700
+ await storage.writeFile(path6, updated);
38796
38701
  return {
38797
38702
  content: [{
38798
38703
  type: "text",
38799
38704
  text: JSON.stringify({
38800
- path: path7,
38705
+ path: path6,
38801
38706
  grade,
38802
38707
  next_review: newSrs.next_review,
38803
38708
  interval: newSrs.interval,
@@ -38826,7 +38731,7 @@ function registerTools(server, storage, metadata, tilPath) {
38826
38731
  })
38827
38732
  },
38828
38733
  async ({ category, slug, title, content, tags, date: date5, fmCategory, aliases, auto_check_backlog }) => {
38829
- const path7 = `${tilPath}/${category}/${slug}.md`;
38734
+ const path6 = `${tilPath}/${category}/${slug}.md`;
38830
38735
  const noteDate = date5 || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
38831
38736
  const fmLines = ["---", `title: "${title.replace(/"/g, '\\"')}"`, `date: ${noteDate}`];
38832
38737
  const effectiveCategory = fmCategory ?? category;
@@ -38843,9 +38748,9 @@ function registerTools(server, storage, metadata, tilPath) {
38843
38748
  fmLines.push("---", "");
38844
38749
  const fullContent = fmLines.join("\n") + content;
38845
38750
  await storage.mkdir(`${tilPath}/${category}`);
38846
- const existed = await storage.exists(path7);
38847
- await storage.writeFile(path7, fullContent);
38848
- const data = { path: path7, created: !existed, category, slug, title };
38751
+ const existed = await storage.exists(path6);
38752
+ await storage.writeFile(path6, fullContent);
38753
+ const data = { path: path6, created: !existed, category, slug, title };
38849
38754
  if (auto_check_backlog) {
38850
38755
  const backlogPath = `${tilPath}/${category}/backlog.md`;
38851
38756
  const backlogContent = await storage.readFile(backlogPath);
@@ -38892,13 +38797,13 @@ function registerTools(server, storage, metadata, tilPath) {
38892
38797
  }
38893
38798
 
38894
38799
  // src/cli/obsidian-install.ts
38895
- var path3 = __toESM(require("path"));
38896
- var fs3 = __toESM(require("fs"));
38800
+ var path2 = __toESM(require("path"));
38801
+ var fs2 = __toESM(require("fs"));
38897
38802
  var import_child_process = require("child_process");
38898
38803
  var PLUGIN_ARTIFACTS = ["main.js", "manifest.json", "styles.css", "migrate-links.mjs"];
38899
38804
  var VERSION_PATTERN = /^\d+\.\d+\.\d+(-\S+)?$/;
38900
38805
  function getPluginArtifacts(packageRoot) {
38901
- return PLUGIN_ARTIFACTS.map((f) => path3.join(packageRoot, f));
38806
+ return PLUGIN_ARTIFACTS.map((f) => path2.join(packageRoot, f));
38902
38807
  }
38903
38808
  function isValidVersion(v) {
38904
38809
  return VERSION_PATTERN.test(v);
@@ -38934,7 +38839,7 @@ function detectElectronVersion(override) {
38934
38839
  if (envVersion && isValidVersion(envVersion))
38935
38840
  return envVersion;
38936
38841
  const plist = "/Applications/Obsidian.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Info.plist";
38937
- if (fs3.existsSync(plist)) {
38842
+ if (fs2.existsSync(plist)) {
38938
38843
  try {
38939
38844
  const output = (0, import_child_process.execFileSync)(
38940
38845
  "/usr/libexec/PlistBuddy",
@@ -38949,12 +38854,12 @@ function detectElectronVersion(override) {
38949
38854
  return null;
38950
38855
  }
38951
38856
  function installObsidianPlugin(vaultPath, packageRoot, options) {
38952
- const pluginDir = path3.join(vaultPath, ".obsidian", "plugins", "oh-my-til");
38857
+ const pluginDir = path2.join(vaultPath, ".obsidian", "plugins", "oh-my-til");
38953
38858
  const warnings = [];
38954
38859
  let rebuilt = false;
38955
38860
  const artifacts = getPluginArtifacts(packageRoot);
38956
38861
  for (const artifact of artifacts) {
38957
- if (!fs3.existsSync(artifact)) {
38862
+ if (!fs2.existsSync(artifact)) {
38958
38863
  return {
38959
38864
  success: false,
38960
38865
  pluginDir,
@@ -38963,14 +38868,14 @@ function installObsidianPlugin(vaultPath, packageRoot, options) {
38963
38868
  };
38964
38869
  }
38965
38870
  }
38966
- fs3.mkdirSync(pluginDir, { recursive: true });
38871
+ fs2.mkdirSync(pluginDir, { recursive: true });
38967
38872
  for (const artifact of artifacts) {
38968
- const dest = path3.join(pluginDir, path3.basename(artifact));
38969
- fs3.copyFileSync(artifact, dest);
38873
+ const dest = path2.join(pluginDir, path2.basename(artifact));
38874
+ fs2.copyFileSync(artifact, dest);
38970
38875
  }
38971
- const pkgJsonPath = path3.join(pluginDir, "package.json");
38972
- if (!fs3.existsSync(pkgJsonPath)) {
38973
- fs3.writeFileSync(pkgJsonPath, buildPluginPackageJson());
38876
+ const pkgJsonPath = path2.join(pluginDir, "package.json");
38877
+ if (!fs2.existsSync(pkgJsonPath)) {
38878
+ fs2.writeFileSync(pkgJsonPath, buildPluginPackageJson());
38974
38879
  }
38975
38880
  try {
38976
38881
  (0, import_child_process.execFileSync)("npm", ["install", "--omit=dev"], {
@@ -38993,19 +38898,19 @@ function installObsidianPlugin(vaultPath, packageRoot, options) {
38993
38898
  );
38994
38899
  return { success: true, pluginDir, warnings, rebuilt: false };
38995
38900
  }
38996
- const versionFile = path3.join(pluginDir, ".electron-version");
38997
- const cachedVersion = fs3.existsSync(versionFile) ? fs3.readFileSync(versionFile, "utf-8").trim() : null;
38901
+ const versionFile = path2.join(pluginDir, ".electron-version");
38902
+ const cachedVersion = fs2.existsSync(versionFile) ? fs2.readFileSync(versionFile, "utf-8").trim() : null;
38998
38903
  if (!needsRebuild(electronVersion, cachedVersion)) {
38999
38904
  return { success: true, pluginDir, warnings, rebuilt: false };
39000
38905
  }
39001
38906
  try {
39002
- const ptyModulePath = path3.join(pluginDir, "node_modules", "node-pty");
38907
+ const ptyModulePath = path2.join(pluginDir, "node_modules", "node-pty");
39003
38908
  (0, import_child_process.execFileSync)("npx", ["@electron/rebuild", "-m", ptyModulePath, "-v", electronVersion], {
39004
38909
  cwd: pluginDir,
39005
38910
  stdio: ["pipe", "pipe", "pipe"],
39006
38911
  encoding: "utf-8"
39007
38912
  });
39008
- fs3.writeFileSync(versionFile, electronVersion);
38913
+ fs2.writeFileSync(versionFile, electronVersion);
39009
38914
  rebuilt = true;
39010
38915
  } catch (err) {
39011
38916
  warnings.push(
@@ -39018,8 +38923,8 @@ function installObsidianPlugin(vaultPath, packageRoot, options) {
39018
38923
  }
39019
38924
 
39020
38925
  // src/core/cli.ts
39021
- var path4 = __toESM(require("path"));
39022
- var os2 = __toESM(require("os"));
38926
+ var path3 = __toESM(require("path"));
38927
+ var os = __toESM(require("os"));
39023
38928
  var VALUE_OPTIONS = /* @__PURE__ */ new Set(["port", "til-path", "out", "title", "subtitle", "github", "mode"]);
39024
38929
  var BOOLEAN_OPTIONS = /* @__PURE__ */ new Set(["no-obsidian"]);
39025
38930
  function parseArgs(args) {
@@ -39039,7 +38944,7 @@ function parseArgs(args) {
39039
38944
  }
39040
38945
  function expandTilde(p) {
39041
38946
  if (p === "~" || p.startsWith("~/")) {
39042
- return path4.join(os2.homedir(), p.slice(1));
38947
+ return path3.join(os.homedir(), p.slice(1));
39043
38948
  }
39044
38949
  return p;
39045
38950
  }
@@ -40036,9 +39941,9 @@ function generateProfileHtml(config2, summaryCardsHtml, heatmapHtml, recentTilsH
40036
39941
  }
40037
39942
 
40038
39943
  // src/cli/index.ts
40039
- var path6 = __toESM(require("path"));
40040
- var fs5 = __toESM(require("fs"));
40041
- var VERSION = true ? "1.3.1-dev.0" : "0.0.0";
39944
+ var path5 = __toESM(require("path"));
39945
+ var fs4 = __toESM(require("fs"));
39946
+ var VERSION = true ? "1.4.0" : "0.0.0";
40042
39947
  function printUsage() {
40043
39948
  console.log(`oh-my-til v${VERSION}
40044
39949
 
@@ -40046,16 +39951,8 @@ Usage:
40046
39951
  oh-my-til mcp [<path>] [options] Start MCP server (stdio)
40047
39952
  oh-my-til install-obsidian [<path>] Install Obsidian desktop plugin only
40048
39953
  oh-my-til deploy [<path>] [options] Generate static site from TIL files
40049
- oh-my-til config get Show current global config
40050
- oh-my-til config set <key> <value> Set a config value
40051
39954
  oh-my-til version Print version
40052
39955
 
40053
- Config keys:
40054
- vault TIL vault path (e.g. ~/my-til)
40055
- ai.provider AI provider: anthropic | openai | ollama
40056
- ai.model AI model name
40057
- ai.apiKey AI API key (stored with chmod 600)
40058
-
40059
39956
  Options (mcp):
40060
39957
  --til-path <path> TIL folder path (default: til)
40061
39958
 
@@ -40089,7 +39986,7 @@ async function main() {
40089
39986
  const parsed = parseArgs(args.slice(1));
40090
39987
  const rawPath = parsed.positional[0];
40091
39988
  const envPath = process.env["TIL_VAULT_PATH"];
40092
- const basePath = path6.resolve(
39989
+ const basePath = path5.resolve(
40093
39990
  rawPath ? expandTilde(rawPath) : envPath ? expandTilde(envPath) : process.cwd()
40094
39991
  );
40095
39992
  const tilPath = parsed.options["til-path"] ?? "til";
@@ -40112,13 +40009,13 @@ async function main() {
40112
40009
  process.on("SIGINT", shutdown);
40113
40010
  process.on("SIGTERM", shutdown);
40114
40011
  } else if (command === "install-obsidian") {
40115
- const obsidianDir = path6.join(basePath, ".obsidian");
40116
- if (!fs5.existsSync(obsidianDir)) {
40012
+ const obsidianDir = path5.join(basePath, ".obsidian");
40013
+ if (!fs4.existsSync(obsidianDir)) {
40117
40014
  console.error("No .obsidian/ folder found. Run this command inside an Obsidian vault.");
40118
40015
  process.exit(1);
40119
40016
  }
40120
40017
  console.log("Installing Obsidian plugin...");
40121
- const packageRoot = path6.resolve(__dirname, "..");
40018
+ const packageRoot = path5.resolve(__dirname, "..");
40122
40019
  const result = installObsidianPlugin(basePath, packageRoot);
40123
40020
  if (result.success) {
40124
40021
  console.log(`Plugin installed: ${result.pluginDir}`);
@@ -40212,13 +40109,13 @@ async function main() {
40212
40109
  { title: entry.title, category: entry.category, createdDate: entry.createdDate, contentHtml: entry.contentHtml, relatedTils },
40213
40110
  config2
40214
40111
  );
40215
- await storage.writeFile(path6.join(outDir, entry.category, `${entry.slug}.html`), tilPageHtml);
40112
+ await storage.writeFile(path5.join(outDir, entry.category, `${entry.slug}.html`), tilPageHtml);
40216
40113
  generated++;
40217
40114
  }
40218
40115
  for (const [category, tils] of categoryMap) {
40219
40116
  tils.sort((a, b) => b.createdDate.localeCompare(a.createdDate));
40220
40117
  const catHtml = generateCategoryIndexHtml({ category, tils }, config2);
40221
- await storage.writeFile(path6.join(outDir, category, "index.html"), catHtml);
40118
+ await storage.writeFile(path5.join(outDir, category, "index.html"), catHtml);
40222
40119
  }
40223
40120
  const heatmapData = computeHeatmapData(statsEntries, deployTilPath, void 0, statsEntries);
40224
40121
  const streak = computeStreak(statsEntries, deployTilPath, void 0, statsEntries);
@@ -40244,11 +40141,9 @@ async function main() {
40244
40141
  const recentTilsHtml = renderRecentTilsHtml(recentTils);
40245
40142
  const allTilsHtml = renderAllTilsHtml(allCategories);
40246
40143
  const profileHtml = generateProfileHtml(config2, summaryCardsHtml, heatmapHtml, recentTilsHtml, allTilsHtml);
40247
- await storage.writeFile(path6.join(outDir, "index.html"), profileHtml);
40144
+ await storage.writeFile(path5.join(outDir, "index.html"), profileHtml);
40248
40145
  console.log(`Generated ${generated} TIL pages + ${categoryMap.size} category indexes + profile`);
40249
- console.log(`Output: ${path6.resolve(basePath, outDir)}/`);
40250
- } else if (command === "config") {
40251
- runConfigCommand(args.slice(1));
40146
+ console.log(`Output: ${path5.resolve(basePath, outDir)}/`);
40252
40147
  } else {
40253
40148
  console.error(`Unknown command: ${command}`);
40254
40149
  printUsage();
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "oh-my-til",
3
3
  "name": "Oh My TIL",
4
- "version": "1.3.0",
4
+ "version": "1.4.0",
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,7 +1,7 @@
1
1
  {
2
2
  "name": "oh-my-til",
3
- "version": "1.3.1-dev.0",
4
- "description": "Oh My TIL Claude Code plugin for TIL learning workflow, with Obsidian integration",
3
+ "version": "1.4.0",
4
+ "description": "Oh My TIL \u2014 Claude Code plugin for TIL learning workflow, with Obsidian integration",
5
5
  "main": "main.js",
6
6
  "bin": {
7
7
  "oh-my-til": "./dist/cli.js"
@@ -18,7 +18,7 @@
18
18
  "build": "node esbuild.config.mjs production && node esbuild.cli.mjs",
19
19
  "build:obsidian": "node esbuild.config.mjs production",
20
20
  "build:cli": "node esbuild.cli.mjs",
21
- "rebuild-pty": "npx @electron/rebuild -m node_modules/node-pty -v ${ELECTRON_VERSION:?'ELECTRON_VERSION 환경변수를 설정하세요 (예: ELECTRON_VERSION=37.10.2 npm run rebuild-pty)'}",
21
+ "rebuild-pty": "npx @electron/rebuild -m node_modules/node-pty -v ${ELECTRON_VERSION:?'ELECTRON_VERSION \ud658\uacbd\ubcc0\uc218\ub97c \uc124\uc815\ud558\uc138\uc694 (\uc608: ELECTRON_VERSION=37.10.2 npm run rebuild-pty)'}",
22
22
  "deploy": "bash scripts/deploy.sh",
23
23
  "check": "vitest run && node esbuild.config.mjs production && tsc --noEmit",
24
24
  "test": "vitest run",