reslop 0.1.9 → 0.2.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 (94) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +2 -1
  3. package/bin/reslop.js +2 -2
  4. package/lib/ansi.js +4 -2
  5. package/lib/api.js +2 -2
  6. package/lib/capabilities.js +7 -14
  7. package/lib/cli.js +28 -47
  8. package/lib/clipboard.js +1 -1
  9. package/lib/{deps/changes.js → deps-diff.js} +309 -19
  10. package/lib/deps.js +498 -29
  11. package/lib/detect.js +1 -7
  12. package/lib/diff/align.js +7 -10
  13. package/lib/diff/diff.js +53 -0
  14. package/lib/diff/display.js +5 -4
  15. package/lib/diff/inline.js +26 -21
  16. package/lib/diff/items.js +6 -4
  17. package/lib/diff/patch.js +1 -3
  18. package/lib/diff/unit.js +392 -0
  19. package/lib/files.js +5 -8
  20. package/lib/{git/branches.js → git-branches.js} +6 -5
  21. package/lib/{git/dependencies.js → git-deps.js} +111 -38
  22. package/lib/git.js +589 -68
  23. package/lib/github.js +28 -197
  24. package/lib/gitlab.js +24 -195
  25. package/lib/highlight/bash.js +1 -3
  26. package/lib/highlight/css.js +1 -3
  27. package/lib/highlight/js.js +11 -14
  28. package/lib/highlight.js +6 -9
  29. package/lib/keys.js +14 -8
  30. package/lib/manifest.js +330 -0
  31. package/lib/remote.js +374 -0
  32. package/lib/render/branches.js +14 -68
  33. package/lib/render/chrome.js +24 -9
  34. package/lib/render/commits.js +6 -59
  35. package/lib/render/diff.js +16 -16
  36. package/lib/render/files.js +6 -34
  37. package/lib/render/frame.js +23 -12
  38. package/lib/render/notes.js +4 -3
  39. package/lib/render/primitives.js +45 -0
  40. package/lib/{render.js → render/render.js} +5 -5
  41. package/lib/render/unit.js +41 -0
  42. package/lib/review.js +750 -14
  43. package/lib/select.js +5 -10
  44. package/lib/session/accessors.js +9 -1
  45. package/lib/session/actions.js +32 -16
  46. package/lib/session/branches.js +116 -128
  47. package/lib/session/change-actions.js +202 -123
  48. package/lib/session/commit.js +105 -112
  49. package/lib/session/compose.js +158 -13
  50. package/lib/session/dispatch.js +104 -60
  51. package/lib/session/files-pane.js +142 -148
  52. package/lib/session/items.js +22 -30
  53. package/lib/session/lifecycle.js +44 -44
  54. package/lib/session/load.js +19 -33
  55. package/lib/session/navigation.js +12 -1
  56. package/lib/session/ops.js +28 -41
  57. package/lib/session/pointer.js +54 -57
  58. package/lib/session/review.js +23 -37
  59. package/lib/session/terminal.js +6 -12
  60. package/lib/session/todos.js +8 -12
  61. package/lib/session/unit.js +208 -0
  62. package/lib/session/update.js +33 -46
  63. package/lib/session/view.js +78 -9
  64. package/lib/session/watch.js +11 -19
  65. package/lib/session.js +21 -246
  66. package/lib/source.js +3 -3
  67. package/lib/update.js +40 -75
  68. package/lib/{git/proc.js → utilities.js} +75 -11
  69. package/lib/wrap.js +1 -3
  70. package/package.json +1 -1
  71. package/lib/deps/items.js +0 -134
  72. package/lib/deps/manifest.js +0 -152
  73. package/lib/deps/merge.js +0 -120
  74. package/lib/deps/proposals.js +0 -164
  75. package/lib/deps/reports.js +0 -180
  76. package/lib/deps/rows.js +0 -176
  77. package/lib/deps/scan.js +0 -138
  78. package/lib/deps/usage.js +0 -68
  79. package/lib/deps/versions.js +0 -119
  80. package/lib/diff.js +0 -33
  81. package/lib/git/changes.js +0 -164
  82. package/lib/git/read.js +0 -253
  83. package/lib/git/run.js +0 -100
  84. package/lib/npm/commands.js +0 -100
  85. package/lib/remote/changes.js +0 -37
  86. package/lib/remote/headers.js +0 -36
  87. package/lib/remote/http.js +0 -122
  88. package/lib/remote/notes.js +0 -94
  89. package/lib/review/format.js +0 -434
  90. package/lib/review/storage.js +0 -150
  91. package/lib/review/store.js +0 -178
  92. package/lib/review/templates.js +0 -49
  93. package/lib/session/origins.js +0 -7
  94. package/lib/sys.js +0 -84
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased][unreleased]
4
4
 
