reslop 0.2.7 → 0.3.0

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 (158) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +16 -14
  3. package/bin/reslop.js +8 -5
  4. package/lib/agents/agents.js +504 -0
  5. package/lib/agents/allow.js +71 -0
  6. package/lib/agents/allow.json +16 -0
  7. package/lib/agents/models.js +460 -0
  8. package/lib/api.js +15 -12
  9. package/lib/cli.js +43 -68
  10. package/lib/{files.js → common/files.js} +48 -55
  11. package/lib/{find.js → common/find.js} +1 -1
  12. package/lib/common/format.js +101 -0
  13. package/lib/{utilities.js → common/process.js} +7 -95
  14. package/lib/common/tasks.json +27 -0
  15. package/lib/common/utilities.js +112 -0
  16. package/lib/dashboard/git.js +9 -16
  17. package/lib/dashboard/model.js +90 -49
  18. package/lib/dashboard/npm.js +17 -13
  19. package/lib/dashboard/tree.js +27 -30
  20. package/lib/{session → dashboard}/watch.js +36 -31
  21. package/lib/{workspace.js → dashboard/workspace.js} +23 -38
  22. package/lib/{deps.js → deps/deps.js} +31 -45
  23. package/lib/{deps-diff.js → deps/diff.js} +35 -25
  24. package/lib/{manifest.js → deps/manifest.js} +47 -30
  25. package/lib/diff/align.js +1 -2
  26. package/lib/diff/diff.js +2 -2
  27. package/lib/diff/display.js +29 -5
  28. package/lib/diff/inline.js +10 -14
  29. package/lib/diff/items.js +6 -4
  30. package/lib/diff/patch.js +39 -3
  31. package/lib/diff/unit.js +45 -16
  32. package/lib/{git-branches.js → git/branches.js} +53 -48
  33. package/lib/{git-deps.js → git/deps.js} +123 -96
  34. package/lib/git/git.js +71 -0
  35. package/lib/git/worktree.js +475 -0
  36. package/lib/highlight/bash.js +1 -2
  37. package/lib/highlight/csv.js +1 -2
  38. package/lib/highlight/dot.js +1 -2
  39. package/lib/{highlight.js → highlight/highlight.js} +42 -11
  40. package/lib/highlight/html.js +20 -18
  41. package/lib/highlight/json.js +1 -2
  42. package/lib/highlight/log.js +1 -2
  43. package/lib/highlight/txt.js +1 -2
  44. package/lib/{session → input}/actions.js +111 -48
  45. package/lib/{editor.js → input/editor.js} +7 -8
  46. package/lib/{keys.js → input/keys.js} +15 -43
  47. package/lib/input/line-edit.js +77 -0
  48. package/lib/input/menu-pick.js +70 -0
  49. package/lib/render/agents.js +640 -0
  50. package/lib/render/branches.js +4 -5
  51. package/lib/render/commits.js +54 -64
  52. package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
  53. package/lib/render/{dash-blocks.js → dashboard/blocks.js} +131 -137
  54. package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
  55. package/lib/render/diff.js +166 -45
  56. package/lib/render/field.js +131 -0
  57. package/lib/render/files.js +9 -14
  58. package/lib/render/footer.js +383 -0
  59. package/lib/render/frame.js +123 -25
  60. package/lib/render/header.js +176 -0
  61. package/lib/render/log.js +319 -0
  62. package/lib/render/menu.js +60 -0
  63. package/lib/render/notes.js +16 -11
  64. package/lib/render/npm.js +322 -196
  65. package/lib/render/packages.js +9 -13
  66. package/lib/render/primitives.js +101 -15
  67. package/lib/render/render.js +7 -11
  68. package/lib/render/repos.js +13 -25
  69. package/lib/render/status.js +370 -0
  70. package/lib/render/status.json +85 -0
  71. package/lib/render/{dash-table.js → table.js} +13 -60
  72. package/lib/render/tiles.js +109 -48
  73. package/lib/render/unit.js +32 -13
  74. package/lib/{report-model.js → report/model.js} +4 -6
  75. package/lib/{report-parse.js → report/parse.js} +9 -14
  76. package/lib/{report-render.js → report/render.js} +46 -14
  77. package/lib/{report-run.js → report/run.js} +42 -30
  78. package/lib/{report-test.js → report/test.js} +56 -55
  79. package/lib/{report-tools.js → report/tools.js} +24 -33
  80. package/lib/review/agent-instructions.md +9 -0
  81. package/lib/review/format.js +426 -0
  82. package/lib/{review-merge.js → review/merge.js} +9 -8
  83. package/lib/review/review.js +190 -0
  84. package/lib/review/store.js +281 -0
  85. package/lib/review/templates.js +83 -0
  86. package/lib/runs/commands.js +14 -0
  87. package/lib/runs/logs.js +299 -0
  88. package/lib/runs/output.js +174 -0
  89. package/lib/runs/process.js +113 -0
  90. package/lib/{runs.js → runs/runs.js} +84 -36
  91. package/lib/runs/scripts.js +150 -0
  92. package/lib/session/agents/agents.js +958 -0
  93. package/lib/session/agents/job.js +320 -0
  94. package/lib/session/agents/jobs.js +355 -0
  95. package/lib/session/agents/options.js +153 -0
  96. package/lib/session/agents/plans.js +83 -0
  97. package/lib/session/branches.js +33 -39
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +91 -52
  100. package/lib/session/compose.js +181 -145
  101. package/lib/session/dashboard.js +23 -22
  102. package/lib/session/dispatch.js +85 -21
  103. package/lib/session/files-pane.js +47 -26
  104. package/lib/session/find.js +16 -29
  105. package/lib/session/import.js +82 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +75 -72
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +6 -3
  110. package/lib/session/npm/history.js +249 -0
  111. package/lib/session/npm/npm.js +815 -0
  112. package/lib/session/{packages.js → npm/packages.js} +21 -14
  113. package/lib/session/ops.js +125 -34
  114. package/lib/session/pointer.js +202 -10
  115. package/lib/session/prefs.js +112 -0
  116. package/lib/session/reads.js +109 -0
  117. package/lib/session/review.js +28 -10
  118. package/lib/session/run-panel.js +40 -0
  119. package/lib/{session.js → session/session.js} +156 -63
  120. package/lib/session/tasks.js +320 -65
  121. package/lib/session/templates.js +1 -1
  122. package/lib/session/terminal.js +94 -12
  123. package/lib/session/unit.js +35 -30
  124. package/lib/session/update.js +194 -6
  125. package/lib/session/view.js +65 -65
  126. package/lib/session/workspace.js +39 -78
  127. package/lib/{capabilities.js → source/capabilities.js} +3 -12
  128. package/lib/{github.js → source/github.js} +102 -32
  129. package/lib/{gitlab.js → source/gitlab.js} +87 -10
  130. package/lib/{remote.js → source/remote.js} +144 -40
  131. package/lib/source/review-threads.graphql +20 -0
  132. package/lib/source/source.js +74 -0
  133. package/lib/term/ansi.js +396 -0
  134. package/lib/{clipboard.js → term/clipboard.js} +3 -6
  135. package/lib/{select.js → term/select.js} +2 -2
  136. package/lib/term/themes/dark.json +122 -0
  137. package/lib/term/themes/light.json +122 -0
  138. package/lib/term/wide.txt +21 -0
  139. package/lib/{wrap.js → term/wrap.js} +4 -4
  140. package/package.json +2 -2
  141. package/lib/ansi.js +0 -545
  142. package/lib/detect.js +0 -34
  143. package/lib/diff/edit.js +0 -33
  144. package/lib/git-worktree.js +0 -455
  145. package/lib/git.js +0 -79
  146. package/lib/npm-commands.js +0 -522
  147. package/lib/render/chrome.js +0 -806
  148. package/lib/render/present.js +0 -19
  149. package/lib/render/report.js +0 -43
  150. package/lib/render/search.js +0 -25
  151. package/lib/review.js +0 -745
  152. package/lib/session/accessors.js +0 -26
  153. package/lib/session/edit-click.js +0 -57
  154. package/lib/session/npm.js +0 -452
  155. package/lib/session/progress.js +0 -41
  156. package/lib/source.js +0 -36
  157. package/lib/update.js +0 -192
  158. /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
