@magnusekdahl/parallix 1.3.3 → 1.3.4

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 (47) hide show
  1. package/docs/agents.md +1 -1
  2. package/docs/use-cases.md +1 -1
  3. package/lib/agents/agents.js +20 -3
  4. package/lib/agents/agents.ts +14 -3
  5. package/lib/agents/mistral.js +128 -14
  6. package/lib/agents/mistral.ts +145 -5
  7. package/lib/commands/active.js +69 -39
  8. package/lib/commands/active.ts +97 -60
  9. package/lib/commands/config.ts +3 -3
  10. package/lib/commands/coverage-gate.ts +1 -1
  11. package/lib/commands/draft.js +1 -1
  12. package/lib/commands/draft.ts +1 -1
  13. package/lib/commands/handoff.js +13 -8
  14. package/lib/commands/handoff.ts +24 -18
  15. package/lib/commands/rebase.js +1 -1
  16. package/lib/commands/rebase.ts +2 -2
  17. package/lib/commands/repair-handoff.js +141 -20
  18. package/lib/commands/repair-handoff.ts +185 -45
  19. package/lib/commands/resolve-conflict.js +1 -1
  20. package/lib/commands/resolve-conflict.ts +2 -2
  21. package/lib/commands/stats-backfill.ts +10 -10
  22. package/lib/commands/stats.js +38 -11
  23. package/lib/commands/stats.ts +40 -96
  24. package/lib/core/fmt.ts +2 -2
  25. package/lib/core/git.ts +2 -2
  26. package/lib/core/gitignore.ts +2 -2
  27. package/lib/core/mission-utils.js +2 -2
  28. package/lib/core/mission-utils.ts +2 -2
  29. package/lib/core/persistent-data-migration.ts +2 -2
  30. package/lib/core/spawn-tee.ts +1 -1
  31. package/lib/core/state-map.ts +2 -2
  32. package/lib/core/storage.ts +1 -1
  33. package/lib/core/verification.ts +1 -1
  34. package/lib/review/rebase.ts +12 -12
  35. package/lib/review/review-artifacts.ts +35 -35
  36. package/lib/review/review-commands.ts +40 -40
  37. package/lib/review/review-events.ts +12 -12
  38. package/lib/review/review-loop.js +236 -7
  39. package/lib/review/review-loop.ts +338 -22
  40. package/lib/review/review-polling.ts +6 -6
  41. package/lib/review/review-prompts.js +8 -4
  42. package/lib/review/review-prompts.ts +12 -8
  43. package/lib/review/review-state.js +1 -1
  44. package/lib/review/review-state.ts +2 -2
  45. package/package.json +3 -2
  46. package/prompts/review-verbose.md +1 -1
  47. package/prompts/review.md +1 -1
@@ -28,11 +28,6 @@ interface StatsOptions {
28
28
  exit?: Function;
29
29
  }
30
30
 
