oh-my-til 1.5.0 → 1.6.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
@@ -116,8 +116,8 @@ claude mcp add --transport http oh-my-til http://localhost:22360/mcp
116
116
  | 글꼴 크기 | `13` | 터미널 글꼴 크기 (px) |
117
117
  | 글꼴 | `Menlo` | 터미널 글꼴 (Menlo, SF Mono, Fira Code, JetBrains Mono 등) |
118
118
  | 행간 | `1.0` | 터미널 행간 (1.0 = 기본, 최대 2.0) |
119
- | TIL 폴더 경로 | `til` | TIL 파일 저장 경로 |
120
- | 새 TIL 파일 자동 열기 | `true` | til/ 폴더에 새 파일 생성 시 자동 오픈 |
119
+ | TIL 폴더 경로 | `til` | TIL 파일 저장 경로. 스킬도 자동으로 이 경로를 따른다 (CLI-first 우선순위: `--til-path` → `TIL_PATH` env → `oh-my-til.json` → Obsidian 설정 → 기본값) |
120
+ | 새 TIL 파일 자동 열기 | `true` | TIL 폴더에 새 파일 생성 시 자동 오픈 |
121
121
  | 시작 시 대시보드 열기 | `false` | Obsidian 시작 시 학습 대시보드 자동 열기 |
122
122
  | Claude 추가 인자 | *(비어 있음)* | `claude` 명령에 전달할 추가 인자 |
123
123
 
@@ -136,6 +136,7 @@ MCP 서버 연결 시 Claude Code에서 사용할 수 있는 도구:
136
136
  | `til_dashboard` | 학습 통계 요약 |
137
137
  | `til_review_list` | 복습 대상 카드 목록 + 통계 (SRS, `include_content` 지원) |
138
138
  | `til_review_update` | 복습 결과 기록 또는 복습 해제 |
139
+ | `til_get_path` | 설정된 TIL 폴더 경로 반환 (SessionStart 훅이 없는 환경의 fallback) |
139
140
 
140
141
  ## Claude 스킬
141
142
 
package/README.md CHANGED
@@ -116,7 +116,7 @@ claude mcp add --transport http oh-my-til http://localhost:22360/mcp
116
116
  | Font Size | `13` | Terminal font size (px) |
117
117
  | Font Family | `Menlo` | Terminal font (Menlo, SF Mono, Fira Code, JetBrains Mono, etc.) |
118
118
  | Line Height | `1.0` | Terminal line spacing (1.0 = default, up to 2.0) |
119
- | TIL Path | `til` | TIL folder path |
119
+ | TIL Path | `til` | TIL folder path. Skills follow it automatically (resolved CLI-first: `--til-path` → `TIL_PATH` env → `oh-my-til.json` → Obsidian setting → default) |
120
120
  | Auto Open New TIL | `true` | Open new TIL files in editor automatically |
121
121
  | Open Dashboard on Startup | `false` | Open learning dashboard when Obsidian starts |
122
122
  | Claude Args | *(empty)* | Additional arguments passed to `claude` command |
@@ -136,6 +136,7 @@ When the MCP server is connected, Claude Code can use these tools:
136
136
  | `til_dashboard` | Learning statistics summary |
137
137
  | `til_review_list` | Due review cards list + stats (SRS, supports `include_content`) |
138
138
  | `til_review_update` | Record review result or remove from review |
139
+ | `til_get_path` | Return the vault's configured TIL folder path (fallback for environments without the SessionStart hook) |
139
140
 
140
141
  ## Claude Skills
141
142
 