@@ -1,16 +1,15 @@
1
1
  'use strict';
2
2
 
3
- const utilities = require('./utilities.js');
4
- const { unquote } = utilities;
3
+ const { unquote } = require('../common/utilities.js');
4
+ const { formatTable } = require('../runs/output.js');
5
5
 
6
6
  const NOTICE_HEAD =
7
7
  'reslop: reduced output; passing results are omitted ' +
8
8
  'and similar problems are grouped';
9
9
 
10
10
  const notice = (rawFile) => {
11
- const target = rawFile
12
- ? `the raw log \`.log/${rawFile}\``
13
- : 'the matching raw log under `.log/`';
11
+ let target = 'the matching raw log under `.log/`';
12
+ if (rawFile) target = `the raw log \`.log/${rawFile}\``;
14
13
  const hint = `Agent: if this report hides a needed detail, read ${target}.`;
15
14
  return `${NOTICE_HEAD}\n${hint}`;
16
15
  };
@@ -38,12 +37,12 @@ const sameValue = (line, value) => {
38
37
  return text === value || unquote(text) === value;
39
38
  };
40
39
 
41
- const startsDiff = (line, item) =>
42
- isComparison(line) ||
43
- isDiffHeader(line) ||
44
- isMarkedDiff(line) ||
45
- sameValue(line, item.expected) ||
46
- sameValue(line, item.actual);
40
+ const startsDiff = (line, item) => {
41
+ if (isComparison(line) || isDiffHeader(line)) return true;
42
+ if (isMarkedDiff(line)) return true;
43
+ if (sameValue(line, item.expected)) return true;
44
+ return sameValue(line, item.actual);
45
+ };
47
46
 
