homeostat 0.1.4 → 0.1.5

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
@@ -6,7 +6,7 @@ Homeostat turns review feedback that led to a code change into short, scored rul
6
6
 
7
7
  ```sh
8
8
  # In a repo for your team's knowledge base:
9
- npx --yes homeostat@0.1.4 init
9
+ npx --yes homeostat@0.1.5 init
10
10
 
11
11
  # In Claude Code:
12
12
  claude plugin marketplace add ysugarmen/homeostat
package/dist/src/daily.js CHANGED
@@ -67,6 +67,14 @@ async function daily(opts, io) {
67
67
  settled++;
68
68
  }
69
69
  }
70
+ // Lessons still waiting in an open review PR: a new lesson that repeats one waits with it.
71
+ git(root, "fetch", "--quiet", "origin", "+refs/heads/homeostat/review-*:refs/remotes/origin/homeostat/review-*");
72
+ const inReview = [...decisions.values()].flatMap((d) => d.decided.flatMap((r) => {
73
+ if (r.decision !== "review" || !r.pr || !open.has(r.pr))
74
+ return [];
75
+ const entry = parseEntry(showFile(root, remote(r.pr), r.path) ?? "").entry;
76
+ return entry ? [entry] : [];
77
+ }));
70
78
  const lessons = [];
71
79
  const decidedHashes = (m) => new Set(decisions.get(m)?.decided.map((r) => r.hash));
72
80
  const heartbeats = {};
@@ -182,6 +190,13 @@ async function daily(opts, io) {
182
190
  }
183
191
  for (const l of lessons)
184
192
  decide(l, files, config, inForce, groups.get(l)?.length ?? 1);
193
+ for (const { l, entry } of fresh) {
194
+ const twin = inReview.find((r) => r.frontmatter.id === entry.frontmatter.id || sameLesson(r, entry));
195
+ if (!twin || l.decision === "declined")
196
+ continue;
197
+ l.decision = "review";
198
+ l.reason = `the same lesson as ${twin.frontmatter.id}, which is waiting in review: decide them together`;
199
+ }
185
200
  for (const [l, group] of groups) {
186
201
  if (group.length < 2)
187
202
  continue;
@@ -263,8 +278,17 @@ async function daily(opts, io) {
263
278
  if (pr.pr) {
264
279
  row.reviewPr = pr.pr.url;
265
280
  const logins = reviewerLogins(config, review, member);
266
- if (logins.length)
267
- await provider.requestReviewers?.(kbRepo, pr.pr.id, logins).catch(() => undefined);
281
+ if (logins.length && provider.requestReviewers) {
282
+ try {
283
+ await provider.requestReviewers(kbRepo, pr.pr.id, logins);
284
+ io.stdout(` asked ${logins.map((l) => `@${l}`).join(", ")} to review ${pr.pr.url}\n`);
285
+ }
286
+ catch (e) {
287
+ // Usually: they are not collaborators on the KB repo. The PR still waits in the queue.
288
+ row.reviewError = `could not request ${logins.map((l) => `@${l}`).join(", ")}: ${e.message}`;
289
+ io.stdout(` ${row.reviewError}; reviewers must be collaborators on ${sources.kbRepo}\n`);
290
+ }
291
+ }
268
292
  }
269
293
  for (const l of review)
270
294
  Object.assign(l, { pr: pr.branch });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homeostat",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Turn your team's code-review lessons into rules every coding agent follows: Claude Code, MCP, Cursor, Copilot, AGENTS.md.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -39,9 +39,9 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "commander": "^15.0.0",
42
- "@homeostat/core": "0.1.4",
43
- "@homeostat/mcp": "0.1.4",
44
- "@homeostat/dashboard": "0.1.4"
42
+ "@homeostat/mcp": "0.1.5",
43
+ "@homeostat/dashboard": "0.1.5",
44
+ "@homeostat/core": "0.1.5"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public",