5
+ ## [0.2.0][] - 2026-09-22
6
+
7
+ - Add a full-size file editor
8
+ - Optimize codebase and reorganize code structure
9
+ - Add pull and push on the commits list screen
10
+ - Fix disk watcher startup on macOS
11
+ - Fix Windows temp-path comparison and same-size file reload polling
12
+
13
+ ## [0.1.9][] - 2026-09-19
14
+
5
15
  - Remove `r` reload hotkey and hint; auto-reload covers disk changes
6
16
  - Improve branch list TUI/TUX
7
17
  - Right-align the time column in the branch list
@@ -95,7 +105,8 @@
95
105
  - Feedback and todos that produce a repair plan for an agent
96
106
  - Intra-line highlighting in unified, mixed, and side-by-side layouts
97
107
 
98
- [unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.1.9...HEAD
108
+ [unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.2.0...HEAD
109
+ [0.2.0]: https://github.com/tshemsedinov/reslop/compare/v0.1.9...v0.2.0
99
110
  [0.1.9]: https://github.com/tshemsedinov/reslop/compare/v0.1.8...v0.1.9
100
111
  [0.1.8]: https://github.com/tshemsedinov/reslop/compare/v0.1.7...v0.1.8
101
112
  [0.1.7]: https://github.com/tshemsedinov/reslop/compare/v0.1.6...v0.1.7
package/README.md CHANGED
@@ -20,10 +20,11 @@ Review → Plan → Repair → Verify
20
20
 
21
21
  - Leave feedback, issues/todos, and in-place code proposals as a repair plan
22
22
  - Review uncommitted git diffs, a given commit, a GitHub PR, or a GitLab MR
23
+ - File-scope review mode: not just diffs, with editor mode
23
24
  - Stage, unstage, or revert each contiguous block of diff lines
24
25
  - Auto-reload local diffs when files change, keeping the current screen
25
26
  - Import GitHub PR and GitLab MR review comments into the local plan for AI
26
- - Commits list: commit, amend, fixup, and drop
27
+ - Commits list: commit, amend, fixup, drop, pull, and push
27
28
  - Branches: checkout, create, rebase, drop, pull, and push
28
29
  - Review each npm dependency once across `package.json` and the lockfile
29
30
  - Propose unused removals, npm audit fixes, and outdated updates as diffs
package/bin/reslop.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- const { run, failLine } = require('../lib/cli.js');
4
+ const { run, errorMessage } = require('../lib/cli.js');
5
5
  const { LEAVE_TERM } = require('../lib/session.js');
6
6
 
7
7
  const fail = (reason) => {
@@ -10,7 +10,7 @@ const fail = (reason) => {
10
10
  } catch {
11
11
  // ignore
12
12
  }
13
- process.stderr.write(failLine(reason));
13
+ process.stderr.write(`reslop: ${errorMessage(reason)}\n`);
14
14
  process.exit(1);
15
15
  };
16
16
 
package/lib/ansi.js CHANGED
@@ -158,8 +158,10 @@ const paint = (text, fgRgb, bgRgb, color, bold) => {
158
158
  const stripAnsi = (text) => text.replace(ANSI_RE, '');
159
159
 
160
160
  const graphemes = (text) => {
161
- const parts = [];
162
- for (const { segment } of SEGMENTER.segment(text)) parts.push(segment);
161
+ const parts = new Array(text.length);
162
+ let n = 0;
163
+ for (const { segment } of SEGMENTER.segment(text)) parts[n++] = segment;
164
+ parts.length = n;
163
165
  return parts;
164
166
  };
165
167
 
package/lib/api.js CHANGED
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  const ansi = require('./ansi.js');
4
- const diff = require('./diff.js');
4
+ const diff = require('./diff/diff.js');
5
5
  const git = require('./git.js');
6
6
  const keys = require('./keys.js');
7
- const render = require('./render.js');
7
+ const render = require('./render/render.js');
8
8
  const highlight = require('./highlight.js');
9
9
  const detect = require('./detect.js');
10
10
  const github = require('./github.js');
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
-
5
3
  const READ_METHODS = [
6
4
  'load',
7
5
  'loadAsync',
8
6
  'loadExtras',
9
7
  'toplevel',
10
8
  'resolveRev',
9
+ 'listFiles',
10
+ 'fileText',
11
11
  ];
12
12
 
13
13
  const CHANGE_METHODS = [
@@ -17,6 +17,7 @@ const CHANGE_METHODS = [
17
17
  'revert',
18
18
  'revertFile',
19
19
  'edit',
20
+ 'writeFile',
20
21
  ];
21
22
 
22
23
  const BRANCH_METHODS = [
@@ -46,9 +47,9 @@ const BRANCH_METHODS = [
46
47
  const SOURCE_CAPS = {
47
48
  local: { read: true, changes: true, branches: true, review: true },
48
49
  commit: { read: true, changes: false, branches: false, review: true },
49
- 'github-pr': { read: true, changes: false, branches: false, review: true },
50
- 'gitlab-mr': { read: true, changes: false, branches: false, review: true },
51
- 'read-only': { read: true, changes: false, branches: false, review: true },
50
+ pr: { read: true, changes: false, branches: false, review: true },
51
+ mr: { read: true, changes: false, branches: false, review: true },
52
+ readonly: { read: true, changes: false, branches: false, review: true },
52
53
  };
53
54
 
54
55
  const withReadOnly = (caps, readOnly) => {
@@ -86,16 +87,9 @@ const sessionCapabilities = (options) => {
86
87
  }
87
88
  const repoCaps = options.repo && options.repo.capabilities;
88
89
  if (repoCaps) return withReadOnly(repoCaps, readOnly);
89
- return capabilitiesFor(readOnly ? 'read-only' : 'local');
90
+ return capabilitiesFor(readOnly ? 'readonly' : 'local');
90
91
  };
91
92
 
92
- const createReviewStorage = (io = {}) => ({
93
- readdirSync: io.readdirSync ?? fs.readdirSync,
94
- readFileSync: io.readFileSync ?? fs.readFileSync,
95
- writeFileSync: io.writeFileSync ?? fs.writeFileSync,
96
- mkdirSync: io.mkdirSync ?? fs.mkdirSync,
97
- });
98
-
99
93
  module.exports = {
100
94
  READ_METHODS,
101
95
  CHANGE_METHODS,
@@ -105,5 +99,4 @@ module.exports = {
105
99
  withReadOnly,
106
100
  attachCapabilities,
107
101
  sessionCapabilities,
108
- createReviewStorage,
109
102
  };
package/lib/cli.js CHANGED
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('node:fs');
4
3
  const path = require('node:path');
5
4
 
5
+ const { exists } = require('metautil');
6
+
6
7
  const { createGitRepo } = require('./git.js');
7
8
  const { githubToken, loadPullRequest } = require('./github.js');
8
9
  const { gitlabToken, loadMergeRequest } = require('./gitlab.js');
@@ -17,13 +18,13 @@ const RANGE_REV = /[~^]/;
17
18
  const USAGE = 'Usage: reslop [-n] [-r] [path | commit | pr-url | mr-url]';
18
19
 
19
20
  const REMOTE_CHANGE = {
20
- 'github-pr': {
21
+ pr: {
21
22
  load: 'loadPullRequest',
22
23
  fallback: loadPullRequest,
23
24
  token: githubToken,
24
25
  target: (selected) => selected.pr,
25
26
  },
26
- 'gitlab-mr': {
27
+ mr: {
27
28
  load: 'loadMergeRequest',
28
29
  fallback: loadMergeRequest,
29
30
  token: gitlabToken,
@@ -38,16 +39,8 @@ const errorMessage = (reason) => {
38
39
  return `${reason}`;
39
40
  };
40
41
 
41
- const failLine = (reason) => `reslop: ${errorMessage(reason)}\n`;
42
-
43
42
  const fail = (stderr, reason) => {
44
- stderr.write(failLine(reason));
45
- return 1;
46
- };
47
-
48
- const failUsage = (stderr, reason) => {
49
- fail(stderr, reason);
50
- stderr.write(`${USAGE}\n`);
43
+ stderr.write(`reslop: ${errorMessage(reason)}\n`);
51
44
  return 1;
52
45
  };
53
46
 
@@ -79,19 +72,10 @@ const looksLikeRev = (spec) => {
79
72
  return RANGE_REV.test(spec);
80
73
  };
81
74
 
82
- const pathExists = (cwd, rel, statSync) => {
83
- try {
84
- statSync(path.resolve(cwd, rel));
85
- return true;
86
- } catch {
87
- return false;
88
- }
89
- };
90
-
91
- const resolveScope = (cwd, paths, repo, statSync = fs.statSync) => {
75
+ const resolveScope = async (cwd, paths, repo) => {
92
76
  if (!paths.length) return { rev: null, paths };
93
77
  const first = paths[0];
94
- if (pathExists(cwd, first, statSync)) {
78
+ if (await exists(path.resolve(cwd, first))) {
95
79
  return { rev: null, paths };
96
80
  }
97
81
  const resolved = repo.resolveRev(cwd, first);
@@ -107,14 +91,11 @@ const resolveScope = (cwd, paths, repo, statSync = fs.statSync) => {
107
91
  return { rev: null, paths };
108
92
  };
109
93
 
110
- const isColorEnabled = (stdout, env) => stdout.isTTY === true && !env.NO_COLOR;
111
-
112
94
  const sessionOptions = (proc, deps, args, env, extra) => ({
113
95
  cwd: deps.cwd ?? proc.cwd(),
114
96
  stdin: proc.stdin,
115
97
  stdout: proc.stdout,
116
- color: isColorEnabled(proc.stdout, env),
117
- getSize: deps.getSize,
98
+ color: proc.stdout.isTTY === true && !env.NO_COLOR,
118
99
  startPane: 'files',
119
100
  newReview: args.newReview,
120
101
  readOnly: args.readOnly,
@@ -122,22 +103,21 @@ const sessionOptions = (proc, deps, args, env, extra) => ({
122
103
  ...extra,
123
104
  });
124
105
 
125
- const openLocalSession = (proc, deps, args, selected, env) => {
106
+ const openLocalSession = async (proc, deps, args, selected, env) => {
126
107
  const cwd = deps.cwd ?? proc.cwd();
127
108
  const rawRepo = deps.repo ?? createGitRepo();
128
- const statSync = deps.statSync ?? fs.statSync;
129
- const scope = resolveScope(cwd, selected.paths, rawRepo, statSync);
109
+ const scope = await resolveScope(cwd, selected.paths, rawRepo);
130
110
  const kind = scope.rev ? 'commit' : 'local';
131
111
  const capabilities = capabilitiesFor(kind, { readOnly: args.readOnly });
132
112
  const repo = attachCapabilities(rawRepo, capabilities);
133
- const session = new Session(
134
- sessionOptions(proc, deps, args, env, {
135
- repo,
136
- paths: scope.paths,
137
- rev: scope.rev,
138
- capabilities,
139
- }),
140
- );
113
+ const extra = {
114
+ repo,
115
+ paths: scope.paths,
116
+ rev: scope.rev,
117
+ capabilities,
118
+ };
119
+ const options = sessionOptions(proc, deps, args, env, extra);
120
+ const session = new Session(options);
141
121
  session.ensureRepo();
142
122
  return session;
143
123
  };
@@ -168,13 +148,12 @@ const openRemoteSession = (proc, deps, args, selected, env) => {
168
148
  const capabilities = capabilitiesFor(selected.kind, {
169
149
  readOnly: args.readOnly,
170
150
  });
171
- return new Session(
172
- sessionOptions(proc, deps, args, env, {
173
- repo: remoteRepo(deps, selected, env, cwd),
174
- paths: selected.paths,
175
- capabilities,
176
- }),
177
- );
151
+ const repo = remoteRepo(deps, selected, env, cwd);
152
+ const extra = { repo, paths: selected.paths, capabilities };
153
+ const options = sessionOptions(proc, deps, args, env, extra);
154
+ const session = new Session(options);
155
+ session.ensureRepo();
156
+ return session;
178
157
  };
179
158
 
180
159
  const loadSession = async (proc, deps, args) => {
@@ -200,7 +179,9 @@ const run = async (proc, deps = {}) => {
200
179
  try {
201
180
  args = parseArgv(proc.argv.slice(2));
202
181
  } catch (error) {
203
- return failUsage(stderr, error);
182
+ fail(stderr, error);
183
+ stderr.write(`${USAGE}\n`);
184
+ return 1;
204
185
  }
205
186
  let session;
206
187
  try {
@@ -224,7 +205,7 @@ const run = async (proc, deps = {}) => {
224
205
  };
225
206
 
226
207
  module.exports = {
227
- failLine,
208
+ errorMessage,
228
209
  parseArgv,
229
210
  resolveScope,
230
211
  run,
package/lib/clipboard.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const { spawnSync } = require('node:child_process');
4
4
 
5
5
  const { ESC } = require('./ansi.js');
6
- const { clipTools, spawnBase } = require('./sys.js');
6
+ const { clipTools, spawnBase } = require('./utilities.js');
7
7
 
8
8
  const CLIP_TIMEOUT_MS = 1000;
9
9