48
47
  const displayMessage = (item) => {
49
48
  const values = item.expected !== '' || item.actual !== '';
@@ -135,13 +134,46 @@ const loggedText = (text, rawFile) => {
135
134
  return `${head}\n\n${body}`;
136
135
  };
137
136
 
137
+ const reportProblem = (item) => {
138
+ const title = item.name || item.message || item.code || 'problem';
139
+ const mark = item.severity === 'warning' ? '△' : '✖';
140
+ const count = item.count > 1 ? ` × ${item.count}` : '';
141
+ const lines = ['', `${mark} ${title.split('\n')[0]}${count}`, ''];
142
+ lines.push(...formatTable(problemRows(item)));
143
+ const message = displayMessage(item);
144
+ if (message) lines.push('', ...quoteLines(message));
145
+ if (item.trace) {
146
+ lines.push('', 'Stack');
147
+ for (const row of item.trace.split(/\r?\n/)) {
148
+ const frame = row.trim().replace(/^at\s+/, '');
149
+ if (frame) lines.push(` at ${frame}`);
150
+ }
151
+ }
152
+ if (item.related.length) {
153
+ lines.push('', 'Related failures');
154
+ for (const row of item.related) lines.push(` ↳ ${row}`);
155
+ }
156
+ return lines;
157
+ };
158
+
159
+ const renderReport = (doc) => {
160
+ const lines = [];
161
+ for (const item of doc.reports) {
162
+ if (lines.length) lines.push('');
163
+ const mark = item.ok ? '✔' : '✖';
164
+ lines.push(`${mark} ${item.tool} · exit ${doc.exit}`, '');
165
+ lines.push(...formatTable(summaryRows(item.summary)));
166
+ for (const entry of item.problems) lines.push(...reportProblem(entry));
167
+ }
168
+ if (doc.stderr) lines.push('', 'Stderr', doc.stderr);
169
+ return `${lines.join('\n')}\n`;
170
+ };
171
+
138
172
  module.exports = {
139
173
  NOTICE_HEAD,
140
174
  notice,
141
- summaryRows,
142
- problemRows,
143
175
  displayMessage,
144
- quoteLines,
145
176
  loggedText,
146
177
  renderDocument,
178
+ renderReport,
147
179
  };
@@ -1,19 +1,18 @@
1
1
  'use strict';
2
2
 
3
3
  const path = require('node:path');
4
- const childProcess = require('node:child_process');
5
- const { spawn } = childProcess;
4
+ const { spawn } = require('node:child_process');
6
5
 
7
- const utilities = require('./utilities.js');
8
- const { spawnBase } = utilities;
9
- const npmCommands = require('./npm-commands.js');
10
- const { nextLogFile, saveLogs } = npmCommands;
11
- const reportParse = require('./report-parse.js');
12
- const { toolHint, buildDocument } = reportParse;
13
- const reportRender = require('./report-render.js');
14
- const { renderDocument } = reportRender;
15
- const runs = require('./runs.js');
16
- const { RunRecorder, commandLabel, summarizeDocument } = runs;
6
+ const { spawnBase } = require('../common/process.js');
7
+ const { withNewline } = require('../common/utilities.js');
8
+ const { nextLogFile, saveLogs } = require('../runs/logs.js');
9
+ const { toolHint, buildDocument } = require('./parse.js');
10
+ const { renderDocument } = require('./render.js');
11
+ const {
12
+ RunRecorder,
13
+ commandLabel,
14
+ summarizeDocument,
15
+ } = require('../runs/runs.js');
17
16
 
18
17
  const OUTPUTS = ['md', 'raw'];
19
18
  const PIPE = ['ignore', 'pipe', 'pipe'];
@@ -73,8 +72,16 @@ const collect = (child, onOut, onErr) =>
73
72
  child.on('close', (status) => finish(status ?? 1));
74
73
  });
