@horizon_works/banto 0.9.0 → 0.9.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.
Files changed (3) hide show
  1. package/gate_core.js +65 -4
  2. package/package.json +1 -1
  3. package/server.js +33 -4
package/gate_core.js CHANGED
@@ -441,14 +441,51 @@ function stamp() {
441
441
  const d = new Date(), p = (n) => String(n).padStart(2, '0');
442
442
  return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
443
443
  }
444
- function remember(text, tag, sig) {
444
+ // ─────────────────────────────────────────────
445
+ // ★出所タグ(2026-09-01)
446
+ //
447
+ // 🚨★由来: 出す側の主から「主の発言・リンク・ファイルを、そのまま角かっこで書きたい」。
448
+ // ★これができると、AIどうしで渡したときに混乱が減る。
449
+ // ★出す側でも、重い書式にしたら45日で951行中1行しか付かなかった=**重いと付かない**。
450
+ // ★★出所そのものを角かっこに入れるだけ。分類しない。
451
+ //
452
+ // ★なぜ機械で締めるか=★裁量に残すと付かない(実測で45日ゼロだった)。
453
+ // ★ただし締めるのは**出口(settle)**だけ。★入口(remember)は促すだけで止めない。
454
+ const 出所の形 = [
455
+ /\[(推測|実測|未確認)\]/g,
456
+ /\[(https?:\/\/[^\]\s]+)\]/g,
457
+ /\[([^\]\s]*\.(?:md|js|mjs|ts|py|json|txt|sql|html|csv|docx?|xlsx?|pdf))\]/g,
458
+ /\[([^\]。、\s]{1,14}(?:さん|先生|様))\]/g,
459
+ ];
460
+
461
+ function readSrc(text) {
462
+ const out = [];
463
+ for (const re of 出所の形) {
464
+ re.lastIndex = 0;
465
+ let m;
466
+ while ((m = re.exec(String(text))) !== null) if (out.indexOf(m[1]) === -1) out.push(m[1]);
467
+ }
468
+ return out;
469
+ }
470
+
471
+ /** ★裏が取れる出所を持っているか(`[推測]` だけ、は持っていない) */
472
+ function 裏が取れる(text) {
473
+ const v = readSrc(text);
474
+ for (let i = 0; i < v.length; i++) if (v[i] !== '推測' && v[i] !== '未確認') return true;
475
+ return false;
476
+ }
477
+
478
+ function remember(text, tag, sig, src) {
445
479
  if (logOwner()) return kitNotice('retain.js add "<本文>" --score 0.5 --tag failure --sig <型キー>');
446
480
  const t = String(text || '').trim();
447
481
  if (!t) throw new Error('記録する内容がありません');
448
482
  const f = logPath();
449
483
  const oneLine = t.split(String.fromCharCode(10)).join(' ').split(String.fromCharCode(13)).join(' ');
484
+ // ★出所タグ。src で足せる(★本文に直接 [推測] などと書いてもよい。拾う)
485
+ const 出所 = String(src || '').trim();
486
+ const 本文 = 出所 ? (oneLine + ' [' + 出所.replace(/[\[\]]/g, '') + ']') : oneLine;
450
487
  const line = '- [ ] ' + stamp() + ' | ' + (tag || 'note')
451
- + (sig ? ' 〔' + sig + '〕' : '') + ' | ' + oneLine + String.fromCharCode(10);
488
+ + (sig ? ' 〔' + sig + '〕' : '') + ' | ' + 本文 + String.fromCharCode(10);
452
489
  if (!fs.existsSync(f)) {
453
490
  fs.writeFileSync(f,
454
491
  '# 気づいたことの下書き帳' + String.fromCharCode(10) + String.fromCharCode(10)
@@ -477,7 +514,15 @@ function remember(text, tag, sig) {
477
514
  + '★同じ型は、他の方でもよく出ます。'
478
515
  : null;
479
516
 
517
+ const 出所の状態 = readSrc(本文);
518
+ const 出所の声 = !出所の状態.length
519
+ ? '★出所がありません。★settle(片づけ)のときに止まります。src に [推測] [実測] [◯◯さん] [URL] のどれかを。'
520
+ : (!裏が取れる(本文)
521
+ ? '★出所が「' + 出所の状態.join(',') + '」だけ=裏が取れません。確かめてから [実測] へ書き換えてください。'
522
+ : null);
523
+
480
524
  return { saved_to: f, pending: open.length, sig: sig || null, sig_count: sigCount,
525
+ 出所: 出所の状態.length ? 出所の状態 : null, 出所について: 出所の声,
481
526
  hint: hint, '★次の一手': 声かけ || undefined };
482
527
  }
483
528
  const ENTRY_RE = new RegExp('^- \\[( |x)\\] (\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}) \\| (.*)$');
@@ -584,15 +629,25 @@ function review(record) {
584
629
  * ids … 片づけた記録の番号(recall / review が返す id)
585
630
  * ruled_to … ルールや手順にしたときの置き場所(sig と一緒に指定)
586
631
  */
587
- function settle(ids, sig, ruledTo) {
632
+ function settle(ids, sig, ruledTo, force) {
588
633
  if (logOwner()) return kitNotice('retain.js promote <行番号...>');
589
634
  const { file, lines, entries } = readLog();
590
635
  if (!fs.existsSync(file)) throw new Error('まだ記録がありません');
591
636
  let closed = 0;
637
+ const 止めた = [];
592
638
  for (const id of (ids || [])) {
593
639
  const i = Number(id) - 1;
594
640
  if (i < 0 || i >= lines.length) continue;
595
- if (lines[i].indexOf('- [ ] ') === 0) { lines[i] = lines[i].replace('- [ ] ', '- [x] '); closed++; }
641
+ if (lines[i].indexOf('- [ ] ') !== 0) continue;
642
+ // 🚨★**出所の関所はここ**(2026-09-01)。★裏の取れない記録を、片づけ済みにしない。
643
+ // ★入口(remember)は締めない。★締めるのは出口だけ。逃げ道は force。
644
+ if (!force && !裏が取れる(lines[i])) {
645
+ 止めた.push({ id: Number(id), 出所: readSrc(lines[i]),
646
+ なぜ: readSrc(lines[i]).length ? '出所が「推測」だけです' : '出所がありません' });
647
+ continue;
648
+ }
649
+ lines[i] = lines[i].replace('- [ ] ', '- [x] ');
650
+ closed++;
596
651
  }
597
652
 
598
653
  let ruledLine = null;
@@ -619,6 +674,12 @@ function settle(ids, sig, ruledTo) {
619
674
  return {
620
675
  saved_to: file,
621
676
  片づけた件数: closed,
677
+ '★止めた件数': 止めた.length || undefined,
678
+ '★止めた理由': 止めた.length ? 止めた : undefined,
679
+ '★止まったときは': 止めた.length
680
+ ? '出所を確かめて本文へ [実測] か [◯◯さん] か [URL] を足してから、もう一度 settle してください。'
681
+ + '★どうしても通すなら force: true(★使ったことは残ります)'
682
+ : undefined,
622
683
  ルールにした印: ruledLine,
623
684
  残りの未処理: after.entries.filter((e) => !e.done).length,
624
685
  note: ruledLine
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@horizon_works/banto",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "AI番頭のOS。ルール・記憶・検査を1本のMCPで配ります。中身をクラウドへ渡さずに検査できます。",
5
5
  "keywords": [
6
6
  "mcp",
package/server.js CHANGED
@@ -256,13 +256,16 @@ const TOOLS = [
256
256
  name: 'remember',
257
257
  description:
258
258
  '気づいたこと・失敗・学びを1行だけ記録します。★会話を止めずに使ってください(記録は促しであって関所ではありません)。' +
259
- '同じ型キーが3件たまったら「ルールか手順に落とす番」だと返します。★溜めるのが目的ではありません。',
259
+ '同じ型キーが3件たまったら「ルールか手順に落とす番」だと返します。★溜めるのが目的ではありません。' +
260
+ '★出所(src)を必ず添えてください。★見たことと考えたことを混ぜないためです。',
260
261
  inputSchema: {
261
262
  type: 'object',
262
263
  properties: {
263
264
  text: { type: 'string', description: '何が起きたか・なぜか(1行)' },
264
265
  tag: { type: 'string', description: 'failure(失敗)/insight(発見)/value(価値観)/fact(事実)' },
265
266
  sig: { type: 'string', description: '型キー。同じ失敗を数えるための短い名前(例: 確かめる前に断定した)' },
267
+ src: { type: 'string', description: '★出所。[推測]/[実測]/[◯◯さん]/URL/ファイル名のどれか。'
268
+ + '★これが無いと settle で止まります。迷ったら 推測。' },
266
269
  },
267
270
  required: ['text'],
268
271
  },
@@ -308,6 +311,7 @@ const TOOLS = [
308
311
  ids: { type: 'array', items: { type: 'number' }, description: '片づけた記録の番号' },
309
312
  sig: { type: 'string', description: 'ルールにした型キー' },
310
313
  ruled_to: { type: 'string', description: 'どこに書いたか' },
314
+ force: { type: 'boolean', description: '★出所が無くても閉じる(★使ったことは残ります)' },
311
315
  },
312
316
  },
313
317
  },
@@ -1637,7 +1641,7 @@ function callTool(name, args) {
1637
1641
  }
1638
1642
 
1639
1643
  if (name === 'remember') {
1640
- return FREE.remember(args && args.text, args && args.tag, args && args.sig);
1644
+ return FREE.remember(args && args.text, args && args.tag, args && args.sig, args && args.src);
1641
1645
  }
1642
1646
 
1643
1647
  if (name === 'recall') {
@@ -1675,7 +1679,7 @@ function callTool(name, args) {
1675
1679
  }
1676
1680
 
1677
1681
  if (name === 'settle') {
1678
- return FREE.settle(args && args.ids, args && args.sig, args && args.ruled_to);
1682
+ return FREE.settle(args && args.ids, args && args.sig, args && args.ruled_to, args && args.force);
1679
1683
  }
1680
1684
 
1681
1685
  if (name === 'checkup') {
@@ -1929,7 +1933,20 @@ let LICENSE_STATE = { allowed: true, status: 'unconfigured', message: '', daysLe
1929
1933
  // ここで送るのは型キーの名前と件数だけ(組み立ては client 側の buildStats に閉じてある)。
1930
1934
  // ★送れなくても、何も止めない。
1931
1935
  if (LICENSE_STATE.allowed && LIC.isSharing && LIC.isSharing()) {
1932
- try { await LIC.reportStats(VERSION, FREE.review(false)); } catch (_) {}
1936
+ // 🚨★失敗を握りつぶさない(2026-09-01)。
1937
+ // ★ここが `catch (_) {}` だったので、「同意しているのに何も届かない」を
1938
+ // ★こちらもお手元も**一度も知ることができなかった**。
1939
+ // ★止めはしない。★手元の控えに1行だけ残す(`checkup` で読める)。
1940
+ try {
1941
+ await LIC.reportStats(VERSION, FREE.review(false));
1942
+ try { fs.writeFileSync(path.join(CORE_HOME, '.stats_last.json'),
1943
+ JSON.stringify({ ok: true, at: new Date().toISOString() }, null, 2), 'utf8'); } catch (_) {}
1944
+ } catch (e) {
1945
+ try { fs.mkdirSync(CORE_HOME, { recursive: true }); } catch (_) {}
1946
+ try { fs.writeFileSync(path.join(CORE_HOME, '.stats_last.json'),
1947
+ JSON.stringify({ ok: false, at: new Date().toISOString(),
1948
+ why: String(e && e.message || e).slice(0, 200) }, null, 2), 'utf8'); } catch (_) {}
1949
+ }
1933
1950
  }
1934
1951
 
1935
1952
  // ★核の新しい版が出ていないかを、1日1回だけ見にいく(★版だけ。中身は取らない)
@@ -1937,6 +1954,18 @@ let LICENSE_STATE = { allowed: true, status: 'unconfigured', message: '', daysLe
1937
1954
  // ★★黙って上書きしない。★出るのは「取ってきますか」まで。
1938
1955
  if (LICENSE_STATE.allowed) {
1939
1956
  try { await 核の版を取りにいく(); } catch (_) {}
1957
+
1958
+ // 🚨★**一度も入っていないときだけ、自動で取る**(2026-09-01)
1959
+ // ★由来=お渡しした方が4日つないで、記録が0件だった。
1960
+ // 調べたら **核そのものが入っていなかった**(`checkup` に「このフォルダには無い」)。
1961
+ // ★`sync_core` は**相手のAIが自分で呼ばないと動かない**作りだった。
1962
+ // → tooling-check.md §1手順5「裁量の出番を残した関所は、経路が増えた瞬間に漏れる」
1963
+ // ★★上の「黙って上書きしない」は**更新の話**。★ここは**初回**で、上書きするものが無い。
1964
+ // だから設計と衝突しない。★更新は今までどおり、お知らせだけ。
1965
+ try {
1966
+ const 初めて = !fs.existsSync(path.join(CORE_HOME, 'instructions.md'));
1967
+ if (初めて) await syncCore();
1968
+ } catch (_) { /* ★取れなくても何も止めない */ }
1940
1969
  }
1941
1970
  } catch (_) {
1942
1971
  // license モジュールが無い構成でも動く