sphica 0.6.21 → 0.6.23

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "sphica",
4
- "version": "0.6.21",
4
+ "version": "0.6.23",
5
5
  "description": "Records Claude Code and Codex sessions on your machine and keeps past implementation and decisions, with their sources, for your agent to find.",
6
6
  "author": {
7
7
  "name": "iroha924",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sphica",
3
- "version": "0.6.21",
3
+ "version": "0.6.23",
4
4
  "description": "Records Claude Code and Codex sessions on your machine and keeps past implementation and decisions, with their sources, for your agent to find.",
5
5
  "author": {
6
6
  "name": "iroha924",
@@ -45,7 +45,7 @@ This file is generated by `node scripts/third-party-notices.mjs`. Do not edit it
45
45
  | fast-deep-equal | 3.1.3 | MIT |
46
46
  | fast-string-truncated-width | 3.0.3 | MIT |
47
47
  | fast-string-width | 3.0.2 | MIT |
48
- | fast-uri | 3.1.7 | BSD-3-Clause |
48
+ | fast-uri | 3.1.8 | BSD-3-Clause |
49
49
  | fast-wrap-ansi | 0.2.2 | MIT |
50
50
  | finalhandler | 2.1.1 | MIT |
51
51
  | forwarded | 0.2.0 | MIT |
@@ -60,7 +60,7 @@ This file is generated by `node scripts/third-party-notices.mjs`. Do not edit it
60
60
  | http-errors | 2.0.1 | MIT |
61
61
  | iconv-lite | 0.7.3 | MIT |
62
62
  | inherits | 2.0.4 | ISC |
63
- | ip-address | 10.7.0 | MIT |
63
+ | ip-address | 10.7.1 | MIT |
64
64
  | ipaddr.js | 1.9.1 | MIT |
65
65
  | is-promise | 4.0.0 | MIT |
66
66
  | isexe | 2.0.0 | ISC |
@@ -1393,7 +1393,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1393
1393
  DEALINGS IN THE SOFTWARE.
1394
1394
  ```
1395
1395
 
1396
- ## fast-uri 3.1.7
1396
+ ## fast-uri 3.1.8
1397
1397
 
1398
1398
  SPDX: BSD-3-Clause
1399
1399
  Source: https://github.com/fastify/fast-uri
@@ -1835,7 +1835,7 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1835
1835
  PERFORMANCE OF THIS SOFTWARE.
1836
1836
  ```
1837
1837
 
1838
- ## ip-address 10.7.0
1838
+ ## ip-address 10.7.1
1839
1839
 
1840
1840
  SPDX: MIT
1841
1841
  Source: https://github.com/beaugunderson/ip-address
@@ -0,0 +1,9 @@
1
+ -- Both full-text indexes must hold terms() output as of revision 9, which includes the parts of camelCase and snake_case names, so they
2
+ -- are rebuilt as `sphica doctor --reindex` does. Tables, views, and triggers are the same as at revision 8.
3
+
4
+ insert into unit_fts (unit_fts) values ('delete-all');
5
+ insert into unit_fts (rowid, body, ident, alias) select id, body, ident, alias from unit_search_text;
6
+ insert into source_fts (source_fts) values ('delete-all');
7
+ insert into source_fts (rowid, lexemes) select id, sphica_terms(text) from source where indexed = 1;
8
+
9
+ pragma user_version = 9;
package/db/schema.sql CHANGED
@@ -1070,4 +1070,4 @@ create trigger capture_delivery_prune_insert instead of insert on capture_delive
1070
1070
  order by d.at, d.id limit 200);
1071
1071
  end;
1072
1072
 
1073
- pragma user_version = 8;
1073
+ pragma user_version = 9;
package/dist/capture.js CHANGED
@@ -10827,6 +10827,12 @@ var segmenter = new Intl.Segmenter("ja", { granularity: "word" });
10827
10827
  var HIRAGANA_ONLY = /^[\p{Script=Hiragana}ー]+$/u;
10828
10828
  var STOP = new Set(["the", "a", "an", "of", "to", "in", "is", "and", "or", "for", "on", "it", "be"]);
10829
10829
  var IDENT = /#\d+|[a-z0-9][a-z0-9_./#-]*[a-z0-9]/g;
10830
+ var WORD = /[\p{L}\p{M}\p{N}_]+/gu;
10831
+ var JAPANESE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ー々]+/u;
10832
+ var COMPOUND = /^[A-Za-z0-9_]*(?:[A-Za-z0-9]_|_[A-Za-z0-9]|[a-z0-9][A-Z])[A-Za-z0-9_]*$/;
10833
+ var CUT = /_+|(?<=[a-z0-9])(?=[A-Z])/;
10834
+ var MAX_PARTS = 6;
10835
+ var NAME_PART = /^(?:[A-Za-z]+[0-9]*|[0-9]+)$/;
10830
10836
  var MAX_TERM = 100;