31
- interface CsvData {
32
- headers: string[];
33
- rows: Record<string, string>[];
34
- }
35
-
36
31
  interface NormalizeStatsRowOptions {
37
32
  repo?: string;
38
33
  rootDir?: string;
@@ -42,67 +37,6 @@ interface LoadStatsCsvOptions {
42
37
  rootDir?: string;
43
38
  }
44
39
 
45
- interface TelemetryToStatsOptions {
46
- agentFamily: string;
47
- durationMinutes?: number;
48
- model?: string;
49
- }
50
-
51
- interface UpsertStatsRowOptions {
52
- filePath?: string;
53
- rootDir?: string;
54
- }
55
-
56
- interface RecordStageStatsOptions {
57
- slug: string;
58
- stage: string;
59
- rootDir?: string;
60
- filePath?: string;
61
- date?: string;
62
- implementer?: string;
63
- reviewer?: string;
64
- prFixRounds?: string;
65
- telemetry?: unknown;
66
- durationMinutes?: number;
67
- model?: string | null;
68
- }
69
-
70
- interface RecordIntegrationStatsOptions {
71
- slug: string;
72
- rootDir?: string;
73
- filePath?: string;
74
- date?: string;
75
- }
76
-
77
- interface RecordActiveStatsOptions {
78
- stage?: string;
79
- slug: string;
80
- rootDir?: string;
81
- prFixRounds?: string;
82
- model?: string;
83
- }
84
-
85
- interface RecordReviewStatsOptions {
86
- stage?: string;
87
- slug: string;
88
- rootDir?: string;
89
- reviewer?: string;
90
- implementer?: string;
91
- prFixRounds?: string;
92
- model?: string;
93
- }
94
-
95
- interface RenderWeeklyStatsReportOptions {
96
- today?: Date | string;
97
- rootDir?: string | null;
98
- }
99
-
100
- interface RenderRangeStatsReportOptions {
101
- rootDir?: string | null;
102
- from?: string;
103
- to?: string;
104
- }
105
-
106
40
  interface StatsRow {
107
41
  date?: string;
108
42
  repo?: string;
@@ -137,16 +71,6 @@ interface StatsRow {
137
71
  missions?: number;
138
72
  }
139
73
 
140
- interface MissionStats {
141
- implementer: string;
142
- missions: number;
143
- averageFixRounds: string;
144
- }
145
-
146
- interface AccModeOptions {
147
- mode?: 'sum' | 'max' | 'replace';
148
- }
149
-
150
74
  interface StatsCsvPathOptions {
151
75
  filePath?: string;
152
76
  rootDir?: string;
@@ -154,14 +78,6 @@ interface StatsCsvPathOptions {
154
78
  forWrite?: boolean;
155
79
  }
156
80
 
157
- interface StatsCmdOptions {
158
- log?: Function;
159
- error?: Function;
160
- exit?: Function;
161
- rootDir?: string;
162
- }
163
-
164
-
165
81
  import * as fs from 'node:fs';
166
82
  import * as path from 'node:path';
167
83
 
@@ -172,7 +88,7 @@ import { readReviewState } from '../review/review-state.js';
172
88
  import * as reviewEvents from '../review/review-events.js';
173
89
  import { git } from '../core/git.js';
174
90
  import { migrateStats } from '../core/persistent-data-migration.js';
175
- import { getPrimaryWorktree, findMissionDir } from '../core/mission-utils.js';
91
+ import { findMissionDir } from '../core/mission-utils.js';
176
92
  import * as forgejo from '../tools/forgejo.js';
177
93
  import * as storage from '../core/storage.js';
178
94
 
@@ -180,16 +96,19 @@ import * as storage from '../core/storage.js';
180
96
  // and one-time header migration of legacy stats files (task-1251).
181
97
  const LEGACY_HEADERS = ['date', 'mission', 'classification', 'implementer', 'pr_fix_rounds'];
182
98
 
183
- // Extended 21-column telemetry schema (task-1314 + task-1251). Legacy 5-column rows are
184
- // migrated in-memory on load: the legacy columns are preserved and the new
185
- // columns default to '' (text) or '0' (numeric). On the next write the file
186
- // header is upgraded and existing rows gain the new columns.
99
+ // Extended 22-column telemetry schema (task-1314 + task-1251 + task-1380). Legacy
100
+ // 5-column rows are migrated in-memory on load: the legacy columns are preserved
101
+ // and the new columns default to '' (text) or '0' (numeric). On the next write the
102
+ // file header is upgraded and existing rows gain the new columns. The `closed`
103
+ // column (task-1380) stores 'yes' for closed/integrated missions and is empty for
104
+ // in-progress stage rows; filtering by `closed === 'yes'` excludes in-progress
105
+ // missions from weekly and range mission counts.
187
106
  const STATS_HEADERS = [
188
107
  'date', 'repo', 'mission', 'classification', 'implementer', 'pr_fix_rounds',
189
108
  'provider', 'model', 'implementer_agent', 'reviewer_agent', 'stage',
190
109
  'input_tokens', 'output_tokens', 'cached_tokens', 'context_tokens',
191
110
  'tool_calls', 'openai_usage_before', 'openai_usage_after',
192
- 'openai_usage_delta', 'duration_minutes', 'cost_usd'
111
+ 'openai_usage_delta', 'duration_minutes', 'cost_usd', 'closed'
193
112
  ];
194
113
 
195
114
  // Columns coerced to non-negative integers on canonicalization.
@@ -382,9 +301,23 @@ function loadStatsCsv(filePath: string | null = null, options: LoadStatsCsvOptio
382
301
  return { headers: [...STATS_HEADERS], rows: [] };
383
302
  }
384
303
 
304
+ // Detect whether the loaded CSV already has the `closed` column (task-1380).
305
+ // Legacy CSVs (pre-closed) lack the column; their rows represent completed
306
+ // missions written at integration time, so default `closed` to 'yes' for
307
+ // backward compatibility. Modern CSVs already have the column set per-row.
308
+ const hasClosedColumn = data.headers.includes('closed');
309
+ const migratedRows = data.rows.map((row: Record<string, string>) => {
310
+ const normalized = normalizeStatsRow(row, { rootDir: options.rootDir });
311
+ if (!hasClosedColumn) {
312
+ // Legacy CSV: all rows are from integration time, treat as closed.
313
+ return { ...normalized, closed: 'yes' };
314
+ }
315
+ return { ...normalized, closed: row.closed || '' };
316
+ });
317
+
385
318
  return {
386
319
  headers: [...STATS_HEADERS],
387
- rows: data.rows.map((row: Record<string, string>) => normalizeStatsRow(row, { rootDir: options.rootDir })),
320
+ rows: migratedRows,
388
321
  };
389
322
  }
390
323
 
@@ -393,7 +326,8 @@ function loadStatsCsv(filePath: string | null = null, options: LoadStatsCsvOptio
393
326
  * Map any row (legacy 5-column or full 21-column) to the full schema, defaulting
394
327
  * missing text columns to '' and numeric columns to '0'. `stage` defaults to
395
328
  * 'default' so legacy rows and integration rows share the (repo, mission, stage)
396
- * upsert key.
329
+ * upsert key. `closed` defaults to '' (unset) — the backward-compat default of
330
+ * 'yes' for legacy CSV rows is applied exclusively in `loadStatsCsv`.
397
331
  */
398
332
  function normalizeStatsRow(row: StatsRow = {} as StatsRow, options: NormalizeStatsRowOptions = {} as NormalizeStatsRowOptions) {
399
333
  const repo = String(row.repo || options.repo || resolveStatsRepoName(options.rootDir)).trim();
@@ -419,6 +353,7 @@ function normalizeStatsRow(row: StatsRow = {} as StatsRow, options: NormalizeSta
419
353
  openai_usage_delta: row.openai_usage_delta || '0',
420
354
  duration_minutes: row.duration_minutes || '0',
421
355
  cost_usd: row.cost_usd || '0',
356
+ closed: row.closed || '',
422
357
  };
423
358
  }
424
359
 
@@ -819,11 +754,14 @@ function rowInWindow(row, window) {
819
754
  */
820
755
  function summarizeMissionWindow(rows, window) {
821
756
  const windowRows = rows.filter(row => rowInWindow(row, window));
757
+ // Filter to only closed missions (task-1380): rows without closed:'yes' are
758
+ // in-progress stage rows and should not inflate mission counts.
759
+ const closedRows = windowRows.filter(row => row.closed === 'yes');
822
760
  // Deduplicate by mission so multi-stage telemetry rows don't inflate counts.
823
761
  // One row per unique repo+mission pair is kept (first occurrence is sufficient
824
762
  // since classification is stable across stages for the same mission in a repo).
825
763
  const seenMissions = new Set();
826
- const uniqueMissions = windowRows.filter(row => {
764
+ const uniqueMissions = closedRows.filter(row => {
827
765
  const key = statsMissionKey(row);
828
766
  if (seenMissions.has(key)) {return false;}
829
767
  seenMissions.add(key);
@@ -834,7 +772,7 @@ function summarizeMissionWindow(rows, window) {
834
772
  const unknown = uniqueMissions.filter(row => normalizeClassification(row.classification) === 'unknown').length;
835
773
  const validMissions = uniqueMissions.filter(row => normalizeClassification(row.classification) !== null);
836
774
  return {
837
- rows: windowRows,
775
+ rows: closedRows,
838
776
  total: validMissions.length,
839
777
  userValue,
840
778
  aiSdlc,
@@ -882,10 +820,12 @@ function summarizeAgentWindow(rows, window, options = {}) {
882
820
  const opts = options;
883
821
  const { rootDir = null, deriveFixRoundsFn = deriveFixRoundsLocalAuthoritative } = opts;
884
822
  const windowRows = rows.filter(row => rowInWindow(row, window));
823
+ // Filter to only closed missions (task-1380).
824
+ const closedWindowRows = windowRows.filter(row => row.closed === 'yes');
885
825
  // Only count missions with a valid classification so the agent table totals
886
826
  // align with the mission-count table (which also excludes null/invalid
887
827
  // classifications via summarizeMissionWindow → validMissions).
888
- const validWindowRows = windowRows.filter(row => normalizeClassification(row.classification) !== null);
828
+ const validWindowRows = closedWindowRows.filter(row => normalizeClassification(row.classification) !== null);
889
829
  // Deduplicate globally by (repo, mission) first so each mission is counted
890
830
  // exactly once across all agent groups — matching the mission-count table.
891
831
  // Prefer the row where model === implementer (the implementer's own model),
@@ -1139,7 +1079,8 @@ function renderMissionPhaseReport(rows, slug, options = {}) {
1139
1079
  const wantedRepo = String(opts.repo || resolveStatsRepoName(opts.rootDir)).trim();
1140
1080
  const missionRows = (rows || []).filter(row =>
1141
1081
  String(row.mission || '').trim().toLowerCase() === wanted &&
1142
- String(row.repo || '').trim() === wantedRepo
1082
+ String(row.repo || '').trim() === wantedRepo &&
1083
+ row.closed === 'yes'
1143
1084
  );
1144
1085
 
1145
1086
  const byStage = new Map();
@@ -1620,6 +1561,7 @@ function canonicalizeStatsRow(row, options = {}) {
1620
1561
  classification: /** @type{string|number|boolean|undefined} */(normalizeClassification(row.classification)),
1621
1562
  implementer: /** @type{string|number|boolean|undefined} */(normalizeImplementer(row.implementer)),
1622
1563
  stage: String(row.stage || '').trim().toLowerCase() || 'default',
1564
+ closed: row.closed || '',
1623
1565
  };
1624
1566
  for (const key of USAGE_NUMBERS) {
1625
1567
  canonical[key] = String(Math.max(0, Number.parseInt(String(/** @type{any} */(normalized)[key]), 10) || 0));
@@ -1709,6 +1651,7 @@ function recordIntegrationStats(options = {}) {
1709
1651
  classification,
1710
1652
  implementer: implementerInfo.implementer,
1711
1653
  pr_fix_rounds: implementerInfo.prFixRounds,
1654
+ closed: 'yes',
1712
1655
  }, { filePath, rootDir });
1713
1656
 
1714
1657
  return {
@@ -2184,6 +2127,7 @@ if (typeof module !== 'undefined') { module.exports = stats; }
2184
2127
  deriveFinalImplementerFromBranchHistory,
2185
2128
  deriveImplementerAndFixRoundsFromPrComments,
2186
2129
  deriveImplementerAndFixRounds,
2130
+ summarizeMissionWindow,
2187
2131
  summarizeAgentWindow,
2188
2132
  colorAverageFixRounds,
2189
2133
  colorMissionCounts,
package/lib/core/fmt.ts CHANGED
@@ -98,8 +98,8 @@ export function branch(text: string): string { return colorize('magenta', text);
98
98
  export function sha(text: string): string { return colorize('yellow', text); }
99
99
  export function command(text: string): string { return colorize('green', text); }
100
100
 
101
- type LogFn = (...args: unknown[]) => void;
102
- type LogFunc = (text: string) => string | null;
101
+ type LogFn = (..._args: unknown[]) => void;
102
+ type LogFunc = (_text: string) => string | null;
103
103
 
104
104
  type Logger = {
105
105
  log: LogFn;
package/lib/core/git.ts CHANGED
@@ -1,4 +1,4 @@
1
- import childProcess, { spawnSync } from 'node:child_process';
1
+ import { spawnSync } from 'node:child_process';
2
2
  import type { SpawnSyncOptions } from 'node:child_process';
3
3
  import * as fsMod from 'node:fs';
4
4
  import * as pathMod from 'node:path';
@@ -90,7 +90,7 @@ interface RebaseStateResult {
90
90
  }
91
91
 
92
92
  interface GitRunner {
93
- (args: string[]): GitResult;
93
+ (_args: string[]): GitResult;
94
94
  }
95
95
 
96
96
  interface DetectRebaseOptions {
@@ -24,11 +24,11 @@ interface EnsureOptions {
24
24
  lstatSyncFn?: typeof fs.lstatSync;
25
25
  readFileSyncFn?: typeof fs.readFileSync;
26
26
  writeFileSyncFn?: typeof fs.writeFileSync;
27
- logFn?: (msg: string) => void;
27
+ logFn?: (_msg: string) => void;
28
28
  }
29
29
 
30
30
  type EnsureWorkflowGitignoreFn = {
31
- (rootDir: string, options?: EnsureOptions): GitignoreResult;
31
+ (_rootDir: string, _options?: EnsureOptions): GitignoreResult;
32
32
  WORKFLOW_ENTRIES: typeof WORKFLOW_ENTRIES;
33
33
  ensureWorkflowGitignore: EnsureWorkflowGitignoreFn;
34
34
  };
@@ -986,7 +986,7 @@ function findMissionDocInBranches(slug, rootDir = process.cwd(), gitRunner) {
986
986
  return candidates;
987
987
  }
988
988
  }
989
- catch (e) {
989
+ catch (_e) {
990
990
  return candidates;
991
991
  }
992
992
  const branches = branchResult.stdout.trim().split('\n')
@@ -1002,7 +1002,7 @@ function findMissionDocInBranches(slug, rootDir = process.cwd(), gitRunner) {
1002
1002
  break;
1003
1003
  }
1004
1004
  }
1005
- catch (err) {
1005
+ catch (_err) {
1006
1006
  // ignore
1007
1007
  }
1008
1008
  }
@@ -929,7 +929,7 @@ export function findMissionDocInBranches(slug: string, rootDir: string = process
929
929
  try {
930
930
  branchResult = runner(['-C', rootDir, 'branch', '-a', '--format=%(refname:short)']);
931
931
  if (branchResult.status !== 0) {return candidates;}
932
- } catch (e) {
932
+ } catch (_e) {
933
933
  return candidates;
934
934
  }
935
935
 
@@ -946,7 +946,7 @@ export function findMissionDocInBranches(slug: string, rootDir: string = process
946
946
  candidates.push({ branch, path: f });
947
947
  break;
948
948
  }
949
- } catch (err) {
949
+ } catch (_err) {
950
950
  // ignore
951
951
  }
952
952
  }
@@ -176,7 +176,7 @@ function migrateStats(options: { sourcePaths?: string[]; sourcePath?: string; de
176
176
 
177
177
  interface BlocklistSource { filePath: string; payload: Record<string, unknown>; blocklist: Record<string, unknown>; }
178
178
 
179
- function readBlocklistSource(filePath: string, warn: (...args: unknown[]) => void, hardFailure = false): BlocklistSource | null {
179
+ function readBlocklistSource(filePath: string, warn: (..._args: unknown[]) => void, hardFailure = false): BlocklistSource | null {
180
180
  if (!filePath || !fs.existsSync(filePath)) {return null;}
181
181
  try {
182
182
  const payload = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Record<string, unknown>;
@@ -208,7 +208,7 @@ function sameValue(left: unknown, right: unknown): boolean {
208
208
  * @param options - Migration configuration (warn callback, destinationPath, sourcePaths)
209
209
  * @returns Result with destination path, merged blocklist, and conflict details
210
210
  */
211
- function migrateAgentBlocklists(options: { warn?: (...args: unknown[]) => void; destinationPath?: string; sourcePaths?: string[] } = {}): { destinationPath: string; blocklist: Record<string, unknown>; conflicts: unknown[] } {
211
+ function migrateAgentBlocklists(options: { warn?: (..._args: unknown[]) => void; destinationPath?: string; sourcePaths?: string[] } = {}): { destinationPath: string; blocklist: Record<string, unknown>; conflicts: unknown[] } {
212
212
  const opts = options;
213
213
  const warn = opts.warn || (() => {});
214
214
  const destinationPath = opts.destinationPath || storage.resolveAgentsLocalPath({ ensureDir: true });
@@ -17,7 +17,7 @@ interface SpawnTeeResult {
17
17
  }
18
18
 
19
19
  interface NoOutputWatchdog {
20
- onNoOutput?: (event: { command: string; args: string[]; pid: number | undefined; elapsedMs: number }) => void;
20
+ onNoOutput?: (_event: { command: string; args: string[]; pid: number | undefined; elapsedMs: number }) => void;
21
21
  initialDelayMs?: number;
22
22
  intervalMs?: number;
23
23
  }
@@ -81,11 +81,11 @@ export function toVirtual(actualState: string, map: Record<string, unknown> = lo
81
81
  }
82
82
 
83
83
  interface TransitionVirtualOptions {
84
- log?: (msg: string) => void;
84
+ log?: (_msg: string) => void;
85
85
  }
86
86
 
87
87
  interface TransitionTaskFn {
88
- (slug: string, actual: string, options: TransitionVirtualOptions): boolean;
88
+ (_slug: string, _actual: string, _options: TransitionVirtualOptions): boolean;
89
89
  }
90
90
 
91
91
  export function transitionVirtual(transitionTaskFn: TransitionTaskFn, slug: string, virtualState: string, options: TransitionVirtualOptions = {}, mapParam?: Record<string, unknown>): boolean {
@@ -89,7 +89,7 @@ export function resolveParallixHome(
89
89
  */
90
90
  export interface ResolveStatsOptions {
91
91
  ensureDir?: boolean;
92
- warn?: (...args: unknown[]) => void;
92
+ warn?: (..._args: unknown[]) => void;
93
93
  }
94
94
 
95
95
  export function resolveStatsPath(options: ResolveStatsOptions = {}): string {
@@ -21,7 +21,7 @@ interface GitResult {
21
21
  error?: Error | null;
22
22
  }
23
23
 
24
- export type GitFn = (args: string[], options?: GitOptions) => GitResult;
24
+ export type GitFn = (_args: string[], _options?: GitOptions) => GitResult;
25
25
 
26
26
  export interface VerificationAdapterConfig {
27
27
  command: string | null;
@@ -14,7 +14,7 @@ import { resolveWorktree, isMissionArtifact, isWorkflowGeneratedArtifact } from
14
14
  import { isProviderEnabled } from './review-adapter.js';
15
15
  import { spawnSync } from 'child_process';
16
16
 
17
- type RunFn = (cmd: string, args: string[], opts: { cwd?: string; encoding?: string; stdio?: unknown[] }) => { status: number | null; stdout?: string; stderr?: string };
17
+ type RunFn = (_cmd: string, _args: string[], _opts: { cwd?: string; encoding?: string; stdio?: unknown[] }) => { status: number | null; stdout?: string; stderr?: string };
18
18
 
19
19
  const _spawnSync = spawnSync as unknown as RunFn;
20
20
 
@@ -39,11 +39,11 @@ export async function commitSafeMissionArtifacts(slug: string, worktree: string,
39
39
  }: {
40
40
  taskFile?: string | null;
41
41
  gitFn?: typeof git;
42
- log?: (msg: string) => void;
43
- error?: (msg: string) => void;
44
- isMissionArtifactFn?: (file: string, slug: string, rootDir: string) => boolean;
45
- isWorkflowGeneratedArtifactFn?: (file: string) => boolean;
46
- resolveStatsRelPathFn?: (rootDir: string) => string | null;
42
+ log?: (_msg: string) => void;
43
+ error?: (_msg: string) => void;
44
+ isMissionArtifactFn?: (_file: string, _slug: string, _rootDir: string) => boolean;
45
+ isWorkflowGeneratedArtifactFn?: (_file: string) => boolean;
46
+ resolveStatsRelPathFn?: (_rootDir: string) => string | null;
47
47
  } = {}): Promise<{ ok: boolean; dirty: boolean; unsafe?: boolean }> {
48
48
  const rootDir = worktree || process.cwd();
49
49
  const statusResult = gitFn(['-C', rootDir, 'status', '--porcelain=v1', '-z']);
@@ -131,15 +131,15 @@ export async function rebaseBeforeReviewRound(slug: string, {
131
131
  legacyIsForgejoReviewEnabledFn = null,
132
132
  isForgejoReviewEnabledFn = null
133
133
  }: {
134
- runFn?: (cmd: string, args: string[], opts: { cwd?: string; encoding?: string; stdio?: unknown[] }) => { status: number | null; stdout?: string; stderr?: string };
134
+ runFn?: (_cmd: string, _args: string[], _opts: { cwd?: string; encoding?: string; stdio?: unknown[] }) => { status: number | null; stdout?: string; stderr?: string };
135
135
  gitFn?: typeof git;
136
136
  taskFile?: string | null;
137
137
  worktree?: string;
138
- log?: (msg: string) => void;
139
- error?: (msg: string) => void;
140
- isReviewProviderEnabledFn?: ((wt: string) => boolean) | undefined | null;
141
- legacyIsForgejoReviewEnabledFn?: ((wt: string) => boolean) | null;
142
- isForgejoReviewEnabledFn?: ((wt: string) => boolean) | null;
138
+ log?: (_msg: string) => void;
139
+ error?: (_msg: string) => void;
140
+ isReviewProviderEnabledFn?: ((_wt: string) => boolean) | undefined | null;
141
+ legacyIsForgejoReviewEnabledFn?: ((_wt: string) => boolean) | null;
142
+ isForgejoReviewEnabledFn?: ((_wt: string) => boolean) | null;
143
143
  } = {}): Promise<{ ok: boolean; sharedFileConflicts: boolean }> {
144
144
  const cleanup = await commitSafeMissionArtifacts(slug, worktree, { taskFile, gitFn, log, error });
145
145
  if (!cleanup.ok) {
@@ -102,12 +102,12 @@ function postWorkflowComment(
102
102
  slug: string,
103
103
  message: string,
104
104
  options: {
105
- log?: (msg: string) => void;
106
- error?: (msg: string) => void;
107
- readTokenFn?: (user: string, opts: { rootDir?: string }) => string | null;
108
- postCommentFn?: (branch: string, token: string, body: string, opts?: Record<string, unknown>) => unknown;
109
- buildMetadataFooterFn?: (s: string, r?: string) => string;
110
- readReviewStateFn?: (s: string, r?: string) => any;
105
+ log?: (_msg: string) => void;
106
+ error?: (_msg: string) => void;
107
+ readTokenFn?: (_user: string, _opts: { rootDir?: string }) => string | null;
108
+ postCommentFn?: (_branch: string, _token: string, _body: string, _opts?: Record<string, unknown>) => unknown;
109
+ buildMetadataFooterFn?: (_s: string, _r?: string) => string;
110
+ readReviewStateFn?: (_s: string, _r?: string) => any;
111
111
  rootDir?: string;
112
112
  reviewIdentity?: string;
113
113
  forgejoUser?: string;
@@ -172,11 +172,11 @@ function recordLocalReviewVerdict(
172
172
  outcome: string,
173
173
  options: {
174
174
  worktree?: string;
175
- writeReviewStateFn?: (s: string, st: any, w: string) => boolean;
176
- createEventFn?: (s: string, t: string, p: Record<string, unknown>, o: Record<string, unknown>) => CreateResult;
177
- readReviewStateFn?: (s: string, r?: string) => any;
178
- log?: (msg: string) => void;
179
- error?: (msg: string) => void;
175
+ writeReviewStateFn?: (_s: string, _st: any, _w: string) => boolean;
176
+ createEventFn?: (_s: string, _t: string, _p: Record<string, unknown>, _o: Record<string, unknown>) => CreateResult;
177
+ readReviewStateFn?: (_s: string, _r?: string) => any;
178
+ log?: (_msg: string) => void;
179
+ error?: (_msg: string) => void;
180
180
  reviewer?: string;
181
181
  } = {}
182
182
  ): void {
@@ -216,18 +216,18 @@ function postWorkflowReview(
216
216
  outcome: string,
217
217
  message: string,
218
218
  options: {
219
- log?: (msg: string) => void;
220
- error?: (msg: string) => void;
221
- readTokenFn?: (user: string, opts: { rootDir?: string }) => string | null;
222
- postReviewFn?: (branch: string, token: string, outcome: string, body: string, opts?: Record<string, unknown>) => unknown;
223
- buildMetadataFooterFn?: (s: string, r?: string) => string;
224
- readReviewStateFn?: (s: string, r?: string) => any;
219
+ log?: (_msg: string) => void;
220
+ error?: (_msg: string) => void;
221
+ readTokenFn?: (_user: string, _opts: { rootDir?: string }) => string | null;
222
+ postReviewFn?: (_branch: string, _token: string, _outcome: string, _body: string, _opts?: Record<string, unknown>) => unknown;
223
+ buildMetadataFooterFn?: (_s: string, _r?: string) => string;
224
+ readReviewStateFn?: (_s: string, _r?: string) => any;
225
225
  worktree?: string;
226
226
  reviewIdentity?: string;
227
227
  forgejoUser?: string;
228
- getPrAuthorFn?: (branch: string, token: string, opts?: Record<string, unknown>) => unknown;
229
- writeReviewStateFn?: (s: string, st: any, w: string) => boolean;
230
- createEventFn?: (s: string, t: string, p: Record<string, unknown>, o: Record<string, unknown>) => CreateResult;
228
+ getPrAuthorFn?: (_branch: string, _token: string, _opts?: Record<string, unknown>) => unknown;
229
+ writeReviewStateFn?: (_s: string, _st: any, _w: string) => boolean;
230
+ createEventFn?: (_s: string, _t: string, _p: Record<string, unknown>, _o: Record<string, unknown>) => CreateResult;
231
231
  } = {}
232
232
  ): { ok: boolean; error?: string; skipped?: boolean; reason?: string; prAuthor?: unknown } {
233
233
  const log = options.log || fmt.log.plain;
@@ -292,8 +292,8 @@ async function consumeReviewerArtifacts(
292
292
  slug: string,
293
293
  reviewer: string,
294
294
  options: {
295
- log?: (msg: string) => void;
296
- error?: (msg: string) => void;
295
+ log?: (_msg: string) => void;
296
+ error?: (_msg: string) => void;
297
297
  readArtifactFn?: typeof readArtifactFile;
298
298
  deleteArtifactFn?: typeof deleteArtifactFile;
299
299
  tmpDir?: string | null;
@@ -301,12 +301,12 @@ async function consumeReviewerArtifacts(
301
301
  worktree?: string;
302
302
  providerEnabled?: boolean | null;
303
303
  forgejoEnabled?: boolean | null;
304
- readTokenFn?: (user: string, opts?: Record<string, unknown>) => string | null;
305
- getCommentsFn?: (branch: string, token: string) => Promise<unknown[]>;
306
- postCommentFn?: (branch: string, token: string, body: string, opts?: Record<string, unknown>) => unknown;
307
- postReviewFn?: (branch: string, token: string, outcome: string, body: string, opts?: Record<string, unknown>) => unknown;
308
- buildMetadataFooterFn?: (s: string, r?: string) => string;
309
- createEventFn?: (s: string, t: string, p: CreateEventParams, o: CreateEventOptions) => CreateEventResult;
304
+ readTokenFn?: (_user: string, _opts?: Record<string, unknown>) => string | null;
305
+ getCommentsFn?: (_branch: string, _token: string) => Promise<unknown[]>;
306
+ postCommentFn?: (_branch: string, _token: string, _body: string, _opts?: Record<string, unknown>) => unknown;
307
+ postReviewFn?: (_branch: string, _token: string, _outcome: string, _body: string, _opts?: Record<string, unknown>) => unknown;
308
+ buildMetadataFooterFn?: (_s: string, _r?: string) => string;
309
+ createEventFn?: (_s: string, _t: string, _p: CreateEventParams, _o: CreateEventOptions) => CreateEventResult;
310
310
  } = {}
311
311
  ): Promise<{ consumed: boolean; ok?: boolean; reviewState?: string | null }> {
312
312
  const log = options.log || fmt.log.plain;
@@ -452,8 +452,8 @@ async function consumeImplementerArtifacts(
452
452
  slug: string,
453
453
  implementer: string,
454
454
  options: {
455
- log?: (msg: string) => void;
456
- error?: (msg: string) => void;
455
+ log?: (_msg: string) => void;
456
+ error?: (_msg: string) => void;
457
457
  readArtifactFn?: typeof readArtifactFile;
458
458
  deleteArtifactFn?: typeof deleteArtifactFile;
459
459
  tmpDir?: string | null;
@@ -461,11 +461,11 @@ async function consumeImplementerArtifacts(
461
461
  worktree?: string;
462
462
  providerEnabled?: boolean | null;
463
463
  forgejoEnabled?: boolean | null;
464
- readTokenFn?: (user: string, opts?: Record<string, unknown>) => string | null;
465
- getCommentsFn?: (branch: string, token: string) => Promise<unknown[]>;
466
- postCommentFn?: (branch: string, token: string, body: string, opts?: Record<string, unknown>) => unknown;
467
- buildMetadataFooterFn?: (s: string, r?: string) => string;
468
- createEventFn?: (s: string, t: string, p: CreateEventParams, o: CreateEventOptions) => CreateEventResult;
464
+ readTokenFn?: (_user: string, _opts?: Record<string, unknown>) => string | null;
465
+ getCommentsFn?: (_branch: string, _token: string) => Promise<unknown[]>;
466
+ postCommentFn?: (_branch: string, _token: string, _body: string, _opts?: Record<string, unknown>) => unknown;
467
+ buildMetadataFooterFn?: (_s: string, _r?: string) => string;
468
+ createEventFn?: (_s: string, _t: string, _p: CreateEventParams, _o: CreateEventOptions) => CreateEventResult;
469
469
  } = {}
470
470
  ): Promise<{ consumed: boolean; ok?: boolean; disposition?: string | null }> {
471
471
  const log = options.log || fmt.log.plain;