@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 CHANGED
@@ -1,22 +1,24 @@
1
- # Brainbase Personal Onboarding Kit
1
+ # Brainbase 個人オンボーディングキット
2
2
 
3
- Brainbase is a local-first MCP server for handing your personal source of truth to AI coding tools.
3
+ Brainbaseは、自分が承認した仕事の前提をCodex、Claude Code、CodeCodeへ渡すための、ローカル優先のMCPサーバーです。
4
4
 
5
- The v1 value is narrow by design: create a canonical local SSOT for yourself, your work, relationships, and decisions, then expose it through MCP tools that Codex, Claude, and CodeCode can call.
5
+ 最初の目標は情報源をすべて接続することではありません。自分、仕事、関係者、判断基準の最小文脈を保存し、10分以内に「同じ前提を説明し直さず役立つ出力」を確認することです。
6
6
 
7
- Ontology 1.0.0 adds a portable semantic contract on top of those local files. It defines types, relation vocabulary, validation constraints, deterministic decision inference, and version-evolution guidance without requiring a hosted Brainbase service.
7
+ Ontology 1.0.0は、ローカルファイルへ持ち運べる意味契約を追加します。ホスト型Brainbaseを必要とせず、型、関係語彙、検証制約、決定論的な判断推論、バージョン移行を定義します。
8
8
 
9
- This repository does not include the internal Brainbase UI, session runtime, xterm transport, workflow mission control, social operations, hosted backend, Infisical setup, or Unson internal data. Those belong in the internal `brainbase-unson` system.
9
+ このリポジトリに、社内BrainbaseのUI、セッション実行基盤、xterm転送、ワークフロー管制、SNS運用、ホスト型バックエンド、Infisical設定、雲孫の社内データは含みません。それらは社内版`brainbase-unson`の範囲です。
10
10
 
11
- ## Manual
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
- ## Agent-assisted Onboarding
19
+ ## エージェントと始める
18
20
 
19
- Brainbase is designed to be adopted from Codex, Claude Code, or CodeCode. The first onboarding goal is a useful answer from your own context, not connector setup.
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` is the Japanese first-run entrypoint for agent-assisted onboarding. It creates the minimum Personal OS directory, but it does not save self, project, relationship, decision, mail, calendar, drive, or task facts until the user approves them. It asks Codex or Claude Code what context you do not want to explain repeatedly, then shows the first prompt to try, the expected value, the minimum seed command, `onboard:demo`, project registration, optional source diagnosis, candidate review, MCP install, and `doctor`. The demo command appears before source diagnosis.
29
+ `onboard:start`は日本語の初回導入コマンドです。最小ディレクトリだけを作り、本人、プロジェクト、関係者、判断、メール、カレンダー、ドライブ、タスクの事実は、利用者が承認するまで保存しません。通常表示は次の一手だけに絞り、全項目は`--details`で確認できます。
30
+
31
+ 公開CLIをインストール済みなら、次の5ステップです。
28
32
 
29
- If the user says "I want to onboard Brainbase", the agent should run this flow instead of returning a checklist. The expected sequence is: build if needed, run `onboard:start`, ask for the smallest context the user wants Brainbase to remember, seed only approved facts, then run `onboard:demo` with a real request. The agent must show the prompt, sample result, what the user no longer had to explain, and the still-unfinished operationalization work. `ready: true`, `first_value_demo_ready`, generated skills, generated routines, and `onboard:install --dry-run` are not completion signals by themselves.
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
- Run the first value demo with a real request. This is the onboarding completion signal:
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, hosted backend, or raw source collector. If it returns `ready: true`, the agent still needs to show the try-this prompt, sample result, and plain-language value: the user did not have to explain the saved work premise or person context again.
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 operationalization checklist is either completed or explicitly deferred:
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: 'Owner of this local Brainbase Personal OS.',
176
+ summary: 'このPersonal OSの本人。',
102
177
  tags: ['self']
103
178
  });
104
- personalEntries.push({
105
- id: `self-${Date.now()}`,
179
+ upsertById(personalEntries, {
180
+ id: `self-${hash(name)}`,
106
181
  type: 'self',
107
- text: `I am ${name}.`,
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.push({
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: 'Seeded during Brainbase onboarding.',
201
+ summary: '現在取り組んでいるプロジェクト。',
127
202
  tags: ['work']
128
203
  });
129
- personalEntries.push({
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.push({
213
+ upsertById(decisions, {
139
214
  id: `decision-${hash(value)}`,
140
- title: 'Seeded decision principle',
215
+ title: 'オンボーディングで登録した判断基準',
141
216
  decision: value,
142
217
  tags: ['principle', 'onboarding'],
143
218
  updatedAt: now
144
219
  });
145
220
  }
146
- for (const encoded of parsed.values.get('relationship') ?? []) {
221
+ for (const encoded of encodedRelationships) {
147
222
  const [person, role, context] = encoded.split('|').map((part) => part.trim());
148
- if (!person || !context) {
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: [...os.personalKg, ...personalEntries],
171
- decisions: [...os.decisions, ...decisions]
242
+ personalKg: personalEntries,
243
+ decisions
172
244
  });
173
245
  });
174
- write(io, `Seeded Brainbase Personal OS at ${dataDir}\n`);
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.connected === true,
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
- write(io, renderValueDemo({ os, scenario: first(parsed, 'scenario') }, format));
253
- return 0;
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
- write(io, `${JSON.stringify(onboardingStatus(os), null, 2)}\n`);
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 [command, ...rest] = argv;
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 upsertGraphEntity(entities, entity) {
762
- const index = entities.findIndex((candidate) => candidate.id === entity.id);
899
+ function upsertById(entries, entry) {
900
+ const index = entries.findIndex((candidate) => candidate.id === entry.id);
763
901
  if (index >= 0) {
764
- entities[index] = entity;
902
+ entries[index] = entry;
765
903
  }
766
904
  else {
767
- entities.push(entity);
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 `Usage:
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 doctor [--dir path]
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
- if (import.meta.url === `file://${process.argv[1]}`) {
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
  }