@unson/brainbase-mcp 0.1.0 → 0.2.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.
- package/README.md +70 -15
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +200 -37
- package/dist/connected-onboarding.d.ts +123 -0
- package/dist/connected-onboarding.js +934 -0
- package/dist/guided-onboarding.d.ts +1 -1
- package/dist/guided-onboarding.js +78 -39
- package/dist/import-extract.d.ts +1 -0
- package/dist/import-extract.js +30 -8
- package/dist/judgment-host.d.ts +205 -0
- package/dist/judgment-host.js +1474 -0
- package/dist/onboarding.d.ts +8 -2
- package/dist/onboarding.js +72 -44
- package/dist/operationalization.js +36 -33
- package/dist/server.d.ts +295 -3
- package/dist/server.js +334 -4
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +42 -8
- package/dist/ssot.d.ts +9 -0
- package/dist/ssot.js +66 -6
- package/dist/tools.js +16 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
# Brainbase
|
|
1
|
+
# Brainbase 個人オンボーディングキット
|
|
2
2
|
|
|
3
|
-
Brainbase
|
|
3
|
+
Brainbaseは、自分が承認した仕事の前提をCodex、Claude Code、CodeCodeへ渡すための、ローカル優先のMCPサーバーです。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
最初の目標は情報源をすべて接続することではありません。自分、仕事、関係者、判断基準の最小文脈を保存し、10分以内に「同じ前提を説明し直さず役立つ出力」を確認することです。
|
|
6
6
|
|
|
7
|
-
Ontology 1.0.0
|
|
7
|
+
Ontology 1.0.0は、ローカルファイルへ持ち運べる意味契約を追加します。ホスト型Brainbaseを必要とせず、型、関係語彙、検証制約、決定論的な判断推論、バージョン移行を定義します。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
このリポジトリに、社内BrainbaseのUI、セッション実行基盤、xterm転送、ワークフロー管制、SNS運用、ホスト型バックエンド、Infisical設定、雲孫の社内データは含みません。それらは社内版`brainbase-unson`の範囲です。
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## マニュアル
|
|
12
12
|
|
|
13
13
|
Read the public onboarding manual at [brainbase.pages.dev](https://brainbase.pages.dev/). It guides users through five phases: choose one real use case, register approved work context, prove the first value, add only necessary sources, and operationalize Skills, routines, and MCP.
|
|
14
14
|
|
|
15
|
+
For the shortest safe path, open [10分で試す](https://brainbase.pages.dev/guide/quick-start). It keeps the first prompt, MCP setup, optional Judgment Host setup, verification, and interruption recovery in one resumable checklist.
|
|
16
|
+
|
|
15
17
|
The manual is the best starting point for first-time users. It explains Brainbase concepts, the first onboarding flow, MCP registration, project context setup, source onboarding, daily routines, and CLI reference.
|
|
16
18
|
|
|
17
|
-
##
|
|
19
|
+
## エージェントと始める
|
|
18
20
|
|
|
19
|
-
Brainbase
|
|
21
|
+
BrainbaseはCodex、Claude Code、CodeCodeから導入できます。最初に目指すのは接続設定ではなく、自分の文脈を使った役立つ出力です。
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
24
|
npm install
|
|
@@ -24,9 +26,21 @@ npm run build
|
|
|
24
26
|
npm run onboard:start -- --target codex
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
`onboard:start
|
|
29
|
+
`onboard:start`は日本語の初回導入コマンドです。最小ディレクトリだけを作り、本人、プロジェクト、関係者、判断、メール、カレンダー、ドライブ、タスクの事実は、利用者が承認するまで保存しません。通常表示は次の一手だけに絞り、全項目は`--details`で確認できます。
|
|
30
|
+
|
|
31
|
+
公開CLIをインストール済みなら、次の5ステップです。
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
```bash
|
|
34
|
+
brainbase onboard:start --target codex
|
|
35
|
+
# 表示された onboard:seed を確認して実行
|
|
36
|
+
brainbase onboard:install --target codex --dry-run
|
|
37
|
+
# 設定を承認・反映し、Codexを再起動
|
|
38
|
+
# 新しいCodexでBrainbaseのget_context/searchを使って実際の依頼を試す
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
リポジトリをcloneした場合も、`npm run onboard:start -- --target codex`から同じ順序で進めます。
|
|
42
|
+
|
|
43
|
+
利用者がBrainbaseの導入を依頼したら、エージェントはチェックリストを返すだけでなく、この公開CLIを実行します。承認された最小文脈を保存し、MCP設定を反映した新しい実エージェントで`get_context`と`search`を使って現実の依頼へ回答します。その実回答を見た本人が「役立った」と判断して初めて初回価値です。`ready: true`、`cli_sample_ready`、CLIの処理時間、合成ペルソナ評価、Skillsやルーティンの生成、`onboard:install --dry-run`だけでは導入完了ではありません。
|
|
30
44
|
|
|
31
45
|
For a Google Workspace / Google Drive / local-notes setup, pass the known answers and let the command surface what still needs approval:
|
|
32
46
|
|
|
@@ -71,21 +85,22 @@ brainbase onboard:seed \
|
|
|
71
85
|
--relationship "Key Partner|collaborator|Context you want AI tools to remember"
|
|
72
86
|
```
|
|
73
87
|
|
|
74
|
-
|
|
88
|
+
Optionally preview the saved context locally. This is not an onboarding completion signal:
|
|
75
89
|
|
|
76
90
|
```bash
|
|
77
91
|
brainbase onboard:demo --scenario "Draft the first note I should send to Key Partner about Current project"
|
|
78
92
|
```
|
|
79
93
|
|
|
80
|
-
`onboard:demo` reads only locally saved, approved facts. It does not call an LLM,
|
|
94
|
+
`onboard:demo` reads only locally saved, approved facts. It does not call an LLM, an agent, or a hosted backend. Its result is only a preview. Continue through MCP installation, restart the selected agent, make a real request using `get_context` and `search`, and ask the user whether that actual answer was useful.
|
|
81
95
|
|
|
82
|
-
After the demo, keep onboarding open until the
|
|
96
|
+
After the demo, keep onboarding open: the preview is not the first-value gate. Continue until a real agent uses Brainbase and the human user confirms that the result was useful.
|
|
97
|
+
|
|
98
|
+
After seed, install and verify MCP before asking for the human value judgment:
|
|
83
99
|
|
|
84
100
|
```bash
|
|
85
|
-
brainbase onboard:skills --target codex
|
|
86
|
-
brainbase onboard:routines --target codex --cwd /path/to/brainbase
|
|
87
101
|
brainbase onboard:install --target codex --dry-run
|
|
88
102
|
brainbase doctor
|
|
103
|
+
# restart Codex, use get_context/search for the real request, then ask whether it was useful
|
|
89
104
|
```
|
|
90
105
|
|
|
91
106
|
The recommended order is public skills, `ohayo` / `oyasumi` / `retro` routines registered paused or confirmation-gated, real MCP config merge after approving the dry-run snippet, source allowlist / import / candidate review decisions, then `doctor` plus MCP `get_context` / `search` verification from a fresh agent session.
|
|
@@ -354,6 +369,7 @@ brainbase onboard:routines --target codex --cwd /path/to/brainbase
|
|
|
354
369
|
brainbase onboard:skills --target codex
|
|
355
370
|
brainbase ontology:show
|
|
356
371
|
brainbase ontology:audit
|
|
372
|
+
brainbase judgment:install --target codex --dry-run
|
|
357
373
|
brainbase doctor
|
|
358
374
|
```
|
|
359
375
|
|
|
@@ -373,6 +389,7 @@ node dist/cli.js onboard:apply --from <candidate-file> --select <id> --write
|
|
|
373
389
|
node dist/cli.js onboard:routines --target codex --cwd "$(pwd)"
|
|
374
390
|
node dist/cli.js onboard:skills --target codex
|
|
375
391
|
node dist/cli.js onboard:recommend --email gmail --calendar google-calendar --drive google-drive --tasks notion
|
|
392
|
+
node dist/cli.js judgment:install --target codex --dry-run
|
|
376
393
|
npm run onboard:init
|
|
377
394
|
npm run onboard:seed -- --name "Your Name"
|
|
378
395
|
npm run onboard:install -- --target codex --dry-run
|
|
@@ -390,6 +407,44 @@ brainbase onboard:seed \
|
|
|
390
407
|
--relationship "Key Partner|collaborator|Works with me on AI adoption"
|
|
391
408
|
```
|
|
392
409
|
|
|
410
|
+
## Judgment Resolver Host for Codex
|
|
411
|
+
|
|
412
|
+
Brainbase includes a local Judgment Resolver core and a Codex lifecycle Host adapter. The Host starts one portable episode at `UserPromptSubmit`, appends ordered tool events at `PostToolUse`, and finalizes the same episode at `Stop`. It builds one canonical context, adopts exactly one receipt for the turn, and gives the model only the selected judgment nodes and audit contract to follow. The full route receipt remains in the local journal instead of being injected into model context. It does not call a hosted Brainbase service, require a secret, or check project access.
|
|
413
|
+
|
|
414
|
+
Preview the Codex `UserPromptSubmit`, `PostToolUse`, and `Stop` hook snippet:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
brainbase judgment:install --target codex --dry-run
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Review and merge all three printed event bindings into `~/.codex/hooks.json`. The command is preview-only unless `--output` is provided; it never merges into or overwrites an existing config. To save a new snippet file before reviewing it:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
brainbase judgment:install --target codex --output /tmp/brainbase-judgment-hooks.json
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Preserve unrelated hooks, then verify the installed bindings and start a new Codex task:
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
brainbase doctor --dir ~/.brainbase/personal-os --judgment-hooks ~/.codex/hooks.json
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
After installation, the Host instructs the AI to begin every user-facing response with an exact owner-visible audit line such as:
|
|
433
|
+
|
|
434
|
+
```text
|
|
435
|
+
🧠 判断参照: 直前の「ログイン後の白画面を直して」を参照 → 実装依頼として継続 ✓
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
The line identifies the concrete current or prior user statement used as judgment evidence and the decision made from it. The excerpt is collapsed to one line, limited to 26 Unicode characters, and redacts secret-like assignments and token formats. For example, a question may show `「この仕組みを説明して」を参照 → 質問として回答 ✓`; an unresolved follow-up shows `⚠️ 判断参照: 「それでいい」の対象を特定できず → 確認質問` instead of looking like a successful resolution.
|
|
439
|
+
|
|
440
|
+
Judgment evidence and knowledge-call evidence are separate. A `🧠 判断参照` line says which request the Resolver judged; it never proves that an MCP lookup happened. The Host emits `📚` only after an actual successful portable MCP call and emits `⚠️` for `isError`, malformed, or empty CallToolResult envelopes. The portable mappings are `get_context` → routing (`Brainbase参照先`), `search` → search (`Brainbase検索`), and `search_personal_kg` → retrieval (`Brainbase取得`). Source selection and exclusions appear only when the tool result contains them. When a turn requires no knowledge lookup and none occurred, the exact audit is `📚 Brainbase未参照: 必須参照なし・実呼び出し0回 ✓`; a knowledge-required turn cannot use that line to satisfy retrieval.
|
|
441
|
+
|
|
442
|
+
Detailed receipts, ordered tool events, the final event snapshot, and the exact owner-visible line are journaled together under `~/.brainbase/personal-os/judgment-journal/`, keyed by session and turn. Replayed tool events with the same `tool_use_id` and content are idempotent; conflicting reuse, corrupt journals, and a `Stop` without a matching active episode fail loudly. Only one receipt is adopted for a turn, and later duplicate hook calls reuse the stored line instead of rendering a possibly different summary. The line reports Resolver judgment evidence; it does not claim that Personal OS knowledge was already retrieved.
|
|
443
|
+
|
|
444
|
+
At `Stop`, the Host verifies that all expected audit lines appear at the beginning, exactly once, and in journal order. The first repairable failure returns one bounded block instruction and binds the non-audit answer body by digest. If the active repair changes that body or still omits the required audit contract, the hook exits nonzero with `judgment_stop_repair_exhausted` instead of completing the episode.
|
|
445
|
+
|
|
446
|
+
Every turn is judged, including questions and follow-up instructions. If a follow-up has no usable referent, the receipt selects clarification and the AI asks what the user meant; it does not refuse merely because classification or project context is incomplete. A receipt is judgment evidence, not permission to write files, send messages, deploy, purchase, or perform any other external effect. Normal host permissions and user approvals still apply.
|
|
447
|
+
|
|
393
448
|
## Install MCP Config
|
|
394
449
|
|
|
395
450
|
Dry-run output:
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
interface CliIo {
|
|
3
|
+
stdin?: AsyncIterable<string | Uint8Array>;
|
|
3
4
|
stdout?: {
|
|
4
5
|
write(chunk: string): unknown;
|
|
5
6
|
};
|
|
@@ -8,4 +9,5 @@ interface CliIo {
|
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
export declare function runCli(argv?: string[], io?: CliIo): Promise<number>;
|
|
12
|
+
export declare function isCliEntrypoint(moduleUrl: string, invokedPath: string | undefined): boolean;
|
|
11
13
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { constants } from 'node:fs';
|
|
2
|
+
import { constants, realpathSync } from 'node:fs';
|
|
3
3
|
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { delimiter, dirname, isAbsolute, join } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -8,15 +8,20 @@ import { resolveDataDir } from './paths.js';
|
|
|
8
8
|
import { auditPersonalOsDirectory } from './ontology-ssot.js';
|
|
9
9
|
import { portableOntology, resolveOntologyVersion } from './ontology.js';
|
|
10
10
|
import { onboardingStatus } from './tools.js';
|
|
11
|
-
import { buildCandidateDrafts, parseOnboardingFormat, renderAgentProtocol, renderCandidateDrafts, renderConnectorRecommendations, renderLocalOnboardingPlan, renderSourceDiagnosis, renderValueDemo } from './onboarding.js';
|
|
11
|
+
import { buildCandidateDrafts, buildValueDemo, parseOnboardingFormat, renderAgentProtocol, renderCandidateDrafts, renderConnectorRecommendations, renderLocalOnboardingPlan, renderSourceDiagnosis, renderValueDemo } from './onboarding.js';
|
|
12
12
|
import { buildExtractedCandidateSet, extractCandidates, loadApplyCandidates, normalizeSource, parseProvider, planApply, renderSourceJsonl, sourcePathFor } from './import-extract.js';
|
|
13
13
|
import { buildClaudeScheduledTasks, buildRoutines, parseRoutineKinds, parseRoutineTarget, renderCodexAutomations } from './routines.js';
|
|
14
14
|
import { assertPublicSafeSkillBundle, buildSkillBundle, parseSkillIds, parseSkillTarget, renderSkillsMarkdown } from './skills.js';
|
|
15
15
|
import { buildProjectRegistrationPlan, parseProjectSource, parseProjectStakeholder, renderProjectRegistrationMarkdown } from './projects.js';
|
|
16
16
|
import { renderGuidedFirstRun } from './guided-onboarding.js';
|
|
17
|
+
import { blockedJudgmentOutput, processJudgmentHook } from './judgment-host.js';
|
|
17
18
|
export async function runCli(argv = process.argv.slice(2), io = process) {
|
|
18
19
|
const parsed = parseArgs(argv);
|
|
19
20
|
try {
|
|
21
|
+
if (parsed.flags.has('help')) {
|
|
22
|
+
write(io, usage());
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
20
25
|
switch (parsed.command) {
|
|
21
26
|
case 'onboard:init':
|
|
22
27
|
return await onboardInit(parsed, io);
|
|
@@ -55,6 +60,10 @@ export async function runCli(argv = process.argv.slice(2), io = process) {
|
|
|
55
60
|
return 0;
|
|
56
61
|
case 'ontology:audit':
|
|
57
62
|
return await ontologyAudit(parsed, io);
|
|
63
|
+
case 'judgment:hook':
|
|
64
|
+
return await judgmentHook(io);
|
|
65
|
+
case 'judgment:install':
|
|
66
|
+
return await judgmentInstall(parsed, io);
|
|
58
67
|
case 'doctor':
|
|
59
68
|
return await doctor(parsed, io);
|
|
60
69
|
case 'mcp':
|
|
@@ -70,6 +79,59 @@ export async function runCli(argv = process.argv.slice(2), io = process) {
|
|
|
70
79
|
return 1;
|
|
71
80
|
}
|
|
72
81
|
}
|
|
82
|
+
async function judgmentHook(io) {
|
|
83
|
+
let payload = {};
|
|
84
|
+
try {
|
|
85
|
+
const input = await readHookStdin(io.stdin ?? process.stdin);
|
|
86
|
+
payload = JSON.parse(input || '{}');
|
|
87
|
+
write(io, `${JSON.stringify(await processJudgmentHook(payload))}\n`);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
91
|
+
const eventName = payload.hook_event_name ?? payload.hookEventName;
|
|
92
|
+
if (eventName === 'Stop' && payload.stop_hook_active === true) {
|
|
93
|
+
writeError(io, `${reason}\n`);
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
write(io, `${JSON.stringify(blockedJudgmentOutput(reason))}\n`);
|
|
97
|
+
}
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
async function judgmentInstall(parsed, io) {
|
|
101
|
+
const target = first(parsed, 'target');
|
|
102
|
+
if (target !== 'codex')
|
|
103
|
+
throw new Error('judgment:install currently requires --target codex');
|
|
104
|
+
const cliPath = fileURLToPath(new URL('./cli.js', import.meta.url));
|
|
105
|
+
const hook = {
|
|
106
|
+
hooks: [{
|
|
107
|
+
type: 'command',
|
|
108
|
+
command: `${JSON.stringify(process.execPath)} ${JSON.stringify(cliPath)} judgment:hook`,
|
|
109
|
+
statusMessage: 'brainbase judgment resolver'
|
|
110
|
+
}]
|
|
111
|
+
};
|
|
112
|
+
const payload = `${JSON.stringify({
|
|
113
|
+
hooks: {
|
|
114
|
+
UserPromptSubmit: [hook],
|
|
115
|
+
PostToolUse: [hook],
|
|
116
|
+
Stop: [hook]
|
|
117
|
+
}
|
|
118
|
+
}, null, 2)}\n`;
|
|
119
|
+
const outputPath = first(parsed, 'output');
|
|
120
|
+
if (parsed.flags.has('dry-run') || !outputPath) {
|
|
121
|
+
write(io, payload);
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
125
|
+
await writeConfigSnippet(outputPath, payload);
|
|
126
|
+
write(io, `Wrote Codex Judgment Host config snippet to ${outputPath}\n`);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
async function readHookStdin(input) {
|
|
130
|
+
let text = '';
|
|
131
|
+
for await (const chunk of input)
|
|
132
|
+
text += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');
|
|
133
|
+
return text;
|
|
134
|
+
}
|
|
73
135
|
async function onboardInit(parsed, io) {
|
|
74
136
|
const dataDir = resolveDataDir(first(parsed, 'dir'));
|
|
75
137
|
await initializePersonalOs(dataDir);
|
|
@@ -78,8 +140,21 @@ async function onboardInit(parsed, io) {
|
|
|
78
140
|
}
|
|
79
141
|
async function onboardSeed(parsed, io) {
|
|
80
142
|
const dataDir = resolveDataDir(first(parsed, 'dir'));
|
|
81
|
-
await initializePersonalOs(dataDir);
|
|
82
143
|
const name = first(parsed, 'name');
|
|
144
|
+
const encodedRelationships = parsed.values.get('relationship') ?? [];
|
|
145
|
+
for (const encoded of encodedRelationships) {
|
|
146
|
+
const [person, , context] = encoded.split('|').map((part) => part.trim());
|
|
147
|
+
if (!person || !context) {
|
|
148
|
+
throw new Error([
|
|
149
|
+
'関係者の形式が正しくありません。',
|
|
150
|
+
'形式: "人|役割|覚えておく文脈"',
|
|
151
|
+
'例: "田中|責任者|Atlas導入の最終判断を担当"',
|
|
152
|
+
'保存内容は変更していません。',
|
|
153
|
+
`再実行: ${seedRetryCommand(parsed, dataDir, encoded)}`
|
|
154
|
+
].join('\n'));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
await initializePersonalOs(dataDir);
|
|
83
158
|
const hasSeedValues = Boolean(name)
|
|
84
159
|
|| ['value', 'project', 'decision-principle', 'relationship'].some((key) => (parsed.values.get(key)?.length ?? 0) > 0);
|
|
85
160
|
if (!parsed.flags.has('non-interactive') && !hasSeedValues) {
|
|
@@ -88,8 +163,8 @@ async function onboardSeed(parsed, io) {
|
|
|
88
163
|
}
|
|
89
164
|
await mutatePersonalOs(dataDir, (os) => {
|
|
90
165
|
const now = new Date().toISOString();
|
|
91
|
-
const personalEntries = [];
|
|
92
|
-
const decisions = [];
|
|
166
|
+
const personalEntries = [...os.personalKg];
|
|
167
|
+
const decisions = [...os.decisions];
|
|
93
168
|
const relationships = [...os.relationships.relationships];
|
|
94
169
|
const graphEntities = [...os.graph.entities];
|
|
95
170
|
if (name) {
|
|
@@ -98,19 +173,19 @@ async function onboardSeed(parsed, io) {
|
|
|
98
173
|
id: 'self',
|
|
99
174
|
type: 'person',
|
|
100
175
|
name,
|
|
101
|
-
summary: '
|
|
176
|
+
summary: 'このPersonal OSの本人。',
|
|
102
177
|
tags: ['self']
|
|
103
178
|
});
|
|
104
|
-
personalEntries
|
|
105
|
-
id: `self-${
|
|
179
|
+
upsertById(personalEntries, {
|
|
180
|
+
id: `self-${hash(name)}`,
|
|
106
181
|
type: 'self',
|
|
107
|
-
text:
|
|
182
|
+
text: `私は${name}です。`,
|
|
108
183
|
tags: ['self'],
|
|
109
184
|
updatedAt: now
|
|
110
185
|
});
|
|
111
186
|
}
|
|
112
187
|
for (const value of parsed.values.get('value') ?? []) {
|
|
113
|
-
personalEntries
|
|
188
|
+
upsertById(personalEntries, {
|
|
114
189
|
id: `value-${hash(value)}`,
|
|
115
190
|
type: 'value',
|
|
116
191
|
text: value,
|
|
@@ -123,10 +198,10 @@ async function onboardSeed(parsed, io) {
|
|
|
123
198
|
id: `project-${hash(project)}`,
|
|
124
199
|
type: 'project',
|
|
125
200
|
name: project,
|
|
126
|
-
summary: '
|
|
201
|
+
summary: '現在取り組んでいるプロジェクト。',
|
|
127
202
|
tags: ['work']
|
|
128
203
|
});
|
|
129
|
-
personalEntries
|
|
204
|
+
upsertById(personalEntries, {
|
|
130
205
|
id: `work-${hash(project)}`,
|
|
131
206
|
type: 'work',
|
|
132
207
|
text: project,
|
|
@@ -135,20 +210,17 @@ async function onboardSeed(parsed, io) {
|
|
|
135
210
|
});
|
|
136
211
|
}
|
|
137
212
|
for (const value of parsed.values.get('decision-principle') ?? []) {
|
|
138
|
-
decisions
|
|
213
|
+
upsertById(decisions, {
|
|
139
214
|
id: `decision-${hash(value)}`,
|
|
140
|
-
title: '
|
|
215
|
+
title: 'オンボーディングで登録した判断基準',
|
|
141
216
|
decision: value,
|
|
142
217
|
tags: ['principle', 'onboarding'],
|
|
143
218
|
updatedAt: now
|
|
144
219
|
});
|
|
145
220
|
}
|
|
146
|
-
for (const encoded of
|
|
221
|
+
for (const encoded of encodedRelationships) {
|
|
147
222
|
const [person, role, context] = encoded.split('|').map((part) => part.trim());
|
|
148
|
-
|
|
149
|
-
throw new Error('relationship must be "person|role|context" or "person||context"');
|
|
150
|
-
}
|
|
151
|
-
relationships.push({
|
|
223
|
+
upsertById(relationships, {
|
|
152
224
|
id: `relationship-${hash(encoded)}`,
|
|
153
225
|
person,
|
|
154
226
|
role: role || undefined,
|
|
@@ -167,13 +239,47 @@ async function onboardSeed(parsed, io) {
|
|
|
167
239
|
return proposedPersonalOs(os, {
|
|
168
240
|
graph: { ...os.graph, entities: graphEntities },
|
|
169
241
|
relationships: { version: 1, relationships },
|
|
170
|
-
personalKg:
|
|
171
|
-
decisions
|
|
242
|
+
personalKg: personalEntries,
|
|
243
|
+
decisions
|
|
172
244
|
});
|
|
173
245
|
});
|
|
174
|
-
|
|
246
|
+
const summary = [
|
|
247
|
+
`Brainbaseへ保存しました: ${dataDir}`,
|
|
248
|
+
...(name ? [`- 本人: ${name}`] : []),
|
|
249
|
+
...(parsed.values.get('value') ?? []).map((value) => `- 価値観: ${value}`),
|
|
250
|
+
...(parsed.values.get('project') ?? []).map((project) => `- プロジェクト: ${project}`),
|
|
251
|
+
...encodedRelationships.map((encoded) => {
|
|
252
|
+
const [person, role] = encoded.split('|').map((part) => part.trim());
|
|
253
|
+
return `- 関係者: ${person}${role ? `(${role})` : ''}`;
|
|
254
|
+
}),
|
|
255
|
+
...(parsed.values.get('decision-principle') ?? []).map((value) => `- 判断基準: ${value}`),
|
|
256
|
+
'- 同じ文脈は更新しました。既存の別データは削除していません。',
|
|
257
|
+
'',
|
|
258
|
+
'次に実行:',
|
|
259
|
+
`brainbase onboard:install --target codex --dir ${shellArg(dataDir)} --dry-run`,
|
|
260
|
+
'設定を承認・反映してエージェントを再起動した後、実際の依頼でBrainbaseのget_contextとsearchを使います。',
|
|
261
|
+
''
|
|
262
|
+
];
|
|
263
|
+
write(io, summary.join('\n'));
|
|
175
264
|
return 0;
|
|
176
265
|
}
|
|
266
|
+
function seedRetryCommand(parsed, dataDir, invalidRelationship) {
|
|
267
|
+
const args = ['brainbase', 'onboard:seed', '--dir', dataDir];
|
|
268
|
+
const append = (flag, value) => {
|
|
269
|
+
if (value)
|
|
270
|
+
args.push(flag, value);
|
|
271
|
+
};
|
|
272
|
+
append('--name', first(parsed, 'name'));
|
|
273
|
+
append('--value', first(parsed, 'value'));
|
|
274
|
+
append('--project', first(parsed, 'project'));
|
|
275
|
+
append('--decision-principle', first(parsed, 'decision-principle'));
|
|
276
|
+
for (const relationship of parsed.values.get('relationship') ?? []) {
|
|
277
|
+
if (relationship !== invalidRelationship)
|
|
278
|
+
append('--relationship', relationship);
|
|
279
|
+
}
|
|
280
|
+
append('--relationship', '田中|責任者|Atlas導入の最終判断を担当');
|
|
281
|
+
return args.map(shellArg).join(' ');
|
|
282
|
+
}
|
|
177
283
|
async function onboardInstall(parsed, io) {
|
|
178
284
|
const target = first(parsed, 'target');
|
|
179
285
|
if (!isInstallTarget(target)) {
|
|
@@ -239,9 +345,9 @@ async function onboardStart(parsed, io) {
|
|
|
239
345
|
assumeGog: parsed.flags.has('assume-gog'),
|
|
240
346
|
gogAvailable,
|
|
241
347
|
project,
|
|
242
|
-
connected: status
|
|
348
|
+
connected: readLocalBackendConnected(status),
|
|
243
349
|
missing
|
|
244
|
-
}, format));
|
|
350
|
+
}, format, parsed.flags.has('details')));
|
|
245
351
|
return 0;
|
|
246
352
|
}
|
|
247
353
|
async function onboardDemo(parsed, io) {
|
|
@@ -249,8 +355,9 @@ async function onboardDemo(parsed, io) {
|
|
|
249
355
|
const dataDir = resolveDataDir(first(parsed, 'dir'));
|
|
250
356
|
await initializePersonalOs(dataDir);
|
|
251
357
|
const os = await loadPersonalOs(dataDir);
|
|
252
|
-
|
|
253
|
-
|
|
358
|
+
const demo = buildValueDemo({ os, scenario: first(parsed, 'scenario') });
|
|
359
|
+
write(io, renderValueDemo({ os, scenario: first(parsed, 'scenario') }, format, parsed.flags.has('details')));
|
|
360
|
+
return demo.ready ? 0 : 1;
|
|
254
361
|
}
|
|
255
362
|
async function onboardRecommend(parsed, io) {
|
|
256
363
|
const format = parseOnboardingFormat(first(parsed, 'format'));
|
|
@@ -713,11 +820,35 @@ async function writeConfigSnippet(outputPath, payload) {
|
|
|
713
820
|
async function doctor(parsed, io) {
|
|
714
821
|
const dataDir = resolveDataDir(first(parsed, 'dir'));
|
|
715
822
|
const os = await loadPersonalOs(dataDir);
|
|
716
|
-
|
|
823
|
+
const status = onboardingStatus(os);
|
|
824
|
+
const judgmentHooksPath = first(parsed, 'judgment-hooks');
|
|
825
|
+
if (!judgmentHooksPath) {
|
|
826
|
+
write(io, `${JSON.stringify(status, null, 2)}\n`);
|
|
827
|
+
return 0;
|
|
828
|
+
}
|
|
829
|
+
const config = JSON.parse(await readFile(judgmentHooksPath, 'utf8'));
|
|
830
|
+
const hooks = config.hooks;
|
|
831
|
+
const requiredEvents = ['UserPromptSubmit', 'PostToolUse', 'Stop'];
|
|
832
|
+
const valid = Boolean(hooks) && requiredEvents.every((eventName) => {
|
|
833
|
+
const bindings = hooks?.[eventName];
|
|
834
|
+
return Array.isArray(bindings) && bindings.some((binding) => {
|
|
835
|
+
const commands = binding.hooks;
|
|
836
|
+
return Array.isArray(commands) && commands.some((command) => (typeof command.command === 'string'
|
|
837
|
+
&& command.command.includes('judgment:hook')));
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
if (!valid)
|
|
841
|
+
throw new Error('judgment_hooks_invalid');
|
|
842
|
+
write(io, `${JSON.stringify({
|
|
843
|
+
...status,
|
|
844
|
+
judgment_hooks: { status: 'ready', events: requiredEvents, source: judgmentHooksPath }
|
|
845
|
+
}, null, 2)}\n`);
|
|
717
846
|
return 0;
|
|
718
847
|
}
|
|
719
848
|
function parseArgs(argv) {
|
|
720
|
-
const [
|
|
849
|
+
const [firstToken, ...remaining] = argv;
|
|
850
|
+
const command = firstToken?.startsWith('--') ? undefined : firstToken;
|
|
851
|
+
const rest = command ? remaining : argv;
|
|
721
852
|
const values = new Map();
|
|
722
853
|
const flags = new Set();
|
|
723
854
|
for (let index = 0; index < rest.length; index += 1) {
|
|
@@ -743,6 +874,13 @@ function parseArgs(argv) {
|
|
|
743
874
|
function first(parsed, key) {
|
|
744
875
|
return parsed.values.get(key)?.[0];
|
|
745
876
|
}
|
|
877
|
+
function readLocalBackendConnected(status) {
|
|
878
|
+
const localBackend = status.localBackend;
|
|
879
|
+
return typeof localBackend === 'object'
|
|
880
|
+
&& localBackend !== null
|
|
881
|
+
&& 'connected' in localBackend
|
|
882
|
+
&& localBackend.connected === true;
|
|
883
|
+
}
|
|
746
884
|
async function commandExists(command) {
|
|
747
885
|
const candidates = isAbsolute(command)
|
|
748
886
|
? [command]
|
|
@@ -758,15 +896,18 @@ async function commandExists(command) {
|
|
|
758
896
|
}
|
|
759
897
|
return false;
|
|
760
898
|
}
|
|
761
|
-
function
|
|
762
|
-
const index =
|
|
899
|
+
function upsertById(entries, entry) {
|
|
900
|
+
const index = entries.findIndex((candidate) => candidate.id === entry.id);
|
|
763
901
|
if (index >= 0) {
|
|
764
|
-
|
|
902
|
+
entries[index] = entry;
|
|
765
903
|
}
|
|
766
904
|
else {
|
|
767
|
-
|
|
905
|
+
entries.push(entry);
|
|
768
906
|
}
|
|
769
907
|
}
|
|
908
|
+
function upsertGraphEntity(entities, entity) {
|
|
909
|
+
upsertById(entities, entity);
|
|
910
|
+
}
|
|
770
911
|
function hash(value) {
|
|
771
912
|
let hashValue = 0;
|
|
772
913
|
for (const char of value) {
|
|
@@ -794,15 +935,22 @@ function proposedPersonalOs(os, proposed) {
|
|
|
794
935
|
return { ...os, ...proposed };
|
|
795
936
|
}
|
|
796
937
|
function usage() {
|
|
797
|
-
return
|
|
938
|
+
return `初回価値まで(5ステップ):
|
|
939
|
+
1. brainbase onboard:start --target codex
|
|
940
|
+
2. 表示された brainbase onboard:seed を確認して実行
|
|
941
|
+
3. brainbase onboard:install --target codex --dry-run
|
|
942
|
+
4. 設定を承認・反映し、Codexを再起動
|
|
943
|
+
5. 新しいエージェントで実際の依頼を送り、役立ったか本人が判断
|
|
944
|
+
|
|
945
|
+
使い方:
|
|
798
946
|
brainbase-mcp
|
|
799
947
|
brainbase mcp
|
|
800
948
|
brainbase onboard:init [--dir path]
|
|
801
949
|
brainbase onboard:seed [--dir path] [--name value] [--value value] [--project value] [--decision-principle value] [--relationship "person|role|context"]
|
|
802
950
|
brainbase onboard:install --target codex|claude|codecode [--dir path] [--dry-run] [--output path]
|
|
803
951
|
brainbase onboard:agent [--format markdown|json]
|
|
804
|
-
brainbase onboard:start [--target codex|claude|codecode] [--dir path] [--name value] [--project value] [--goal value] [--status value] [--role value] [--email value] [--calendar value] [--drive value] [--drive-folder id] [--local-folder path] [--tasks value] [--format markdown|json]
|
|
805
|
-
brainbase onboard:demo [--dir path] [--scenario value] [--format markdown|json]
|
|
952
|
+
brainbase onboard:start [--target codex|claude|codecode] [--dir path] [--name value] [--value value] [--project value] [--decision-principle value] [--goal value] [--status value] [--role value] [--email value] [--calendar value] [--drive value] [--drive-folder id] [--local-folder path] [--tasks value] [--format markdown|json] [--details]
|
|
953
|
+
brainbase onboard:demo [--dir path] [--scenario value] [--format markdown|json] [--details]
|
|
806
954
|
brainbase onboard:recommend [--email value] [--calendar value] [--drive value] [--tasks value] [--format markdown|json]
|
|
807
955
|
brainbase onboard:diagnose-sources [--dir path] [--email value] [--calendar value] [--drive value] [--drive-folder id] [--tasks value] [--assume-gog] [--gog-command command] [--format markdown|json]
|
|
808
956
|
brainbase onboard:plan [--profile google-workspace-local] [--host value] [--email value] [--secondary-email value] [--calendar value] [--drive value] [--drive-folder id] [--local-folder path] [--tasks value] [--inactive-task-tool value] [--format markdown|json]
|
|
@@ -815,10 +963,25 @@ function usage() {
|
|
|
815
963
|
brainbase onboard:skills --target codex|claude|portable [--skills id,id] [--out dir] [--format markdown|json]
|
|
816
964
|
brainbase ontology:show
|
|
817
965
|
brainbase ontology:audit [--dir path] [--ontology-version 0.0.0|1.0.0]
|
|
818
|
-
brainbase
|
|
966
|
+
brainbase judgment:install --target codex [--dry-run] [--output path]
|
|
967
|
+
brainbase judgment:hook
|
|
968
|
+
brainbase doctor [--dir path] [--judgment-hooks path]
|
|
819
969
|
`;
|
|
820
970
|
}
|
|
821
|
-
|
|
971
|
+
function shellArg(value) {
|
|
972
|
+
return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : JSON.stringify(value);
|
|
973
|
+
}
|
|
974
|
+
export function isCliEntrypoint(moduleUrl, invokedPath) {
|
|
975
|
+
if (!invokedPath)
|
|
976
|
+
return false;
|
|
977
|
+
try {
|
|
978
|
+
return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(invokedPath);
|
|
979
|
+
}
|
|
980
|
+
catch {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (isCliEntrypoint(import.meta.url, process.argv[1])) {
|
|
822
985
|
const code = await runCli();
|
|
823
986
|
process.exit(code);
|
|
824
987
|
}
|