package/dist/cli.js CHANGED
@@ -14096,7 +14096,8 @@ var require_dist2 = __commonJS({
14096
14096
  var config_exports = {};
14097
14097
  __export(config_exports, {
14098
14098
  loadOmtConfig: () => loadOmtConfig,
14099
- loadSiteConfig: () => loadSiteConfig
14099
+ loadSiteConfig: () => loadSiteConfig,
14100
+ resolveTilPath: () => resolveTilPath
14100
14101
  });
14101
14102
  function loadOmtConfig(basePath) {
14102
14103
  const configPath = path4.join(basePath, "oh-my-til.json");
@@ -14108,6 +14109,20 @@ function loadOmtConfig(basePath) {
14108
14109
  return {};
14109
14110
  }
14110
14111
  }
14112
+ function resolveTilPath(basePath, cliOverride, fallback = "til") {
14113
+ if (cliOverride)
14114
+ return cliOverride;
14115
+ const env = process.env["TIL_PATH"];
14116
+ if (env)
14117
+ return env;
14118
+ const cfg = loadOmtConfig(basePath);
14119
+ if (cfg.tilPath)
14120
+ return cfg.tilPath;
14121
+ const legacy = cfg.deploy?.["til-path"];
14122
+ if (legacy)
14123
+ return legacy;
14124
+ return fallback;
14125
+ }
14111
14126
  var path4, fs3, loadSiteConfig;
14112
14127
  var init_config = __esm({
14113
14128
  "src/core/config.ts"() {
@@ -38794,6 +38809,17 @@ function registerTools(server, storage, metadata, tilPath) {
38794
38809
  return { content: [{ type: "text", text: JSON.stringify({ checked: true, slug, backlogPath }) }] };
38795
38810
  }
38796
38811
  );
38812
+ server.registerTool(
38813
+ "til_get_path",
38814
+ {
38815
+ title: "Get TIL Path",
38816
+ description: "Returns the vault's configured TIL folder path. Use when ${TIL_PATH} context is unavailable (e.g., no SessionStart hook).",
38817
+ inputSchema: external_exports3.object({})
38818
+ },
38819
+ async () => {
38820
+ return { content: [{ type: "text", text: JSON.stringify({ tilPath }) }] };
38821
+ }
38822
+ );
38797
38823
  }
38798
38824
 
38799
38825
  // src/cli/obsidian-install.ts
@@ -38949,6 +38975,11 @@ function expandTilde(p) {
38949
38975
  return p;
38950
38976
  }
38951
38977
 
38978
+ // src/core/regex.ts
38979
+ function escapeRegExp(str) {
38980
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38981
+ }
38982
+
38952
38983
  // src/core/markdown.ts
38953
38984
  function escapeHtml(text) {
38954
38985
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -39149,18 +39180,28 @@ function renderMarkdown(md) {
39149
39180
  const tokens = tokenize(body);
39150
39181
  return renderTokens(tokens);
39151
39182
  }
39152
- function rewriteTilLinks(html, existingFiles) {
39153
- return html.replace(
39154
- /<a href="til\/([^"]+\.md)">([\s\S]*?)<\/a>/g,
39155
- (_match, tilPath, text) => {
39156
- const fullPath = `til/${tilPath}`;
39157
- if (existingFiles && !existingFiles.has(fullPath)) {
39158
- return `<a class="missing-link" title="Document not yet written">${text}</a>`;
39159
- }
39160
- const pathWithoutExt = tilPath.replace(/\.md$/, "");
39161
- return `<a href="../${pathWithoutExt}.html">${text}</a>`;
39183
+ var TIL_LINK_REGEX_CACHE = /* @__PURE__ */ new Map();
39184
+ function getTilLinkRegex(tilPath) {
39185
+ let cached2 = TIL_LINK_REGEX_CACHE.get(tilPath);
39186
+ if (!cached2) {
39187
+ cached2 = new RegExp(
39188
+ `<a href="${escapeRegExp(tilPath)}\\/([^"]+\\.md)">([\\s\\S]*?)</a>`,
39189
+ "g"
39190
+ );
39191
+ TIL_LINK_REGEX_CACHE.set(tilPath, cached2);
39192
+ }
39193
+ return cached2;
39194
+ }
39195
+ function rewriteTilLinks(html, tilPath, existingFiles) {
39196
+ const pattern = getTilLinkRegex(tilPath);
39197
+ return html.replace(pattern, (_match, matchedSlug, text) => {
39198
+ const fullPath = `${tilPath}/${matchedSlug}`;
39199
+ if (existingFiles && !existingFiles.has(fullPath)) {
39200
+ return `<a class="missing-link" title="Document not yet written">${text}</a>`;
39162
39201
  }
39163
- );
39202
+ const pathWithoutExt = matchedSlug.replace(/\.md$/, "");
39203
+ return `<a href="../${pathWithoutExt}.html">${text}</a>`;
39204
+ });
39164
39205
  }
39165
39206
 
39166
39207
  // src/core/profile.ts
@@ -39943,7 +39984,7 @@ function generateProfileHtml(config2, summaryCardsHtml, heatmapHtml, recentTilsH
39943
39984
  // src/cli/index.ts
39944
39985
  var path5 = __toESM(require("path"));
39945
39986
  var fs4 = __toESM(require("fs"));
39946
- var VERSION = true ? "1.5.0" : "0.0.0";
39987
+ var VERSION = true ? "1.6.0" : "0.0.0";
39947
39988
  function printUsage() {
39948
39989
  console.log(`oh-my-til v${VERSION}
39949
39990
 
@@ -39989,7 +40030,8 @@ async function main() {
39989
40030
  const basePath = path5.resolve(
39990
40031
  rawPath ? expandTilde(rawPath) : envPath ? expandTilde(envPath) : process.cwd()
39991
40032
  );
39992
- const tilPath = parsed.options["til-path"] ?? "til";
40033
+ const { resolveTilPath: resolveTilPath2 } = await Promise.resolve().then(() => (init_config(), config_exports));
40034
+ const tilPath = resolveTilPath2(basePath, parsed.options["til-path"]);
39993
40035
  if (command === "mcp") {
39994
40036
  const storage = new FsStorage(basePath);
39995
40037
  const metadata = new FsMetadata(basePath);
@@ -40037,7 +40079,7 @@ async function main() {
40037
40079
  const { loadOmtConfig: loadOmtConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
40038
40080
  const omtConfig = loadOmtConfig2(basePath);
40039
40081
  const dc = omtConfig.deploy ?? {};
40040
- const deployTilPath = parsed.options["til-path"] ?? dc["til-path"] ?? tilPath;
40082
+ const deployTilPath = tilPath;
40041
40083
  const outDir = parsed.options["out"] ?? dc.out ?? "_site";
40042
40084
  const siteTitle = parsed.options["title"] ?? dc.title ?? "TIL";
40043
40085
  const subtitle = parsed.options["subtitle"] ?? dc.subtitle;
@@ -40088,7 +40130,7 @@ async function main() {
40088
40130
  const title = headings.length > 0 ? headings[0] : typeof fmTitle === "string" && fmTitle ? fmTitle : slug.replace(/-/g, " ");
40089
40131
  const fmDate = meta3?.frontmatter?.["date"];
40090
40132
  const createdDate = typeof fmDate === "string" ? fmDate.slice(0, 10) : new Date(file2.ctime).toISOString().slice(0, 10);
40091
- const contentHtml = rewriteTilLinks(renderMarkdown(content), existingTilPaths);
40133
+ const contentHtml = rewriteTilLinks(renderMarkdown(content), deployTilPath, existingTilPaths);
40092
40134
  const summary = extractSummary(content);
40093
40135
  tilPageEntries.push({ title, category, slug, createdDate, contentHtml, summary });
40094
40136
  if (!categoryMap.has(category))
package/main.js CHANGED
@@ -6844,17 +6844,17 @@ var require_visit = __commonJS({
6844
6844
  visit.BREAK = BREAK;
6845
6845
  visit.SKIP = SKIP;
6846
6846
  visit.REMOVE = REMOVE;
6847
- function visit_(key, node, visitor, path2) {
6848
- const ctrl = callVisitor(key, node, visitor, path2);
6847
+ function visit_(key, node, visitor, path3) {
6848
+ const ctrl = callVisitor(key, node, visitor, path3);
6849
6849
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
6850
- replaceNode(key, path2, ctrl);
6851
- return visit_(key, ctrl, visitor, path2);
6850
+ replaceNode(key, path3, ctrl);
6851
+ return visit_(key, ctrl, visitor, path3);
6852
6852
  }
6853
6853
  if (typeof ctrl !== "symbol") {
6854
6854
  if (identity.isCollection(node)) {
6855
- path2 = Object.freeze(path2.concat(node));
6855
+ path3 = Object.freeze(path3.concat(node));
6856
6856
  for (let i = 0; i < node.items.length; ++i) {
6857
- const ci = visit_(i, node.items[i], visitor, path2);
6857
+ const ci = visit_(i, node.items[i], visitor, path3);
6858
6858
  if (typeof ci === "number")
6859
6859
  i = ci - 1;
6860
6860
  else if (ci === BREAK)
@@ -6865,13 +6865,13 @@ var require_visit = __commonJS({
6865
6865
  }
6866
6866
  }
6867
6867
  } else if (identity.isPair(node)) {
6868
- path2 = Object.freeze(path2.concat(node));
6869
- const ck = visit_("key", node.key, visitor, path2);
6868
+ path3 = Object.freeze(path3.concat(node));
6869
+ const ck = visit_("key", node.key, visitor, path3);
6870
6870
  if (ck === BREAK)
6871
6871
  return BREAK;
6872
6872
  else if (ck === REMOVE)
6873
6873
  node.key = null;
6874
- const cv = visit_("value", node.value, visitor, path2);
6874
+ const cv = visit_("value", node.value, visitor, path3);
6875
6875
  if (cv === BREAK)
6876
6876
  return BREAK;
6877
6877
  else if (cv === REMOVE)
@@ -6892,17 +6892,17 @@ var require_visit = __commonJS({
6892
6892
  visitAsync.BREAK = BREAK;
6893
6893
  visitAsync.SKIP = SKIP;
6894
6894
  visitAsync.REMOVE = REMOVE;
6895
- async function visitAsync_(key, node, visitor, path2) {
6896
- const ctrl = await callVisitor(key, node, visitor, path2);
6895
+ async function visitAsync_(key, node, visitor, path3) {
6896
+ const ctrl = await callVisitor(key, node, visitor, path3);
6897
6897
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
6898
- replaceNode(key, path2, ctrl);
6899
- return visitAsync_(key, ctrl, visitor, path2);
6898
+ replaceNode(key, path3, ctrl);
6899
+ return visitAsync_(key, ctrl, visitor, path3);
6900
6900
  }
6901
6901
  if (typeof ctrl !== "symbol") {
6902
6902
  if (identity.isCollection(node)) {
6903
- path2 = Object.freeze(path2.concat(node));
6903
+ path3 = Object.freeze(path3.concat(node));
6904
6904
  for (let i = 0; i < node.items.length; ++i) {
6905
- const ci = await visitAsync_(i, node.items[i], visitor, path2);
6905
+ const ci = await visitAsync_(i, node.items[i], visitor, path3);
6906
6906
  if (typeof ci === "number")
6907
6907
  i = ci - 1;
6908
6908
  else if (ci === BREAK)
@@ -6913,13 +6913,13 @@ var require_visit = __commonJS({
6913
6913
  }
6914
6914
  }
6915
6915
  } else if (identity.isPair(node)) {
6916
- path2 = Object.freeze(path2.concat(node));
6917
- const ck = await visitAsync_("key", node.key, visitor, path2);
6916
+ path3 = Object.freeze(path3.concat(node));
6917
+ const ck = await visitAsync_("key", node.key, visitor, path3);
6918
6918
  if (ck === BREAK)
6919
6919
  return BREAK;
6920
6920
  else if (ck === REMOVE)
6921
6921
  node.key = null;
6922
- const cv = await visitAsync_("value", node.value, visitor, path2);
6922
+ const cv = await visitAsync_("value", node.value, visitor, path3);
6923
6923
  if (cv === BREAK)
6924
6924
  return BREAK;
6925
6925
  else if (cv === REMOVE)
@@ -6946,24 +6946,24 @@ var require_visit = __commonJS({
6946
6946
  }
6947
6947
  return visitor;
6948
6948
  }
6949
- function callVisitor(key, node, visitor, path2) {
6949
+ function callVisitor(key, node, visitor, path3) {
6950
6950
  var _a, _b, _c, _d, _e;
6951
6951
  if (typeof visitor === "function")
6952
- return visitor(key, node, path2);
6952
+ return visitor(key, node, path3);
6953
6953
  if (identity.isMap(node))
6954
- return (_a = visitor.Map) == null ? void 0 : _a.call(visitor, key, node, path2);
6954
+ return (_a = visitor.Map) == null ? void 0 : _a.call(visitor, key, node, path3);
6955
6955
  if (identity.isSeq(node))
6956
- return (_b = visitor.Seq) == null ? void 0 : _b.call(visitor, key, node, path2);
6956
+ return (_b = visitor.Seq) == null ? void 0 : _b.call(visitor, key, node, path3);
6957
6957
  if (identity.isPair(node))
6958
- return (_c = visitor.Pair) == null ? void 0 : _c.call(visitor, key, node, path2);
6958
+ return (_c = visitor.Pair) == null ? void 0 : _c.call(visitor, key, node, path3);
6959
6959
  if (identity.isScalar(node))
6960
- return (_d = visitor.Scalar) == null ? void 0 : _d.call(visitor, key, node, path2);
6960
+ return (_d = visitor.Scalar) == null ? void 0 : _d.call(visitor, key, node, path3);
6961
6961
  if (identity.isAlias(node))
6962
- return (_e = visitor.Alias) == null ? void 0 : _e.call(visitor, key, node, path2);
6962
+ return (_e = visitor.Alias) == null ? void 0 : _e.call(visitor, key, node, path3);
6963
6963
  return void 0;
6964
6964
  }
6965
- function replaceNode(key, path2, node) {
6966
- const parent = path2[path2.length - 1];
6965
+ function replaceNode(key, path3, node) {
6966
+ const parent = path3[path3.length - 1];
6967
6967
  if (identity.isCollection(parent)) {
6968
6968
  parent.items[key] = node;
6969
6969
  } else if (identity.isPair(parent)) {
@@ -7576,10 +7576,10 @@ var require_Collection = __commonJS({
7576
7576
  var createNode = require_createNode();
7577
7577
  var identity = require_identity();
7578
7578
  var Node2 = require_Node();
7579
- function collectionFromPath(schema, path2, value) {
7579
+ function collectionFromPath(schema, path3, value) {
7580
7580
  let v = value;
7581
- for (let i = path2.length - 1; i >= 0; --i) {
7582
- const k = path2[i];
7581
+ for (let i = path3.length - 1; i >= 0; --i) {
7582
+ const k = path3[i];
7583
7583
  if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
7584
7584
  const a = [];
7585
7585
  a[k] = v;
@@ -7598,7 +7598,7 @@ var require_Collection = __commonJS({
7598
7598
  sourceObjects: /* @__PURE__ */ new Map()
7599
7599
  });
7600
7600
  }
7601
- var isEmptyPath = (path2) => path2 == null || typeof path2 === "object" && !!path2[Symbol.iterator]().next().done;
7601
+ var isEmptyPath = (path3) => path3 == null || typeof path3 === "object" && !!path3[Symbol.iterator]().next().done;
7602
7602
  var Collection = class extends Node2.NodeBase {
7603
7603
  constructor(type, schema) {
7604
7604
  super(type);
@@ -7628,11 +7628,11 @@ var require_Collection = __commonJS({
7628
7628
  * be a Pair instance or a `{ key, value }` object, which may not have a key
7629
7629
  * that already exists in the map.
7630
7630
  */
7631
- addIn(path2, value) {
7632
- if (isEmptyPath(path2))
7631
+ addIn(path3, value) {
7632
+ if (isEmptyPath(path3))
7633
7633
  this.add(value);
7634
7634
  else {
7635
- const [key, ...rest] = path2;
7635
+ const [key, ...rest] = path3;
7636
7636
  const node = this.get(key, true);
7637
7637
  if (identity.isCollection(node))
7638
7638
  node.addIn(rest, value);
@@ -7646,8 +7646,8 @@ var require_Collection = __commonJS({
7646
7646
  * Removes a value from the collection.
7647
7647
  * @returns `true` if the item was found and removed.
7648
7648
  */
7649
- deleteIn(path2) {
7650
- const [key, ...rest] = path2;
7649
+ deleteIn(path3) {
7650
+ const [key, ...rest] = path3;
7651
7651
  if (rest.length === 0)
7652
7652
  return this.delete(key);
7653
7653
  const node = this.get(key, true);
@@ -7661,8 +7661,8 @@ var require_Collection = __commonJS({
7661
7661
  * scalar values from their surrounding node; to disable set `keepScalar` to
7662
7662
  * `true` (collections are always returned intact).
7663
7663
  */
7664
- getIn(path2, keepScalar) {
7665
- const [key, ...rest] = path2;
7664
+ getIn(path3, keepScalar) {
7665
+ const [key, ...rest] = path3;
7666
7666
  const node = this.get(key, true);
7667
7667
  if (rest.length === 0)
7668
7668
  return !keepScalar && identity.isScalar(node) ? node.value : node;
@@ -7680,8 +7680,8 @@ var require_Collection = __commonJS({
7680
7680
  /**
7681
7681
  * Checks if the collection includes a value with the key `key`.
7682
7682
  */
7683
- hasIn(path2) {
7684
- const [key, ...rest] = path2;
7683
+ hasIn(path3) {
7684
+ const [key, ...rest] = path3;
7685
7685
  if (rest.length === 0)
7686
7686
  return this.has(key);
7687
7687
  const node = this.get(key, true);
@@ -7691,8 +7691,8 @@ var require_Collection = __commonJS({
7691
7691
  * Sets a value in this collection. For `!!set`, `value` needs to be a
7692
7692
  * boolean to add/remove the item from the set.
7693
7693
  */
7694
- setIn(path2, value) {
7695
- const [key, ...rest] = path2;
7694
+ setIn(path3, value) {
7695
+ const [key, ...rest] = path3;
7696
7696
  if (rest.length === 0) {
7697
7697
  this.set(key, value);
7698
7698
  } else {
@@ -10214,9 +10214,9 @@ var require_Document = __commonJS({
10214
10214
  this.contents.add(value);
10215
10215
  }
10216
10216
  /** Adds a value to the document. */
10217
- addIn(path2, value) {
10217
+ addIn(path3, value) {
10218
10218
  if (assertCollection(this.contents))
10219
- this.contents.addIn(path2, value);
10219
+ this.contents.addIn(path3, value);
10220
10220
  }
10221
10221
  /**
10222
10222
  * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
@@ -10291,14 +10291,14 @@ var require_Document = __commonJS({
10291
10291
  * Removes a value from the document.
10292
10292
  * @returns `true` if the item was found and removed.
10293
10293
  */
10294
- deleteIn(path2) {
10295
- if (Collection.isEmptyPath(path2)) {
10294
+ deleteIn(path3) {
10295
+ if (Collection.isEmptyPath(path3)) {
10296
10296
  if (this.contents == null)
10297
10297
  return false;
10298
10298
  this.contents = null;
10299
10299
  return true;
10300
10300
  }
10301
- return assertCollection(this.contents) ? this.contents.deleteIn(path2) : false;
10301
+ return assertCollection(this.contents) ? this.contents.deleteIn(path3) : false;
10302
10302
  }
10303
10303
  /**
10304
10304
  * Returns item at `key`, or `undefined` if not found. By default unwraps
@@ -10313,10 +10313,10 @@ var require_Document = __commonJS({
10313
10313
  * scalar values from their surrounding node; to disable set `keepScalar` to
10314
10314
  * `true` (collections are always returned intact).
10315
10315
  */
10316
- getIn(path2, keepScalar) {
10317
- if (Collection.isEmptyPath(path2))
10316
+ getIn(path3, keepScalar) {
10317
+ if (Collection.isEmptyPath(path3))
10318
10318
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
10319
- return identity.isCollection(this.contents) ? this.contents.getIn(path2, keepScalar) : void 0;
10319
+ return identity.isCollection(this.contents) ? this.contents.getIn(path3, keepScalar) : void 0;
10320
10320
  }
10321
10321
  /**
10322
10322
  * Checks if the document includes a value with the key `key`.
@@ -10327,10 +10327,10 @@ var require_Document = __commonJS({
10327
10327
  /**
10328
10328
  * Checks if the document includes a value at `path`.
10329
10329
  */
10330
- hasIn(path2) {
10331
- if (Collection.isEmptyPath(path2))
10330
+ hasIn(path3) {
10331
+ if (Collection.isEmptyPath(path3))
10332
10332
  return this.contents !== void 0;
10333
- return identity.isCollection(this.contents) ? this.contents.hasIn(path2) : false;
10333
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path3) : false;
10334
10334
  }
10335
10335
  /**
10336
10336
  * Sets a value in this document. For `!!set`, `value` needs to be a
@@ -10347,13 +10347,13 @@ var require_Document = __commonJS({
10347
10347
  * Sets a value in this document. For `!!set`, `value` needs to be a
10348
10348
  * boolean to add/remove the item from the set.
10349
10349
  */
10350
- setIn(path2, value) {
10351
- if (Collection.isEmptyPath(path2)) {
10350
+ setIn(path3, value) {
10351
+ if (Collection.isEmptyPath(path3)) {
10352
10352
  this.contents = value;
10353
10353
  } else if (this.contents == null) {
10354
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path2), value);
10354
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path3), value);
10355
10355
  } else if (assertCollection(this.contents)) {
10356
- this.contents.setIn(path2, value);
10356
+ this.contents.setIn(path3, value);
10357
10357
  }
10358
10358
  }
10359
10359
  /**
@@ -12316,9 +12316,9 @@ var require_cst_visit = __commonJS({
12316
12316
  visit.BREAK = BREAK;
12317
12317
  visit.SKIP = SKIP;
12318
12318
  visit.REMOVE = REMOVE;
12319
- visit.itemAtPath = (cst, path2) => {
12319
+ visit.itemAtPath = (cst, path3) => {
12320
12320
  let item = cst;
12321
- for (const [field, index] of path2) {
12321
+ for (const [field, index] of path3) {
12322
12322
  const tok = item == null ? void 0 : item[field];
12323
12323
  if (tok && "items" in tok) {
12324
12324
  item = tok.items[index];
@@ -12327,23 +12327,23 @@ var require_cst_visit = __commonJS({
12327
12327
  }
12328
12328
  return item;
12329
12329
  };
12330
- visit.parentCollection = (cst, path2) => {
12331
- const parent = visit.itemAtPath(cst, path2.slice(0, -1));
12332
- const field = path2[path2.length - 1][0];
12330
+ visit.parentCollection = (cst, path3) => {
12331
+ const parent = visit.itemAtPath(cst, path3.slice(0, -1));
12332
+ const field = path3[path3.length - 1][0];
12333
12333
  const coll = parent == null ? void 0 : parent[field];
12334
12334
  if (coll && "items" in coll)
12335
12335
  return coll;
12336
12336
  throw new Error("Parent collection not found");
12337
12337
  };
12338
- function _visit(path2, item, visitor) {
12339
- let ctrl = visitor(item, path2);
12338
+ function _visit(path3, item, visitor) {
12339
+ let ctrl = visitor(item, path3);
12340
12340
  if (typeof ctrl === "symbol")
12341
12341
  return ctrl;
12342
12342
  for (const field of ["key", "value"]) {
12343
12343
  const token = item[field];
12344
12344
  if (token && "items" in token) {
12345
12345
  for (let i = 0; i < token.items.length; ++i) {
12346
- const ci = _visit(Object.freeze(path2.concat([[field, i]])), token.items[i], visitor);
12346
+ const ci = _visit(Object.freeze(path3.concat([[field, i]])), token.items[i], visitor);
12347
12347
  if (typeof ci === "number")
12348
12348
  i = ci - 1;
12349
12349
  else if (ci === BREAK)
@@ -12354,10 +12354,10 @@ var require_cst_visit = __commonJS({
12354
12354
  }
12355
12355
  }
12356
12356
  if (typeof ctrl === "function" && field === "key")
12357
- ctrl = ctrl(item, path2);
12357
+ ctrl = ctrl(item, path3);
12358
12358
  }
12359
12359
  }
12360
- return typeof ctrl === "function" ? ctrl(item, path2) : ctrl;
12360
+ return typeof ctrl === "function" ? ctrl(item, path3) : ctrl;
12361
12361
  }
12362
12362
  exports2.visit = visit;
12363
12363
  }
@@ -13640,14 +13640,14 @@ var require_parser = __commonJS({
13640
13640
  case "scalar":
13641
13641
  case "single-quoted-scalar":
13642
13642
  case "double-quoted-scalar": {
13643
- const fs = this.flowScalar(this.type);
13643
+ const fs2 = this.flowScalar(this.type);
13644
13644
  if (atNextItem || it.value) {
13645
- map.items.push({ start, key: fs, sep: [] });
13645
+ map.items.push({ start, key: fs2, sep: [] });
13646
13646
  this.onKeyLine = true;
13647
13647
  } else if (it.sep) {
13648
- this.stack.push(fs);
13648
+ this.stack.push(fs2);
13649
13649
  } else {
13650
- Object.assign(it, { key: fs, sep: [] });
13650
+ Object.assign(it, { key: fs2, sep: [] });
13651
13651
  this.onKeyLine = true;
13652
13652
  }
13653
13653
  return;
@@ -13776,13 +13776,13 @@ var require_parser = __commonJS({
13776
13776
  case "scalar":
13777
13777
  case "single-quoted-scalar":
13778
13778
  case "double-quoted-scalar": {
13779
- const fs = this.flowScalar(this.type);
13779
+ const fs2 = this.flowScalar(this.type);
13780
13780
  if (!it || it.value)
13781
- fc.items.push({ start: [], key: fs, sep: [] });
13781
+ fc.items.push({ start: [], key: fs2, sep: [] });
13782
13782
  else if (it.sep)
13783
- this.stack.push(fs);
13783
+ this.stack.push(fs2);
13784
13784
  else
13785
- Object.assign(it, { key: fs, sep: [] });
13785
+ Object.assign(it, { key: fs2, sep: [] });
13786
13786
  return;
13787
13787
  }
13788
13788
  case "flow-map-end":
@@ -15497,7 +15497,13 @@ var TILSettingTab = class extends import_obsidian3.PluginSettingTab {
15497
15497
  })
15498
15498
  );
15499
15499
  containerEl.createEl("h3", { text: "TIL Settings" });
15500
- new import_obsidian3.Setting(containerEl).setName("Auto-open new TIL files").setDesc("Automatically open new .md files created in the til/ folder in the editor").addToggle(
15500
+ new import_obsidian3.Setting(containerEl).setName("TIL Path").setDesc("TIL folder path relative to the vault root (default: til). Skills, dashboard, and watcher all follow this. Precedence at load time: TIL_PATH env var \u2192 oh-my-til.json's tilPath \u2192 this setting \u2192 default 'til'.").addText(
15501
+ (text) => text.setPlaceholder("til").setValue(this.plugin.settings.tilPath).onChange(async (value) => {
15502
+ this.plugin.settings.tilPath = value.trim() || "til";
15503
+ await this.plugin.saveSettings();
15504
+ })
15505
+ );
15506
+ new import_obsidian3.Setting(containerEl).setName("Auto-open new TIL files").setDesc("Automatically open new .md files created in the TIL folder in the editor").addToggle(
15501
15507
  (toggle) => toggle.setValue(this.plugin.settings.autoOpenNewTIL).onChange(async (value) => {
15502
15508
  this.plugin.settings.autoOpenNewTIL = value;
15503
15509
  await this.plugin.saveSettings();
@@ -15545,6 +15551,35 @@ var TILWatcher = class {
15545
15551
  }
15546
15552
  };
15547
15553
 
15554
+ // src/core/config.ts
15555
+ var path2 = __toESM(require("path"));
15556
+ var fs = __toESM(require("fs"));
15557
+ function loadOmtConfig(basePath) {
15558
+ const configPath = path2.join(basePath, "oh-my-til.json");
15559
+ try {
15560
+ const raw = fs.readFileSync(configPath, "utf-8");
15561
+ const parsed = JSON.parse(raw);
15562
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
15563
+ } catch (e) {
15564
+ return {};
15565
+ }
15566
+ }
15567
+ function resolveTilPath(basePath, cliOverride, fallback = "til") {
15568
+ var _a;
15569
+ if (cliOverride)
15570
+ return cliOverride;
15571
+ const env = process.env["TIL_PATH"];
15572
+ if (env)
15573
+ return env;
15574
+ const cfg = loadOmtConfig(basePath);
15575
+ if (cfg.tilPath)
15576
+ return cfg.tilPath;
15577
+ const legacy = (_a = cfg.deploy) == null ? void 0 : _a["til-path"];
15578
+ if (legacy)
15579
+ return legacy;
15580
+ return fallback;
15581
+ }
15582
+
15548
15583
  // src/obsidian/main.ts
15549
15584
  var electronRequire2 = window.require;
15550
15585
  var TILPlugin = class extends import_obsidian5.Plugin {
@@ -15666,7 +15701,13 @@ var TILPlugin = class extends import_obsidian5.Plugin {
15666
15701
  this.app.workspace.detachLeavesOfType(VIEW_TYPE_TIL_DASHBOARD);
15667
15702
  }
15668
15703
  async loadSettings() {
15669
- this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
15704
+ const merged = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
15705
+ const adapter = this.app.vault.adapter;
15706
+ const vaultPath = adapter.basePath;
15707
+ if (vaultPath) {
15708
+ merged.tilPath = resolveTilPath(vaultPath, void 0, merged.tilPath);
15709
+ }
15710
+ this.settings = merged;
15670
15711
  }
15671
15712
  async saveSettings() {
15672
15713
  var _a;
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.5.0",
4
+ "version": "1.6.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,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-til",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Oh My TIL \u2014 Claude Code plugin for TIL learning workflow, with Obsidian integration",
5
5
  "main": "main.js",
6
6
  "bin": {