reslop 0.1.7 → 0.1.9

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 (98) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +26 -38
  3. package/bin/reslop.js +3 -1
  4. package/lib/ansi.js +2 -0
  5. package/lib/capabilities.js +109 -0
  6. package/lib/cli.js +18 -14
  7. package/lib/deps/changes.js +422 -0
  8. package/lib/deps/items.js +134 -0
  9. package/lib/deps/manifest.js +152 -0
  10. package/lib/deps/merge.js +120 -0
  11. package/lib/deps/proposals.js +164 -0
  12. package/lib/deps/reports.js +180 -0
  13. package/lib/deps/rows.js +176 -0
  14. package/lib/deps/scan.js +138 -0
  15. package/lib/deps/usage.js +68 -0
  16. package/lib/deps/versions.js +119 -0
  17. package/lib/deps.js +117 -1345
  18. package/lib/diff/align.js +226 -0
  19. package/lib/diff/display.js +101 -0
  20. package/lib/diff/edit.js +47 -0
  21. package/lib/diff/inline.js +300 -0
  22. package/lib/diff/items.js +101 -0
  23. package/lib/diff/parse.js +156 -0
  24. package/lib/diff/patch.js +103 -0
  25. package/lib/diff.js +13 -932
  26. package/lib/editor.js +37 -0
  27. package/lib/files.js +118 -4
  28. package/lib/git/branches.js +383 -0
  29. package/lib/git/changes.js +164 -0
  30. package/lib/git/dependencies.js +489 -0
  31. package/lib/git/proc.js +162 -0
  32. package/lib/git/read.js +253 -0
  33. package/lib/git/run.js +100 -0
  34. package/lib/git.js +88 -1193
  35. package/lib/github.js +92 -222
  36. package/lib/gitlab.js +72 -216
  37. package/lib/highlight/bash.js +5 -9
  38. package/lib/highlight/core.js +1 -1
  39. package/lib/highlight/css.js +1 -3
  40. package/lib/highlight/csv.js +1 -3
  41. package/lib/highlight/dart.js +6 -20
  42. package/lib/highlight/dot.js +13 -13
  43. package/lib/highlight/html.js +1 -5
  44. package/lib/highlight/js.js +151 -115
  45. package/lib/highlight/json.js +1 -3
  46. package/lib/highlight/log.js +1 -3
  47. package/lib/highlight/txt.js +5 -10
  48. package/lib/highlight.js +4 -4
  49. package/lib/keys.js +67 -168
  50. package/lib/npm/commands.js +100 -0
  51. package/lib/remote/changes.js +37 -0
  52. package/lib/remote/headers.js +36 -0
  53. package/lib/remote/http.js +122 -0
  54. package/lib/remote/notes.js +94 -0
  55. package/lib/render/branches.js +240 -0
  56. package/lib/render/chrome.js +595 -0
  57. package/lib/render/commits.js +138 -0
  58. package/lib/render/diff.js +307 -0
  59. package/lib/render/files.js +186 -0
  60. package/lib/render/frame.js +202 -0
  61. package/lib/render/notes.js +201 -0
  62. package/lib/render/present.js +25 -0
  63. package/lib/render/primitives.js +200 -0
  64. package/lib/render.js +13 -1688
  65. package/lib/review/format.js +434 -0
  66. package/lib/review/storage.js +150 -0
  67. package/lib/review/store.js +178 -0
  68. package/lib/review/templates.js +49 -0
  69. package/lib/review.js +14 -747
  70. package/lib/select.js +3 -5
  71. package/lib/session/accessors.js +25 -0
  72. package/lib/session/actions.js +316 -0
  73. package/lib/session/branches.js +302 -0
  74. package/lib/session/change-actions.js +323 -0
  75. package/lib/session/commit.js +286 -0
  76. package/lib/session/compose.js +540 -0
  77. package/lib/session/dispatch.js +115 -0
  78. package/lib/session/files-pane.js +243 -0
  79. package/lib/session/items.js +282 -0
  80. package/lib/session/lifecycle.js +516 -0
  81. package/lib/session/load.js +85 -0
  82. package/lib/session/navigation.js +94 -0
  83. package/lib/session/ops.js +110 -0
  84. package/lib/session/origins.js +7 -0
  85. package/lib/session/pointer.js +161 -0
  86. package/lib/session/progress.js +29 -0
  87. package/lib/session/review.js +88 -0
  88. package/lib/session/templates.js +64 -0
  89. package/lib/session/terminal.js +169 -0
  90. package/lib/session/todos.js +269 -0
  91. package/lib/session/update.js +125 -0
  92. package/lib/session/view.js +131 -0
  93. package/lib/session/watch.js +219 -0
  94. package/lib/session.js +598 -2350
  95. package/lib/source.js +11 -11
  96. package/lib/sys.js +25 -4
  97. package/lib/wrap.js +4 -2
  98. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,35 @@
