pierre-review 0.1.22 → 0.1.24

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.
@@ -154,8 +154,8 @@ export async function claudeReviewRoutes(app) {
154
154
  return {
155
155
  error: 'Conflict',
156
156
  message: result.reason === 'already_running'
157
- ? 'A review is already running for this PR.'
158
- : 'Another review is in progress; try again shortly.',
157
+ ? 'A review is already running or queued for this PR.'
158
+ : 'The review queue is full; try again once some finish.',
159
159
  };
160
160
  }
161
161
  reply.status(202);
@@ -0,0 +1,35 @@
1
+ import { getInsights, getRepoAnalytics } from '../../db/queries.js';
2
+ import { accountIdOf } from '../plugins/auth.js';
3
+ function parseIntList(raw) {
4
+ if (!raw)
5
+ return null;
6
+ const ids = raw
7
+ .split(',')
8
+ .map((s) => Number.parseInt(s.trim(), 10))
9
+ .filter((n) => Number.isFinite(n));
10
+ return ids.length > 0 ? ids : null;
11
+ }
12
+ export async function insightsRoutes(app) {
13
+ // Per-repo sprint/team stats for the Insights panel. Scoped to the account;
14
+ // `repoIds` narrows to the active watched-repo selection.
15
+ app.get('/api/insights', async (req) => {
16
+ const q = req.query;
17
+ return getInsights({
18
+ accountId: accountIdOf(req),
19
+ repoIds: parseIntList(q.repoIds),
20
+ });
21
+ });
22
+ // Heavier per-repo analytics for the drill-down chart panel — loaded on demand.
23
+ // Ownership-scoped: a repo not owned by the account 404s.
24
+ app.get('/api/insights/:repoId/analytics', async (req, reply) => {
25
+ const { repoId } = req.params;
26
+ const id = Number.parseInt(repoId, 10);
27
+ const data = Number.isFinite(id)
28
+ ? await getRepoAnalytics(accountIdOf(req), id)
29
+ : null;
30
+ if (!data)
31
+ return reply.code(404).send({ error: 'repo not found' });
32
+ return data;
33
+ });
34
+ }
35
+ //# sourceMappingURL=insights.js.map
@@ -8,7 +8,7 @@ const dismissSchema = {
8
8
  required: ['kind', 'refId'],
9
9
  additionalProperties: false,
10
10
  properties: {
11
- kind: { type: 'string', enum: ['review_request', 'thread'] },
11
+ kind: { type: 'string', enum: ['review_request', 'thread', 'claude_review'] },
12
12
  refId: { type: 'integer' },
13
13
  },
14
14
  },
@@ -23,6 +23,7 @@ export async function meRoutes(app) {
23
23
  awaitingReview: myTurn.awaitingReview.length,
24
24
  yourPrsActivity: myTurn.yourPrs.length,
25
25
  threadsAwaiting: myTurn.threadsAwaiting.length,
26
+ claudeReviewsToAction: myTurn.claudeReviewsToAction.length,
26
27
  },
27
28
  claudeReviewEnabled: config.claudeReviewEnabled,
28
29
  deploymentMode: config.deploymentMode,
@@ -34,7 +35,8 @@ export async function meRoutes(app) {
34
35
  await dismissMyTurn(accountIdOf(req), kind, refId);
35
36
  return { status: 'ok' };
36
37
  });
37
- // The "Done" tab: entries dismissed in the past 90 days (review_request + thread).
38
+ // The "Done" tab: entries dismissed in the past 90 days (review_request + thread
39
+ // + claude_review).
38
40
  app.get('/api/my-turn/done', async (req) => getCompletedDismissals(accountIdOf(req), 90));
39
41
  // Un-dismiss: move a completed entry back to the inbox.
