@horizon_works/banto 0.8.5 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/add_to_config.js CHANGED
@@ -1,107 +1,167 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * mcp_config.json へ番頭を1ブロック足す(★既にあるものを消さない)
3
+ * 設定へ番頭を1ブロック足す(★既にあるものを消さない)
4
4
  *
5
5
  * 🚨 由来: 2026-08-28。ターミナル1行で入れられるようにしたら、
6
6
  * ★**既存の設定を消した**。原因=ファイルが BOM つき UTF-8 で `JSON.parse` が落ち、
7
7
  * ★`catch` が「無い」とみなして空から作り直していた。
8
8
  * ★★**読めなかったことと、無いことは別**(→ premise-check §3)。
9
9
  *
10
+ * 🚨 由来その2: 同日夜。★Antigravity の中で **Claude Code の拡張機能**を使っている方から
11
+ * 「Antigravity純正でないと読まれない」と報告。★**置き場所が違うだけ**だった。
12
+ * ★★**AIごとに読む場所が違う。★決め打ちにしない。**
13
+ *
14
+ * ★置き場所(実測・2026-08-28)
15
+ * Antigravity 純正 : ~/.gemini/antigravity/mcp_config.json
16
+ * Claude Code : <作業フォルダ>/.mcp.json ← ★プロジェクト単位。いちばん確実
17
+ * ~/.claude.json の mcpServers ← 全体
18
+ *
10
19
  * ★この道具の約束
11
20
  * ① 読めなければ **書かない**(★控えを作って、人に見てもらう)
12
- * ② 既にある `mcpServers` は **そのまま残す**
21
+ * ② 既にある mcpServers は **そのまま残す**
13
22
  * ③ 書く前に **控えを取る**(.bak-日時)
14
23
  * ④ 書いたあと **読み直して確かめる**(★書けたと言う前に)
24
+ * ⑤ ★**在る設定ファイルすべてに書く**(★どのAIで開いても繋がるように)
15
25
  *
16
26
  * 使い方:
17
- * node add_to_config.js <鍵.署名> [設定ファイルのパス]
18
- * ★パスを省くと、この環境の既定の場所を探す
27
+ * npx -y @horizon_works/banto setup "<鍵.署名>"
28
+ * npx -y @horizon_works/banto setup "<鍵>" --only <設定ファイルのパス>
19
29
  */
20
30
  const fs = require('fs');
21
31
  const os = require('os');
22
32
  const path = require('path');
23
33
 
24
- // ★`npx ... setup <鍵>` で呼ばれると argv[2] が 'setup' になる。★1つずらす
25
34
  const 引数 = process.argv.slice(2).filter((x) => x !== 'setup');
26
- const 鍵 = 引数[0] || process.env.BANTO_KEY || '';
35
+ const 鍵 = 引数.find((x) => !x.startsWith('--')) || process.env.BANTO_KEY || '';
27
36
  if (!鍵) {
28
37
  console.error('使い方: npx -y @horizon_works/banto setup "<鍵.署名>"');
29
38
  process.exit(1);
30
39
  }
40
+ const 取る = (k) => { const i = 引数.indexOf(k); return i === -1 ? null : 引数[i + 1]; };
41
+ const だけ = 取る('--only') || process.env.BANTO_CONFIG || null;
31
42
 