2
2
 
3
3
  ## [Unreleased][unreleased]
4
4
 
5
+ - Remove `r` reload hotkey and hint; auto-reload covers disk changes
6
+ - Improve branch list TUI/TUX
7
+ - Right-align the time column in the branch list
8
+ - Refresh the branch list after push, pull, and rebase
9
+ - Keep diff viewer order when staging or unstaging a block
10
+ - Add a commits list screen with commit operation
11
+ - Show staged/total rather than staged/unstaged in files list
12
+ - Show file ages in the file list right-aligned
13
+ - Sort the file list by path from the repo root
14
+ - Update file-list hotkeys and hints
15
+ - Fix dependency updates, locks and audit
16
+
17
+ ## [0.1.8][] - 2026-09-17
18
+
19
+ - Simplify session controllers, input dispatch, and loading orchestration
20
+ - Enforce structural limits and session dependency direction on library files
21
+ - Show staged/unstaged +/- and staged/remaining totals in status line
22
+ - Jump by word with Ctrl+Left/Right in all text editors
23
+ - Support PgUp, PgDn, Home, and End in lists and multiline editors
24
+ - Support mouse click in submenu and confirmation choices on the hint line
25
+ - Fix TODO wrapped text; keep up/down navigation inside a multiline todos
26
+ - Fix identifier diffs as whole words compare, not just letters
27
+ - Keep reslop open when a local repo has no unstaged changes
28
+ - Fix invisible cursor movement in scrolling diffs
29
+ - Auto-reload worktree on disk changes, keeping the current UI state
30
+ - Fix disk watch paths on Windows
31
+ - Set git push upstream to `origin <branch>` when missing
32
+ - Save in-place diff edits to the reviewed file
33
+
5
34
  ## [0.1.7][] - 2026-09-16
6
35
 
7
36
  - Make TODOs repo-wide, not per-file todos
@@ -66,7 +95,9 @@
66
95
  - Feedback and todos that produce a repair plan for an agent
67
96
  - Intra-line highlighting in unified, mixed, and side-by-side layouts
68
97
 
