misakanet 2.44.0 → 2.44.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "misakanet",
3
- "version": "2.44.0",
3
+ "version": "2.44.2",
4
4
  "description": "Failure-memory network for AI coding agents: search evidence-rated failure lessons, submit new failures, and turn crashes into reusable knowledge.",
5
5
  "author": {
6
6
  "name": "Ikalus1988",
package/lib/client.js CHANGED
@@ -126,17 +126,20 @@ window.__ModuleLoader__.load({
126
126
  var en = {
127
127
  "guide.description": "What this session asked, what came back, and what you filed",
128
128
  "panel.asked": "What this session asked",
129
- "panel.asked.none": "No MisakaNet search has run in this session yet.",
129
+ "panel.asked.none": "No MisakaNet search has run in this session yet — the entry point is the composer: type /misakanet, then what broke.",
130
130
  "panel.reports": "Reports you filed",
131
131
  "panel.reports.none": "Nothing filed from this session.",
132
132
  "panel.trust": "How much these lessons are trusted",
133
- "panel.trust.none": "No lesson surfaced yet.",
133
+ "panel.trust.none": "No lesson surfaced yet — the entry point is the composer: type /misakanet, then what broke.",
134
+ "panel.hint.open": "Open the search box over the composer",
135
+ "panel.hint.openLabel": "Open the search box",
136
+ "panel.hint.openTitle": "Opens the composer's own MisakaNet overlay, which carries the same instruction. It does not type into the composer: a view occupant receives props, and the draft is reachable only from a live session scope, so nothing here can put `/misakanet ` in the box for you.",
134
137
  "panel.activity": "Your activity",
135
138
  "panel.voice": "Voice",
136
139
  "panel.voice.on": "Voice cues: on",
137
140
  "panel.voice.off": "Voice cues: off",
138
141
  "overlay.searching": "Searching…",
139
- "overlay.empty": "Type what broke after /misakanet, e.g. /misakanet pip install timeout",
142
+ "overlay.empty": "After /misakanet, type what broke, e.g. /misakanet pip install timeout",
140
143
  "overlay.none": "No lesson covers this yet. Ask the agent to file it (misakanet_submit_intake), or file it at misakanet.org — that is how the next person finds it here.",
141
144
  "overlay.failed": "Search failed: {error} — the library is at misakanet.org; nothing was sent but the query.",
142
145
  "overlay.footnote": "Reads are anonymous. This overlay sends only the query to misakanet.org; your votes and reports are the only other things a page ever sends.",
@@ -169,11 +172,13 @@ window.__ModuleLoader__.load({
169
172
  "unit.votes": "votes",
170
173
  "unit.report": "report",
171
174
  "unit.reports": "reports",
175
+ "unit.reportFiled": "report filed",
176
+ "unit.reportsFiled": "reports filed",
172
177
  "panel.asked.searching": "searching…",
173
178
  "panel.asked.noMatch": "no lesson matched",
174
179
  "panel.asked.top": "· top: ",
175
180
  "panel.asked.noQuery": "(no query text)",
176
- "panel.asked.nothingFound": "nothing found",
181
+ "panel.asked.nothingFound": "found nothing",
177
182
  "panel.asked.voteHelpful": "Helpful",
178
183
  "panel.asked.voteNotHelpful": "not helpful",
179
184
  "panel.asked.voteNone": "not voted",
@@ -212,8 +217,6 @@ window.__ModuleLoader__.load({
212
217
  "panel.trust.footnote": "The level is the lesson's own stored evidence. The count is other people's confirmations, not yours: one makes the reuse event appear, the second is what agents read as E4.",
213
218
  "panel.activity.session": "this session: ",
214
219
  "panel.activity.browser": "on this browser: ",
215
- "panel.activity.reused": " reused · ",
216
- "panel.activity.filed": " filed · ",
217
220
  "panel.activity.conversion": "conversion",
218
221
  "panel.activity.scope": "The browser column survives a reload; the session column is rebuilt from this conversation's transcript and starts again in a new session.",
219
222
  "panel.voice.cue": "The cue is the server's own choice for the last search (",
@@ -240,7 +243,23 @@ window.__ModuleLoader__.load({
240
243
  "settings.invalidJson": "invalid JSON",
241
244
  "settings.what.form": "form",
242
245
  "settings.what.row": "row",
243
- "panel.activity.withLesson": " with a lesson · ",
246
+ // The counter strip and the activity line below are the two places a reader meets these
247
+ // numbers, and every word between them is a lookup: the unit words are `unit.*`, the rest are
248
+ // one-slot templates. Both halves are needed, because where the words go differs — English
249
+ // writes the plural noun then the verb (`{count} surfaced`), a language that takes a measure
250
+ // word before its noun writes one phrase (`{n} {unit}命中过课程`). Concatenating either half at
251
+ // the call site is what made the Chinese panel read `0 with a lesson · 0 lessons surfaced`,
252
+ // and splitting the phrase across two slots later made it read `1 篇 篇被课程复用`.
253
+ "panel.strip.hitTpl": "{count} hit · ",
254
+ "panel.strip.missTpl": "{count} found nothing · ",
255
+ "panel.strip.surfacedTpl": "{count} surfaced · ",
256
+ "panel.strip.helpfulTpl": "({n} helpful) · ",
257
+ "panel.strip.convertedTpl": "({n} became a lesson)",
258
+ "panel.activity.reusedTpl": "{count} reused · ",
259
+ "panel.activity.reportsTpl": "{count} · ",
260
+ "panel.activity.filedTpl": "{count}",
261
+ "unit.conversion": "conversion",
262
+ "unit.conversions": "conversions",
244
263
  "footer.session": "session: ",
245
264
  "footer.actionHint": "Copy the MisakaNet summary of the session that last used it",
246
265
  "settings.copyPatch": "Copy patch snippet",
@@ -250,17 +269,20 @@ window.__ModuleLoader__.load({
250
269
  var zh = {
251
270
  "guide.description": "本会话问过什么、拿回了什么、你报了什么",
252
271
  "panel.asked": "本会话问过什么",
253
- "panel.asked.none": "本会话还没有发起过 MisakaNet 检索。",
272
+ "panel.asked.none": "本会话还没有发起过 MisakaNet 检索 —— 入口就在输入框:先打 /misakanet,再写清楚出了什么问题。",
254
273
  "panel.reports": "你报过的问题",
255
274
  "panel.reports.none": "本会话没有报过任何问题。",
256
275
  "panel.trust": "这些课程的可信度",
257
- "panel.trust.none": "还没有命中过课程。",
276
+ "panel.trust.none": "还没有命中过课程 —— 入口就在输入框:先打 /misakanet,再写清楚出了什么问题。",
277
+ "panel.hint.open": "在输入框上方打开检索框",
278
+ "panel.hint.openLabel": "打开检索框",
279
+ "panel.hint.openTitle": "打开输入框自己的 MisakaNet 浮层,里面写着同样的用法。它不会替你在输入框里打字:视图组件只拿得到 props,草稿只有活的 session 作用域才能改,所以从这里没办法替你填入「/misakanet 」。",
258
280
  "panel.activity": "你的活动",
259
281
  "panel.voice": "语音",
260
282
  "panel.voice.on": "语音提示:开",
261
283
  "panel.voice.off": "语音提示:关",
262
284
  "overlay.searching": "检索中……",
263
- "overlay.empty": "在 /misakanet 后面写清楚出了什么问题,例如 /misakanet pip install 超时",
285
+ "overlay.empty": "在 /misakanet 后面写清楚出了什么问题,例如 /misakanet pip install 超时。",
264
286
  "overlay.none": "还没有课程覆盖这个问题。可以让 agent 报料(misakanet_submit_intake),或直接去 misakanet.org 提交 —— 下一个人就能在这里搜到它。",
265
287
  "overlay.failed": "检索失败:{error} —— 课程库在 misakanet.org;这次只发出了查询本身。",
266
288
  "overlay.footnote": "读取是匿名的。这个浮层只把查询发给 misakanet.org;除此之外,页面只会发出你的投票与报料。",
@@ -293,11 +315,13 @@ window.__ModuleLoader__.load({
293
315
  "unit.votes": "票",
294
316
  "unit.report": "条报料",
295
317
  "unit.reports": "条报料",
318
+ "unit.reportFiled": "条报料",
319
+ "unit.reportsFiled": "条报料",
296
320
  "panel.asked.searching": "检索中……",
297
321
  "panel.asked.noMatch": "没有命中任何课程",
298
322
  "panel.asked.top": "· 最佳:",
299
323
  "panel.asked.noQuery": "(没记下查询原文)",
300
- "panel.asked.nothingFound": "没有命中",
324
+ "panel.asked.nothingFound": "没查到结果",
301
325
  "panel.asked.voteHelpful": "有用",
302
326
  "panel.asked.voteNotHelpful": "没用",
303
327
  "panel.asked.voteNone": "未投票",
@@ -336,8 +360,6 @@ window.__ModuleLoader__.load({
336
360
  "panel.trust.footnote": "等级来自课程自己存的证据;计数是别人的确认,不是你的:前者让复用事件出现,后者才是 agent 读到的 E4。",
337
361
  "panel.activity.session": "本会话:",
338
362
  "panel.activity.browser": "本浏览器:",
339
- "panel.activity.reused": " 次复用 · ",
340
- "panel.activity.filed": " 条报料 · ",
341
363
  "panel.activity.conversion": "转化",
342
364
  "panel.activity.scope": "浏览器那一列能跨刷新保留;会话那一列由本次对话的转录重建,换一个会话就从零开始。",
343
365
  "panel.voice.cue": "提示音是服务端为上一次检索自己选的(",
@@ -364,7 +386,16 @@ window.__ModuleLoader__.load({
364
386
  "settings.invalidJson": "JSON 不合法",
365
387
  "settings.what.form": "表单",
366
388
  "settings.what.row": "条目",
367
- "panel.activity.withLesson": " 篇命中课程 · ",
389
+ "panel.strip.hitTpl": "{n} {unit}命中过课程 · ",
390
+ "panel.strip.missTpl": "{n} {unit}没命中 · ",
391
+ "panel.strip.surfacedTpl": "{n} {unit}命中过课程 · ",
392
+ "panel.strip.helpfulTpl": "({n} 有用) · ",
393
+ "panel.strip.convertedTpl": "({n} 转化为课程)",
394
+ "panel.activity.reusedTpl": "{n} {unit}被课程复用 · ",
395
+ "panel.activity.reportsTpl": "{n} {unit} · ",
396
+ "panel.activity.filedTpl": "{n} {unit}",
397
+ "unit.conversion": "次转化",
398
+ "unit.conversions": "次转化",
368
399
  "footer.session": "会话:",
369
400
  "footer.actionHint": "复制最近一次使用 MisakaNet 的那个会话的摘要",
370
401
  "settings.copyPatch": "复制 patch 片段",
@@ -959,11 +990,21 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
959
990
  *
960
991
  * `many` is explicit rather than `singular + "s"` because English is not regular: the first
961
992
  * version of this helper printed "2 searchs", which its own verification run showed and the
962
- * commit message then misquoted. Passing the plural at the call site makes the irregular one
963
- * impossible to get wrong silently.
993
+ * commit message then misquoted.
994
+ *
995
+ * The fallback keeps `many` optional, but it may only ever *derive an English plural*. A unit
996
+ * that is not an English word — every `unit.*` key holds a Chinese one under zh — does not
997
+ * pluralize with a trailing "s", so appending one did not make the count wrong in a way a reader
998
+ * could parse: it made `count(0, "票")` print `"票s"` and `count(0, "转化")` print `"转化s"`,
999
+ * which is the screenshot this file was fixed from. A unit that is not `[A-Za-z]…` therefore
1000
+ * keeps its singular in the plural slot, and the guard is the *character class*, not a list of
1001
+ * nouns: a new unit in a language this helper has never seen cannot grow an English plural.
1002
+ * Where English itself is irregular the call site still passes `many`, because no default can
1003
+ * know "search" wants "searches".
964
1004
  */
965
1005
  function count(n, singular, many) {
966
- return n + " " + (n === 1 ? singular : many || singular + "s");
1006
+ var plural = many || (/^[A-Za-z][A-Za-z '\u2019-]*$/.test(singular) ? singular + "s" : singular);
1007
+ return n + " " + (n === 1 ? singular : plural);
967
1008
  }
968
1009
 
969
1010
  /** mm:ss for today, else the date — the event's own time, never the render time. */
@@ -1301,11 +1342,17 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1301
1342
  rows.push(h("div", { key: "scope", style: DIM },
1302
1343
  T("panel.counters")));
1303
1344
 
1345
+ // The strip and the activity line below are the two places a reader meets these counters, and
1346
+ // every word between the numbers is a lookup. Hard-coding it at the call site is what made the
1347
+ // Chinese panel read `0 with a lesson · 0 lessons surfaced · 0 became a lesson` — the numbers
1348
+ // were right and the sentence was English.
1304
1349
  rows.push(h("div", { key: "strip", style: Object.assign({}, BOX, { marginTop: "8px" }) },
1305
- count(searches.length, T("unit.search"), T("unit.searches")) + " · " + withLesson + " with a lesson · " +
1306
- noMatch + " " + T("panel.asked.nothingFound") + " · " + count(lessons.length, "lesson") + " surfaced · " +
1307
- count(votes.length, T("unit.vote")) + " (" + helpful + " helpful) · " +
1308
- count(intakes.length, "report") + " (" + converted + " became a lesson)"));
1350
+ count(searches.length, T("unit.search"), T("unit.searches")) + " · " +
1351
+ T("panel.strip.hitTpl", { count: count(withLesson, T("unit.lesson"), T("unit.lessons")), n: withLesson, unit: T("unit.lessons") }) +
1352
+ T("panel.strip.missTpl", { count: count(noMatch, T("unit.lesson"), T("unit.lessons")), n: noMatch, unit: T("unit.lessons") }) +
1353
+ T("panel.strip.surfacedTpl", { count: count(lessons.length, T("unit.lesson"), T("unit.lessons")), n: lessons.length, unit: T("unit.lessons") }) +
1354
+ count(votes.length, T("unit.vote"), T("unit.votes")) + " " + T("panel.strip.helpfulTpl", { count: count(helpful, T("unit.vote"), T("unit.votes")), n: helpful, unit: T("unit.votes") }) +
1355
+ count(intakes.length, T("unit.report"), T("unit.reports")) + " " + T("panel.strip.convertedTpl", { count: count(converted, T("unit.report"), T("unit.reports")), n: converted, unit: T("unit.reports") })));
1309
1356
 
1310
1357
  // ① what this session asked
1311
1358
  //
@@ -1352,9 +1399,47 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1352
1399
  );
1353
1400
  }
1354
1401
 
1402
+ /**
1403
+ * The entry point, which the panel is the first place a person looks for and the last place
1404
+ * it used to mention.
1405
+ *
1406
+ * The real entry is the composer's `/misakanet` source, and the empty states below said only
1407
+ * what was missing (`panel.asked.none`, `panel.trust.none`). What was checked before settling
1408
+ * for a hint plus this one button:
1409
+ *
1410
+ * * **Prefilling the composer is out of reach.** The draft lives behind
1411
+ * `ctx.conversation.input.for(actx).setDraft(text)` — a *session-scoped* service the panel
1412
+ * cannot hold: a view occupant is handed props, and the panel's `apply(ctx)` is the client
1413
+ * root. Reaching it would mean injecting a service this plugin does not declare, which on a
1414
+ * line without it is the dead-button case this file already refuses (see the deferred
1415
+ * `sidebarRightTabs` inject). So no prefilled `/misakanet ` and no text that claims one.
1416
+ * * **Opening the overlay is in reach**, because it is this module's own state: the card
1417
+ * registered on `conversation.input.overlay` renders from `overlayOf(sessionId)`, and the
1418
+ * panel receives that same session id. `runOverlaySearch(sessionId, "")` is the module's one
1419
+ * opener; it starts no request for an empty query and sends nothing — it just puts
1420
+ * `overlay.empty` in front of the person, where the instruction now is.
1421
+ *
1422
+ * The button is offered only with a session id (the `main` seat gets none): without one there is
1423
+ * no overlay to open, and a button that cannot do what its label says is worse than the text.
1424
+ */
1425
+ function openOverlayFromPanel() {
1426
+ if (!sessionId) return;
1427
+ runOverlaySearch(sessionId, "");
1428
+ }
1429
+
1430
+ /** The section's own empty-state sentence, plus the overlay opener when a session can take it. */
1431
+ function entryPointHint(key, message) {
1432
+ return h("div", { key: key, style: DIM },
1433
+ T(message) + " ",
1434
+ sessionId ? h("button", {
1435
+ type: "button", style: BUTTON, title: T("panel.hint.openTitle"),
1436
+ onClick: openOverlayFromPanel,
1437
+ }, T("panel.hint.openLabel")) : null);
1438
+ }
1439
+
1355
1440
  rows.push(h("div", { key: "s1", style: { marginTop: "12px", fontWeight: 600 } }, T("panel.asked")));
1356
1441
  if (!searches.length) {
1357
- rows.push(h("div", { key: "s1e", style: DIM }, T("panel.asked.none")));
1442
+ rows.push(entryPointHint("s1e", "panel.asked.none"));
1358
1443
  } else {
1359
1444
  var searchRows = searches.slice().reverse().slice(0, 20).map(function (entry) {
1360
1445
  var key = "q" + entry.callId + entry.at;
@@ -1466,7 +1551,7 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1466
1551
  // reuse signal (E3), two or more is E4 — which is also why the rule below is stated once.
1467
1552
  rows.push(h("div", { key: "s3", style: { marginTop: "12px", fontWeight: 600 } }, T("panel.trust")));
1468
1553
  if (!lessons.length) {
1469
- rows.push(h("div", { key: "s3e", style: DIM }, T("panel.trust.none")));
1554
+ rows.push(entryPointHint("s3e", "panel.trust.none"));
1470
1555
  } else {
1471
1556
  // The rule once, not per row: it is the same sentence for every lesson, and repeating it
1472
1557
  // four times buries the numbers it is there to explain.
@@ -1492,12 +1577,16 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
1492
1577
  // ④ your activity, two scopes
1493
1578
  rows.push(h("div", { key: "s4", style: { marginTop: "12px", fontWeight: 600 } }, T("panel.activity")));
1494
1579
  rows.push(h("div", { key: "s4r", style: BOX },
1495
- h("div", {}, T("panel.activity.session") + count(searches.length, "search", "searches") + " · " + count(lessons.length, "lesson") +
1496
- T("panel.activity.reused") + count(votes.length, "vote") + " · " + count(intakes.length, "report") + T("panel.activity.filed") +
1497
- count(converted, T("panel.activity.conversion"))),
1580
+ h("div", {}, T("panel.activity.session") + count(searches.length, T("unit.search"), T("unit.searches")) + " · " +
1581
+ T("panel.activity.reusedTpl", { count: count(lessons.length, T("unit.lesson"), T("unit.lessons")), n: lessons.length, unit: T("unit.lessons") }) +
1582
+ count(votes.length, T("unit.vote"), T("unit.votes")) + " · " +
1583
+ T("panel.activity.reportsTpl", { count: count(intakes.length, T("unit.reportFiled"), T("unit.reportsFiled")), n: intakes.length, unit: T("unit.reportsFiled") }) +
1584
+ T("panel.activity.filedTpl", { count: count(converted, T("unit.conversion"), T("unit.conversions")), n: converted, unit: T("unit.conversions") })),
1498
1585
  h("div", {}, T("panel.activity.browser") + count(stats.searches || 0, T("unit.search"), T("unit.searches")) + " · " +
1499
- count(stats.lessonsReused || 0, "lesson") + " reused · " + count(stats.votes || 0, "vote") + " · " +
1500
- count(stats.reportsFiled || 0, "report") + " filed · " + count(stats.reportsConverted || 0, "conversion")),
1586
+ T("panel.activity.reusedTpl", { count: count(stats.lessonsReused || 0, T("unit.lesson"), T("unit.lessons")), n: stats.lessonsReused || 0, unit: T("unit.lessons") }) +
1587
+ count(stats.votes || 0, T("unit.vote"), T("unit.votes")) + " · " +
1588
+ T("panel.activity.reportsTpl", { count: count(stats.reportsFiled || 0, T("unit.reportFiled"), T("unit.reportsFiled")), n: stats.reportsFiled || 0, unit: T("unit.reportsFiled") }) +
1589
+ T("panel.activity.filedTpl", { count: count(stats.reportsConverted || 0, T("unit.conversion"), T("unit.conversions")), n: stats.reportsConverted || 0, unit: T("unit.conversions") })),
1501
1590
  h("div", { style: DIM }, T("panel.activity.scope"))));
1502
1591
 
1503
1592
  // ⑤ voice
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "misakanet",
3
- "version": "2.44.0",
3
+ "version": "2.44.2",
4
4
  "license": "Apache-2.0",
5
5
  "description": "MisakaNet failure-memory skill — evidence-rated failure lessons for AI agents. DSH bundle: registers the MCP tools as mcp__misakanet__* (misakanet_search/get_lesson/…), served by the public Streamable HTTP endpoint https://misakanet.org/mcp — live tools from an npm install, in any profile, with no local python. A profile that prefers the repo's own stdio server can override the bundle row.",
6
6
  "icon": "icon.svg",