40
42
  app.post('/api/my-turn/undismiss', { schema: dismissSchema }, async (req) => {
@@ -1,4 +1,4 @@
1
- import { getPrDetail, markPrViewed } from '../../db/queries.js';
1
+ import { getPrDetail, markAllViewed, markPrViewed } from '../../db/queries.js';
2
2
  import { hydratePrDetail } from '../../sync/hydrate-detail.js';
3
3
  import { accountIdOf } from '../plugins/auth.js';
4
4
  const idParamSchema = {
@@ -16,7 +16,22 @@ const markViewedSchema = {
16
16
  properties: { sha: { type: 'string' } },
17
17
  },
18
18
  };
19
+ const markAllViewedSchema = {
20
+ body: {
21
+ type: 'object',
22
+ additionalProperties: false,
23
+ properties: { repoIds: { type: 'array', items: { type: 'integer' } } },
24
+ },
25
+ };
19
26
  export async function prRoutes(app) {
27
+ // Bulk "mark all seen": stamp every open PR (optionally scoped to repoIds) viewed
28
+ // at its head, clearing all new-since badges at once. Static path — no :id — so it
29
+ // doesn't collide with /api/prs/:id.
30
+ app.post('/api/prs/mark-all-viewed', { schema: markAllViewedSchema }, async (req) => {
31
+ const { repoIds } = (req.body ?? {});
32
+ const count = await markAllViewed(accountIdOf(req), repoIds && repoIds.length > 0 ? repoIds : null);
33
+ return { status: 'ok', count };
34
+ });
20
35
  app.get('/api/prs/:id', { schema: idParamSchema }, async (req, reply) => {
21
36
  const { id } = req.params;
22
37
  const accountId = accountIdOf(req);
package/dist/app.js CHANGED
@@ -16,6 +16,7 @@ import { threadRoutes } from './api/routes/threads.js';
16
16
  import { meRoutes } from './api/routes/me.js';
17
17
  import { openPrsRoutes } from './api/routes/open-prs.js';
18
18
  import { mergersRoutes } from './api/routes/mergers.js';
19
+ import { insightsRoutes } from './api/routes/insights.js';
19
20
  import { claudeReviewRoutes } from './api/routes/claude-review.js';
20
21
  export async function buildApp() {
21
22
  const app = Fastify({
@@ -126,6 +127,7 @@ export async function buildApp() {
126
127
  await app.register(meRoutes);
127
128
  await app.register(openPrsRoutes);
128
129
  await app.register(mergersRoutes);
130
+ await app.register(insightsRoutes);
129
131
  // Claude Review is local-only + opt-in. Only register its routes when enabled,
130
132
  // so the clone-manager / gh-CLI dependency is unreachable in cloud mode.
131
133
  if (config.claudeReviewEnabled)
package/dist/config.js CHANGED
@@ -126,8 +126,15 @@ export const config = {
126
126
  // Turn cap for a diff-only run. These are TOOL-LESS (only submit_review), so they
127
127
  // should finish in ~2 turns; a tight cap is a cheap runaway guard.
128
128
  reviewDiffOnlyMaxTurns: intFromEnv('REVIEW_DIFF_ONLY_MAX_TURNS', 6),
129
- // At most one review per PR; this caps concurrent reviews across all PRs.
130
- reviewConcurrency: intFromEnv('REVIEW_CONCURRENCY', 1),
129
+ // At most one review per PR; this caps concurrent reviews across all PRs. Default
130
+ // 4 so the user can bulk-review (extras queue, see review-manager). Raising this
131
+ // also DISABLES the pasted-key override (which mutates process.env and is only
132
+ // safe at concurrency 1 — see local-settings.applyUserAnthropicKey); ambient auth
133
+ // is used instead. Set REVIEW_CONCURRENCY=1 to restore the pasted-key path.
134
+ reviewConcurrency: intFromEnv('REVIEW_CONCURRENCY', 4),
135
+ // Hard ceiling on QUEUED (not-yet-started) reviews, a runaway guard for bulk
136
+ // triggering; further starts return 'busy' until the queue drains.
137
+ reviewMaxQueued: intFromEnv('REVIEW_MAX_QUEUED', 50),
131
138
  // ---- Claude Review routing (diff-only vs worktree) — THE THRESHOLDS ----
132
139
  // The deterministic pre-check (review/routing.ts) decides, BEFORE the agent runs,
133
140
  // whether a PR can be reviewed from its diff alone (fast, tool-less, no worktree)