69
- [unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.1.7...HEAD
98
+ [unreleased]: https://github.com/tshemsedinov/reslop/compare/v0.1.9...HEAD
99
+ [0.1.9]: https://github.com/tshemsedinov/reslop/compare/v0.1.8...v0.1.9
100
+ [0.1.8]: https://github.com/tshemsedinov/reslop/compare/v0.1.7...v0.1.8
70
101
  [0.1.7]: https://github.com/tshemsedinov/reslop/compare/v0.1.6...v0.1.7
71
102
  [0.1.6]: https://github.com/tshemsedinov/reslop/compare/v0.1.5...v0.1.6
72
103
  [0.1.5]: https://github.com/tshemsedinov/reslop/compare/v0.1.4...v0.1.5
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # 👁️ reslop — Review AI generated code and plan changes
1
+ # 👁️ reslop
2
+
3
+ ## Review AI generated code and plan changes
2
4
 
3
5
  [![ci status](https://github.com/tshemsedinov/reslop/workflows/Testing%20CI/badge.svg)](https://github.com/tshemsedinov/reslop/actions?query=workflow%3A%22Testing+CI%22+branch%3Amain)
4
6
  [![snyk](https://snyk.io/test/github/tshemsedinov/reslop/badge.svg)](https://snyk.io/test/github/tshemsedinov/reslop)
@@ -16,15 +18,18 @@ for the agent.
16
18
  Review → Plan → Repair → Verify
17
19
  ```
18
20
 
19
- - Leave feedback, issues/todos, and in-place code proposals as a repair plan.
20
- - Review uncommitted git diffs, a given commit, a GitHub PR, or a GitLab MR.
21
- - Stage, unstage, or revert each contiguous block of diff lines.
22
- - Import GitHub PR and GitLab MR review comments into the local plan for AI.
23
- - Commit, amend, or fixup staged changes from the file list.
24
- - Branches: checkout, create, rebase, drop, pull and push.
25
- - Review each npm dependency once across `package.json` and the lockfile.
26
- - Propose unused removals, npm audit fixes, and outdated updates as diffs.
27
- - Code highlighting, diff compare: unified, mixed, and side-by-side layouts.
21
+ - Leave feedback, issues/todos, and in-place code proposals as a repair plan
22
+ - Review uncommitted git diffs, a given commit, a GitHub PR, or a GitLab MR
23
+ - Stage, unstage, or revert each contiguous block of diff lines
24
+ - Auto-reload local diffs when files change, keeping the current screen
25
+ - Import GitHub PR and GitLab MR review comments into the local plan for AI
26
+ - Commits list: commit, amend, fixup, and drop
27
+ - Branches: checkout, create, rebase, drop, pull, and push
28
+ - Review each npm dependency once across `package.json` and the lockfile
29
+ - Propose unused removals, npm audit fixes, and outdated updates as diffs
30
+ - Code highlighting and intra-line diffs: unified, mixed, and side-by-side
31
+ - Auto-update patch and minor releases; confirm a new major
32
+ - Work with git, npm and fetch in background
28
33
 
29
34
  ## Install
30
35
 
@@ -50,36 +55,19 @@ Reviews go in `.review/YYYY-MM-DD-NN.md` with frontmatter `status`:
50
55
  - `partial`: AI started; some items remain
51
56
  - `done`: all items marked `[x]`
52
57
 
53
- ## Hotkeys
54
-
55
- | Key | Action |
56
- | ------------------- | --------------------------------------------- |
57
- | `a` / `u` / `d` | Stage / unstage / drop this diff |
58
- | `c` | File list: `c` commit / `a` amend / `f` fixup |
59
- | `b` | List branches; `n` new; `r` rebase; `d` drop |
60
- | `p` / `s` | git pull/push |
61
- | `f` / `t` / `e` | Feedback / repo todo / edit added lines |
62
- | `r` | Reload diffs from disk |
63
- | `m` | Mode: unified / mixed / side-by-side |
64
- | `⏎` | Open the selected file |
65
- | `→` / `j` | Next remaining diff / file |
66
- | `←` / `k` | Previous remaining diff / file |
67
- | `Ctrl-e` / `Ctrl-y` | Scroll one line down / up |
68
- | `Ctrl-f` / `Ctrl-b` | Scroll page down / up |
69
- | `Ctrl-d` / `Ctrl-u` | Scroll half-page down / up |
70
- | `⌫` / `Del` | Remove the selected todo |
71
- | `Esc` / `q` | Quit |
72
-
73
58
  ## Future
74
59
 
75
- - Import issues from GitHub and GitLab.
76
- - Security and code-quality audit; propose a repair plan.
77
- - Send anonymized code blocks for expert review.
78
- - Send questions and the repair plan to experts for approval.
79
- - Ask experts.
80
- - Verify the codebase.
81
- - Apply refactoring skills.
82
- - Call agents, harnesses, and IDEs to execute prepared plans.
60
+ - Import issues from GitHub and GitLab
61
+ - Search, blame, and file history in the review
62
+ - Security and code-quality audit; propose a repair plan
63
+ - Send anonymized code blocks for expert review
64
+ - Send questions and the repair plan to experts for approval
65
+ - Ask experts
66
+ - Verify the codebase
67
+ - Apply refactoring skills
68
+ - Call agents, harnesses, and IDEs to execute prepared plans
69
+ - Squash, cherry-pick, and interactive rebase
70
+ - Merge, rename, and conflict resolution
83
71
 
84
72
  ## License
85
73
 
package/bin/reslop.js CHANGED
@@ -7,7 +7,9 @@ const { LEAVE_TERM } = require('../lib/session.js');
7
7
  const fail = (reason) => {
8
8
  try {
9
9
  process.stdout.write(LEAVE_TERM);
10
- } catch {}
10
+ } catch {
11
+ // ignore
12
+ }
11
13
  process.stderr.write(failLine(reason));
12
14
  process.exit(1);
13
15
  };
package/lib/ansi.js CHANGED
@@ -67,6 +67,7 @@ const THEME = {
67
67
  buttonFg: [0x6a, 0x6a, 0x7e],
68
68
  buttonBg: [0x2a, 0x2a, 0x40],
69
69
  buttonHotFg: [0xff, 0xff, 0xff],
70
+ currentBg: [0xa8, 0xcc, 0xb4],
70
71
  checkFg: [0xf4, 0xf4, 0xfc],
71
72
  checkBg: [0x52, 0x52, 0x74],
72
73
  checkDoneFg: [0xf0, 0xff, 0xf4],
@@ -75,6 +76,7 @@ const THEME = {
75
76
  warnFg: [0xe8, 0xc8, 0x4a],
76
77
  mutedFg: [0x7a, 0x7a, 0x8a],
77
78
  shaFg: [0x56, 0x9c, 0xd6],
79
+ shaDarkFg: [0x15, 0x4a, 0x8c],
78
80
  stagedDelLineFg: [0xa0, 0xa0, 0xa8],
79
81
  stagedDelLineBg: [0x20, 0x20, 0x26],
80
82
  stagedDelCharFg: [0xf0, 0xf0, 0xf4],
@@ -0,0 +1,109 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+
5
+ const READ_METHODS = [
6
+ 'load',
7
+ 'loadAsync',
8
+ 'loadExtras',
9
+ 'toplevel',
10
+ 'resolveRev',
11
+ ];
12
+
13
+ const CHANGE_METHODS = [
14
+ 'add',
15
+ 'addAsync',
16
+ 'unstage',
17
+ 'revert',
18
+ 'revertFile',
19
+ 'edit',
20
+ ];
21
+
22
+ const BRANCH_METHODS = [
23
+ 'commit',
24
+ 'commitAsync',
25
+ 'hasStaged',
26
+ 'lastMessage',
27
+ 'listBranches',
28
+ 'listCommits',
29
+ 'currentBranch',
30
+ 'checkout',
31
+ 'checkoutAsync',
32
+ 'createBranch',
33
+ 'createBranchAsync',
34
+ 'rebase',
35
+ 'rebaseAsync',
36
+ 'drop',
37
+ 'dropAsync',
38
+ 'dropCommit',
39
+ 'dropCommitAsync',
40
+ 'pull',
41
+ 'pullAsync',
42
+ 'push',
43
+ 'pushAsync',
44
+ ];
45
+
46
+ const SOURCE_CAPS = {
47
+ local: { read: true, changes: true, branches: true, review: true },
48
+ 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 },
52
+ };
53
+
54
+ const withReadOnly = (caps, readOnly) => {
55
+ if (!readOnly) return { ...caps };
56
+ return { ...caps, changes: false, branches: false };
57
+ };
58
+
59
+ const capabilitiesFor = (kind, extra = {}) => {
60
+ const base = SOURCE_CAPS[kind] ?? SOURCE_CAPS.local;
61
+ return withReadOnly(base, extra.readOnly === true);
62
+ };
63
+
64
+ const copyMethods = (target, source, names) => {
65
+ for (const name of names) {
66
+ const method = source[name];
67
+ if (typeof method === 'function') target[name] = method;
68
+ }
69
+ };
70
+
71
+ const attachCapabilities = (impl, caps) => {
72
+ const source = { capabilities: { ...caps } };
73
+ if (caps.read) copyMethods(source, impl, READ_METHODS);
74
+ if (caps.changes) copyMethods(source, impl, CHANGE_METHODS);
75
+ if (caps.branches) copyMethods(source, impl, BRANCH_METHODS);
76
+ return source;
77
+ };
78
+
79
+ const sessionCapabilities = (options) => {
80
+ const readOnly = options.readOnly === true;
81
+ if (options.capabilities) return withReadOnly(options.capabilities, readOnly);
82
+ if (options.rev) return capabilitiesFor('commit', { readOnly });
83
+ const source = options.change && options.change.source;
84
+ if (source && SOURCE_CAPS[source]) {
85
+ return capabilitiesFor(source, { readOnly });
86
+ }
87
+ const repoCaps = options.repo && options.repo.capabilities;
88
+ if (repoCaps) return withReadOnly(repoCaps, readOnly);
89
+ return capabilitiesFor(readOnly ? 'read-only' : 'local');
90
+ };
91
+
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
+ module.exports = {
100
+ READ_METHODS,
101
+ CHANGE_METHODS,
102
+ BRANCH_METHODS,
103
+ SOURCE_CAPS,
104
+ capabilitiesFor,
105
+ withReadOnly,
106
+ attachCapabilities,
107
+ sessionCapabilities,
108
+ createReviewStorage,
109
+ };
package/lib/cli.js CHANGED
@@ -8,6 +8,7 @@ const { githubToken, loadPullRequest } = require('./github.js');
8
8
  const { gitlabToken, loadMergeRequest } = require('./gitlab.js');
9
9
  const { selectChangeSource, createLoadedSource } = require('./source.js');
10
10
  const { Session } = require('./session.js');
11
+ const { capabilitiesFor, attachCapabilities } = require('./capabilities.js');
11
12
 
12
13
  const HEX_REV = /^[0-9a-f]{4,40}$/i;
13
14
  const HEAD_REV = /^HEAD([~^].*)?$/i;
@@ -121,22 +122,20 @@ const sessionOptions = (proc, deps, args, env, extra) => ({
121
122
  ...extra,
122
123
  });
123
124
 
124
- const startSession = (options, extra = {}) => {
125
- const session = new Session(options);
126
- if (extra.load) session.load();
127
- return session;
128
- };
129
-
130
125
  const openLocalSession = (proc, deps, args, selected, env) => {
131
126
  const cwd = deps.cwd ?? proc.cwd();
132
- const repo = deps.repo ?? createGitRepo();
127
+ const rawRepo = deps.repo ?? createGitRepo();
133
128
  const statSync = deps.statSync ?? fs.statSync;
134
- const scope = resolveScope(cwd, selected.paths, repo, statSync);
135
- const session = startSession(
129
+ const scope = resolveScope(cwd, selected.paths, rawRepo, statSync);
130
+ const kind = scope.rev ? 'commit' : 'local';
131
+ const capabilities = capabilitiesFor(kind, { readOnly: args.readOnly });
132
+ const repo = attachCapabilities(rawRepo, capabilities);
133
+ const session = new Session(
136
134
  sessionOptions(proc, deps, args, env, {
137
135
  repo,
138
136
  paths: scope.paths,
139
137
  rev: scope.rev,
138
+ capabilities,
140
139
  }),
141
140
  );
142
141
  session.ensureRepo();
@@ -148,27 +147,32 @@ const remoteRepo = (deps, selected, env, cwd) => {
148
147
  const remote = REMOTE_CHANGE[selected.kind];
149
148
  const loader = deps[remote.load] ?? remote.fallback;
150
149
  const token = remote.token(env);
151
- const loadRemote = async () => {
150
+ const loadRemote = async (_cwd, _paths, options = {}) => {
152
151
  const loaded = await loader(remote.target(selected), {
153
152
  cwd,
154
153
  paths: selected.paths,
155
154
  token,
156
155
  fetch: deps.fetch,
156
+ signal: options.signal,
157
157
  });
158
158
  const items = loaded.items ?? [];
159
159
  return { ...loaded, items: [...items] };
160
160
  };
161
- const repo = createLoadedSource({ items: [] });
162
- repo.loadAsync = loadRemote;
163
- return repo;
161
+ return createLoadedSource({ items: [] }, selected.kind, {
162
+ loadAsync: loadRemote,
163
+ });
164
164
  };
165
165
 
166
166
  const openRemoteSession = (proc, deps, args, selected, env) => {
167
167
  const cwd = deps.cwd ?? proc.cwd();
168
- return startSession(
168
+ const capabilities = capabilitiesFor(selected.kind, {
169
+ readOnly: args.readOnly,
170
+ });
171
+ return new Session(
169
172
  sessionOptions(proc, deps, args, env, {
170
173
  repo: remoteRepo(deps, selected, env, cwd),
171
174
  paths: selected.paths,
175
+ capabilities,
172
176
  }),
173
177
  );
174
178
  };