@bunshin-cc/bunshin 0.3.1 → 0.3.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 (2) hide show
  1. package/dist/bin.js +30 -2
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -4175,11 +4175,13 @@ var init_exports = {};
4175
4175
  __export(init_exports, {
4176
4176
  CODEX_HOOK_EVENTS: () => CODEX_HOOK_EVENTS,
4177
4177
  HOOK_EVENTS: () => HOOK_EVENTS,
4178
+ LEARN_COMMAND: () => LEARN_COMMAND,
4178
4179
  PRE_TOOL_MATCHER: () => PRE_TOOL_MATCHER,
4179
4180
  bunshinHookEntry: () => bunshinHookEntry,
4180
4181
  claudeSettingsPath: () => claudeSettingsPath,
4181
4182
  countBunshinHooks: () => countBunshinHooks,
4182
4183
  installClaudeHooks: () => installClaudeHooks,
4184
+ installLearnCommand: () => installLearnCommand,
4183
4185
  installMcpConfig: () => installMcpConfig,
4184
4186
  installPiExtension: () => installPiExtension,
4185
4187
  isBunshinEntry: () => isBunshinEntry,
@@ -4290,6 +4292,24 @@ function mcpLauncher(bin) {
4290
4292
  }
4291
4293
  return { command: process.execPath, args: [script, "mcp"] };
4292
4294
  }
4295
+ function installLearnCommand({ file, remove = false }) {
4296
+ try {
4297
+ const current = fileExists(file) ? fs13.readFileSync(file, "utf8") : null;
4298
+ const ours = current === null || current === LEARN_COMMAND;
4299
+ if (remove) {
4300
+ if (current !== null && ours) fs13.unlinkSync(file);
4301
+ return { file, installed: 0, changed: current !== null && ours };
4302
+ }
4303
+ if (!ours) return { file, installed: 0, changed: false, error: "a different learn.md is already there; left as it is" };
4304
+ if (current === null) {
4305
+ fs13.mkdirSync(path10.dirname(file), { recursive: true });
4306
+ fs13.writeFileSync(file, LEARN_COMMAND);
4307
+ }
4308
+ return { file, installed: 1, changed: current === null };
4309
+ } catch (err) {
4310
+ return { file, installed: 0, changed: false, error: err instanceof Error ? err.message : String(err) };
4311
+ }
4312
+ }
4293
4313
  function installMcpConfig({ file, launcher, remove = false }) {
4294
4314
  try {
4295
4315
  const before = fileExists(file) ? JSON.parse(fs13.readFileSync(file, "utf8")) : {};
@@ -4362,15 +4382,18 @@ function runInit(opts = {}) {
4362
4382
  if (hooks.error) warnings.push(`could not update ${hooks.file}: ${hooks.error}`);
4363
4383
  }
4364
4384
  let mcp;
4385
+ let learnCommand;
4365
4386
  if ((opts.removeHooks || !opts.noHooks) && config.harness === "claude-code" && scope === "repo") {
4366
4387
  mcp = installMcpConfig({ file: path10.join(cwd, ".mcp.json"), launcher: opts.mcp ?? mcpLauncher(opts.bin), remove: opts.removeHooks });
4367
4388
  if (mcp.error) warnings.push(`could not update ${mcp.file}: ${mcp.error}`);
4389
+ learnCommand = installLearnCommand({ file: path10.join(cwd, ".claude", "commands", "learn.md"), remove: opts.removeHooks });
4390
+ if (learnCommand.error) warnings.push(`/learn not installed at ${learnCommand.file}: ${learnCommand.error}`);
4368
4391
  }
4369
4392
  if (!resolveBackend(config)) {
4370
4393
  warnings.push("No model backend found. Install and sign in to Codex or Claude Code before using automatic learning.");
4371
4394
  }
4372
4395
  store.log("init", { root, scope, role: config.role, orgUnit: config.orgUnit, hooks: hooks?.installed ?? 0 });
4373
- return { root, config, created: !existed, hooks, ...mcp ? { mcp } : {}, warnings };
4396
+ return { root, config, created: !existed, hooks, ...mcp ? { mcp } : {}, ...learnCommand ? { learnCommand } : {}, warnings };
4374
4397
  }
4375
4398
  function otherHarnessInstructions() {
4376
4399
  return [
@@ -4386,7 +4409,7 @@ function otherHarnessInstructions() {
4386
4409
  " Claude Code plugin users need no settings.json wiring \u2014 the plugin ships hooks/hooks.json."
4387
4410
  ].join("\n");
4388
4411
  }
4389
- var PRE_TOOL_MATCHER, HOOK_EVENTS, CODEX_HOOK_EVENTS;
4412
+ var PRE_TOOL_MATCHER, HOOK_EVENTS, CODEX_HOOK_EVENTS, LEARN_COMMAND;
4390
4413
  var init_init = __esm({
4391
4414
  "src/init.ts"() {
4392
4415
  "use strict";
@@ -4410,6 +4433,7 @@ var init_init = __esm({
4410
4433
  { event: "PreCompact", arg: "pre-compact", timeout: 10 },
4411
4434
  { event: "SessionEnd", arg: "session-end", timeout: 3 }
4412
4435
  ];
4436
+ LEARN_COMMAND = '---\ndescription: Capture what this session learned into the Bunshin library now (applied locally as a trial, submitted for the nightly review)\nargument-hint: "[what to capture, e.g. \\"the ledger must be reconciled before closing\\"]"\nallowed-tools: Bash(bunshin learn:*)\n---\n\nCapture knowledge from this session into Bunshin.\n\nRun exactly this with the Bash tool (it takes up to two minutes; use a timeout of 180000 ms):\n\n```bash\nbunshin learn "$ARGUMENTS"\n```\n\n`bunshin learn` forks this conversation (`claude -p --resume <this session> --fork-session`),\nso the reflection sees everything you saw without re-sending the transcript. It applies what it\nfinds locally as a trial and queues it for the nightly review:\n\n- edits to this repo\'s SPEC.md / TESTING.md / SKILL.md land in the working tree (they show up\n in `git diff`);\n- knowledge with no repo home becomes a local overlay on the Bunshin library;\n- per-machine values stay private.\n\nRelay its output to the user as it is. Each line reads\n`captured: <summary> \u2192 <section or file#anchor>`. If it prints `nothing new to capture.`, say so\nin one line; that is the normal outcome. If it reports an error, show it and stop; do not retry\nin a loop, and do not edit the library files by hand.\n\nIf the user named nothing specific, run it without arguments. Do not continue with other work in\nthe same reply unless the user asked you to.\n';
4413
4437
  }
4414
4438
  });
4415
4439
 
@@ -8440,6 +8464,10 @@ Key saved privately outside the project.
8440
8464
  `);
8441
8465
  if (result.hooks?.error) throw new Error(`Connected, but hooks could not be configured: ${result.hooks.error}. Fix the file and run setup again.`);
8442
8466
  if (result.hooks) out(`${result.hooks.installed} ${harness === "pi" ? "extension" : "hooks"} configured in ${result.hooks.file}
8467
+ `);
8468
+ if (result.learnCommand?.installed) out(`/learn installed in ${result.learnCommand.file}
8469
+ `);
8470
+ else if (result.learnCommand?.error) out(`/learn not installed: ${result.learnCommand.error} (${result.learnCommand.file})
8443
8471
  `);
8444
8472
  if (result.hooks?.sharedWithWorktrees) out("Codex shares these hooks with the repository\u2019s linked worktrees. Each connected worktree keeps its own Bunshin store.\n");
8445
8473
  if (harness === "codex" && result.hooks) out("Run codex interactively in this project. Review and accept project trust, then run /hooks and review and trust the five Bunshin hooks.\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunshin-cc/bunshin",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Shared memory for company AI agents. Connect Codex, Claude Code, or Pi to Bunshin.",
5
5
  "type": "module",
6
6
  "bin": {