next-issue 2.4.0 → 2.4.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.
package/dist/state.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- export const STATE_DIR = ".next-issue";
1
+ import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ export const STATE_DIR = '.next-issue';
4
4
  function dir(repo) {
5
5
  return join(repo.root, STATE_DIR);
6
6
  }
@@ -11,10 +11,10 @@ export async function readState(repo, issue) {
11
11
  const path = file(repo, issue);
12
12
  let text;
13
13
  try {
14
- text = await readFile(path, "utf8");
14
+ text = await readFile(path, 'utf8');
15
15
  }
16
16
  catch (error) {
17
- if (error.code === "ENOENT") {
17
+ if (error.code === 'ENOENT') {
18
18
  return undefined;
19
19
  }
20
20
  throw error;
@@ -31,34 +31,34 @@ export async function readState(repo, issue) {
31
31
  }
32
32
  return parsed;
33
33
  }
34
- const PHASES = ["claimed", "implemented", "review"];
34
+ const PHASES = ['claimed', 'implemented', 'review'];
35
35
  function isRecord(value) {
36
- return typeof value === "object" && value !== null;
36
+ return typeof value === 'object' && value !== null;
37
37
  }
38
38
  function isOptional(value, type) {
39
39
  return value === undefined || typeof value === type;
40
40
  }
41
41
  function isBaseFix(value) {
42
- return isRecord(value) && typeof value.sha === "string" && typeof value.head === "string";
42
+ return isRecord(value) && typeof value.sha === 'string' && typeof value.head === 'string';
43
43
  }
44
44
  function isReviewRound(value) {
45
45
  return (isRecord(value) &&
46
- typeof value.round === "number" &&
47
- typeof value.fingerprint === "string" &&
48
- typeof value.findings === "string" &&
49
- isOptional(value.dispute, "string"));
46
+ typeof value.round === 'number' &&
47
+ typeof value.fingerprint === 'string' &&
48
+ typeof value.findings === 'string' &&
49
+ isOptional(value.dispute, 'string'));
50
50
  }
51
51
  function isState(value) {
52
52
  return (isRecord(value) &&
53
- typeof value.issue === "number" &&
53
+ typeof value.issue === 'number' &&
54
54
  PHASES.includes(value.phase) &&
55
- typeof value.branch === "string" &&
56
- isOptional(value.pr, "number") &&
57
- isOptional(value.title, "string") &&
58
- typeof value.ciFixes === "number" &&
59
- isOptional(value.commitFixes, "number") &&
55
+ typeof value.branch === 'string' &&
56
+ isOptional(value.pr, 'number') &&
57
+ isOptional(value.title, 'string') &&
58
+ typeof value.ciFixes === 'number' &&
59
+ isOptional(value.commitFixes, 'number') &&
60
60
  (value.baseFix === undefined || isBaseFix(value.baseFix)) &&
61
- typeof value.reviewRounds === "number" &&
61
+ typeof value.reviewRounds === 'number' &&
62
62
  Array.isArray(value.reviewLog) &&
63
63
  value.reviewLog.every(isReviewRound));
64
64
  }
@@ -68,7 +68,7 @@ export async function savedIssues(repo) {
68
68
  names = await readdir(dir(repo));
69
69
  }
70
70
  catch (error) {
71
- if (error.code === "ENOENT") {
71
+ if (error.code === 'ENOENT') {
72
72
  return [];
73
73
  }
74
74
  throw error;
package/dist/summary.mjs CHANGED
@@ -1,16 +1,16 @@
1
- const OUTCOMES = ["done", "needs-human", "error", "skipped", "stopped"];
1
+ const OUTCOMES = ['done', 'needs-human', 'error', 'skipped', 'stopped'];
2
2
  export function formatSummary(summary) {
3
- const tally = OUTCOMES.map((outcome) => `${summary.issues.filter((report) => report.outcome === outcome).length} ${outcome}`).join(", ");
3
+ const tally = OUTCOMES.map((outcome) => `${summary.issues.filter((report) => report.outcome === outcome).length} ${outcome}`).join(', ');
4
4
  const lines = [`run ${summary.runId}`, `issues ${summary.issues.length}: ${tally}`];
5
5
  for (const report of summary.issues) {
6
6
  lines.push(` ${issueLine(report)}`);
7
7
  }
8
8
  const roles = Object.entries(summary.roles)
9
9
  .map(([role, totals]) => `${role} ${tokens(totals.total)} in ${totals.calls}`)
10
- .join(", ");
11
- lines.push(`tokens ${tokens(summary.total.total)}${roles.length > 0 ? ` (${roles})` : ""}`);
10
+ .join(', ');
11
+ lines.push(`tokens ${tokens(summary.total.total)}${roles.length > 0 ? ` (${roles})` : ''}`);
12
12
  lines.push(`log ${summary.logFile}`);
13
- return `${lines.join("\n")}\n`;
13
+ return `${lines.join('\n')}\n`;
14
14
  }
15
15
  function issueLine(report) {
16
16
  const parts = [`#${report.issue} ${report.outcome.padEnd(11)}`];
@@ -29,12 +29,12 @@ function issueLine(report) {
29
29
  if (report.reason !== undefined) {
30
30
  parts.push(`(${report.reason})`);
31
31
  }
32
- return parts.join(" ").trimEnd();
32
+ return parts.join(' ').trimEnd();
33
33
  }
34
34
  function duration(ms) {
35
35
  const seconds = Math.round(ms / 1000);
36
36
  const minutes = Math.floor(seconds / 60);
37
- return minutes === 0 ? `${seconds}s` : `${minutes}m${String(seconds % 60).padStart(2, "0")}s`;
37
+ return minutes === 0 ? `${seconds}s` : `${minutes}m${String(seconds % 60).padStart(2, '0')}s`;
38
38
  }
39
39
  function tokens(value) {
40
40
  if (value < 1000) {
package/dist/verdict.mjs CHANGED
@@ -1,42 +1,44 @@
1
1
  export const VERDICT_SCHEMA = {
2
- type: "object",
2
+ type: 'object',
3
3
  properties: {
4
- summary: { type: "string", description: "Short summary of the review" },
4
+ summary: { type: 'string', description: 'Short summary of the review' },
5
5
  findings: {
6
- type: "array",
7
- description: "One entry per problem. Empty when nothing is wrong.",
6
+ type: 'array',
7
+ description: 'One entry per problem. Empty when nothing is wrong.',
8
8
  items: {
9
- type: "object",
9
+ type: 'object',
10
10
  properties: {
11
11
  severity: {
12
- type: "string",
13
- enum: ["blocking", "minor"],
14
- description: "blocking for a wrong result, a missing part of the issue, a regression or a broken convention; minor for taste and style",
12
+ type: 'string',
13
+ enum: ['blocking', 'minor'],
14
+ description: 'blocking for a wrong result, a missing part of the issue, a regression or a broken convention; minor for taste and style',
15
15
  },
16
- detail: { type: "string", description: "What is wrong and where" },
16
+ detail: { type: 'string', description: 'What is wrong and where' },
17
17
  },
18
- required: ["severity", "detail"],
18
+ required: ['severity', 'detail'],
19
19
  },
20
20
  },
21
21
  },
22
- required: ["summary", "findings"],
22
+ required: ['summary', 'findings'],
23
23
  };
24
- const SEVERITIES = ["blocking", "minor"];
24
+ const SEVERITIES = ['blocking', 'minor'];
25
25
  export function readVerdict(value) {
26
- if (typeof value !== "object" || value === null) {
26
+ if (typeof value !== 'object' || value === null) {
27
27
  return undefined;
28
28
  }
29
29
  const { summary, findings } = value;
30
- if (typeof summary !== "string" || !Array.isArray(findings)) {
30
+ if (typeof summary !== 'string' || !Array.isArray(findings)) {
31
31
  return undefined;
32
32
  }
33
33
  const checked = [];
34
34
  for (const item of findings) {
35
- if (typeof item !== "object" || item === null) {
35
+ if (typeof item !== 'object' || item === null) {
36
36
  return undefined;
37
37
  }
38
38
  const { severity, detail } = item;
39
- if (typeof severity !== "string" || !SEVERITIES.includes(severity) || typeof detail !== "string") {
39
+ if (typeof severity !== 'string' ||
40
+ !SEVERITIES.includes(severity) ||
41
+ typeof detail !== 'string') {
40
42
  return undefined;
41
43
  }
42
44
  checked.push({ severity: severity, detail });
@@ -44,27 +46,30 @@ export function readVerdict(value) {
44
46
  return { summary, findings: checked };
45
47
  }
46
48
  export function blockingFindings(verdict) {
47
- return verdict.findings.filter((finding) => finding.severity === "blocking");
49
+ return verdict.findings.filter((finding) => finding.severity === 'blocking');
48
50
  }
49
51
  export function openFindings(verdict, lastRound) {
50
52
  return lastRound ? blockingFindings(verdict) : verdict.findings;
51
53
  }
52
- const LEFT = "The findings above stay as they are: no blocking finding is open and the review budget is spent.";
54
+ const LEFT = 'The findings above stay as they are: no blocking finding is open and the review budget is spent.';
53
55
  export function formatVerdict(verdict, open) {
54
56
  const approved = open.length === 0;
55
- const head = approved ? "Review: approved" : "Review: changes requested";
57
+ const head = approved ? 'Review: approved' : 'Review: changes requested';
56
58
  const lines = verdict.findings.map((finding) => `- **${finding.severity}** ${finding.detail}`);
57
- const note = approved && verdict.findings.length > 0 ? LEFT : "";
58
- return [`### ${head}`, verdict.summary, lines.join("\n"), note]
59
+ const note = approved && verdict.findings.length > 0 ? LEFT : '';
60
+ return [`### ${head}`, verdict.summary, lines.join('\n'), note]
59
61
  .filter((part) => part.length > 0)
60
- .join("\n\n");
62
+ .join('\n\n');
61
63
  }
62
64
  export function formatFindings(findings) {
63
- return findings.map((finding) => `- ${finding.detail}`).join("\n");
65
+ return findings.map((finding) => `- ${finding.detail}`).join('\n');
64
66
  }
65
67
  export function fingerprint(findings) {
66
68
  return findings
67
- .map((finding) => finding.detail.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim())
69
+ .map((finding) => finding.detail
70
+ .toLowerCase()
71
+ .replace(/[^a-z0-9]+/g, ' ')
72
+ .trim())
68
73
  .sort()
69
- .join("|");
74
+ .join('|');
70
75
  }
package/dist/version.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join } from "node:path";
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
3
  export async function packageVersion() {
4
- const raw = await readFile(join(import.meta.dirname, "..", "package.json"), "utf8");
4
+ const raw = await readFile(join(import.meta.dirname, '..', 'package.json'), 'utf8');
5
5
  return JSON.parse(raw).version;
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-issue",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "description": "Autonomous issue harness driven by the pi SDK",
5
5
  "keywords": [
6
6
  "github",
@@ -34,6 +34,10 @@
34
34
  "start": "node src/cli.mts",
35
35
  "test": "node --test \"test/**/*.test.mts\"",
36
36
  "typecheck": "tsc --noEmit",
37
+ "lint": "oxlint --deny-warnings",
38
+ "lint:fix": "oxlint --fix",
39
+ "format": "oxfmt",
40
+ "format:check": "oxfmt --check",
37
41
  "prepack": "npm run build",
38
42
  "prepublishOnly": "npm run typecheck && npm test"
39
43
  },
@@ -41,6 +45,8 @@
41
45
  "@earendil-works/pi-ai": "^1.5.0",
42
46
  "@types/node": "^26.6.6",
43
47
  "@types/ws": "^8.18.2",
48
+ "oxfmt": "^0.74.0",
49
+ "oxlint": "^1.89.0",
44
50
  "typescript": "^7.0.2"
45
51
  },
46
52
  "dependencies": {