@bunshin-cc/bunshin 0.8.0 → 0.8.1

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 +62 -41
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1127,6 +1127,23 @@ function scopePath(tree, source) {
1127
1127
  function inBoundary(section, reader) {
1128
1128
  return !isScopeLayer(section.layer) || reader.layers.includes(section.layer);
1129
1129
  }
1130
+ function parentScope(tree, layer) {
1131
+ const k = layerKind(layer);
1132
+ if (k === "source") {
1133
+ const g = tree.sources[layerValue(layer)]?.group ?? null;
1134
+ return g && tree.groups.some((x) => x.id === g) ? groupLayer(g) : "org";
1135
+ }
1136
+ if (k === "group") {
1137
+ const g = tree.groups.find((x) => x.id === layerValue(layer));
1138
+ return g?.parent && tree.groups.some((x) => x.id === g.parent) ? groupLayer(g.parent) : "org";
1139
+ }
1140
+ return null;
1141
+ }
1142
+ function ancestorScopes(tree, layer) {
1143
+ const out = [];
1144
+ for (let cur = parentScope(tree, layer); cur && !out.includes(cur); cur = parentScope(tree, cur)) out.push(cur);
1145
+ return out;
1146
+ }
1130
1147
  function sourcesIn(tree, layer) {
1131
1148
  if (layer === "org") return Object.keys(tree.sources);
1132
1149
  if (layerKind(layer) === "source") return [layerValue(layer)];
@@ -1172,13 +1189,11 @@ function layerValue(layer) {
1172
1189
  const i = layer.indexOf(":");
1173
1190
  return i < 0 ? "" : layer.slice(i + 1);
1174
1191
  }
1192
+ function privateLayer(ctx) {
1193
+ return ctx.kind === "robot" ? `robot:${ctx.id}` : `user:${ctx.userId}`;
1194
+ }
1175
1195
  function layersFor(ctx) {
1176
- if (ctx.kind === "robot" && ctx.robot) {
1177
- return ["fleet", `arm:${ctx.robot.armModel}`, `mount:${ctx.robot.mount}`, `robot:${ctx.id}`];
1178
- }
1179
- const team = ctx.person?.team ?? "";
1180
- const dept = department(team);
1181
- return ["org", ...dept ? [`dept:${dept}`] : [], ...team ? [`team:${team}`] : [], `user:${ctx.userId}`];
1196
+ return ["org", privateLayer(ctx)];
1182
1197
  }
1183
1198
  function isLocalLayer(layer) {
1184
1199
  const kind = layerKind(layer);
@@ -1191,6 +1206,7 @@ function withScope(layers, scope) {
1191
1206
  return i < 0 ? [...layers, ...path28] : [...layers.slice(0, i), ...path28, ...layers.slice(i)];
1192
1207
  }
1193
1208
  function readerFor(ctx, ownerClearance, scope) {
1209
+ if (ctx.kind === "robot" && !scope?.source) scope = { ...scope, source: `robot:${ctx.id}` };
1194
1210
  const source = scope?.source ? { source: scope.source } : {};
1195
1211
  if (ctx.kind === "robot") {
1196
1212
  const rank = (c) => c === "private" ? 0 : CLEARANCE_RANK[c];
@@ -1213,9 +1229,6 @@ function explainSectionAccess(section, reader) {
1213
1229
  const kind = layerKind(section.layer);
1214
1230
  if (kind === "user" && section.layer !== `user:${reader.userId}`) return { allowed: false, reason: "other-user" };
1215
1231
  if (kind === "robot" && section.layer !== `robot:${reader.contextId}`) return { allowed: false, reason: "other-robot" };
1216
- if ((kind === "arm" || kind === "mount") && reader.role === "robot" && !reader.layers.includes(section.layer)) {
1217
- return { allowed: false, reason: "other-hardware" };
1218
- }
1219
1232
  const access = explainAccess(
1220
1233
  {
1221
1234
  clearance: section.clearance,
@@ -1236,42 +1249,25 @@ function scopeAffinity(section, reader) {
1236
1249
  function baseAffinity(section, reader) {
1237
1250
  const kind = layerKind(section.layer);
1238
1251
  if (kind === "group" || kind === "source") {
1239
- const i = reader.layers.indexOf(section.layer);
1240
- if (i < 0) return 0.05;
1252
+ if (!reader.layers.includes(section.layer)) return 0.05;
1241
1253
  const path28 = reader.layers.filter((l) => {
1242
1254
  const k = layerKind(l);
1243
1255
  return k === "group" || k === "source";
1244
1256
  });
1245
1257
  return 0.6 + 0.4 * ((path28.indexOf(section.layer) + 1) / path28.length);
1246
1258
  }
1247
- if (reader.layers.includes(section.layer)) {
1248
- const order = reader.role === "robot" ? FLEET_LAYER_ORDER : COMPANY_LAYER_ORDER;
1249
- return 0.5 + 0.5 * (order.indexOf(kind) / Math.max(1, order.length - 1));
1250
- }
1251
- if (kind === "team") {
1252
- const team = layerValue(section.layer);
1253
- if (department(team) === department(reader.team)) return 0.3;
1254
- return 0.05;
1255
- }
1256
- if (kind === "dept") return 0.1;
1257
- return 0.2;
1259
+ if (isLocalLayer(section.layer)) return reader.layers.includes(section.layer) ? 1 : 0;
1260
+ return 0.5;
1258
1261
  }
1259
1262
  function reachesReader(section, reader) {
1260
- if (reader.layers.includes(section.layer) || section.kind === "guardrail") return true;
1261
- const kind = layerKind(section.layer);
1262
- if (kind === "org" || kind === "dept" || FLEET_LAYER_ORDER.includes(kind)) return canRead(section, reader);
1263
- if (kind !== "team") return false;
1264
- return [layerValue(section.layer), ...section.allowedTeams ?? []].some((t) => !!t && orgUnitUnder(reader.team, t));
1263
+ return reader.layers.includes(section.layer) && canRead(section, reader);
1265
1264
  }
1266
- var COMPANY_LAYER_ORDER, FLEET_LAYER_ORDER;
1267
1265
  var init_layers = __esm({
1268
1266
  "../core/src/v2/layers.ts"() {
1269
1267
  "use strict";
1270
1268
  init_permissions();
1271
1269
  init_text();
1272
1270
  init_scopes();
1273
- COMPANY_LAYER_ORDER = ["org", "dept", "team", "user"];
1274
- FLEET_LAYER_ORDER = ["fleet", "arm", "mount", "robot"];
1275
1271
  }
1276
1272
  });
1277
1273
 
@@ -1455,6 +1451,9 @@ var init_document = __esm({
1455
1451
  });
1456
1452
 
1457
1453
  // ../core/src/v2/sections/markdown.ts
1454
+ function repoLayer(repo) {
1455
+ return `source:${repo.includes("/") ? "repo" : "folder"}:${repo}`;
1456
+ }
1458
1457
  function repoSectionId(repo, path28, anchor = "") {
1459
1458
  return anchor ? `repo:${repo}/${path28}#${anchor}` : `repo:${repo}/${path28}`;
1460
1459
  }
@@ -1487,7 +1486,7 @@ function parseRepoDoc(doc, opts) {
1487
1486
  id: meta.id ? repoSectionId(doc.repo, doc.path, meta.id) : repoSectionId(doc.repo, doc.path),
1488
1487
  orgId: opts.orgId,
1489
1488
  kind,
1490
- layer: `team:${owner}`,
1489
+ layer: opts.layer ?? repoLayer(doc.repo),
1491
1490
  title,
1492
1491
  body: doc.text,
1493
1492
  when,
@@ -2081,8 +2080,9 @@ var init_engine = __esm({
2081
2080
 
2082
2081
  // ../core/src/v2/guidelines.ts
2083
2082
  function reflectionGuidelines(sections, reader, opts = {}) {
2083
+ const covering = opts.layer ? /* @__PURE__ */ new Set(["org", opts.layer, ...opts.tree ? ancestorScopes(opts.tree, opts.layer) : []]) : void 0;
2084
2084
  const pool = reader ? servable(sections, reader) : sections;
2085
- return pool.filter((s) => s.delivery === "reflect" && s.status === "active" && (!reader || inBoundary(s, reader))).filter((s) => !opts.layer || s.layer === "org" || opts.layer === s.layer || opts.layer.startsWith(`${s.layer}/`)).sort((a, b) => generality(a.layer) - generality(b.layer) || a.id.localeCompare(b.id)).map((s) => ({ id: s.id, title: s.title, layer: s.layer, body: s.body }));
2085
+ return pool.filter((s) => s.delivery === "reflect" && s.status === "active" && (!reader || inBoundary(s, reader))).filter((s) => !covering || covering.has(s.layer)).sort((a, b) => generality(a.layer) - generality(b.layer) || a.id.localeCompare(b.id)).map((s) => ({ id: s.id, title: s.title, layer: s.layer, body: s.body }));
2086
2086
  }
2087
2087
  function guidelineRules(body) {
2088
2088
  const at = body.search(/^## /m);
@@ -2120,7 +2120,7 @@ var init_guidelines = __esm({
2120
2120
  init_engine();
2121
2121
  init_scopes();
2122
2122
  GUIDELINES_ANCHOR = "rules";
2123
- generality = (l) => ({ org: 0, dept: 1, fleet: 1, group: 2, team: 2, arm: 3, mount: 3, source: 3, robot: 4, user: 4 })[layerKind(l)] ?? 5;
2123
+ generality = (l) => ({ org: 0, group: 1, source: 2, robot: 3, user: 3 })[layerKind(l)] ?? 4;
2124
2124
  }
2125
2125
  });
2126
2126
 
@@ -3129,6 +3129,17 @@ var init_generalize = __esm({
3129
3129
  }
3130
3130
  });
3131
3131
 
3132
+ // ../core/src/v2/adopt.ts
3133
+ var init_adopt = __esm({
3134
+ "../core/src/v2/adopt.ts"() {
3135
+ "use strict";
3136
+ init_text();
3137
+ init_layers();
3138
+ init_scopes();
3139
+ init_sources();
3140
+ }
3141
+ });
3142
+
3132
3143
  // ../core/src/v2/index.ts
3133
3144
  var init_v2 = __esm({
3134
3145
  "../core/src/v2/index.ts"() {
@@ -3153,6 +3164,7 @@ var init_v2 = __esm({
3153
3164
  init_sources();
3154
3165
  init_scopes();
3155
3166
  init_generalize();
3167
+ init_adopt();
3156
3168
  }
3157
3169
  });
3158
3170
 
@@ -3329,7 +3341,8 @@ function indexWorkingTree(v2, config, cwd, opts = {}) {
3329
3341
  const now = opts.now ?? Date.now();
3330
3342
  const cached = v2.readRepoIndex(root);
3331
3343
  const ownerTeam = config.ownerTeam || config.orgUnit || "unknown";
3332
- const sameRepo = cached && cached.ownerTeam === ownerTeam;
3344
+ const source = sourceFor(root, v2.store.root);
3345
+ const sameRepo = cached && cached.ownerTeam === ownerTeam && cached.format === INDEX_FORMAT;
3333
3346
  const repo = sameRepo && cached.repo && !opts.force ? cached.repo : repoName(root);
3334
3347
  const fresh = sameRepo && cached.repo === repo && cached.paths && cached.listedAt && now - Date.parse(cached.listedAt) < LIST_TTL_MS && !opts.force;
3335
3348
  const paths = fresh ? cached.paths : listDocs(root);
@@ -3352,11 +3365,12 @@ function indexWorkingTree(v2, config, cwd, opts = {}) {
3352
3365
  changed = true;
3353
3366
  try {
3354
3367
  const text2 = fs6.readFileSync(abs, "utf8");
3355
- files[rel] = { mtimeMs: stat.mtimeMs, size: stat.size, sections: parseRepoDoc({ repo, path: rel, text: text2 }, { orgId: config.orgId, ownerTeam }) };
3368
+ files[rel] = { mtimeMs: stat.mtimeMs, size: stat.size, sections: parseRepoDoc({ repo, path: rel, text: text2 }, { orgId: config.orgId, ownerTeam, layer: source ? sourceLayer(source) : "org" }) };
3356
3369
  } catch {
3357
3370
  }
3358
3371
  }
3359
3372
  const index = {
3373
+ format: INDEX_FORMAT,
3360
3374
  repo,
3361
3375
  ownerTeam,
3362
3376
  listedAt: fresh ? cached.listedAt : new Date(now).toISOString(),
@@ -3397,11 +3411,12 @@ function relativeToRoot(root, file, cwd = root) {
3397
3411
  }
3398
3412
  return null;
3399
3413
  }
3400
- var DOC_NAMES, SKIP_DIRS, LIST_TTL_MS, rootCache, isDoc, deployedSkill;
3414
+ var DOC_NAMES, SKIP_DIRS, LIST_TTL_MS, rootCache, isDoc, deployedSkill, INDEX_FORMAT;
3401
3415
  var init_repo = __esm({
3402
3416
  "src/v2/repo.ts"() {
3403
3417
  "use strict";
3404
3418
  init_v22();
3419
+ init_source();
3405
3420
  DOC_NAMES = /* @__PURE__ */ new Set(["spec.md", "testing.md", "skill.md"]);
3406
3421
  SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".bunshin"]);
3407
3422
  LIST_TTL_MS = 6e4;
@@ -3412,6 +3427,7 @@ var init_repo = __esm({
3412
3427
  return DOC_NAMES.has(parts[parts.length - 1].toLowerCase());
3413
3428
  };
3414
3429
  deployedSkill = (root, rel) => /(^|\/)SKILL\.md$/i.test(rel) && fs6.existsSync(path5.join(root, path5.posix.dirname(rel), ".bunshin.json"));
3430
+ INDEX_FORMAT = 2;
3415
3431
  }
3416
3432
  });
3417
3433
 
@@ -4381,6 +4397,11 @@ function childDir(root, name) {
4381
4397
  const dir = path8.join(root, name);
4382
4398
  return path8.dirname(path8.resolve(dir)) === path8.resolve(root) && insideRoot(root, dir) ? dir : null;
4383
4399
  }
4400
+ function layerRank(layer, tree) {
4401
+ const kind = layerKind(layer);
4402
+ if (kind === "group") return 1 + (tree ? groupChain(tree, layerValue(layer)).length : 0);
4403
+ return kind === "source" ? 1e3 : kind === "user" || kind === "robot" ? 2e3 : 0;
4404
+ }
4384
4405
  function planDeploy(bundle, p, harnesses) {
4385
4406
  const active = bundle.sections.filter((s) => s.status === "active" || s.status === "stale");
4386
4407
  const byName = /* @__PURE__ */ new Map();
@@ -4391,7 +4412,7 @@ function planDeploy(bundle, p, harnesses) {
4391
4412
  continue;
4392
4413
  }
4393
4414
  const prev = byName.get(s.skill.name);
4394
- if (!prev || (LAYER_RANK[layerKind(s.layer)] ?? 0) > (LAYER_RANK[layerKind(prev.layer)] ?? 0)) byName.set(s.skill.name, s);
4415
+ if (!prev || layerRank(s.layer, bundle.scopes) > layerRank(prev.layer, bundle.scopes)) byName.set(s.skill.name, s);
4395
4416
  }
4396
4417
  const skills = [...byName.values()].sort((a, b) => a.skill.name.localeCompare(b.skill.name));
4397
4418
  const instructions = active.filter((s) => s.delivery === "always" && !s.skill && !s.profileOf).sort((a, b) => (a.packPath ?? a.title).localeCompare(b.packPath ?? b.title));
@@ -5013,7 +5034,7 @@ ${EXCLUDE_END}
5013
5034
  } catch {
5014
5035
  }
5015
5036
  }
5016
- var settingsFile, orgKey, sha, BLOCK_BEGIN, BLOCK_END, MANIFEST, MAX_FILE, isExecutable, safeJoin, SAFE_NAME, neutralizeMarkers, LAYER_RANK, docMarker, BORN_SLACK_MS, LINK_DIRS, EXCLUDE_BEGIN, EXCLUDE_END;
5037
+ var settingsFile, orgKey, sha, BLOCK_BEGIN, BLOCK_END, MANIFEST, MAX_FILE, isExecutable, safeJoin, SAFE_NAME, neutralizeMarkers, docMarker, BORN_SLACK_MS, LINK_DIRS, EXCLUDE_BEGIN, EXCLUDE_END;
5017
5038
  var init_deploy = __esm({
5018
5039
  "src/v2/deploy.ts"() {
5019
5040
  "use strict";
@@ -5038,7 +5059,6 @@ var init_deploy = __esm({
5038
5059
  safeJoin = containedPath;
5039
5060
  SAFE_NAME = /^(?!\.\.?$)[A-Za-z0-9][A-Za-z0-9._-]*$/;
5040
5061
  neutralizeMarkers = (text2) => text2.replace(/<!--(?=\s*bunshin:)/gi, "<!-\u200B-");
5041
- LAYER_RANK = { org: 0, dept: 1, group: 2, team: 2, source: 3, user: 3, fleet: 0, arm: 1, mount: 2, robot: 3 };
5042
5062
  docMarker = (id, version2) => `<!-- bunshin:doc id=${id} v=${version2} -->`;
5043
5063
  BORN_SLACK_MS = 2e3;
5044
5064
  LINK_DIRS = {
@@ -6129,7 +6149,8 @@ async function uploadIndex(store, cwd, deadline = Date.now() + 3e4) {
6129
6149
  }
6130
6150
  });
6131
6151
  const commit = repoCommit(root);
6132
- const res = await v2api(config, "POST", "/sections/index", { repo: index.repo, ...commit ? { commit } : {}, ownerTeam: index.ownerTeam, docs }, { deadline, timeoutMs: 3e4 });
6152
+ const source = sourceFor(root, store.root);
6153
+ const res = await v2api(config, "POST", "/sections/index", { repo: index.repo, ...commit ? { commit } : {}, ...source ? { source } : {}, ownerTeam: index.ownerTeam, docs }, { deadline, timeoutMs: 3e4 });
6133
6154
  return typeof res.indexed === "number" ? res.indexed : docs.length;
6134
6155
  }
6135
6156
  var isLocalId, carriesTrace;
@@ -8030,7 +8051,7 @@ function cmdSuggest(store, args, cwd, out, err) {
8030
8051
  orgId: config.orgId,
8031
8052
  contextId: view.ctx.id,
8032
8053
  authorUserId: config.userId,
8033
- target: known ? { sectionId: known } : { layer: "fleet", storage: { type: "harness", path: rel, symbol: symbol ?? "" } },
8054
+ target: known ? { sectionId: known } : { storage: { type: "harness", path: rel, symbol: symbol ?? "" } },
8034
8055
  op: { op: "append", text: rationale },
8035
8056
  rationale,
8036
8057
  evidenceEpisodeIds: sessionId ? [episodeIdFor(view.ctx.id, sessionId)] : [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunshin-cc/bunshin",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Shared memory for company AI agents. Connect Codex, Claude Code, or Pi to Bunshin.",
5
5
  "type": "module",
6
6
  "bin": {