10831
10837
  var OKURIGANA = /^(\p{Script=Han}+)[\p{Script=Hiragana}ー]+$/u;
10832
10838
  function singular(w) {
@@ -10842,8 +10848,9 @@ function singular(w) {
10842
10848
  return w.slice(0, -1);
10843
10849
  return w.endsWith("s") && !/(?:ss|us|is)$/.test(w) ? w.slice(0, -1) : w;
10844
10850
  }
10845
- function terms(text) {
10846
- const norm = text.normalize("NFKC").toLowerCase();
10851
+ function terms(text, parts = true) {
10852
+ const nfkc = text.normalize("NFKC");
10853
+ const norm = nfkc.toLowerCase();
10847
10854
  const out = [];
10848
10855
  const keep = (w) => {
10849
10856
  if (w.length > MAX_TERM || STOP.has(w) || HIRAGANA_ONLY.test(w))
@@ -10856,6 +10863,18 @@ function terms(text) {
10856
10863
  for (const m of norm.matchAll(IDENT))
10857
10864
  if (m[0].length >= 3)
10858
10865
  keep(m[0]);
10866
+ if (parts)
10867
+ for (const [word] of nfkc.matchAll(WORD))
10868
+ for (const w of word.split(JAPANESE)) {
10869
+ if (w.length > MAX_TERM || !COMPOUND.test(w))
10870
+ continue;
10871
+ const cut = w.split(CUT).filter(Boolean);
10872
+ if (cut.length <= MAX_PARTS && cut.every((p) => NAME_PART.test(p))) {
10873
+ for (const p of cut)
10874
+ if (p.length >= 2)
10875
+ keep(p.toLowerCase());
10876
+ }
10877
+ }
10859
10878
  return out.filter(Boolean);
10860
10879
  }
10861
10880
  var QUESTION = new Set([
@@ -11110,7 +11129,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
11110
11129
 
11111
11130
  // server/src/sqlite.ts
11112
11131
  var SCHEMA_GENERATION = 2;
11113
- var SCHEMA_REVISION = 8;
11132
+ var SCHEMA_REVISION = 9;
11114
11133
  var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
11115
11134
  var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
11116
11135
  function requireRuntime() {
package/dist/cli.js CHANGED
@@ -3016,6 +3016,12 @@ var segmenter = new Intl.Segmenter("ja", { granularity: "word" });
3016
3016
  var HIRAGANA_ONLY = /^[\p{Script=Hiragana}ー]+$/u;
3017
3017
  var STOP = new Set(["the", "a", "an", "of", "to", "in", "is", "and", "or", "for", "on", "it", "be"]);
3018
3018
  var IDENT = /#\d+|[a-z0-9][a-z0-9_./#-]*[a-z0-9]/g;
3019
+ var WORD = /[\p{L}\p{M}\p{N}_]+/gu;
3020
+ var JAPANESE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ー々]+/u;
3021
+ var COMPOUND = /^[A-Za-z0-9_]*(?:[A-Za-z0-9]_|_[A-Za-z0-9]|[a-z0-9][A-Z])[A-Za-z0-9_]*$/;
3022
+ var CUT = /_+|(?<=[a-z0-9])(?=[A-Z])/;
3023
+ var MAX_PARTS = 6;
3024
+ var NAME_PART = /^(?:[A-Za-z]+[0-9]*|[0-9]+)$/;
3019
3025
  var MAX_TERM = 100;
3020
3026
  var OKURIGANA = /^(\p{Script=Han}+)[\p{Script=Hiragana}ー]+$/u;
3021
3027
  function singular(w) {
@@ -3031,8 +3037,9 @@ function singular(w) {
3031
3037
  return w.slice(0, -1);
3032
3038
  return w.endsWith("s") && !/(?:ss|us|is)$/.test(w) ? w.slice(0, -1) : w;
3033
3039
  }
3034
- function terms(text) {
3035
- const norm = text.normalize("NFKC").toLowerCase();
3040
+ function terms(text, parts = true) {
3041
+ const nfkc = text.normalize("NFKC");
3042
+ const norm = nfkc.toLowerCase();
3036
3043
  const out = [];
3037
3044
  const keep = (w) => {
3038
3045
  if (w.length > MAX_TERM || STOP.has(w) || HIRAGANA_ONLY.test(w))
@@ -3045,6 +3052,18 @@ function terms(text) {
3045
3052
  for (const m of norm.matchAll(IDENT))
3046
3053
  if (m[0].length >= 3)
3047
3054
  keep(m[0]);
3055
+ if (parts)
3056
+ for (const [word] of nfkc.matchAll(WORD))
3057
+ for (const w of word.split(JAPANESE)) {
3058
+ if (w.length > MAX_TERM || !COMPOUND.test(w))
3059
+ continue;
3060
+ const cut = w.split(CUT).filter(Boolean);
3061
+ if (cut.length <= MAX_PARTS && cut.every((p) => NAME_PART.test(p))) {
3062
+ for (const p of cut)
3063
+ if (p.length >= 2)
3064
+ keep(p.toLowerCase());
3065
+ }
3066
+ }
3048
3067
  return out.filter(Boolean);
3049
3068
  }
3050
3069
  var QUESTION = new Set([
@@ -3626,7 +3645,7 @@ ${indent}${caution(note)}` : ""}`);
3626
3645
 
3627
3646
  // server/src/sqlite.ts
3628
3647
  var SCHEMA_GENERATION = 2;
3629
- var SCHEMA_REVISION = 8;
3648
+ var SCHEMA_REVISION = 9;
3630
3649
  var sphicaHome = () => process.env.SPHICA_HOME || path3.join(os2.homedir(), ".sphica");
3631
3650
  var dbFile = () => process.env.SPHICA_DB || path3.join(sphicaHome(), "sphica.db");
3632
3651
  function requireRuntime() {
@@ -16097,7 +16116,9 @@ var terms_golden_default = {
16097
16116
  "server/src/db-write.ts",
16098
16117
  "connectwriter",
16099
16118
  "ot-123",
16100
- "#201"
16119
+ "#201",
16120
+ "connect",
16121
+ "writer"
16101
16122
  ]
16102
16123
  },
16103
16124
  {
@@ -16110,21 +16131,39 @@ var terms_golden_default = {
16110
16131
  "name",
16111
16132
  "snake_case_name",
16112
16133
  "camelcasename",
16113
- "kebab-case-name"
16134
+ "kebab-case-name",
16135
+ "snake",
16136
+ "case",
16137
+ "name",
16138
+ "camel",
16139
+ "case",
16140
+ "name"
16114
16141
  ]
16115
16142
  },
16116
16143
  {
16117
16144
  text: "fullwidth ABC 123 と カタカナ",
16118
16145
  terms: ["fullwidth", "abc", "123", "カタカナ", "fullwidth", "abc", "123"]
16119
16146
  },
16120
- { text: "書きました 書きます 書く", terms: ["書", "書", "書"] },
16121
- { text: "emoji 🚀 と 絵文字 👍🏽 のあいだ", terms: ["emoji", "絵文字", "emoji"] },
16122
- { text: "é café と が(が)", terms: ["é", "café", "caf"] },
16147
+ {
16148
+ text: "書きました 書きます 書く",
16149
+ terms: ["書", "書", "書"]
16150
+ },
16151
+ {
16152
+ text: "emoji 🚀 と 絵文字 👍🏽 のあいだ",
16153
+ terms: ["emoji", "絵文字", "emoji"]
16154
+ },
16155
+ {
16156
+ text: "é café と が(が)",
16157
+ terms: ["é", "café", "caf"]
16158
+ },
16123
16159
  {
16124
16160
  text: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa base64 aGVsbG8gd29ybGQgaGVsbG8gd29ybGQ=",
16125
16161
  terms: ["base64", "agvsbg8gd29ybgqgagvsbg8gd29ybgq", "base64", "agvsbg8gd29ybgqgagvsbg8gd29ybgq"]
16126
16162
  },
16127
- { text: "the a an of to in is and or for on it be", terms: [] },
16163
+ {
16164
+ text: "the a an of to in is and or for on it be",
16165
+ terms: []
16166
+ },
16128
16167
  {
16129
16168
  text: "保存先は Postgres じゃなくて SQLite にしよう。各ユーザーに DB サーバーを立てさせたくない。",
16130
16169
  terms: [
@@ -16159,10 +16198,13 @@ var terms_golden_default = {
16159
16198
  "postgre"
16160
16199
  ]
16161
16200
  },
16162
- { text: "WAL モードも有効にしておいて。", terms: ["wal", "モード", "有効", "wal"] },
16201
+ {
16202
+ text: "WAL モードも有効にしておいて。",
16203
+ terms: ["wal", "モード", "有効", "wal"]
16204
+ },
16163
16205
  {
16164
16206
  text: "open() で journal_mode = wal を設定しました。",
16165
- terms: ["open", "journal_mode", "wal", "設定", "open", "journal_mode", "wal"]
16207
+ terms: ["open", "journal_mode", "wal", "設定", "open", "journal_mode", "wal", "journal", "mode"]
16166
16208
  },
16167
16209
  {
16168
16210
  text: "利用状況の送信(テレメトリ)は入れない。ローカルで完結させるのがこのアプリの売りだから。",
@@ -16186,15 +16228,24 @@ var terms_golden_default = {
16186
16228
  },
16187
16229
  {
16188
16230
  text: "fetchCover に最大 3 回の再試行を入れました。",
16189
- terms: ["fetchcover", "最大", "3", "回", "再", "試行", "入", "fetchcover"]
16231
+ terms: ["fetchcover", "最大", "3", "回", "再", "試行", "入", "fetchcover", "fetch", "cover"]
16232
+ },
16233
+ {
16234
+ text: "今日はここまで。お疲れさま。",
16235
+ terms: ["今日", "お疲れさま"]
16236
+ },
16237
+ {
16238
+ text: "お疲れさまでした。",
16239
+ terms: ["お疲れさま"]
16190
16240
  },
16191
- { text: "今日はここまで。お疲れさま。", terms: ["今日", "お疲れさま"] },
16192
- { text: "お疲れさまでした。", terms: ["お疲れさま"] },
16193
16241
  {
16194
16242
  text: "入力の検証に zod を使うのもありかもね。まだ決めないけど。",
16195
16243
  terms: ["入力", "検証", "zod", "使", "決", "zod"]
16196
16244
  },
16197
- { text: "候補として覚えておきます。", terms: ["候補", "覚"] },
16245
+ {
16246
+ text: "候補として覚えておきます。",
16247
+ terms: ["候補", "覚"]
16248
+ },
16198
16249
  {
16199
16250
  text: "Store every timestamp in UTC. Convert to local time only when showing it.",
16200
16251
  terms: [
@@ -16238,7 +16289,8 @@ var terms_golden_default = {
16238
16289
  "write",
16239
16290
  "iso",
16240
16291
  "8601",
16241
- "utc"
16292
+ "utc",
16293
+ "stored"
16242
16294
  ]
16243
16295
  },
16244
16296
  {
@@ -16282,7 +16334,9 @@ var terms_golden_default = {
16282
16334
  "there",
16283
16335
  "reverted",
16284
16336
  "main",
16285
- "thread"
16337
+ "thread",
16338
+ "offscreen",
16339
+ "canva"
16286
16340
  ]
16287
16341
  },
16288
16342
  {
@@ -16301,9 +16355,18 @@ var terms_golden_default = {
16301
16355
  text: "検索結果のキャッシュを Redis に置きます。",
16302
16356
  terms: ["検索", "結果", "キャッシュ", "redis", "置", "redis"]
16303
16357
  },
16304
- { text: "ログその1を受け取りました。", terms: ["ログ", "1", "受け取り"] },
16305
- { text: "ログその2を受け取りました。", terms: ["ログ", "2", "受け取り"] },
16306
- { text: "ログその3を受け取りました。", terms: ["ログ", "3", "受け取り"] },
16358
+ {
16359
+ text: "ログその1を受け取りました。",
16360
+ terms: ["ログ", "1", "受け取り"]
16361
+ },
16362
+ {
16363
+ text: "ログその2を受け取りました。",
16364
+ terms: ["ログ", "2", "受け取り"]
16365
+ },
16366
+ {
16367
+ text: "ログその3を受け取りました。",
16368
+ terms: ["ログ", "3", "受け取り"]
16369
+ },
16307
16370
  {
16308
16371
  text: "CSV export leaks private notes",
16309
16372
  terms: ["csv", "export", "leak", "private", "note", "csv", "export", "leak", "private", "note"]
@@ -16343,7 +16406,10 @@ var terms_golden_default = {
16343
16406
  "csv"
16344
16407
  ]
16345
16408
  },
16346
- { text: "Use pnpm for installs", terms: ["use", "pnpm", "install", "use", "pnpm", "install"] },
16409
+ {
16410
+ text: "Use pnpm for installs",
16411
+ terms: ["use", "pnpm", "install", "use", "pnpm", "install"]
16412
+ },
16347
16413
  {
16348
16414
  text: "Switch the lockfile to pnpm. npm install took minutes on CI.",
16349
16415
  terms: [
@@ -16421,7 +16487,10 @@ var terms_golden_default = {
16421
16487
  text: "Consider synchronous = OFF for speed.",
16422
16488
  terms: ["consider", "synchronous", "off", "speed", "consider", "synchronous", "off", "speed"]
16423
16489
  },
16424
- { text: "chore: switch to pnpm", terms: ["chore", "switch", "pnpm", "chore", "switch", "pnpm"] },
16490
+ {
16491
+ text: "chore: switch to pnpm",
16492
+ terms: ["chore", "switch", "pnpm", "chore", "switch", "pnpm"]
16493
+ },
16425
16494
  {
16426
16495
  text: "Exclude notes from CSV export",
16427
16496
  terms: ["exclude", "note", "from", "csv", "export", "exclude", "note", "from", "csv", "export"]
@@ -16444,7 +16513,8 @@ var terms_golden_default = {
16444
16513
  "write",
16445
16514
  "title",
16446
16515
  "author",
16447
- "only"
16516
+ "only",
16517
+ "csv"
16448
16518
  ]
16449
16519
  },
16450
16520
  {
@@ -16484,10 +16554,14 @@ var terms_golden_default = {
16484
16554
  "drop",
16485
16555
  "author",
16486
16556
  "from",
16487
- "export"
16557
+ "export",
16558
+ "csv"
16488
16559
  ]
16489
16560
  },
16490
- { text: "検索は前方一致にする", terms: ["検索", "前方", "一致"] },
16561
+ {
16562
+ text: "検索は前方一致にする",
16563
+ terms: ["検索", "前方", "一致"]
16564
+ },
16491
16565
  {
16492
16566
  text: "書名の検索は前方一致にする。全文検索はデータが 1 万冊を超えるまで見送る。",
16493
16567
  terms: ["書名", "検索", "前方", "一致", "全文", "検索", "データ", "1", "万", "冊", "超", "見送"]
package/dist/deliver.js CHANGED
@@ -28,6 +28,12 @@ var segmenter = new Intl.Segmenter("ja", { granularity: "word" });
28
28
  var HIRAGANA_ONLY = /^[\p{Script=Hiragana}ー]+$/u;
29
29
  var STOP = new Set(["the", "a", "an", "of", "to", "in", "is", "and", "or", "for", "on", "it", "be"]);
30
30
  var IDENT = /#\d+|[a-z0-9][a-z0-9_./#-]*[a-z0-9]/g;
31
+ var WORD = /[\p{L}\p{M}\p{N}_]+/gu;
32
+ var JAPANESE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ー々]+/u;
33
+ var COMPOUND = /^[A-Za-z0-9_]*(?:[A-Za-z0-9]_|_[A-Za-z0-9]|[a-z0-9][A-Z])[A-Za-z0-9_]*$/;
34
+ var CUT = /_+|(?<=[a-z0-9])(?=[A-Z])/;
35
+ var MAX_PARTS = 6;
36
+ var NAME_PART = /^(?:[A-Za-z]+[0-9]*|[0-9]+)$/;
31
37
  var MAX_TERM = 100;
32
38
  var OKURIGANA = /^(\p{Script=Han}+)[\p{Script=Hiragana}ー]+$/u;
33
39
  function singular(w) {
@@ -43,8 +49,9 @@ function singular(w) {
43
49
  return w.slice(0, -1);
44
50
  return w.endsWith("s") && !/(?:ss|us|is)$/.test(w) ? w.slice(0, -1) : w;
45
51
  }
46
- function terms(text) {
47
- const norm = text.normalize("NFKC").toLowerCase();
52
+ function terms(text, parts = true) {
53
+ const nfkc = text.normalize("NFKC");
54
+ const norm = nfkc.toLowerCase();
48
55
  const out = [];
49
56
  const keep = (w) => {
50
57
  if (w.length > MAX_TERM || STOP.has(w) || HIRAGANA_ONLY.test(w))
@@ -57,6 +64,18 @@ function terms(text) {
57
64
  for (const m of norm.matchAll(IDENT))
58
65
  if (m[0].length >= 3)
59
66
  keep(m[0]);
67
+ if (parts)
68
+ for (const [word] of nfkc.matchAll(WORD))
69
+ for (const w of word.split(JAPANESE)) {
70
+ if (w.length > MAX_TERM || !COMPOUND.test(w))
71
+ continue;
72
+ const cut = w.split(CUT).filter(Boolean);
73
+ if (cut.length <= MAX_PARTS && cut.every((p) => NAME_PART.test(p))) {
74
+ for (const p of cut)
75
+ if (p.length >= 2)
76
+ keep(p.toLowerCase());
77
+ }
78
+ }
60
79
  return out.filter(Boolean);
61
80
  }
62
81
  var QUESTION = new Set([
@@ -11135,7 +11154,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
11135
11154
 
11136
11155
  // server/src/sqlite.ts
11137
11156
  var SCHEMA_GENERATION = 2;
11138
- var SCHEMA_REVISION = 8;
11157
+ var SCHEMA_REVISION = 9;
11139
11158
  var sphicaHome = () => process.env.SPHICA_HOME || path3.join(os.homedir(), ".sphica");
11140
11159
  var dbFile = () => process.env.SPHICA_DB || path3.join(sphicaHome(), "sphica.db");
11141
11160
  function requireRuntime() {
@@ -4070,13 +4070,14 @@ var require_fast_uri = __commonJS(function(exports, module) {
4070
4070
  if (!malformedIPLiteral) {
4071
4071
  malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4072
4072
  }
4073
- if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4074
- if (uri.indexOf("%") !== -1) {
4075
- if (parsed.host !== undefined && !malformedIPLiteral) {
4076
- const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4077
- parsed.host = reescapeHostDelimiters(host, isIP);
4078
- }
4073
+ if (uri.indexOf("%") !== -1 && parsed.host !== undefined && !malformedIPLiteral) {
4074
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4075
+ if (!isIP) {
4076
+ host = normalizePercentEncoding(host.toLowerCase());
4079
4077
  }
4078
+ parsed.host = reescapeHostDelimiters(host, isIP);
4079
+ }
4080
+ if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4080
4081
  if (parsed.path) {
4081
4082
  parsed.path = normalizePathEncoding(parsed.path);
4082
4083
  }
@@ -45541,6 +45542,12 @@ var segmenter = new Intl.Segmenter("ja", { granularity: "word" });
45541
45542
  var HIRAGANA_ONLY = /^[\p{Script=Hiragana}ー]+$/u;
45542
45543
  var STOP = new Set(["the", "a", "an", "of", "to", "in", "is", "and", "or", "for", "on", "it", "be"]);
45543
45544
  var IDENT = /#\d+|[a-z0-9][a-z0-9_./#-]*[a-z0-9]/g;
45545
+ var WORD = /[\p{L}\p{M}\p{N}_]+/gu;
45546
+ var JAPANESE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ー々]+/u;
45547
+ var COMPOUND = /^[A-Za-z0-9_]*(?:[A-Za-z0-9]_|_[A-Za-z0-9]|[a-z0-9][A-Z])[A-Za-z0-9_]*$/;
45548
+ var CUT = /_+|(?<=[a-z0-9])(?=[A-Z])/;
45549
+ var MAX_PARTS = 6;
45550
+ var NAME_PART = /^(?:[A-Za-z]+[0-9]*|[0-9]+)$/;
45544
45551
  var MAX_TERM = 100;
45545
45552
  var OKURIGANA = /^(\p{Script=Han}+)[\p{Script=Hiragana}ー]+$/u;
45546
45553
  function singular(w) {
@@ -45556,8 +45563,9 @@ function singular(w) {
45556
45563
  return w.slice(0, -1);
45557
45564
  return w.endsWith("s") && !/(?:ss|us|is)$/.test(w) ? w.slice(0, -1) : w;
45558
45565
  }
45559
- function terms(text) {
45560
- const norm = text.normalize("NFKC").toLowerCase();
45566
+ function terms(text, parts = true) {
45567
+ const nfkc = text.normalize("NFKC");
45568
+ const norm = nfkc.toLowerCase();
45561
45569
  const out = [];
45562
45570
  const keep = (w) => {
45563
45571
  if (w.length > MAX_TERM || STOP.has(w) || HIRAGANA_ONLY.test(w))
@@ -45570,6 +45578,18 @@ function terms(text) {
45570
45578
  for (const m of norm.matchAll(IDENT))
45571
45579
  if (m[0].length >= 3)
45572
45580
  keep(m[0]);
45581
+ if (parts)
45582
+ for (const [word] of nfkc.matchAll(WORD))
45583
+ for (const w of word.split(JAPANESE)) {
45584
+ if (w.length > MAX_TERM || !COMPOUND.test(w))
45585
+ continue;
45586
+ const cut = w.split(CUT).filter(Boolean);
45587
+ if (cut.length <= MAX_PARTS && cut.every((p) => NAME_PART.test(p))) {
45588
+ for (const p of cut)
45589
+ if (p.length >= 2)
45590
+ keep(p.toLowerCase());
45591
+ }
45592
+ }
45573
45593
  return out.filter(Boolean);
45574
45594
  }
45575
45595
  var QUESTION = new Set([
@@ -45870,7 +45890,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
45870
45890
 
45871
45891
  // server/src/sqlite.ts
45872
45892
  var SCHEMA_GENERATION = 2;
45873
- var SCHEMA_REVISION = 8;
45893
+ var SCHEMA_REVISION = 9;
45874
45894
  var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
45875
45895
  var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
45876
45896
  function requireRuntime() {
@@ -47884,13 +47904,13 @@ var isoDate = (v) => {
47884
47904
  const d = new Date(`${v}T00:00:00Z`);
47885
47905
  return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === v;
47886
47906
  };
47887
- var WORD = /[\p{Script=Latin}\p{Script=Greek}\p{Script=Cyrillic}\p{M}\p{N}_]/u;
47907
+ var WORD2 = /[\p{Script=Latin}\p{Script=Greek}\p{Script=Cyrillic}\p{M}\p{N}_]/u;
47888
47908
  var NUMBER = /(?<![\p{Script=Latin}\p{Script=Greek}\p{Script=Cyrillic}\p{M}\p{N}\p{Pd}_.+\uFF0B\u2212])[+-]?(?:[0-9]{1,3}(?:,[0-9]{3})+(?![0-9])|[0-9]+)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/gu;
47889
47909
  function standsAlone(quote, value) {
47890
47910
  for (let at = quote.indexOf(value);at >= 0; at = quote.indexOf(value, at + 1)) {
47891
47911
  const before = [...quote.slice(Math.max(0, at - 2), at)].at(-1) ?? "";
47892
47912
  const after = String.fromCodePoint(quote.codePointAt(at + value.length) ?? 32);
47893
- if (!WORD.test(before) && !WORD.test(after))
47913
+ if (!WORD2.test(before) && !WORD2.test(after))
47894
47914
  return true;
47895
47915
  }
47896
47916
  return false;
package/dist/mcp.js CHANGED
@@ -4070,13 +4070,14 @@ var require_fast_uri = __commonJS(function(exports, module) {
4070
4070
  if (!malformedIPLiteral) {
4071
4071
  malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4072
4072
  }
4073
- if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4074
- if (uri.indexOf("%") !== -1) {
4075
- if (parsed.host !== undefined && !malformedIPLiteral) {
4076
- const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4077
- parsed.host = reescapeHostDelimiters(host, isIP);
4078
- }
4073
+ if (uri.indexOf("%") !== -1 && parsed.host !== undefined && !malformedIPLiteral) {
4074
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4075
+ if (!isIP) {
4076
+ host = normalizePercentEncoding(host.toLowerCase());
4079
4077
  }
4078
+ parsed.host = reescapeHostDelimiters(host, isIP);
4079
+ }
4080
+ if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4080
4081
  if (parsed.path) {
4081
4082
  parsed.path = normalizePathEncoding(parsed.path);
4082
4083
  }
@@ -34728,6 +34729,12 @@ var segmenter = new Intl.Segmenter("ja", { granularity: "word" });
34728
34729
  var HIRAGANA_ONLY = /^[\p{Script=Hiragana}ー]+$/u;
34729
34730
  var STOP = new Set(["the", "a", "an", "of", "to", "in", "is", "and", "or", "for", "on", "it", "be"]);
34730
34731
  var IDENT = /#\d+|[a-z0-9][a-z0-9_./#-]*[a-z0-9]/g;
34732
+ var WORD = /[\p{L}\p{M}\p{N}_]+/gu;
34733
+ var JAPANESE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ー々]+/u;
34734
+ var COMPOUND = /^[A-Za-z0-9_]*(?:[A-Za-z0-9]_|_[A-Za-z0-9]|[a-z0-9][A-Z])[A-Za-z0-9_]*$/;
34735
+ var CUT = /_+|(?<=[a-z0-9])(?=[A-Z])/;
34736
+ var MAX_PARTS = 6;
34737
+ var NAME_PART = /^(?:[A-Za-z]+[0-9]*|[0-9]+)$/;
34731
34738
  var MAX_TERM = 100;
34732
34739
  var OKURIGANA = /^(\p{Script=Han}+)[\p{Script=Hiragana}ー]+$/u;
34733
34740
  function singular(w) {
@@ -34744,8 +34751,9 @@ function singular(w) {
34744
34751
  return w.endsWith("s") && !/(?:ss|us|is)$/.test(w) ? w.slice(0, -1) : w;
34745
34752
  }
34746
34753
  var identTerm = (x) => singular(x.normalize("NFKC").toLowerCase());
34747
- function terms(text) {
34748
- const norm = text.normalize("NFKC").toLowerCase();
34754
+ function terms(text, parts = true) {
34755
+ const nfkc = text.normalize("NFKC");
34756
+ const norm = nfkc.toLowerCase();
34749
34757
  const out = [];
34750
34758
  const keep = (w) => {
34751
34759
  if (w.length > MAX_TERM || STOP.has(w) || HIRAGANA_ONLY.test(w))
@@ -34758,6 +34766,18 @@ function terms(text) {
34758
34766
  for (const m of norm.matchAll(IDENT))
34759
34767
  if (m[0].length >= 3)
34760
34768
  keep(m[0]);
34769
+ if (parts)
34770
+ for (const [word] of nfkc.matchAll(WORD))
34771
+ for (const w of word.split(JAPANESE)) {
34772
+ if (w.length > MAX_TERM || !COMPOUND.test(w))
34773
+ continue;
34774
+ const cut = w.split(CUT).filter(Boolean);
34775
+ if (cut.length <= MAX_PARTS && cut.every((p) => NAME_PART.test(p))) {
34776
+ for (const p of cut)
34777
+ if (p.length >= 2)
34778
+ keep(p.toLowerCase());
34779
+ }
34780
+ }
34761
34781
  return out.filter(Boolean);
34762
34782
  }
34763
34783
  var QUESTION = new Set([
@@ -34804,7 +34824,7 @@ var QUESTION = new Set([
34804
34824
  ]);
34805
34825
  var FOLDED_QUESTION = new Set([...QUESTION].flatMap((w) => terms(w)));
34806
34826
  function queryTerms(question) {
34807
- return [...new Set(terms(question).filter((w) => !FOLDED_QUESTION.has(w)))].slice(0, 24);
34827
+ return [...new Set(terms(question, false).filter((w) => !FOLDED_QUESTION.has(w)))].slice(0, 24);
34808
34828
  }
34809
34829
  function ftsQuery(question) {
34810
34830
  const ws = queryTerms(question);
@@ -45691,7 +45711,7 @@ var HOST_MARKS = new Set([".orphaned_at", ".in_use"]);
45691
45711
 
45692
45712
  // server/src/sqlite.ts
45693
45713
  var SCHEMA_GENERATION = 2;
45694
- var SCHEMA_REVISION = 8;
45714
+ var SCHEMA_REVISION = 9;
45695
45715
  var sphicaHome = () => process.env.SPHICA_HOME || path2.join(os.homedir(), ".sphica");
45696
45716
  var dbFile = () => process.env.SPHICA_DB || path2.join(sphicaHome(), "sphica.db");
45697
45717
  function requireRuntime() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sphica",
3
- "version": "0.6.21",
3
+ "version": "0.6.23",
4
4
  "description": "Records Claude Code and Codex sessions on your machine and keeps past implementation and decisions, with their sources, for your agent to find.",
5
5
  "license": "MIT",
6
6
  "type": "module",