reslop 0.2.6 → 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 +27 -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 +61 -66
  10. package/lib/{files.js → common/files.js} +51 -52
  11. package/lib/common/find.js +61 -0
  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 +117 -51
  18. package/lib/dashboard/npm.js +133 -32
  19. package/lib/dashboard/tree.js +27 -30
  20. package/lib/{session → dashboard}/watch.js +41 -31
  21. package/lib/dashboard/workspace.js +151 -0
  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 -95
  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 +240 -71
  45. package/lib/{editor.js → input/editor.js} +7 -8
  46. package/lib/{keys.js → input/keys.js} +36 -39
  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/dashboard/blocks.js +490 -0
  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 +22 -8
  58. package/lib/render/footer.js +383 -0
  59. package/lib/render/frame.js +127 -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 +29 -13
  64. package/lib/render/npm.js +322 -196
  65. package/lib/render/packages.js +89 -0
  66. package/lib/render/primitives.js +109 -16
  67. package/lib/render/render.js +7 -11
  68. package/lib/render/repos.js +214 -0
  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} +19 -61
  72. package/lib/render/tiles.js +203 -47
  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 +36 -44
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +122 -56
  100. package/lib/session/compose.js +197 -146
  101. package/lib/session/dashboard.js +183 -37
  102. package/lib/session/dispatch.js +120 -22
  103. package/lib/session/files-pane.js +60 -24
  104. package/lib/session/find.js +81 -0
  105. package/lib/session/import.js +82 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +96 -66
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +8 -3
  110. package/lib/session/npm/history.js +249 -0
  111. package/lib/session/npm/npm.js +815 -0
  112. package/lib/session/npm/packages.js +150 -0
  113. package/lib/session/ops.js +125 -34
  114. package/lib/session/pointer.js +216 -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} +166 -59
  120. package/lib/session/tasks.js +321 -66
  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 +72 -59
  126. package/lib/session/workspace.js +416 -0
  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 -505
  142. package/lib/detect.js +0 -34
  143. package/lib/diff/edit.js +0 -33
  144. package/lib/git-worktree.js +0 -437
  145. package/lib/git.js +0 -79
  146. package/lib/npm-commands.js +0 -522
  147. package/lib/render/chrome.js +0 -628
  148. package/lib/render/dash-blocks.js +0 -245
  149. package/lib/render/present.js +0 -19
  150. package/lib/render/report.js +0 -43
  151. package/lib/review.js +0 -744
  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,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>`