@whittlelabs/sifter 0.3.0 → 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 (3) hide show
  1. package/bin.js +49 -26
  2. package/bin.js.map +3 -3
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -15370,15 +15370,17 @@ var require_client = __commonJS({
15370
15370
  throw new Error(`Attention pool not found by name: "${ref.name}"`);
15371
15371
  return pool.id;
15372
15372
  }
15373
- // ── Subscribers ──────────────────────────────────────────────────
15374
- async listSubscribers(poolId) {
15375
- return this.request("GET", `/api/attention-pools/${poolId}/subscribers`);
15373
+ // ── Members ──────────────────────────────────────────────────────
15374
+ // A subscriber joins a pool by registering as a member of it. These
15375
+ // map to the server's `/api/attention-pools/:poolId/members` routes.
15376
+ async listMembers(poolId) {
15377
+ return this.request("GET", `/api/attention-pools/${poolId}/members`);
15376
15378
  }
15377
- async registerSubscriber(poolId, options) {
15378
- return this.request("POST", `/api/attention-pools/${poolId}/subscribers`, options);
15379
+ async registerMember(poolId, options) {
15380
+ return this.request("POST", `/api/attention-pools/${poolId}/members`, options);
15379
15381
  }
15380
- async updateSubscriber(poolId, subscriberId, options) {
15381
- return this.request("PUT", `/api/attention-pools/${poolId}/subscribers/${subscriberId}`, options);
15382
+ async updateMember(poolId, memberId, options) {
15383
+ return this.request("PUT", `/api/attention-pools/${poolId}/members/${memberId}`, options);
15382
15384
  }
15383
15385
  // ── Jobs (producer) ──────────────────────────────────────────────
15384
15386
  /**
@@ -15801,13 +15803,17 @@ var require_subscribe = __commonJS({
15801
15803
  for (const ref of opts.pools) {
15802
15804
  const id = await opts.jobsClient.resolvePoolRef(ref);
15803
15805
  resolvedPoolIds.push(id);
15804
- await opts.jobsClient.registerSubscriber(id, {
15806
+ await opts.jobsClient.registerMember(id, {
15805
15807
  identityType: opts.subscriber.identityType,
15806
15808
  identityId: opts.subscriber.identityId,
15807
15809
  intelligence: opts.subscriber.intelligence,
15808
- capabilities: opts.subscriber.capabilities,
15809
15810
  ...opts.subscriber.displayName !== void 0 ? { displayName: opts.subscriber.displayName } : {},
15810
- ...opts.subscriber.metadata !== void 0 ? { metadata: opts.subscriber.metadata } : {}
15811
+ // The server has no first-class capabilities column; carry the
15812
+ // advertised set in metadata so it persists on the member row.
15813
+ metadata: {
15814
+ ...opts.subscriber.metadata,
15815
+ capabilities: opts.subscriber.capabilities
15816
+ }
15811
15817
  });
15812
15818
  }
15813
15819
  logger.info("subscribe loop started", {
@@ -18448,7 +18454,7 @@ var import_shuttle = __toESM(require_dist4());
18448
18454
  // package.json
18449
18455
  var package_default = {
18450
18456
  name: "@whittlelabs/sifter",
18451
- version: "0.3.0",
18457
+ version: "0.3.2",
18452
18458
  description: "Whittle Sifter: paired AI reviewer for Whittle Sift attention pools.",
18453
18459
  bin: {
18454
18460
  "whittle-sifter": "./dist/bin.js"
@@ -18525,12 +18531,19 @@ var sifterBrand = {
18525
18531
  id: "scaffold-run-config",
18526
18532
  phase: "post-pair",
18527
18533
  // The substrate's run command requires a YAML describing pools,
18528
- // executors, and Jobs auth. The web form that minted the pair
18529
- // command bakes the user's product-level choices (executor type,
18530
- // poll interval, caps) into the init flags. This step reads
18531
- // those choices from `ctx.setupOptions` and writes the matching
18532
- // YAML. For `claude-code` we prompt interactively for `cwd` if
18533
- // the user didn't pass `--cwd`; other executors don't need one.
18534
+ // executors, and Jobs auth. The web form that minted the pair command
18535
+ // bakes the user's product-level choices (executor type, poll interval)
18536
+ // into the init flags; this step reads them from `ctx.setupOptions`.
18537
+ //
18538
+ // A re-pair must refresh the pairing-derived fields (Jobs URL, service
18539
+ // identity, pools) even when sifter.yaml already exists otherwise a
18540
+ // stale file silently shadows the new pairing (exactly how a leftover
18541
+ // `localhost:3005` and an old `cwd` once survived a fresh pair). So we
18542
+ // merge rather than skip: rebuild the pairing fields from `pairing`, carry
18543
+ // over the user's executor block (e.g. claude-code `cwd`) and polling
18544
+ // unless a fresh flag overrides them, and log what happened. We only
18545
+ // (re)scaffold an executor block — which may prompt for `cwd` — when there
18546
+ // isn't one to preserve.
18534
18547
  run: async (ctx) => {
18535
18548
  const yamlPath = (0, import_path.join)(ctx.configDir, "sifter.yaml");
18536
18549
  const pairing = new import_shuttle.PairingConfigStore((0, import_path.join)(ctx.configDir, "config.json")).read();
@@ -18539,8 +18552,18 @@ var sifterBrand = {
18539
18552
  "Pairing returned no attention pools. The Sift backend should have provisioned one for this user during pre-pair."
18540
18553
  );
18541
18554
  }
18542
- const executor = ctx.setupOptions.executor ?? "claude-code";
18543
- const executorBlock = await buildExecutorBlock(executor, ctx);
18555
+ let existing = {};
18556
+ try {
18557
+ existing = (0, import_yaml.parse)(await (0, import_promises.readFile)(yamlPath, "utf8")) ?? {};
18558
+ } catch (err) {
18559
+ if (err.code !== "ENOENT") throw err;
18560
+ }
18561
+ const isUpdate = Object.keys(existing).length > 0;
18562
+ const existingExecutors = existing.executors ?? {};
18563
+ const existingPools = Array.isArray(existing.pools) ? existing.pools : [];
18564
+ const priorExecutor = typeof existingPools[0]?.executor === "string" ? existingPools[0].executor : void 0;
18565
+ const executor = ctx.setupOptions.executor ?? priorExecutor ?? "claude-code";
18566
+ const executorBlock = !ctx.setupOptions.executor && existingExecutors[executor] !== void 0 ? existingExecutors[executor] : await buildExecutorBlock(executor, ctx);
18544
18567
  const doc = {
18545
18568
  auth: {
18546
18569
  jobsApiUrl: pairing.jobsApiUrl
@@ -18555,18 +18578,18 @@ var sifterBrand = {
18555
18578
  executor
18556
18579
  })),
18557
18580
  executors: {
18581
+ ...existingExecutors,
18558
18582
  [executor]: executorBlock
18559
18583
  }
18560
18584
  };
18561
- if (ctx.setupOptions.pollIntervalMs !== void 0) {
18562
- doc.polling = { intervalMs: ctx.setupOptions.pollIntervalMs };
18585
+ const pollIntervalMs = ctx.setupOptions.pollIntervalMs ?? existing.polling?.intervalMs;
18586
+ if (pollIntervalMs !== void 0) {
18587
+ doc.polling = { intervalMs: pollIntervalMs };
18563
18588
  }
18564
18589
  await (0, import_promises.mkdir)(ctx.configDir, { recursive: true });
18565
- await (0, import_promises.writeFile)(yamlPath, (0, import_yaml.stringify)(doc), { flag: "wx" }).catch(
18566
- (err) => {
18567
- if (err.code === "EEXIST") return;
18568
- throw err;
18569
- }
18590
+ await (0, import_promises.writeFile)(yamlPath, (0, import_yaml.stringify)(doc));
18591
+ ctx.log(
18592
+ isUpdate ? `Refreshed ${yamlPath} from this pairing (Jobs URL, identity, pools); kept your executor and polling settings.` : `Wrote ${yamlPath}.`
18570
18593
  );
18571
18594
  }
18572
18595
  }