reslop 0.2.0 → 0.2.3
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/CHANGELOG.md +27 -1
- package/README.md +13 -2
- package/lib/ansi.js +263 -6
- package/lib/api.js +2 -0
- package/lib/capabilities.js +6 -0
- package/lib/cli.js +13 -2
- package/lib/deps.js +43 -3
- package/lib/git-branches.js +228 -17
- package/lib/git-reword-todo.js +20 -0
- package/lib/git.js +20 -3
- package/lib/keys.js +10 -4
- package/lib/npm-commands.js +513 -0
- package/lib/render/branches.js +23 -58
- package/lib/render/chrome.js +156 -99
- package/lib/render/commits.js +643 -31
- package/lib/render/diff.js +4 -4
- package/lib/render/frame.js +51 -5
- package/lib/render/notes.js +22 -5
- package/lib/render/npm.js +228 -0
- package/lib/render/primitives.js +48 -34
- package/lib/render/render.js +3 -3
- package/lib/report-model.js +127 -0
- package/lib/report-parse.js +94 -0
- package/lib/report-render.js +87 -0
- package/lib/report-run.js +164 -0
- package/lib/report-test.js +506 -0
- package/lib/report-tools.js +311 -0
- package/lib/review-merge.js +270 -0
- package/lib/review.js +38 -6
- package/lib/session/actions.js +81 -5
- package/lib/session/commit.js +187 -19
- package/lib/session/compose.js +96 -32
- package/lib/session/dispatch.js +36 -2
- package/lib/session/files-pane.js +36 -6
- package/lib/session/lifecycle.js +116 -16
- package/lib/session/navigation.js +1 -0
- package/lib/session/npm.js +335 -0
- package/lib/session/pointer.js +12 -1
- package/lib/session/todos.js +29 -1
- package/lib/session/view.js +14 -3
- package/lib/session/watch.js +120 -8
- package/lib/session.js +11 -2
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased][unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.2.3][] - 2026-09-27
|
|
6
|
+
|
|
7
|
+
- Add brief and full commit lists, toggled with `v`
|
|
8
|
+
- Select a commit from the list to view its diff
|
|
9
|
+
- Edit a multi-line commit message
|
|
10
|
+
- Propose unused removals only for libraries that export code
|
|
11
|
+
- Improve TUI/TUX, multiple small fixes
|
|
12
|
+
- Implement npm command runner with termination and re-run
|
|
13
|
+
- Filter tests output to reduce AI costs: `npm t` and other scripts
|
|
14
|
+
- Save script logs under `.log/`
|
|
15
|
+
- Group similar problems in test and other output
|
|
16
|
+
|
|
17
|
+
## [0.2.1][] - 2026-09-23
|
|
18
|
+
|
|
19
|
+
- Type a new commit message at the top of the list
|
|
20
|
+
- Improve `fixup!` functions: create and apply
|
|
21
|
+
- Reword the selected commit message with `r`
|
|
22
|
+
- Add light theme: `-light` option and `l` hotkey to switch
|
|
23
|
+
- Support checkbox state in TODOs, UI for state change
|
|
24
|
+
- Reload the file list when leaving todos, branches, or commits
|
|
25
|
+
- Let agents run `editing` reviews and leave `done` files unchanged
|
|
26
|
+
- Skip npm audit and outdated unless package files changed
|
|
27
|
+
- Sync and merge TODOs with file instead of overwriting it
|
|
28
|
+
|
|
5
29
|
## [0.2.0][] - 2026-09-22
|
|
6
30
|
|
|
7
31
|
- Add a full-size file editor
|
|
@@ -105,7 +129,9 @@
|
|
|
105
129
|
- Feedback and todos that produce a repair plan for an agent
|
|
106
130
|
- Intra-line highlighting in unified, mixed, and side-by-side layouts
|
|
107
131
|
|
|
108
|
-
[unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.2.
|
|
132
|
+
[unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.2.3...HEAD
|
|
133
|
+
[0.2.3]: https://github.com/tshemsedinov/reslop/compare/v0.2.1...v0.2.3
|
|
134
|
+
[0.2.1]: https://github.com/tshemsedinov/reslop/compare/v0.2.0...v0.2.1
|
|
109
135
|
[0.2.0]: https://github.com/tshemsedinov/reslop/compare/v0.1.9...v0.2.0
|
|
110
136
|
[0.1.9]: https://github.com/tshemsedinov/reslop/compare/v0.1.8...v0.1.9
|
|
111
137
|
[0.1.8]: https://github.com/tshemsedinov/reslop/compare/v0.1.7...v0.1.8
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Review → Plan → Repair → Verify
|
|
|
24
24
|
- Stage, unstage, or revert each contiguous block of diff lines
|
|
25
25
|
- Auto-reload local diffs when files change, keeping the current screen
|
|
26
26
|
- Import GitHub PR and GitLab MR review comments into the local plan for AI
|
|
27
|
-
- Commits list: commit, amend, fixup, drop, pull, and push
|
|
27
|
+
- Commits list: commit, amend, fixup, reword, drop, pull, and push
|
|
28
28
|
- Branches: checkout, create, rebase, drop, pull, and push
|
|
29
29
|
- Review each npm dependency once across `package.json` and the lockfile
|
|
30
30
|
- Propose unused removals, npm audit fixes, and outdated updates as diffs
|
|
@@ -39,6 +39,7 @@ npm i -g reslop
|
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
Works on Linux, macOS, and Windows. On Windows use Windows Terminal.
|
|
42
|
+
Requires Node.js `>=18.15.0`.
|
|
42
43
|
|
|
43
44
|
## Usage
|
|
44
45
|
|
|
@@ -48,6 +49,17 @@ Works on Linux, macOS, and Windows. On Windows use Windows Terminal.
|
|
|
48
49
|
- `reslop https://github.com/metarhia/metacom/pull/555` GitHub/GitLab PR/MR
|
|
49
50
|
- `reslop -n` start a new review even if the latest is still editing
|
|
50
51
|
- `reslop -r` read-only mode
|
|
52
|
+
- `reslop -light` light color theme (default dark)
|
|
53
|
+
- `reslop t -- <program> [args]` run one program and print a reduced report
|
|
54
|
+
|
|
55
|
+
Prefix each program in an npm script with `reslop t --`. Keep `&&` between those wraps, so each program is captured on its own and a failing step does not start the next one:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
"lint": "reslop t -- eslint . && reslop t -- prettier -c \"**/*.js\"",
|
|
59
|
+
"test": "reslop t -- npm run -s lint && reslop t -- node --test"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The report is Markdown: failures, diagnostics, and a short summary. Passing results are omitted and similar problems are grouped. The same text is saved under `.log/`. Set `RESLOP_OUTPUT=raw` to pass the command through unchanged. The review screen runs scripts in raw mode and applies its own filter once.
|
|
51
63
|
|
|
52
64
|
Reviews go in `.review/YYYY-MM-DD-NN.md` with frontmatter `status`:
|
|
53
65
|
|
|
@@ -64,7 +76,6 @@ Reviews go in `.review/YYYY-MM-DD-NN.md` with frontmatter `status`:
|
|
|
64
76
|
- Send anonymized code blocks for expert review
|
|
65
77
|
- Send questions and the repair plan to experts for approval
|
|
66
78
|
- Ask experts
|
|
67
|
-
- Verify the codebase
|
|
68
79
|
- Apply refactoring skills
|
|
69
80
|
- Call agents, harnesses, and IDEs to execute prepared plans
|
|
70
81
|
- Squash, cherry-pick, and interactive rebase
|
package/lib/ansi.js
CHANGED
|
@@ -40,7 +40,7 @@ const WIDE_CP = [
|
|
|
40
40
|
0x1fae9, 0x1faf0, 0x1faf8, 0x20000, 0x2fffd, 0x30000, 0x3fffd,
|
|
41
41
|
];
|
|
42
42
|
|
|
43
|
-
const
|
|
43
|
+
const DARK_UI = {
|
|
44
44
|
delLineFg: [0xcc, 0x7a, 0x7a],
|
|
45
45
|
delLineBg: [0x3a, 0x1a, 0x1a],
|
|
46
46
|
delCharFg: [0xff, 0xff, 0xff],
|
|
@@ -60,7 +60,6 @@ const THEME = {
|
|
|
60
60
|
headerDirFg: [0x6e, 0x6e, 0x6e],
|
|
61
61
|
headerSlashFg: [0x8a, 0x8a, 0x8a],
|
|
62
62
|
headerFileFg: [0x4e, 0x4e, 0x4e],
|
|
63
|
-
headerMetaFg: [0x5a, 0x5a, 0x5a],
|
|
64
63
|
chromeFg: [0xd0, 0xd0, 0xe0],
|
|
65
64
|
chromeBg: [0x18, 0x18, 0x24],
|
|
66
65
|
noteBg: [0x24, 0x24, 0x36],
|
|
@@ -87,7 +86,7 @@ const THEME = {
|
|
|
87
86
|
stagedAddCharBg: [0x4e, 0x4e, 0x58],
|
|
88
87
|
};
|
|
89
88
|
|
|
90
|
-
const
|
|
89
|
+
const DARK_CODE = {
|
|
91
90
|
keyword: [197, 134, 192],
|
|
92
91
|
control: [197, 134, 192],
|
|
93
92
|
storage: [86, 156, 214],
|
|
@@ -122,7 +121,7 @@ const CODE_FG = {
|
|
|
122
121
|
logDebug: [140, 160, 180],
|
|
123
122
|
};
|
|
124
123
|
|
|
125
|
-
const
|
|
124
|
+
const DARK_LOG_COLS = [
|
|
126
125
|
[210, 210, 220],
|
|
127
126
|
[120, 180, 220],
|
|
128
127
|
[180, 160, 220],
|
|
@@ -133,6 +132,142 @@ const LOG_COL_FG = [
|
|
|
133
132
|
[200, 170, 140],
|
|
134
133
|
];
|
|
135
134
|
|
|
135
|
+
const LIGHT_UI = {
|
|
136
|
+
delLineFg: [0xb3, 0x1d, 0x28],
|
|
137
|
+
delLineBg: [0xff, 0xeb, 0xe9],
|
|
138
|
+
delCharFg: [0x1f, 0x23, 0x28],
|
|
139
|
+
delCharBg: [0xff, 0xc1, 0xc0],
|
|
140
|
+
addLineFg: [0x1a, 0x7f, 0x37],
|
|
141
|
+
addLineBg: [0xe6, 0xff, 0xec],
|
|
142
|
+
addCharFg: [0x1f, 0x23, 0x28],
|
|
143
|
+
addCharBg: [0xab, 0xf2, 0xbc],
|
|
144
|
+
ctxFg: [0x57, 0x60, 0x6a],
|
|
145
|
+
ctxBg: [0xff, 0xff, 0xff],
|
|
146
|
+
splitGutterFg: [0xea, 0xee, 0xf2],
|
|
147
|
+
splitGutterBg: [0xea, 0xee, 0xf2],
|
|
148
|
+
headerFg: [0x24, 0x29, 0x2f],
|
|
149
|
+
headerBg: [0xd8, 0xde, 0xe4],
|
|
150
|
+
headerChromeFg: [0x42, 0x4a, 0x53],
|
|
151
|
+
headerRepoFg: [0x24, 0x29, 0x2f],
|
|
152
|
+
headerDirFg: [0x57, 0x60, 0x6a],
|
|
153
|
+
headerSlashFg: [0x8c, 0x95, 0x9f],
|
|
154
|
+
headerFileFg: [0x1f, 0x23, 0x28],
|
|
155
|
+
chromeFg: [0x24, 0x29, 0x2f],
|
|
156
|
+
chromeBg: [0xf0, 0xf2, 0xf5],
|
|
157
|
+
noteBg: [0xe8, 0xf0, 0xfe],
|
|
158
|
+
buttonFg: [0x57, 0x60, 0x6a],
|
|
159
|
+
buttonBg: [0xe1, 0xe5, 0xea],
|
|
160
|
+
buttonHotFg: [0x0f, 0x14, 0x19],
|
|
161
|
+
currentBg: [0xac, 0xe1, 0xc2],
|
|
162
|
+
checkFg: [0x1f, 0x23, 0x28],
|
|
163
|
+
checkBg: [0xc8, 0xcf, 0xd8],
|
|
164
|
+
checkDoneFg: [0x0f, 0x3d, 0x1f],
|
|
165
|
+
checkDoneBg: [0xab, 0xf2, 0xbc],
|
|
166
|
+
errorFg: [0xcf, 0x22, 0x2e],
|
|
167
|
+
warnFg: [0x9a, 0x67, 0x00],
|
|
168
|
+
mutedFg: [0x6e, 0x77, 0x81],
|
|
169
|
+
shaFg: [0x05, 0x50, 0xae],
|
|
170
|
+
shaDarkFg: [0x0b, 0x3d, 0x70],
|
|
171
|
+
stagedDelLineFg: [0x57, 0x60, 0x6a],
|
|
172
|
+
stagedDelLineBg: [0xf3, 0xf4, 0xf6],
|
|
173
|
+
stagedDelCharFg: [0x1f, 0x23, 0x28],
|
|
174
|
+
stagedDelCharBg: [0xd8, 0xdc, 0xe1],
|
|
175
|
+
stagedAddLineFg: [0x42, 0x4a, 0x53],
|
|
176
|
+
stagedAddLineBg: [0xea, 0xec, 0xef],
|
|
177
|
+
stagedAddCharFg: [0x0f, 0x14, 0x19],
|
|
178
|
+
stagedAddCharBg: [0xcc, 0xd1, 0xd7],
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const LIGHT_CODE = {
|
|
182
|
+
keyword: [175, 0, 219],
|
|
183
|
+
control: [175, 0, 219],
|
|
184
|
+
storage: [0, 0, 255],
|
|
185
|
+
literal: [0, 0, 255],
|
|
186
|
+
string: [163, 21, 21],
|
|
187
|
+
template: [163, 21, 21],
|
|
188
|
+
escape: [238, 0, 0],
|
|
189
|
+
interpolation: [0, 16, 128],
|
|
190
|
+
number: [9, 134, 88],
|
|
191
|
+
comment: [0, 128, 0],
|
|
192
|
+
function: [121, 94, 38],
|
|
193
|
+
method: [121, 94, 38],
|
|
194
|
+
className: [38, 127, 153],
|
|
195
|
+
type: [38, 127, 153],
|
|
196
|
+
interface: [38, 127, 153],
|
|
197
|
+
property: [0, 16, 128],
|
|
198
|
+
variable: [0, 16, 128],
|
|
199
|
+
parameter: [0, 16, 128],
|
|
200
|
+
constant: [0, 112, 193],
|
|
201
|
+
operator: [60, 60, 60],
|
|
202
|
+
punct: [60, 60, 60],
|
|
203
|
+
regex: [129, 31, 63],
|
|
204
|
+
decorator: [121, 94, 38],
|
|
205
|
+
tag: [128, 0, 0],
|
|
206
|
+
attr: [229, 0, 0],
|
|
207
|
+
plain: [31, 31, 31],
|
|
208
|
+
punctDim: [140, 140, 140],
|
|
209
|
+
logDate: [60, 60, 70],
|
|
210
|
+
logError: [205, 49, 49],
|
|
211
|
+
logWarn: [160, 110, 0],
|
|
212
|
+
logInfo: [0, 90, 180],
|
|
213
|
+
logDebug: [100, 110, 125],
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const LIGHT_LOG_COLS = [
|
|
217
|
+
[60, 60, 70],
|
|
218
|
+
[0, 95, 150],
|
|
219
|
+
[110, 70, 170],
|
|
220
|
+
[30, 120, 60],
|
|
221
|
+
[150, 95, 20],
|
|
222
|
+
[170, 50, 90],
|
|
223
|
+
[20, 120, 120],
|
|
224
|
+
[130, 90, 50],
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
const PALETTES = {
|
|
228
|
+
dark: { ui: DARK_UI, code: DARK_CODE, logCols: DARK_LOG_COLS },
|
|
229
|
+
light: { ui: LIGHT_UI, code: LIGHT_CODE, logCols: LIGHT_LOG_COLS },
|
|
230
|
+
};
|
|
231
|
+
const THEME_NAMES = Object.keys(PALETTES);
|
|
232
|
+
|
|
233
|
+
const applyColors = (into, from) => {
|
|
234
|
+
for (const key of Object.keys(from)) {
|
|
235
|
+
const src = from[key];
|
|
236
|
+
let dst = into[key];
|
|
237
|
+
if (!dst) {
|
|
238
|
+
dst = [0, 0, 0];
|
|
239
|
+
into[key] = dst;
|
|
240
|
+
}
|
|
241
|
+
dst[0] = src[0];
|
|
242
|
+
dst[1] = src[1];
|
|
243
|
+
dst[2] = src[2];
|
|
244
|
+
}
|
|
245
|
+
return into;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const cloneColors = (colors) => {
|
|
249
|
+
const into = Array.isArray(colors) ? [] : {};
|
|
250
|
+
return applyColors(into, colors);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const THEME = cloneColors(DARK_UI);
|
|
254
|
+
const CODE_FG = cloneColors(DARK_CODE);
|
|
255
|
+
const LOG_COL_FG = cloneColors(DARK_LOG_COLS);
|
|
256
|
+
let currentTheme = 'dark';
|
|
257
|
+
|
|
258
|
+
const setTheme = (name) => {
|
|
259
|
+
if (!Object.hasOwn(PALETTES, name)) {
|
|
260
|
+
throw new Error(`unknown theme ${name}`);
|
|
261
|
+
}
|
|
262
|
+
const palette = PALETTES[name];
|
|
263
|
+
applyColors(THEME, palette.ui);
|
|
264
|
+
applyColors(CODE_FG, palette.code);
|
|
265
|
+
applyColors(LOG_COL_FG, palette.logCols);
|
|
266
|
+
currentTheme = name;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const themeName = () => currentTheme;
|
|
270
|
+
|
|
136
271
|
const codeFg = (style) => {
|
|
137
272
|
if (typeof style !== 'string') return CODE_FG.plain;
|
|
138
273
|
if (Object.hasOwn(CODE_FG, style)) return CODE_FG[style];
|
|
@@ -143,9 +278,14 @@ const codeFg = (style) => {
|
|
|
143
278
|
return CODE_FG.plain;
|
|
144
279
|
};
|
|
145
280
|
|
|
146
|
-
const
|
|
281
|
+
const channels = (rgb) => {
|
|
282
|
+
if (typeof rgb === 'number') return `${rgb};${rgb};${rgb}`;
|
|
283
|
+
return `${rgb[0]};${rgb[1]};${rgb[2]}`;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const fg = (rgb) => `${ESC}[38;2;${channels(rgb)}m`;
|
|
147
287
|
|
|
148
|
-
const bg = (rgb) => `${ESC}[48;2;${rgb
|
|
288
|
+
const bg = (rgb) => `${ESC}[48;2;${channels(rgb)}m`;
|
|
149
289
|
|
|
150
290
|
const seq = (fgRgb, bgRgb) => `${fg(fgRgb)}${bg(bgRgb)}`;
|
|
151
291
|
|
|
@@ -215,6 +355,117 @@ const truncateVisible = (text, width) => {
|
|
|
215
355
|
return out;
|
|
216
356
|
};
|
|
217
357
|
|
|
358
|
+
const csiLength = (text, index) => {
|
|
359
|
+
if (text[index] !== ESC || text[index + 1] !== '[') return 0;
|
|
360
|
+
let at = index + 2;
|
|
361
|
+
while (at < text.length) {
|
|
362
|
+
const code = text.charCodeAt(at);
|
|
363
|
+
at += 1;
|
|
364
|
+
if (code >= 64 && code <= 126) return at - index;
|
|
365
|
+
}
|
|
366
|
+
return 0;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const firstGrapheme = (text) => {
|
|
370
|
+
const next = SEGMENTER.segment(text)[Symbol.iterator]().next();
|
|
371
|
+
if (next.done || !next.value) return '';
|
|
372
|
+
return next.value.segment;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const clipAnsi = (text, width) => {
|
|
376
|
+
const raw = `${text ?? ''}`;
|
|
377
|
+
if (width <= 0) return '';
|
|
378
|
+
if (visibleWidth(raw) <= width) return raw;
|
|
379
|
+
let used = 0;
|
|
380
|
+
let out = '';
|
|
381
|
+
let index = 0;
|
|
382
|
+
while (index < raw.length && used < width) {
|
|
383
|
+
const csi = csiLength(raw, index);
|
|
384
|
+
if (csi) {
|
|
385
|
+
out += raw.slice(index, index + csi);
|
|
386
|
+
index += csi;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const ch = firstGrapheme(raw.slice(index));
|
|
390
|
+
if (!ch) break;
|
|
391
|
+
const w = graphemeWidth(ch, used);
|
|
392
|
+
if (used + w > width) break;
|
|
393
|
+
out += ch;
|
|
394
|
+
used += w;
|
|
395
|
+
index += ch.length;
|
|
396
|
+
}
|
|
397
|
+
if (stripAnsi(out) !== stripAnsi(raw)) out += RESET;
|
|
398
|
+
return out;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const sgrParams = (seq) => {
|
|
402
|
+
const inner = seq.slice(2, -1);
|
|
403
|
+
if (!inner) return [0];
|
|
404
|
+
return inner.split(';').map((part) => (part === '' ? 0 : Number(part)));
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const colorSpan = (params, index) => {
|
|
408
|
+
const mode = params[index + 1];
|
|
409
|
+
if (mode === 5) return 2;
|
|
410
|
+
if (mode === 2) return 4;
|
|
411
|
+
return 0;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const keepFgParams = (params) => {
|
|
415
|
+
const kept = [];
|
|
416
|
+
for (let i = 0; i < params.length; i++) {
|
|
417
|
+
const code = params[i];
|
|
418
|
+
if (code === 38 || code === 48) {
|
|
419
|
+
const extra = colorSpan(params, i);
|
|
420
|
+
if (code === 38 && extra) {
|
|
421
|
+
kept.push(code, ...params.slice(i + 1, i + 1 + extra));
|
|
422
|
+
}
|
|
423
|
+
i += extra;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (code >= 40 && code <= 49) continue;
|
|
427
|
+
if (code >= 100 && code <= 107) continue;
|
|
428
|
+
if (code === 7 || code === 27) continue;
|
|
429
|
+
kept.push(code);
|
|
430
|
+
}
|
|
431
|
+
return kept;
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const foregroundOn = (text, bgRgb) => {
|
|
435
|
+
const raw = `${text ?? ''}`;
|
|
436
|
+
const ground = bg(bgRgb);
|
|
437
|
+
let out = '';
|
|
438
|
+
let index = 0;
|
|
439
|
+
while (index < raw.length) {
|
|
440
|
+
if (raw[index] !== ESC) {
|
|
441
|
+
const next = raw.indexOf(ESC, index);
|
|
442
|
+
const end = next < 0 ? raw.length : next;
|
|
443
|
+
out += raw.slice(index, end);
|
|
444
|
+
index = end;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const csi = csiLength(raw, index);
|
|
448
|
+
if (!csi) {
|
|
449
|
+
out += raw[index];
|
|
450
|
+
index += 1;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const seq = raw.slice(index, index + csi);
|
|
454
|
+
index += csi;
|
|
455
|
+
if (!seq.endsWith('m')) {
|
|
456
|
+
out += seq;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
const kept = keepFgParams(sgrParams(seq));
|
|
460
|
+
const reset = kept.includes(0);
|
|
461
|
+
const rest = kept.filter((code) => code !== 0);
|
|
462
|
+
if (reset) out += `${ESC}[0m`;
|
|
463
|
+
out += ground;
|
|
464
|
+
if (rest.length) out += `${ESC}[${rest.join(';')}m`;
|
|
465
|
+
}
|
|
466
|
+
return out;
|
|
467
|
+
};
|
|
468
|
+
|
|
218
469
|
module.exports = {
|
|
219
470
|
ESC,
|
|
220
471
|
RESET,
|
|
@@ -222,6 +473,10 @@ module.exports = {
|
|
|
222
473
|
EL,
|
|
223
474
|
THEME,
|
|
224
475
|
CODE_FG,
|
|
476
|
+
PALETTES,
|
|
477
|
+
THEME_NAMES,
|
|
478
|
+
setTheme,
|
|
479
|
+
themeName,
|
|
225
480
|
codeFg,
|
|
226
481
|
fg,
|
|
227
482
|
bg,
|
|
@@ -232,4 +487,6 @@ module.exports = {
|
|
|
232
487
|
graphemeWidth,
|
|
233
488
|
visibleWidth,
|
|
234
489
|
truncateVisible,
|
|
490
|
+
clipAnsi,
|
|
491
|
+
foregroundOn,
|
|
235
492
|
};
|
package/lib/api.js
CHANGED
package/lib/capabilities.js
CHANGED
|
@@ -18,6 +18,7 @@ const CHANGE_METHODS = [
|
|
|
18
18
|
'revertFile',
|
|
19
19
|
'edit',
|
|
20
20
|
'writeFile',
|
|
21
|
+
'stagePath',
|
|
21
22
|
];
|
|
22
23
|
|
|
23
24
|
const BRANCH_METHODS = [
|
|
@@ -25,6 +26,7 @@ const BRANCH_METHODS = [
|
|
|
25
26
|
'commitAsync',
|
|
26
27
|
'hasStaged',
|
|
27
28
|
'lastMessage',
|
|
29
|
+
'commitMessage',
|
|
28
30
|
'listBranches',
|
|
29
31
|
'listCommits',
|
|
30
32
|
'currentBranch',
|
|
@@ -38,6 +40,10 @@ const BRANCH_METHODS = [
|
|
|
38
40
|
'dropAsync',
|
|
39
41
|
'dropCommit',
|
|
40
42
|
'dropCommitAsync',
|
|
43
|
+
'applyFixup',
|
|
44
|
+
'applyFixupAsync',
|
|
45
|
+
'reword',
|
|
46
|
+
'rewordAsync',
|
|
41
47
|
'pull',
|
|
42
48
|
'pullAsync',
|
|
43
49
|
'push',
|
package/lib/cli.js
CHANGED
|
@@ -10,12 +10,15 @@ const { gitlabToken, loadMergeRequest } = require('./gitlab.js');
|
|
|
10
10
|
const { selectChangeSource, createLoadedSource } = require('./source.js');
|
|
11
11
|
const { Session } = require('./session.js');
|
|
12
12
|
const { capabilitiesFor, attachCapabilities } = require('./capabilities.js');
|
|
13
|
+
const { setTheme } = require('./ansi.js');
|
|
14
|
+
const { runCapture } = require('./report-run.js');
|
|
13
15
|
|
|
14
16
|
const HEX_REV = /^[0-9a-f]{4,40}$/i;
|
|
15
17
|
const HEAD_REV = /^HEAD([~^].*)?$/i;
|
|
16
18
|
const RANGE_REV = /[~^]/;
|
|
17
19
|
|
|
18
|
-
const USAGE =
|
|
20
|
+
const USAGE =
|
|
21
|
+
'Usage: reslop [-n] [-r] [-light] [path | commit | pr-url | mr-url]';
|
|
19
22
|
|
|
20
23
|
const REMOTE_CHANGE = {
|
|
21
24
|
pr: {
|
|
@@ -48,6 +51,7 @@ const parseArgv = (argv) => {
|
|
|
48
51
|
const paths = [];
|
|
49
52
|
let newReview = false;
|
|
50
53
|
let readOnly = false;
|
|
54
|
+
let theme = 'dark';
|
|
51
55
|
for (const arg of argv) {
|
|
52
56
|
if (arg === '-n') {
|
|
53
57
|
newReview = true;
|
|
@@ -57,12 +61,16 @@ const parseArgv = (argv) => {
|
|
|
57
61
|
readOnly = true;
|
|
58
62
|
continue;
|
|
59
63
|
}
|
|
64
|
+
if (arg === '-light') {
|
|
65
|
+
theme = 'light';
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
60
68
|
if (arg.startsWith('-')) {
|
|
61
69
|
throw new Error(`unknown option ${arg}`);
|
|
62
70
|
}
|
|
63
71
|
paths.push(arg);
|
|
64
72
|
}
|
|
65
|
-
return { paths, newReview, readOnly };
|
|
73
|
+
return { paths, newReview, readOnly, theme };
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
const looksLikeRev = (spec) => {
|
|
@@ -173,6 +181,8 @@ const runUi = async (session, proc) => {
|
|
|
173
181
|
};
|
|
174
182
|
|
|
175
183
|
const run = async (proc, deps = {}) => {
|
|
184
|
+
const argv = proc.argv.slice(2);
|
|
185
|
+
if (argv[0] === 't') return runCapture(proc, deps);
|
|
176
186
|
const stdout = proc.stdout;
|
|
177
187
|
const stderr = proc.stderr;
|
|
178
188
|
let args;
|
|
@@ -183,6 +193,7 @@ const run = async (proc, deps = {}) => {
|
|
|
183
193
|
stderr.write(`${USAGE}\n`);
|
|
184
194
|
return 1;
|
|
185
195
|
}
|
|
196
|
+
setTheme(args.theme);
|
|
186
197
|
let session;
|
|
187
198
|
try {
|
|
188
199
|
session = await loadSession(proc, deps, args);
|
package/lib/deps.js
CHANGED
|
@@ -73,6 +73,46 @@ const pkgHasExportEntry = (pkg) => {
|
|
|
73
73
|
return Object.keys(exported).length > 0;
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
+
const isTypeKey = (key) => key === 'types' || key === 'typings';
|
|
77
|
+
|
|
78
|
+
const isRuntimeFile = (value) => {
|
|
79
|
+
if (typeof value !== 'string' || !value) return false;
|
|
80
|
+
return !value.endsWith('.d.ts');
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const hasRuntimeExport = (value) => {
|
|
84
|
+
if (isRuntimeFile(value)) return true;
|
|
85
|
+
if (Array.isArray(value)) {
|
|
86
|
+
for (const item of value) {
|
|
87
|
+
if (hasRuntimeExport(item)) return true;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
if (!value || typeof value !== 'object') return false;
|
|
92
|
+
const keys = Object.keys(value);
|
|
93
|
+
for (const key of keys) {
|
|
94
|
+
if (isTypeKey(key)) continue;
|
|
95
|
+
if (hasRuntimeExport(value[key])) return true;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const pkgHasBin = (pkg) => {
|
|
101
|
+
if (!pkg || typeof pkg !== 'object') return false;
|
|
102
|
+
const bin = pkg.bin;
|
|
103
|
+
if (typeof bin === 'string') return !!bin;
|
|
104
|
+
if (!bin || typeof bin !== 'object' || Array.isArray(bin)) return false;
|
|
105
|
+
return Object.keys(bin).length > 0;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const pkgExportsLibrary = (pkg) => {
|
|
109
|
+
if (!pkg || typeof pkg !== 'object') return false;
|
|
110
|
+
if (pkgHasBin(pkg)) return false;
|
|
111
|
+
if (isRuntimeFile(pkg.main)) return true;
|
|
112
|
+
if (isRuntimeFile(pkg.module)) return true;
|
|
113
|
+
return hasRuntimeExport(pkg.exports);
|
|
114
|
+
};
|
|
115
|
+
|
|
76
116
|
const SKIP_WALK = ['node_modules', '.git', '.review', 'coverage', 'dist'];
|
|
77
117
|
|
|
78
118
|
const collectUsedFromManifest = (used, rel) => {
|
|
@@ -156,8 +196,8 @@ const readDepPkg = (root, pkgDir, name) => {
|
|
|
156
196
|
|
|
157
197
|
const depHasExportEntry = (root, pkgDir, name) => {
|
|
158
198
|
const pkg = readDepPkg(root, pkgDir, name);
|
|
159
|
-
if (!pkg) return
|
|
160
|
-
return
|
|
199
|
+
if (!pkg) return false;
|
|
200
|
+
return pkgExportsLibrary(pkg);
|
|
161
201
|
};
|
|
162
202
|
|
|
163
203
|
const collectExportEntries = (root, pkgDir, names) => {
|
|
@@ -349,7 +389,7 @@ const markOutdatedChanges = (changes, outdated) =>
|
|
|
349
389
|
markChanges(changes, outdated, 'outdated');
|
|
350
390
|
|
|
351
391
|
const depExports = (exportEntries, name) => {
|
|
352
|
-
if (!exportEntries || !exportEntries.has(name)) return
|
|
392
|
+
if (!exportEntries || !exportEntries.has(name)) return false;
|
|
353
393
|
return exportEntries.get(name) === true;
|
|
354
394
|
};
|
|
355
395
|
|