oh-my-til 1.3.0 → 1.3.1-dev.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.
Files changed (2) hide show
  1. package/dist/cli.js +273 -168
  2. package/package.json +3 -3
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, path6) {
114
- const ctrl = callVisitor(key, node, visitor, path6);
113
+ function visit_(key, node, visitor, path7) {
114
+ const ctrl = callVisitor(key, node, visitor, path7);
115
115
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
116
- replaceNode(key, path6, ctrl);
117
- return visit_(key, ctrl, visitor, path6);
116
+ replaceNode(key, path7, ctrl);
117
+ return visit_(key, ctrl, visitor, path7);
118
118
  }
119
119
  if (typeof ctrl !== "symbol") {
120
120
  if (identity.isCollection(node)) {
121
- path6 = Object.freeze(path6.concat(node));
121
+ path7 = Object.freeze(path7.concat(node));
122
122
  for (let i = 0; i < node.items.length; ++i) {
123
- const ci = visit_(i, node.items[i], visitor, path6);
123
+ const ci = visit_(i, node.items[i], visitor, path7);
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
- path6 = Object.freeze(path6.concat(node));
135
- const ck = visit_("key", node.key, visitor, path6);
134
+ path7 = Object.freeze(path7.concat(node));
135
+ const ck = visit_("key", node.key, visitor, path7);
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, path6);
140
+ const cv = visit_("value", node.value, visitor, path7);
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, path6) {
162
- const ctrl = await callVisitor(key, node, visitor, path6);
161
+ async function visitAsync_(key, node, visitor, path7) {
162
+ const ctrl = await callVisitor(key, node, visitor, path7);
163
163
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
164
- replaceNode(key, path6, ctrl);
165
- return visitAsync_(key, ctrl, visitor, path6);
164
+ replaceNode(key, path7, ctrl);
165
+ return visitAsync_(key, ctrl, visitor, path7);
166
166
  }
167
167
  if (typeof ctrl !== "symbol") {
168
168
  if (identity.isCollection(node)) {
169
- path6 = Object.freeze(path6.concat(node));
169
+ path7 = Object.freeze(path7.concat(node));
170
170
  for (let i = 0; i < node.items.length; ++i) {
171
- const ci = await visitAsync_(i, node.items[i], visitor, path6);
171
+ const ci = await visitAsync_(i, node.items[i], visitor, path7);
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
- path6 = Object.freeze(path6.concat(node));
183
- const ck = await visitAsync_("key", node.key, visitor, path6);
182
+ path7 = Object.freeze(path7.concat(node));
183
+ const ck = await visitAsync_("key", node.key, visitor, path7);
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, path6);
188
+ const cv = await visitAsync_("value", node.value, visitor, path7);
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, path6) {
215
+ function callVisitor(key, node, visitor, path7) {
216
216
  if (typeof visitor === "function")
217
- return visitor(key, node, path6);
217
+ return visitor(key, node, path7);
218
218
  if (identity.isMap(node))
219
- return visitor.Map?.(key, node, path6);
219
+ return visitor.Map?.(key, node, path7);
220
220
  if (identity.isSeq(node))
221
- return visitor.Seq?.(key, node, path6);
221
+ return visitor.Seq?.(key, node, path7);
222
222
  if (identity.isPair(node))
223
- return visitor.Pair?.(key, node, path6);
223
+ return visitor.Pair?.(key, node, path7);
224
224
  if (identity.isScalar(node))
225
- return visitor.Scalar?.(key, node, path6);
225
+ return visitor.Scalar?.(key, node, path7);
226
226
  if (identity.isAlias(node))
227
- return visitor.Alias?.(key, node, path6);
227
+ return visitor.Alias?.(key, node, path7);
228
228
  return void 0;
229
229
  }
230
- function replaceNode(key, path6, node) {
231
- const parent = path6[path6.length - 1];
230
+ function replaceNode(key, path7, node) {
231
+ const parent = path7[path7.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, path6, value) {
839
+ function collectionFromPath(schema, path7, value) {
840
840
  let v = value;
841
- for (let i = path6.length - 1; i >= 0; --i) {
842
- const k = path6[i];
841
+ for (let i = path7.length - 1; i >= 0; --i) {
842
+ const k = path7[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 = (path6) => path6 == null || typeof path6 === "object" && !!path6[Symbol.iterator]().next().done;
861
+ var isEmptyPath = (path7) => path7 == null || typeof path7 === "object" && !!path7[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(path6, value) {
892
- if (isEmptyPath(path6))
891
+ addIn(path7, value) {
892
+ if (isEmptyPath(path7))
893
893
  this.add(value);
894
894
  else {
895
- const [key, ...rest] = path6;
895
+ const [key, ...rest] = path7;
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(path6) {
910
- const [key, ...rest] = path6;
909
+ deleteIn(path7) {
910
+ const [key, ...rest] = path7;
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(path6, keepScalar) {
925
- const [key, ...rest] = path6;
924
+ getIn(path7, keepScalar) {
925
+ const [key, ...rest] = path7;
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(path6) {
944
- const [key, ...rest] = path6;
943
+ hasIn(path7) {
944
+ const [key, ...rest] = path7;
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(path6, value) {
955
- const [key, ...rest] = path6;
954
+ setIn(path7, value) {
955
+ const [key, ...rest] = path7;
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(path6, value) {
3459
+ addIn(path7, value) {
3460
3460
  if (assertCollection(this.contents))
3461
- this.contents.addIn(path6, value);
3461
+ this.contents.addIn(path7, 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(path6) {
3537
- if (Collection.isEmptyPath(path6)) {
3536
+ deleteIn(path7) {
3537
+ if (Collection.isEmptyPath(path7)) {
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(path6) : false;
3543
+ return assertCollection(this.contents) ? this.contents.deleteIn(path7) : 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(path6, keepScalar) {
3559
- if (Collection.isEmptyPath(path6))
3558
+ getIn(path7, keepScalar) {
3559
+ if (Collection.isEmptyPath(path7))
3560
3560
  return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
3561
- return identity.isCollection(this.contents) ? this.contents.getIn(path6, keepScalar) : void 0;
3561
+ return identity.isCollection(this.contents) ? this.contents.getIn(path7, 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(path6) {
3573
- if (Collection.isEmptyPath(path6))
3572
+ hasIn(path7) {
3573
+ if (Collection.isEmptyPath(path7))
3574
3574
  return this.contents !== void 0;
3575
- return identity.isCollection(this.contents) ? this.contents.hasIn(path6) : false;
3575
+ return identity.isCollection(this.contents) ? this.contents.hasIn(path7) : 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(path6, value) {
3593
- if (Collection.isEmptyPath(path6)) {
3592
+ setIn(path7, value) {
3593
+ if (Collection.isEmptyPath(path7)) {
3594
3594
  this.contents = value;
3595
3595
  } else if (this.contents == null) {
3596
- this.contents = Collection.collectionFromPath(this.schema, Array.from(path6), value);
3596
+ this.contents = Collection.collectionFromPath(this.schema, Array.from(path7), value);
3597
3597
  } else if (assertCollection(this.contents)) {
3598
- this.contents.setIn(path6, value);
3598
+ this.contents.setIn(path7, 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, path6) => {
5546
+ visit.itemAtPath = (cst, path7) => {
5547
5547
  let item = cst;
5548
- for (const [field, index] of path6) {
5548
+ for (const [field, index] of path7) {
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, path6) => {
5558
- const parent = visit.itemAtPath(cst, path6.slice(0, -1));
5559
- const field = path6[path6.length - 1][0];
5557
+ visit.parentCollection = (cst, path7) => {
5558
+ const parent = visit.itemAtPath(cst, path7.slice(0, -1));
5559
+ const field = path7[path7.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(path6, item, visitor) {
5566
- let ctrl = visitor(item, path6);
5565
+ function _visit(path7, item, visitor) {
5566
+ let ctrl = visitor(item, path7);
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(path6.concat([[field, i]])), token.items[i], visitor);
5573
+ const ci = _visit(Object.freeze(path7.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, path6);
5584
+ ctrl = ctrl(item, path7);
5585
5585
  }
5586
5586
  }
5587
- return typeof ctrl === "function" ? ctrl(item, path6) : ctrl;
5587
+ return typeof ctrl === "function" ? ctrl(item, path7) : 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 fs5 = this.flowScalar(this.type);
6866
+ const fs6 = this.flowScalar(this.type);
6867
6867
  if (atNextItem || it.value) {
6868
- map2.items.push({ start, key: fs5, sep: [] });
6868
+ map2.items.push({ start, key: fs6, sep: [] });
6869
6869
  this.onKeyLine = true;
6870
6870
  } else if (it.sep) {
6871
- this.stack.push(fs5);
6871
+ this.stack.push(fs6);
6872
6872
  } else {
6873
- Object.assign(it, { key: fs5, sep: [] });
6873
+ Object.assign(it, { key: fs6, 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 fs5 = this.flowScalar(this.type);
7001
+ const fs6 = this.flowScalar(this.type);
7002
7002
  if (!it || it.value)
7003
- fc.items.push({ start: [], key: fs5, sep: [] });
7003
+ fc.items.push({ start: [], key: fs6, sep: [] });
7004
7004
  else if (it.sep)
7005
- this.stack.push(fs5);
7005
+ this.stack.push(fs6);
7006
7006
  else
7007
- Object.assign(it, { key: fs5, sep: [] });
7007
+ Object.assign(it, { key: fs6, 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(path6) {
10519
- let input = path6;
10518
+ function removeDotSegments(path7) {
10519
+ let input = path7;
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 [path6, query] = wsComponent.resourceName.split("?");
10719
- wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
10718
+ const [path7, query] = wsComponent.resourceName.split("?");
10719
+ wsComponent.path = path7 && path7 !== "/" ? path7 : 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, fs5, exportName) {
14082
+ function addFormats(ajv, list, fs6, 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, fs5[f]);
14087
+ ajv.addFormat(f, fs6[f]);
14088
14088
  }
14089
14089
  module2.exports = exports2 = formatsPlugin;
14090
14090
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -14099,31 +14099,126 @@ __export(config_exports, {
14099
14099
  loadSiteConfig: () => loadSiteConfig
14100
14100
  });
14101
14101
  function loadOmtConfig(basePath) {
14102
- const configPath = path4.join(basePath, "oh-my-til.json");
14102
+ const configPath = path5.join(basePath, "oh-my-til.json");
14103
14103
  try {
14104
- const raw = fs3.readFileSync(configPath, "utf-8");
14104
+ const raw = fs4.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 path4, fs3, loadSiteConfig;
14111
+ var path5, fs4, loadSiteConfig;
14112
14112
  var init_config = __esm({
14113
14113
  "src/core/config.ts"() {
14114
- path4 = __toESM(require("path"));
14115
- fs3 = __toESM(require("fs"));
14114
+ path5 = __toESM(require("path"));
14115
+ fs4 = __toESM(require("fs"));
14116
14116
  loadSiteConfig = loadOmtConfig;
14117
14117
  }
14118
14118
  });
14119
14119
 
14120
- // src/adapters/fs-adapter.ts
14121
- var fs = __toESM(require("fs/promises"));
14120
+ // src/cli/global-config.ts
14121
+ var fs = __toESM(require("fs"));
14122
+ var os = __toESM(require("os"));
14122
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
+ // src/adapters/fs-adapter.ts
14216
+ var fs2 = __toESM(require("fs/promises"));
14217
+ var path2 = __toESM(require("path"));
14123
14218
  var import_yaml = __toESM(require_dist());
14124
14219
  function resolveSafe(resolvedBase, filePath) {
14125
- const resolved = path.resolve(resolvedBase, filePath);
14126
- if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + path.sep)) {
14220
+ const resolved = path2.resolve(resolvedBase, filePath);
14221
+ if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + path2.sep)) {
14127
14222
  throw new Error(`Path traversal denied: ${filePath}`);
14128
14223
  }
14129
14224
  return resolved;
@@ -14131,11 +14226,11 @@ function resolveSafe(resolvedBase, filePath) {
14131
14226
  var FsStorage = class {
14132
14227
  constructor(basePath) {
14133
14228
  this.basePath = basePath;
14134
- this.resolvedBase = path.resolve(basePath);
14229
+ this.resolvedBase = path2.resolve(basePath);
14135
14230
  }
14136
14231
  async readFile(filePath) {
14137
14232
  try {
14138
- return await fs.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14233
+ return await fs2.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14139
14234
  } catch {
14140
14235
  return null;
14141
14236
  }
@@ -14149,7 +14244,7 @@ var FsStorage = class {
14149
14244
  const fullDir = resolveSafe(this.resolvedBase, dir || ".");
14150
14245
  let dirents;
14151
14246
  try {
14152
- dirents = await fs.readdir(fullDir, { withFileTypes: true });
14247
+ dirents = await fs2.readdir(fullDir, { withFileTypes: true });
14153
14248
  } catch {
14154
14249
  return;
14155
14250
  }
@@ -14162,7 +14257,7 @@ var FsStorage = class {
14162
14257
  } else if (d.isFile()) {
14163
14258
  const ext = d.name.includes(".") ? d.name.split(".").pop() : "";
14164
14259
  try {
14165
- const stat2 = await fs.stat(resolveSafe(this.resolvedBase, relative));
14260
+ const stat2 = await fs2.stat(resolveSafe(this.resolvedBase, relative));
14166
14261
  entries.push({
14167
14262
  path: relative,
14168
14263
  extension: ext,
@@ -14177,7 +14272,7 @@ var FsStorage = class {
14177
14272
  }
14178
14273
  async exists(filePath) {
14179
14274
  try {
14180
- await fs.access(resolveSafe(this.resolvedBase, filePath));
14275
+ await fs2.access(resolveSafe(this.resolvedBase, filePath));
14181
14276
  return true;
14182
14277
  } catch {
14183
14278
  return false;
@@ -14185,15 +14280,15 @@ var FsStorage = class {
14185
14280
  }
14186
14281
  async writeFile(filePath, content) {
14187
14282
  const fullPath = resolveSafe(this.resolvedBase, filePath);
14188
- await fs.mkdir(path.dirname(fullPath), { recursive: true });
14189
- await fs.writeFile(fullPath, content, "utf-8");
14283
+ await fs2.mkdir(path2.dirname(fullPath), { recursive: true });
14284
+ await fs2.writeFile(fullPath, content, "utf-8");
14190
14285
  }
14191
14286
  async mkdir(dirPath) {
14192
- await fs.mkdir(resolveSafe(this.resolvedBase, dirPath), { recursive: true });
14287
+ await fs2.mkdir(resolveSafe(this.resolvedBase, dirPath), { recursive: true });
14193
14288
  }
14194
14289
  async remove(filePath) {
14195
14290
  try {
14196
- await fs.unlink(resolveSafe(this.resolvedBase, filePath));
14291
+ await fs2.unlink(resolveSafe(this.resolvedBase, filePath));
14197
14292
  } catch {
14198
14293
  }
14199
14294
  }
@@ -14206,13 +14301,13 @@ var FsMetadata = class {
14206
14301
  constructor(basePath, storage) {
14207
14302
  this.basePath = basePath;
14208
14303
  this.linkScanCache = null;
14209
- this.resolvedBase = path.resolve(basePath);
14304
+ this.resolvedBase = path2.resolve(basePath);
14210
14305
  this.storage = storage ?? new FsStorage(basePath);
14211
14306
  }
14212
14307
  async getFileMetadata(filePath) {
14213
14308
  let content;
14214
14309
  try {
14215
- content = await fs.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14310
+ content = await fs2.readFile(resolveSafe(this.resolvedBase, filePath), "utf-8");
14216
14311
  } catch {
14217
14312
  return null;
14218
14313
  }
@@ -14742,8 +14837,8 @@ function getErrorMap() {
14742
14837
 
14743
14838
  // node_modules/zod/v3/helpers/parseUtil.js
14744
14839
  var makeIssue = (params) => {
14745
- const { data, path: path6, errorMaps, issueData } = params;
14746
- const fullPath = [...path6, ...issueData.path || []];
14840
+ const { data, path: path7, errorMaps, issueData } = params;
14841
+ const fullPath = [...path7, ...issueData.path || []];
14747
14842
  const fullIssue = {
14748
14843
  ...issueData,
14749
14844
  path: fullPath
@@ -14858,11 +14953,11 @@ var errorUtil;
14858
14953
 
14859
14954
  // node_modules/zod/v3/types.js
14860
14955
  var ParseInputLazyPath = class {
14861
- constructor(parent, value, path6, key) {
14956
+ constructor(parent, value, path7, key) {
14862
14957
  this._cachedPath = [];
14863
14958
  this.parent = parent;
14864
14959
  this.data = value;
14865
- this._path = path6;
14960
+ this._path = path7;
14866
14961
  this._key = key;
14867
14962
  }
14868
14963
  get path() {
@@ -18786,10 +18881,10 @@ function mergeDefs(...defs) {
18786
18881
  function cloneDef(schema) {
18787
18882
  return mergeDefs(schema._zod.def);
18788
18883
  }
18789
- function getElementAtPath(obj, path6) {
18790
- if (!path6)
18884
+ function getElementAtPath(obj, path7) {
18885
+ if (!path7)
18791
18886
  return obj;
18792
- return path6.reduce((acc, key) => acc?.[key], obj);
18887
+ return path7.reduce((acc, key) => acc?.[key], obj);
18793
18888
  }
18794
18889
  function promiseAllObject(promisesObj) {
18795
18890
  const keys = Object.keys(promisesObj);
@@ -19172,11 +19267,11 @@ function aborted(x, startIndex = 0) {
19172
19267
  }
19173
19268
  return false;
19174
19269
  }
19175
- function prefixIssues(path6, issues) {
19270
+ function prefixIssues(path7, issues) {
19176
19271
  return issues.map((iss) => {
19177
19272
  var _a2;
19178
19273
  (_a2 = iss).path ?? (_a2.path = []);
19179
- iss.path.unshift(path6);
19274
+ iss.path.unshift(path7);
19180
19275
  return iss;
19181
19276
  });
19182
19277
  }
@@ -19359,7 +19454,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
19359
19454
  }
19360
19455
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
19361
19456
  const result = { errors: [] };
19362
- const processError = (error49, path6 = []) => {
19457
+ const processError = (error49, path7 = []) => {
19363
19458
  var _a2, _b;
19364
19459
  for (const issue2 of error49.issues) {
19365
19460
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -19369,7 +19464,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
19369
19464
  } else if (issue2.code === "invalid_element") {
19370
19465
  processError({ issues: issue2.issues }, issue2.path);
19371
19466
  } else {
19372
- const fullpath = [...path6, ...issue2.path];
19467
+ const fullpath = [...path7, ...issue2.path];
19373
19468
  if (fullpath.length === 0) {
19374
19469
  result.errors.push(mapper(issue2));
19375
19470
  continue;
@@ -19401,8 +19496,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
19401
19496
  }
19402
19497
  function toDotPath(_path) {
19403
19498
  const segs = [];
19404
- const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19405
- for (const seg of path6) {
19499
+ const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
19500
+ for (const seg of path7) {
19406
19501
  if (typeof seg === "number")
19407
19502
  segs.push(`[${seg}]`);
19408
19503
  else if (typeof seg === "symbol")
@@ -31807,13 +31902,13 @@ function resolveRef(ref, ctx) {
31807
31902
  if (!ref.startsWith("#")) {
31808
31903
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
31809
31904
  }
31810
- const path6 = ref.slice(1).split("/").filter(Boolean);
31811
- if (path6.length === 0) {
31905
+ const path7 = ref.slice(1).split("/").filter(Boolean);
31906
+ if (path7.length === 0) {
31812
31907
  return ctx.rootSchema;
31813
31908
  }
31814
31909
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
31815
- if (path6[0] === defsKey) {
31816
- const key = path6[1];
31910
+ if (path7[0] === defsKey) {
31911
+ const key = path7[1];
31817
31912
  if (!key || !ctx.defs[key]) {
31818
31913
  throw new Error(`Reference not found: ${ref}`);
31819
31914
  }
@@ -37688,9 +37783,9 @@ function extractCategory(filePath, tilPath) {
37688
37783
  const parts = relative.split("/");
37689
37784
  return parts.length >= 2 ? parts[0] : "(uncategorized)";
37690
37785
  }
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 };
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 };
37694
37789
  }
37695
37790
  function findUnresolvedMentions(unresolvedLinks, topic, tilPath) {
37696
37791
  const lowerTopic = topic.toLowerCase();
@@ -37865,10 +37960,10 @@ function parseBacklogSections(content) {
37865
37960
  if (itemMatch) {
37866
37961
  const done = itemMatch[1] !== " ";
37867
37962
  const rawPath = itemMatch[3].trim();
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 };
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 };
37872
37967
  if (sources[slug] && sources[slug].length > 0) {
37873
37968
  item.sourceUrls = sources[slug];
37874
37969
  }
@@ -38488,7 +38583,7 @@ function registerTools(server, storage, metadata, tilPath) {
38488
38583
  const texts = await Promise.all(batch.map((f) => storage.readFile(f.path)));
38489
38584
  for (let j = 0; j < batch.length; j++) {
38490
38585
  const text = texts[j];
38491
- if (text !== null && text.toLowerCase().includes(lowerTopic)) {
38586
+ if (text != null && text.toLowerCase().includes(lowerTopic)) {
38492
38587
  contentMatches.push(batch[j].path);
38493
38588
  }
38494
38589
  if (pathMatches.length + contentMatches.length >= 20)
@@ -38678,31 +38773,31 @@ function registerTools(server, storage, metadata, tilPath) {
38678
38773
  action: external_exports3.enum(["review", "remove"]).optional().describe("review (default): record review, remove: remove from review schedule")
38679
38774
  })
38680
38775
  },
38681
- async ({ path: path6, grade, action }) => {
38776
+ async ({ path: path7, grade, action }) => {
38682
38777
  const effectiveAction = action ?? "review";
38683
- const content = await storage.readFile(path6);
38778
+ const content = await storage.readFile(path7);
38684
38779
  if (content === null) {
38685
- return { content: [{ type: "text", text: JSON.stringify({ error: `File not found: ${path6}` }) }], isError: true };
38780
+ return { content: [{ type: "text", text: JSON.stringify({ error: `File not found: ${path7}` }) }], isError: true };
38686
38781
  }
38687
38782
  if (effectiveAction === "remove") {
38688
38783
  const updated2 = removeFrontmatterSrs(content);
38689
- await storage.writeFile(path6, updated2);
38690
- return { content: [{ type: "text", text: JSON.stringify({ path: path6, removed: true }) }] };
38784
+ await storage.writeFile(path7, updated2);
38785
+ return { content: [{ type: "text", text: JSON.stringify({ path: path7, removed: true }) }] };
38691
38786
  }
38692
38787
  if (grade === void 0) {
38693
38788
  return { content: [{ type: "text", text: "Error: grade (0-5) is required when action=review" }], isError: true };
38694
38789
  }
38695
- const fileMeta = await metadata.getFileMetadata(path6);
38790
+ const fileMeta = await metadata.getFileMetadata(path7);
38696
38791
  const fm = fileMeta?.frontmatter ?? {};
38697
38792
  const currentSrs = parseSrsMetadata(fm) ?? createDefaultSrsMetadata();
38698
38793
  const newSrs = computeNextReview(currentSrs, grade);
38699
38794
  const updated = updateFrontmatterSrs(content, newSrs);
38700
- await storage.writeFile(path6, updated);
38795
+ await storage.writeFile(path7, updated);
38701
38796
  return {
38702
38797
  content: [{
38703
38798
  type: "text",
38704
38799
  text: JSON.stringify({
38705
- path: path6,
38800
+ path: path7,
38706
38801
  grade,
38707
38802
  next_review: newSrs.next_review,
38708
38803
  interval: newSrs.interval,
@@ -38731,7 +38826,7 @@ function registerTools(server, storage, metadata, tilPath) {
38731
38826
  })
38732
38827
  },
38733
38828
  async ({ category, slug, title, content, tags, date: date5, fmCategory, aliases, auto_check_backlog }) => {
38734
- const path6 = `${tilPath}/${category}/${slug}.md`;
38829
+ const path7 = `${tilPath}/${category}/${slug}.md`;
38735
38830
  const noteDate = date5 || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
38736
38831
  const fmLines = ["---", `title: "${title.replace(/"/g, '\\"')}"`, `date: ${noteDate}`];
38737
38832
  const effectiveCategory = fmCategory ?? category;
@@ -38748,9 +38843,9 @@ function registerTools(server, storage, metadata, tilPath) {
38748
38843
  fmLines.push("---", "");
38749
38844
  const fullContent = fmLines.join("\n") + content;
38750
38845
  await storage.mkdir(`${tilPath}/${category}`);
38751
- const existed = await storage.exists(path6);
38752
- await storage.writeFile(path6, fullContent);
38753
- const data = { path: path6, created: !existed, category, slug, title };
38846
+ const existed = await storage.exists(path7);
38847
+ await storage.writeFile(path7, fullContent);
38848
+ const data = { path: path7, created: !existed, category, slug, title };
38754
38849
  if (auto_check_backlog) {
38755
38850
  const backlogPath = `${tilPath}/${category}/backlog.md`;
38756
38851
  const backlogContent = await storage.readFile(backlogPath);
@@ -38797,13 +38892,13 @@ function registerTools(server, storage, metadata, tilPath) {
38797
38892
  }
38798
38893
 
38799
38894
  // src/cli/obsidian-install.ts
38800
- var path2 = __toESM(require("path"));
38801
- var fs2 = __toESM(require("fs"));
38895
+ var path3 = __toESM(require("path"));
38896
+ var fs3 = __toESM(require("fs"));
38802
38897
  var import_child_process = require("child_process");
38803
38898
  var PLUGIN_ARTIFACTS = ["main.js", "manifest.json", "styles.css", "migrate-links.mjs"];
38804
38899
  var VERSION_PATTERN = /^\d+\.\d+\.\d+(-\S+)?$/;
38805
38900
  function getPluginArtifacts(packageRoot) {
38806
- return PLUGIN_ARTIFACTS.map((f) => path2.join(packageRoot, f));
38901
+ return PLUGIN_ARTIFACTS.map((f) => path3.join(packageRoot, f));
38807
38902
  }
38808
38903
  function isValidVersion(v) {
38809
38904
  return VERSION_PATTERN.test(v);
@@ -38839,7 +38934,7 @@ function detectElectronVersion(override) {
38839
38934
  if (envVersion && isValidVersion(envVersion))
38840
38935
  return envVersion;
38841
38936
  const plist = "/Applications/Obsidian.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/Info.plist";
38842
- if (fs2.existsSync(plist)) {
38937
+ if (fs3.existsSync(plist)) {
38843
38938
  try {
38844
38939
  const output = (0, import_child_process.execFileSync)(
38845
38940
  "/usr/libexec/PlistBuddy",
@@ -38854,12 +38949,12 @@ function detectElectronVersion(override) {
38854
38949
  return null;
38855
38950
  }
38856
38951
  function installObsidianPlugin(vaultPath, packageRoot, options) {
38857
- const pluginDir = path2.join(vaultPath, ".obsidian", "plugins", "oh-my-til");
38952
+ const pluginDir = path3.join(vaultPath, ".obsidian", "plugins", "oh-my-til");
38858
38953
  const warnings = [];
38859
38954
  let rebuilt = false;
38860
38955
  const artifacts = getPluginArtifacts(packageRoot);
38861
38956
  for (const artifact of artifacts) {
38862
- if (!fs2.existsSync(artifact)) {
38957
+ if (!fs3.existsSync(artifact)) {
38863
38958
  return {
38864
38959
  success: false,
38865
38960
  pluginDir,
@@ -38868,14 +38963,14 @@ function installObsidianPlugin(vaultPath, packageRoot, options) {
38868
38963
  };
38869
38964
  }
38870
38965
  }
38871
- fs2.mkdirSync(pluginDir, { recursive: true });
38966
+ fs3.mkdirSync(pluginDir, { recursive: true });
38872
38967
  for (const artifact of artifacts) {
38873
- const dest = path2.join(pluginDir, path2.basename(artifact));
38874
- fs2.copyFileSync(artifact, dest);
38968
+ const dest = path3.join(pluginDir, path3.basename(artifact));
38969
+ fs3.copyFileSync(artifact, dest);
38875
38970
  }
38876
- const pkgJsonPath = path2.join(pluginDir, "package.json");
38877
- if (!fs2.existsSync(pkgJsonPath)) {
38878
- fs2.writeFileSync(pkgJsonPath, buildPluginPackageJson());
38971
+ const pkgJsonPath = path3.join(pluginDir, "package.json");
38972
+ if (!fs3.existsSync(pkgJsonPath)) {
38973
+ fs3.writeFileSync(pkgJsonPath, buildPluginPackageJson());
38879
38974
  }
38880
38975
  try {
38881
38976
  (0, import_child_process.execFileSync)("npm", ["install", "--omit=dev"], {
@@ -38898,19 +38993,19 @@ function installObsidianPlugin(vaultPath, packageRoot, options) {
38898
38993
  );
38899
38994
  return { success: true, pluginDir, warnings, rebuilt: false };
38900
38995
  }
38901
- const versionFile = path2.join(pluginDir, ".electron-version");
38902
- const cachedVersion = fs2.existsSync(versionFile) ? fs2.readFileSync(versionFile, "utf-8").trim() : null;
38996
+ const versionFile = path3.join(pluginDir, ".electron-version");
38997
+ const cachedVersion = fs3.existsSync(versionFile) ? fs3.readFileSync(versionFile, "utf-8").trim() : null;
38903
38998
  if (!needsRebuild(electronVersion, cachedVersion)) {
38904
38999
  return { success: true, pluginDir, warnings, rebuilt: false };
38905
39000
  }
38906
39001
  try {
38907
- const ptyModulePath = path2.join(pluginDir, "node_modules", "node-pty");
39002
+ const ptyModulePath = path3.join(pluginDir, "node_modules", "node-pty");
38908
39003
  (0, import_child_process.execFileSync)("npx", ["@electron/rebuild", "-m", ptyModulePath, "-v", electronVersion], {
38909
39004
  cwd: pluginDir,
38910
39005
  stdio: ["pipe", "pipe", "pipe"],
38911
39006
  encoding: "utf-8"
38912
39007
  });
38913
- fs2.writeFileSync(versionFile, electronVersion);
39008
+ fs3.writeFileSync(versionFile, electronVersion);
38914
39009
  rebuilt = true;
38915
39010
  } catch (err) {
38916
39011
  warnings.push(
@@ -38923,8 +39018,8 @@ function installObsidianPlugin(vaultPath, packageRoot, options) {
38923
39018
  }
38924
39019
 
38925
39020
  // src/core/cli.ts
38926
- var path3 = __toESM(require("path"));
38927
- var os = __toESM(require("os"));
39021
+ var path4 = __toESM(require("path"));
39022
+ var os2 = __toESM(require("os"));
38928
39023
  var VALUE_OPTIONS = /* @__PURE__ */ new Set(["port", "til-path", "out", "title", "subtitle", "github", "mode"]);
38929
39024
  var BOOLEAN_OPTIONS = /* @__PURE__ */ new Set(["no-obsidian"]);
38930
39025
  function parseArgs(args) {
@@ -38944,7 +39039,7 @@ function parseArgs(args) {
38944
39039
  }
38945
39040
  function expandTilde(p) {
38946
39041
  if (p === "~" || p.startsWith("~/")) {
38947
- return path3.join(os.homedir(), p.slice(1));
39042
+ return path4.join(os2.homedir(), p.slice(1));
38948
39043
  }
38949
39044
  return p;
38950
39045
  }
@@ -39941,9 +40036,9 @@ function generateProfileHtml(config2, summaryCardsHtml, heatmapHtml, recentTilsH
39941
40036
  }
39942
40037
 
39943
40038
  // src/cli/index.ts
39944
- var path5 = __toESM(require("path"));
39945
- var fs4 = __toESM(require("fs"));
39946
- var VERSION = true ? "1.3.0" : "0.0.0";
40039
+ var path6 = __toESM(require("path"));
40040
+ var fs5 = __toESM(require("fs"));
40041
+ var VERSION = true ? "1.3.1-dev.0" : "0.0.0";
39947
40042
  function printUsage() {
39948
40043
  console.log(`oh-my-til v${VERSION}
39949
40044
 
@@ -39951,8 +40046,16 @@ Usage:
39951
40046
  oh-my-til mcp [<path>] [options] Start MCP server (stdio)
39952
40047
  oh-my-til install-obsidian [<path>] Install Obsidian desktop plugin only
39953
40048
  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
39954
40051
  oh-my-til version Print version
39955
40052
 
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
+
39956
40059
  Options (mcp):
39957
40060
  --til-path <path> TIL folder path (default: til)
39958
40061
 
@@ -39986,7 +40089,7 @@ async function main() {
39986
40089
  const parsed = parseArgs(args.slice(1));
39987
40090
  const rawPath = parsed.positional[0];
39988
40091
  const envPath = process.env["TIL_VAULT_PATH"];
39989
- const basePath = path5.resolve(
40092
+ const basePath = path6.resolve(
39990
40093
  rawPath ? expandTilde(rawPath) : envPath ? expandTilde(envPath) : process.cwd()
39991
40094
  );
39992
40095
  const tilPath = parsed.options["til-path"] ?? "til";
@@ -40009,13 +40112,13 @@ async function main() {
40009
40112
  process.on("SIGINT", shutdown);
40010
40113
  process.on("SIGTERM", shutdown);
40011
40114
  } else if (command === "install-obsidian") {
40012
- const obsidianDir = path5.join(basePath, ".obsidian");
40013
- if (!fs4.existsSync(obsidianDir)) {
40115
+ const obsidianDir = path6.join(basePath, ".obsidian");
40116
+ if (!fs5.existsSync(obsidianDir)) {
40014
40117
  console.error("No .obsidian/ folder found. Run this command inside an Obsidian vault.");
40015
40118
  process.exit(1);
40016
40119
  }
40017
40120
  console.log("Installing Obsidian plugin...");
40018
- const packageRoot = path5.resolve(__dirname, "..");
40121
+ const packageRoot = path6.resolve(__dirname, "..");
40019
40122
  const result = installObsidianPlugin(basePath, packageRoot);
40020
40123
  if (result.success) {
40021
40124
  console.log(`Plugin installed: ${result.pluginDir}`);
@@ -40109,13 +40212,13 @@ async function main() {
40109
40212
  { title: entry.title, category: entry.category, createdDate: entry.createdDate, contentHtml: entry.contentHtml, relatedTils },
40110
40213
  config2
40111
40214
  );
40112
- await storage.writeFile(path5.join(outDir, entry.category, `${entry.slug}.html`), tilPageHtml);
40215
+ await storage.writeFile(path6.join(outDir, entry.category, `${entry.slug}.html`), tilPageHtml);
40113
40216
  generated++;
40114
40217
  }
40115
40218
  for (const [category, tils] of categoryMap) {
40116
40219
  tils.sort((a, b) => b.createdDate.localeCompare(a.createdDate));
40117
40220
  const catHtml = generateCategoryIndexHtml({ category, tils }, config2);
40118
- await storage.writeFile(path5.join(outDir, category, "index.html"), catHtml);
40221
+ await storage.writeFile(path6.join(outDir, category, "index.html"), catHtml);
40119
40222
  }
40120
40223
  const heatmapData = computeHeatmapData(statsEntries, deployTilPath, void 0, statsEntries);
40121
40224
  const streak = computeStreak(statsEntries, deployTilPath, void 0, statsEntries);
@@ -40141,9 +40244,11 @@ async function main() {
40141
40244
  const recentTilsHtml = renderRecentTilsHtml(recentTils);
40142
40245
  const allTilsHtml = renderAllTilsHtml(allCategories);
40143
40246
  const profileHtml = generateProfileHtml(config2, summaryCardsHtml, heatmapHtml, recentTilsHtml, allTilsHtml);
40144
- await storage.writeFile(path5.join(outDir, "index.html"), profileHtml);
40247
+ await storage.writeFile(path6.join(outDir, "index.html"), profileHtml);
40145
40248
  console.log(`Generated ${generated} TIL pages + ${categoryMap.size} category indexes + profile`);
40146
- console.log(`Output: ${path5.resolve(basePath, outDir)}/`);
40249
+ console.log(`Output: ${path6.resolve(basePath, outDir)}/`);
40250
+ } else if (command === "config") {
40251
+ runConfigCommand(args.slice(1));
40147
40252
  } else {
40148
40253
  console.error(`Unknown command: ${command}`);
40149
40254
  printUsage();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oh-my-til",
3
- "version": "1.3.0",
4
- "description": "Oh My TIL \u2014 Claude Code plugin for TIL learning workflow, with Obsidian integration",
3
+ "version": "1.3.1-dev.0",
4
+ "description": "Oh My TIL 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 \ud658\uacbd\ubcc0\uc218\ub97c \uc124\uc815\ud558\uc138\uc694 (\uc608: 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 환경변수를 설정하세요 (예: 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",