32
- // ★置き場を探す(★見つからなければ、いちばんありそうな所に作る)
33
- function 設定の場所() {
34
- if (引数[1]) return 引数[1];
35
- if (process.env.BANTO_CONFIG) return process.env.BANTO_CONFIG;
43
+ const ブロック = {
44
+ command: 'npx',
45
+ args: ['-y', '@horizon_works/banto'],
46
+ env: { BANTO_KEY: 鍵 },
47
+ };
48
+ const 印 = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
49
+
50
+ /** ★どこに書くかを決める(★在るものすべて+Claude Code は無くても作る) */
51
+ function 置き場所たち() {
52
+ if (だけ) return [{ path: だけ, 種類: '指定', 作ってよい: true }];
36
53
  const home = os.homedir();
37
54
  const 候補 = [
38
- path.join(home, '.gemini', 'antigravity', 'mcp_config.json'),
39
- path.join(home, '.gemini', 'mcp_config.json'),
40
- path.join(process.cwd(), 'mcp_config.json'),
55
+ { path: path.join(home, '.gemini', 'antigravity', 'mcp_config.json'),
56
+ 種類: 'Antigravity 純正', 作ってよい: true },
57
+ { path: path.join(process.cwd(), '.mcp.json'),
58
+ 種類: 'Claude Code(このフォルダ)', 作ってよい: true },
41
59
  ];
42
- for (const p of 候補) { try { if (fs.existsSync(p)) return p; } catch (_) {} }
43
- return 候補[0]; // ★無ければ、いちばん標準の所に作る
60
+ // ★在るものと、作ってよいものを返す
61
+ return 候補;
44
62
  }
45
63
 
46
- const P = 設定の場所();
47
- const 印 = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
64
+ /** 1つの設定ファイルへ書く。★戻り値=何が起きたか */
65
+ function 書く() {
66
+ const P = 先.path;
67
+ let j = { mcpServers: {} };
68
+ let 既にある = false;
48
69
 
49
- let j = { mcpServers: {} };
50
- let 既にある = false;
51
- if (fs.existsSync(P)) {
52
- 既にある = true;
53
- let s;
54
- try { s = fs.readFileSync(P, 'utf8'); }
55
- catch (e) { console.error('🚨 読めません: ' + e.message + '\n★何も書いていません。'); process.exit(1); }
56
- // ★BOM を外してから読む(★これで落ちて、前の版は中身を消した)
57
- s = s.replace(/^/, '');
58
- try { j = JSON.parse(s); }
59
- catch (e) {
60
- console.error('🚨 いまの設定ファイルが読めませんでした(' + e.message + ')');
61
- console.error(' ' + P);
62
- console.error('★何も書いていません。★消さないために止めました。');
63
- console.error('★このファイルを開いて、形が壊れていないかご確認ください。');
64
- process.exit(1);
70
+ if (fs.existsSync(P)) {
71
+ 既にある = true;
72
+ let s;
73
+ try { s = fs.readFileSync(P, 'utf8'); }
74
+ catch (e) { return { ok: false, why: '読めません: ' + e.message }; }
75
+ s = s.replace(/^/, ''); // ★BOM を外す
76
+ try { j = JSON.parse(s); }
77
+ catch (e) { return { ok: false, why: '形が壊れています(' + e.message.slice(0, 60) + ')' }; }
78
+ if (typeof j !== 'object' || j === null || Array.isArray(j)) {
79
+ return { ok: false, why: '形が思っていたものと違います' };
80
+ }
81
+ } else if (!先.作ってよい) {
82
+ return { ok: false, skip: true, why: 'ありません' };
65
83
  }
66
- if (typeof j !== 'object' || j === null || Array.isArray(j)) {
67
- console.error('🚨 設定の形が思っていたものと違います。★何も書いていません。');
68
- process.exit(1);
69
- }
70
- }
71
84
 
72
- j.mcpServers = j.mcpServers || {};
73
- const 前からあった = Object.keys(j.mcpServers).filter((k) => k !== 'banto');
85
+ j.mcpServers = j.mcpServers || {};
86
+ const = Object.keys(j.mcpServers).filter((k) => k !== 'banto');
87
+ j.mcpServers.banto = ブロック;
74
88
 
75
- j.mcpServers.banto = {
76
- command: 'npx',
77
- args: ['-y', '@horizon_works/banto'],
78
- env: { BANTO_KEY: 鍵 },
79
- };
89
+ if (既にある) {
90
+ try { fs.copyFileSync(P, P + '.bak-' + 印); }
91
+ catch (e) { return { ok: false, why: '控えが取れません: ' + e.message }; }
92
+ }
93
+ try {
94
+ fs.mkdirSync(path.dirname(P), { recursive: true });
95
+ fs.writeFileSync(P, JSON.stringify(j, null, 2) + '\n', 'utf8');
96
+ } catch (e) { return { ok: false, why: '書けません: ' + e.message }; }
80
97
 
81
- // ★控えを取ってから書く
82
- if (既にある) {
83
- try { fs.copyFileSync(P, P + '.bak-' + 印); }
84
- catch (e) { console.error('🚨 控えが取れませんでした: ' + e.message + '\n★何も書いていません。'); process.exit(1); }
98
+ // ★書いたと言う前に、読み直して確かめる
99
+ let 確認;
100
+ try { 確認 = JSON.parse(fs.readFileSync(P, 'utf8').replace(/^/, '')); }
101
+ catch (e) { return { ok: false, why: '書いたものが読めません' }; }
102
+ const 消えた = 前.filter((k) => !(確認.mcpServers && 確認.mcpServers[k]));
103
+ if (消えた.length) return { ok: false, why: '消えたものがあります: ' + 消えた.join(', ') };
104
+ if (!確認.mcpServers.banto) return { ok: false, why: 'banto が入っていません' };
105
+
106
+ return { ok: true, 前, 新規: !既にある, 控え: 既にある ? path.basename(P) + '.bak-' + 印 : null };
85
107
  }
86
- fs.mkdirSync(path.dirname(P), { recursive: true });
87
- fs.writeFileSync(P, JSON.stringify(j, null, 2) + '\n', 'utf8');
88
108
 
89
- // ★書いたと言う前に、読み直して確かめる
90
- let 確認;
91
- try { 確認 = JSON.parse(fs.readFileSync(P, 'utf8').replace(/^/, '')); }
92
- catch (e) { console.error('🚨 書いたものが読めません: ' + e.message); process.exit(1); }
109
+ const 結果 = 置き場所たち().map((先) => ({ 先, r: 書く(先) }));
93
110
 
94
- const 残っている = 前からあった.filter((k) => 確認.mcpServers && 確認.mcpServers[k]);
95
- const 消えた = 前からあった.filter((k) => !(確認.mcpServers && 確認.mcpServers[k]));
111
+ console.log('');
112
+ let 成功 = 0;
113
+ for (const { 先, r } of 結果) {
114
+ if (r.ok) {
115
+ 成功++;
116
+ console.log('✅ ' + 先.種類);
117
+ console.log(' ' + 先.path + (r.新規 ? ' (新しく作りました)' : ''));
118
+ console.log(' 前からあったもの: ' + (r.前.length ? r.前.join(', ') : '(なし)') + ' → そのまま残っています');
119
+ if (r.控え) console.log(' 控え: ' + r.控え);
120
+ } else if (r.skip) {
121
+ console.log('- ' + 先.種類 + ' (' + r.why + '。飛ばしました)');
122
+ } else {
123
+ console.error('🚨 ' + 先.種類 + ' ' + r.why);
124
+ console.error(' ' + 先.path);
125
+ console.error(' ★このファイルには書いていません。');
126
+ }
127
+ }
96
128
 
97
- console.log('✅ 書きました: ' + P);
98
- console.log(' banto: ' + (確認.mcpServers.banto ? '入りました' : '❌ 入っていません'));
99
- console.log(' 前からあったもの: ' + (前からあった.length ? 前からあった.join(', ') : '(なし)'));
100
- console.log(' そのまま残った : ' + (残っている.length ? 残っている.join(', ') : '(なし)'));
101
- if (消えた.length) {
102
- console.error('🚨 消えたものがあります: ' + 消えた.join(', '));
103
- console.error(' ★控えから戻せます: ' + P + '.bak-' + 印);
104
- process.exit(1);
129
+ if (!成功) {
130
+ console.error('\n🚨 どこにも書けませんでした。');
131
+ process.exitCode = 1;
132
+ } else {
133
+ console.log('\n★このあと、お使いのAIを開き直してください。');
134
+ console.log('★Antigravity でも Claude Code でも、同じ番頭が動きます。');
135
+
136
+ // ★何ができるようになるかを、ここで伝える(2026-08-29)
137
+ // 由来=依頼元「セットアップ後に、こんなことができるようになったと伝えられるといいのかもね」。
138
+ // ★★「できました」とは書かない。この時点ではまだ繋がっていない
139
+ // (MCPを起動するのはホスト側)ので、「開き直すと、こうなります」と書く。
140
+ // ★道具の名前と説明は server.js の実物から取っている(推測で書かない)。
141
+ console.log([
142
+ '',
143
+ '───────────────────────',
144
+ ' 開き直すと、こうなります',
145
+ '───────────────────────',
146
+ '',
147
+ '■ 道具が34個 使えるようになります。よく使うのはこの4つです。',
148
+ '',
149
+ ' 「このファイルを検査して」',
150
+ ' 外へ出してはいけない固有名や内部メモが入っていないかを調べます。',
151
+ ' ★中身をAIに読ませずに、有無だけ返します。',
152
+ '',
153
+ ' 「これを覚えておいて」',
154
+ ' 気づいたこと・失敗・学びを1行だけ残します。次の窓でも残ります。',
155
+ '',
156
+ ' 「番頭の棚を開いて」',
157
+ ' 使えるサプリの一覧がブラウザで開きます。入れる/外すを選べます。',
158
+ '',
159
+ ' 「自己点検して」',
160
+ ' ちゃんと入っているか、鍵の残りがどれだけかが出ます。',
161
+ '',
162
+ '■ そのあと「番頭の核を取ってきて」と言ってください。',
163
+ ' 作法・記憶・検査の決まりごとが入ります。',
164
+ ' ★既にお名前と持ち場が決まっている番頭には、聞き直させません。',
165
+ '',
166
+ ].join('\n'));
105
167
  }
106
- if (既にある) console.log(' 控え: ' + path.basename(P) + '.bak-' + 印);
107
- console.log('\n★このあと、Antigravity を開き直してください。');
package/gate_core.js CHANGED
@@ -464,7 +464,21 @@ function remember(text, tag, sig) {
464
464
  const hint = (sigCount && sigCount >= 3)
465
465
  ? '★この型は' + sigCount + '件目です。ルールか手順に落とす番です。'
466
466
  : '';
467
- return { saved_to: f, pending: open.length, sig: sig || null, sig_count: sigCount, hint: hint };
467
+
468
+ // 🚨★**3件たまったら、番頭が本人へ声をかける**(2026-08-28)
469
+ // ★由来=「同じ型は他の方でも多い」と言えることが、この仕組みの値打ち。
470
+ // ★ところが、その値打ちを**本人に伝える場所を1つも作っていなかった**(★未配線)。
471
+ // ★★**言うのは「相談してみますか」まで。★決めるのは本人。**
472
+ // ★勝手に外へ送らない(★送るのは型キーの名前と件数だけ・書面のとおり)。
473
+ const 声かけ = (sigCount && sigCount >= 3)
474
+ ? '★同じ型を' + sigCount + '回踏んでいます。'
475
+ + '★ここは「気をつける」では止まりません。★ルールか手順に落としてください。'
476
+ + '★どう直すか迷うときは、番頭を出している側(毎月の相談会)へ持ち込めます。'
477
+ + '★同じ型は、他の方でもよく出ます。'
478
+ : null;
479
+
480
+ return { saved_to: f, pending: open.length, sig: sig || null, sig_count: sigCount,
481
+ hint: hint, '★次の一手': 声かけ || undefined };
468
482
  }
469
483
  const ENTRY_RE = new RegExp('^- \\[( |x)\\] (\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}) \\| (.*)$');
470
484
  const RULED_RE = new RegExp('^- (\\d{4}-\\d{2}-\\d{2}) 〔(.+?)〕 → (.*)$');
package/license/client.js CHANGED
@@ -243,11 +243,20 @@ function decide(c, now) {
243
243
  const HOME = process.env.BANTO_HOME || path.join(require('os').homedir(), '.banto');
244
244
  const STATS_CACHE = path.join(HOME, '.stats.json');
245
245
 
246
- /** 送ることに同意しているか(★既定オフ。ファイルが無ければオフ) */
246
+ /**
247
+ * 送ることに同意しているか(★既定オフ。ファイルが無ければオフ)
248
+ *
249
+ * 🚨★2026-08-28、いったん**既定オンへ変えて、その日のうちに戻した**。
250
+ * ★理由=**既に送った書面に「既定では送りません」と書いてある**。
251
+ * ★書面が先に出ている以上、★コードをそちらへ合わせるのが筋。
252
+ * ★★**書面と動きが食い違ったら、直すのは動きのほう**(★相手はもう読んでいる)。
253
+ * ★オンにしたい相手が出たら、★**その人の書面を先に作ってから**変える。
254
+ */
247
255
  function isSharing() {
248
256
  try { return JSON.parse(fs.readFileSync(path.join(HOME, 'share.json'), 'utf8')).stats === true; }
249
257
  catch (_) { return false; }
250
258
  }
259
+
251
260
  /** 同意を切り替える(★人が明示的に呼んだときだけ) */
252
261
  function setSharing(on) {
253
262
  try { fs.mkdirSync(HOME, { recursive: true }); } catch (_) {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@horizon_works/banto",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "AI番頭のOS。ルール・記憶・検査を1本のMCPで配ります。中身をクラウドへ渡さずに検査できます。",
5
5
  "keywords": [
6
6
  "mcp",
package/server.js CHANGED
@@ -281,7 +281,7 @@ const TOOLS = [
281
281
  {
282
282
  name: 'share_stats',
283
283
  description:
284
- '★型キーの統計を、番頭を作った側(ホライズンワークス)へ送るかどうかを決めます。**既定は送りません**。' +
284
+ '★型キーの統計を、番頭を出している側へ送るかどうかを決めます。**既定は送りません**。' +
285
285
  '送るのは「型キーの名前と件数」だけで、記録の本文・顧客名・文章は一切送りません。' +
286
286
  '★呼ぶと、いま実際に送られる中身がそのまま見えます。いつでも止められます。',
287
287
  inputSchema: {