@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 +127 -67
- package/gate_core.js +15 -1
- package/license/client.js +10 -1
- package/package.json +1 -1
- package/server.js +1 -1
package/add_to_config.js
CHANGED
|
@@ -1,107 +1,167 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
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
|
-
* ② 既にある
|
|
21
|
+
* ② 既にある mcpServers は **そのまま残す**
|
|
13
22
|
* ③ 書く前に **控えを取る**(.bak-日時)
|
|
14
23
|
* ④ 書いたあと **読み直して確かめる**(★書けたと言う前に)
|
|
24
|
+
* ⑤ ★**在る設定ファイルすべてに書く**(★どのAIで開いても繋がるように)
|
|
15
25
|
*
|
|
16
26
|
* 使い方:
|
|
17
|
-
*
|
|
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 鍵 =
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
40
|
-
path.join(process.cwd(), '
|
|
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
|
-
|
|
43
|
-
return
|
|
60
|
+
// ★在るものと、作ってよいものを返す
|
|
61
|
+
return 候補;
|
|
44
62
|
}
|
|
45
63
|
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
/** 1つの設定ファイルへ書く。★戻り値=何が起きたか */
|
|
65
|
+
function 書く(先) {
|
|
66
|
+
const P = 先.path;
|
|
67
|
+
let j = { mcpServers: {} };
|
|
68
|
+
let 既にある = false;
|
|
48
69
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
85
|
+
j.mcpServers = j.mcpServers || {};
|
|
86
|
+
const 前 = Object.keys(j.mcpServers).filter((k) => k !== 'banto');
|
|
87
|
+
j.mcpServers.banto = ブロック;
|
|
74
88
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
83
|
-
try { fs.
|
|
84
|
-
catch (e) {
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
98
|
-
console.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
console.
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
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: {
|