75
74
 
76
- const beginRun = (cwd, command) =>
77
- new RunRecorder(cwd, commandLabel(command.program, command.args));
75
+ const beginRun = (cwd, command) => {
76
+ let slot = null;
77
+ try {
78
+ slot = nextLogFile(cwd, path.basename(command.program));
79
+ } catch {
80
+ // ignore errors
81
+ }
82
+ const label = commandLabel(command.program, command.args);
83
+ return new RunRecorder(cwd, label, { slot });
84
+ };
78
85
 
79
86
  const documentOf = (command, result, cwd) => {
80
87
  const hint = toolHint(command.program, command.args);
@@ -84,7 +91,7 @@ const documentOf = (command, result, cwd) => {
84
91
 
85
92
  const rawStreams = (stdout, stderr) => {
86
93
  if (!stderr.trim()) return stdout;
87
- const lead = stdout.endsWith('\n') || !stdout ? stdout : `${stdout}\n`;
94
+ const lead = withNewline(stdout);
88
95
  return `${lead}# stderr\n${stderr}`;
89
96
  };
90
97
 
@@ -99,44 +106,49 @@ const runInherit = async (command, cwd, env) => {
99
106
 
100
107
  const runRaw = async (proc, command, cwd, env) => {
101
108
  const recorder = beginRun(cwd, command);
102
- let child;
103
- try {
104
- child = spawnCommand(command, { cwd, env, stdio: PIPE });
105
- } catch {
106
- recorder.finish(1);
107
- return 1;
108
- }
109
109
  const onOut = (chunk) => {
110
110
  proc.stdout.write(chunk);
111
111
  recorder.feed(chunk);
112
112
  };
113
113
  const onErr = (chunk) => proc.stderr.write(chunk);
114
+ const childEnv = { ...env, RESLOP_CAPTURE: '1' };
115
+ let child = null;
116
+ try {
117
+ child = spawnCommand(command, { cwd, env: childEnv, stdio: PIPE });
118
+ } catch {
119
+ recorder.finish(1);
120
+ }
121
+ if (!child) return 1;
114
122
  const result = await collect(child, onOut, onErr);
115
123
  const doc = documentOf(command, result, cwd);
124
+ const slot = recorder.slot;
116
125
  recorder.finish(result.status, summarizeDocument(doc));
126
+ if (!slot) return result.status;
127
+ const text = renderDocument(doc, slot.rawName);
128
+ const raw = rawStreams(result.stdout, result.stderr);
129
+ try {
130
+ await saveLogs(slot, text, raw);
131
+ } catch {
132
+ // the record remains even if the log files cannot be written
133
+ }
117
134
  return result.status;
118
135
  };
119
136
 
120
137
  const runReport = async (proc, command, cwd, env) => {
121
138
  const recorder = beginRun(cwd, command);
122
139
  const fail = (error) => proc.stderr.write(`reslop: ${error.message}\n`);
123
- let child;
140
+ let child = null;
124
141
  try {
125
142
  const childEnv = { ...env, RESLOP_CAPTURE: '1' };
126
143
  child = spawnCommand(command, { cwd, env: childEnv, stdio: PIPE });
127
144
  } catch (error) {
128
145
  fail(error);
129
146
  recorder.finish(1);
130
- return 1;
131
147
  }
148
+ if (!child) return 1;
132
149
  const result = await collect(child, (chunk) => recorder.feed(chunk));
133
150
  const doc = documentOf(command, result, cwd);
134
- let slot = null;
135
- try {
136
- slot = nextLogFile(cwd, path.basename(command.program));
137
- } catch (error) {
138
- fail(error);
139
- }
151
+ const slot = recorder.slot;
140
152
  const text = renderDocument(doc, slot ? slot.rawName : '');
141
153
  recorder.finish(result.status, summarizeDocument(doc));
142
154
  proc.stdout.write(text);
@@ -1,10 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const model = require('./report-model.js');
4
- const npmCommands = require('./npm-commands.js');
5
- const { isPassingTest } = npmCommands;
6
- const utilities = require('./utilities.js');
7
- const { unquote } = utilities;
3
+ const { jsonParse } = require('metautil');
4
+ const model = require('./model.js');
5
+ const { isPassingTest } = require('../runs/output.js');
6
+ const { unquote } = require('../common/utilities.js');
8
7
 
9
8
  const { summary, problem, report, userTrace, errorKey } = model;
10
9
 
@@ -75,57 +74,62 @@ const extraFrom = (fields) => {
75
74
  return extra;
76
75
  };
77
76
 
77
+ const either = (...values) => values.find(Boolean) ?? values.at(-1);
78
+
79
+ const defined = (...values) =>
80
+ values.find((value) => value !== null && value !== undefined);
81
+
82
+ const errorParts = (map, parsed) => {
83
+ const raw = either(
84
+ map.get('error'),
85
+ map.get('message'),
86
+ parsed.get('message'),
87
+ );
88
+ const text = show(raw);
89
+ const header = ERROR_HEAD.exec(text);
90
+ const [lead, name, tag] = header ?? [];
91
+ return {
92
+ message: header ? text.slice(lead.length).trim() : text,
93
+ type: show(either(map.get('name'), name, parsed.get('name'))),
94
+ code: show(either(map.get('code'), tag, parsed.get('code'))),
95
+ };
96
+ };
97
+
78
98
  const problemFromFields = (name, fields, root, fallback = '') => {
79
99
  const map = new Map(fields);
80
100
  const stack = show(map.get('stack'));
81
101
  const parsed = new Map(exceptionFields(stack.split(/\r?\n/)));
82
- const text = show(
83
- map.get('error') || map.get('message') || parsed.get('message'),
84
- );
85
- const header = ERROR_HEAD.exec(text);
86
- const message = header ? text.slice(header[0].length).trim() : text;
87
- const location = show(map.get('at') || map.get('location'));
88
- const duration = show(map.get('duration_ms') || map.get('duration'));
89
- const details = {
90
- name,
91
- message,
92
- duration,
93
- code: show(map.get('code') || (header && header[2]) || parsed.get('code')),
94
- operator: show(map.get('operator')),
95
- expected: show(
96
- map.get('expected') ?? map.get('wanted') ?? map.get('expect'),
97
- ),
98
- actual: show(map.get('actual') ?? map.get('found') ?? map.get('got')),
99
- extra: extraFrom(fields),
100
- };
101
- const type = show(
102
- map.get('name') || (header && header[1]) || parsed.get('name'),
102
+ const { message, type, code } = errorParts(map, parsed);
103
+ const location = show(either(map.get('at'), map.get('location')));
104
+ const duration = show(either(map.get('duration_ms'), map.get('duration')));
105
+ const operator = show(map.get('operator'));
106
+ const wanted = defined(
107
+ map.get('expected'),
108
+ map.get('wanted'),
109
+ map.get('expect'),
103
110
  );
104
- if (type) details.extra.unshift(['type', type]);
105
- const known =
106
- message ||
107
- stack ||
108
- location ||
109
- details.operator ||
110
- details.code ||
111
- details.expected ||
112
- details.actual;
113
- if (!known) details.message = fallback;
111
+ const got = defined(map.get('actual'), map.get('found'), map.get('got'));
112
+ const expected = show(wanted);
113
+ const actual = show(got);
114
+ const extra = extraFrom(fields);
115
+ if (type) extra.unshift(['type', type]);
116
+ const details = { name, message, duration, code, operator, expected, actual };
117
+ const facts = [message, stack, location, operator, code, expected, actual];
118
+ if (!facts.some(Boolean)) details.message = fallback;
114
119
  const source = show(map.get('at')) || stack || location;
115
- const key = errorKey({ ...details, type }, source, root);
120
+ const key = errorKey({ ...details, extra, type }, source, root);
116
121
  const trace = userTrace(parsed.get('stack') || stack || location, root);
117
- return problem({ ...details, key, trace });
122
+ return problem({ ...details, extra, key, trace });
118
123
  };
119
124
 
120
125
  const quoted = (value) => value.startsWith('"') || value.startsWith('\x27');
121
126
 
122
127
  const scalarText = (value) => {
123
128
  if (value.startsWith('"')) {
124
- try {
125
- return JSON.parse(value.replace(/\r?\n/g, '\\n'));
126
- } catch {
127
- return unquote(value);
128
- }
129
+ const text = value.replace(/\r?\n/g, '\\n');
130
+ const parsed = jsonParse(text);
131
+ if (typeof parsed === 'string') return parsed;
132
+ return unquote(value);
129
133
  }
130
134
  return unquote(value);
131
135
  };
@@ -354,11 +358,9 @@ const parseNodeJsonl = (text, root) => {
354
358
  const events = [];
355
359
  for (const line of lines) {
356
360
  if (line[0] !== '{') return null;
357
- try {
358
- events.push(JSON.parse(line));
359
- } catch {
360
- return null;
361
- }
361
+ const event = jsonParse(line);
362
+ if (event === null) return null;
363
+ events.push(event);
362
364
  }
363
365
  return parseNodeEvents(events, root);
364
366
  };
@@ -536,13 +538,12 @@ const takeSpecFrame = (lines, index, current) => {
536
538
  return block.next - index;
537
539
  };
538
540
 
539
- const hasDetail = (item) =>
540
- item.message ||
541
- item.trace ||
542
- item.code ||
543
- item.operator ||
544
- item.expected ||
545
- item.actual;
541
+ const hasDetail = (item) => {
542
+ const message = item.message || item.trace;
543
+ const code = item.code || item.operator;
544
+ const values = item.expected || item.actual;
545
+ return message || code || values;
546
+ };
546
547
 
547
548
  const dropPreviews = (problems) => {
548
549
  const named = new Set();
@@ -1,10 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const deps = require('./deps.js');
4
- const { parseAuditReport, parseOutdatedReport } = deps;
5
- const model = require('./report-model.js');
6
-
7
- const { summary, problem, report, userTrace } = model;
3
+ const { parseAuditReport, parseOutdatedReport } = require('../deps/deps.js');
4
+ const { summary, problem, report, userTrace } = require('./model.js');
5
+ const { trimText } = require('../common/utilities.js');
8
6
 
9
7
  const PRETTIER_SUMMARY = [
10
8
  'Code style issues',
@@ -41,33 +39,28 @@ const parseEslintJson = (data, root) => {
41
39
  item.column || 0,
42
40
  root,
43
41
  );
44
- problems.push(
45
- problem({
46
- severity: item.severity === 1 ? 'warning' : 'error',
47
- message: item.message || '',
48
- code: item.ruleId || '',
49
- trace,
50
- }),
51
- );
42
+ const severity = item.severity === 1 ? 'warning' : 'error';
43
+ const message = item.message || '';
44
+ const code = item.ruleId || '';
45
+ const issue = { severity, message, code, trace };
46
+ problems.push(problem(issue));
52
47
  }
53
48
  }
54
49
  return eslintReport(problems);
55
50
  };
56
51
 
57
- const eslintLine = (line) => {
58
- const match = /^\s*(\d+):(\d+)\s+(error|warning)\s+(.*)$/.exec(line);
52
+ const eslintLine = (text) => {
53
+ const match = /^\s*(\d+):(\d+)\s+(error|warning)\s+(.*)$/.exec(text);
59
54
  if (!match) return null;
60
55
  const rest = match[4];
61
56
  const ruleAt = rest.lastIndexOf(' ');
62
- const message = ruleAt < 0 ? rest : rest.slice(0, ruleAt);
57
+ const raw = ruleAt < 0 ? rest : rest.slice(0, ruleAt);
63
58
  const code = ruleAt < 0 ? '' : rest.slice(ruleAt + 2).trim();
64
- return {
65
- line: match[1],
66
- column: match[2],
67
- severity: match[3],
68
- message: message.trim(),
69
- code,
70
- };
59
+ const line = match[1];
60
+ const column = match[2];
61
+ const severity = match[3];
62
+ const message = raw.trim();
63
+ return { line, column, severity, message, code };
71
64
  };
72
65
 
73
66
  const isPathLine = (line) => {
@@ -141,14 +134,12 @@ const auditExtra = (item) => {
141
134
  const parseAudit = (text) => {
142
135
  const problems = [];
143
136
  for (const item of parseAuditReport(text).values()) {
144
- problems.push(
145
- problem({
146
- severity: item.severity || 'error',
147
- name: item.name,
148
- message: item.title || '',
149
- extra: auditExtra(item),
150
- }),
151
- );
137
+ const severity = item.severity || 'error';
138
+ const { name } = item;
139
+ const message = item.title || '';
140
+ const extra = auditExtra(item);
141
+ const issue = { severity, name, message, extra };
142
+ problems.push(problem(issue));
152
143
  }
153
144
  return problems.length ? errorReport('audit', problems) : null;
154
145
  };
@@ -183,7 +174,7 @@ const parseOutdated = (text) => {
183
174
  };
184
175
 
185
176
  const pushLs = (problems, text) => {
186
- const message = `${text ?? ''}`.trim();
177
+ const message = trimText(text);
187
178
  if (message) problems.push(problem({ message }));
188
179
  };
189
180
 
@@ -226,7 +217,7 @@ const parseToolJson = (data, root) => {
226
217
  };
227
218
 
228
219
  const parseAuditText = (text) => {
229
- if (`${text ?? ''}`.trim().startsWith('{')) return null;
220
+ if (trimText(text).startsWith('{')) return null;
230
221
  const lines = `${text ?? ''}`.split(/\r?\n/);
231
222
  const problems = [];
232
223
  for (let index = 0; index < lines.length; index++) {
@@ -0,0 +1,9 @@
1
+ - Execute reviews with `status` `editing, partial`
2
+ - Do not start or change `done` review files
3
+ - Work through unchecked items. After finishing one, mark it `[x]`
4
+ - Mark `[x]` only near the end, after code changes are done and tests pass
5
+ - For `code` items, replace added lines at that location
6
+ - Set `status` to `partial` if some items remain, or `done` if all are `[x]`
7
+ - Do not redo items already marked `[x]`
8
+ - Run the full check with `npm t`
9
+ - Run specific test files with `reslop t -- node --test <files>`