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,9 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const files = require('../files.js');
3
+ const files = require('../common/files.js');
4
4
  const { listPath, isTaskItem, isTasksEntry, TASKS_FILE } = files;
5
- const actions = require('./actions.js');
6
- const { LIST_PANES } = actions;
5
+ const { LIST_PANES } = require('../input/actions.js');
7
6
 
8
7
  const freshListScroll = () =>
9
8
  Object.fromEntries(LIST_PANES.map((pane) => [pane, 0]));
@@ -20,6 +19,8 @@ class Navigation {
20
19
  this.branchCursor = 0;
21
20
  this.commitCursor = 0;
22
21
  this.npmCursor = 0;
22
+ this.packagesCursor = 0;
23
+ this.agentCursor = 0;
23
24
  this.scroll = 0;
24
25
  this.listScrollByPane = freshListScroll();
25
26
  this.reviewPath = null;
@@ -28,9 +29,12 @@ class Navigation {
28
29
  this.selection = null;
29
30
  this.mouseAnchor = null;
30
31
  this.pendingClick = null;
32
+ this.scrollDrag = null;
31
33
  this.lastClick = null;
32
34
  this.tasksOpen = false;
33
35
  this.tasksFocus = 0;
36
+ this.find = null;
37
+ this.import = null;
34
38
  }
35
39
 
36
40
  clampIndex(length) {
@@ -96,6 +100,7 @@ class Navigation {
96
100
  this.selection = null;
97
101
  this.mouseAnchor = null;
98
102
  this.pendingClick = null;
103
+ this.scrollDrag = null;
99
104
  }
100
105
 
101
106
  get listScroll() {
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ const { stripAnsi } = require('../../term/ansi.js');
4
+ const { buildDocument } = require('../../report/parse.js');
5
+ const report = require('../../report/render.js');
6
+ const { renderDocument, renderReport } = report;
7
+ const runStore = require('../../runs/runs.js');
8
+ const { Tracker, summarizeDocument } = runStore;
9
+ const { runName } = require('../../dashboard/model.js');
10
+ const { reduceOutput } = require('../../runs/output.js');
11
+ const { trimText, pad2 } = require('../../common/utilities.js');
12
+
13
+ const TESTS_RE = /^(?:TAP version \d+|\s*(?:not )?ok\b|[ℹi]\s+tests\s+\d+)/m;
14
+
15
+ const filteredOutput = (raw, root, status, rawFile) => {
16
+ const text = stripAnsi(`${raw ?? ''}`);
17
+ if (status === '' || !TESTS_RE.test(text)) {
18
+ const output = reduceOutput(raw, root, status);
19
+ return { output, log: output, result: null };
20
+ }
21
+ const doc = buildDocument(raw, '', root, status, 'test');
22
+ const result = summarizeDocument(doc);
23
+ const output = renderReport(doc);
24
+ const log = renderDocument(doc, rawFile);
25
+ return { output, log, result };
26
+ };
27
+
28
+ const runLabel = (entry) =>
29
+ entry.kind === 'bin' ? `npm exec ${entry.name}` : `npm run ${entry.name}`;
30
+
31
+ const historyMark = (run) => {
32
+ if (run.status === 'running') return 'run';
33
+ if (run.status === 'stopped') return 'stop';
34
+ if (run.exit === 0 || run.exit === '0') return 'pass';
35
+ return 'fail';
36
+ };
37
+
38
+ const historyCounts = (run) => {
39
+ const result = run.result;
40
+ if (result && typeof result.tests === 'number') {
41
+ const fail = result.failed ?? 0;
42
+ const ok = result.passed ?? Math.max(0, result.tests - fail);
43
+ return { done: result.tests, ok, fail, total: result.tests };
44
+ }
45
+ const progress = run.progress;
46
+ if (!progress) return null;
47
+ const ok = progress.done ?? 0;
48
+ const fail = progress.failed ?? 0;
49
+ if (!ok && !fail && !(progress.expected > 0)) return null;
50
+ const total = progress.expected > 0 ? progress.expected : null;
51
+ return { done: ok + fail, ok, fail, total };
52
+ };
53
+
54
+ const countText = (value) => (typeof value === 'number' ? `${value}` : '');
55
+
56
+ const historyExit = (run, counts) => {
57
+ if (counts || run.status !== 'exited') return '';
58
+ if (run.exit === '' || run.exit === null || run.exit === undefined) return '';
59
+ return `exit ${run.exit}`;
60
+ };
61
+
62
+ const earlierTotal = (list, run) => {
63
+ let best = 0;
64
+ let at = -1;
65
+ for (const item of list) {
66
+ if (item === run || item.name !== run.name) continue;
67
+ if (item.status === 'running') continue;
68
+ const counts = historyCounts(item);
69
+ if (!counts || typeof counts.total !== 'number') continue;
70
+ const started = item.startedAt || 0;
71
+ if (started < at) continue;
72
+ at = started;
73
+ best = counts.total;
74
+ }
75
+ return best;
76
+ };
77
+
78
+ const noteTests = (run, list, text) => {
79
+ const plain = stripAnsi(`${text ?? ''}`);
80
+ if (!TESTS_RE.test(plain)) return;
81
+ const tracker = new Tracker();
82
+ tracker.feed(plain);
83
+ if (!Object.hasOwn(run, 'expected')) run.expected = earlierTotal(list, run);
84
+ run.progress = {
85
+ done: tracker.progress.done,
86
+ failed: tracker.progress.failed,
87
+ expected: run.expected,
88
+ };
89
+ };
90
+
91
+ const RUN_KEEP = 50;
92
+
93
+ const LOG_GAP_MS = 2000;
94
+
95
+ const RESLOP_RUN = /^reslop(?:\.js)?\s+t\s+--\s+/;
96
+
97
+ const RESLOP_T = /reslop(?:\.js)?\s+t\s+--/;
98
+
99
+ const launchedReslop = (run) => {
100
+ if (run.externalId) return false;
101
+ const command = `${run.command ?? ''}`;
102
+ return RESLOP_T.test(command);
103
+ };
104
+
105
+ const historyName = (run) => {
106
+ const script = trimText(run.name);
107
+ if (script) return script;
108
+ return trimText(run.command).replace(RESLOP_RUN, '');
109
+ };
110
+
111
+ const sameWindow = (left, right) => {
112
+ const start = Number(left.startedAt) || 0;
113
+ const end = Number(left.endedAt) || start;
114
+ const at = Number(right.startedAt) || 0;
115
+ const done = Number(right.endedAt) || at;
116
+ return done >= start - LOG_GAP_MS && at <= end + LOG_GAP_MS;
117
+ };
118
+
119
+ const logBase = (name) => {
120
+ const file = `${name ?? ''}`;
121
+ if (!file || file.includes('/') || file.includes('\\')) return '';
122
+ return file;
123
+ };
124
+
125
+ const externalStatus = (record) => {
126
+ if (record.status === 'running') return 'running';
127
+ if (record.status === 'aborted') return 'stopped';
128
+ return 'exited';
129
+ };
130
+
131
+ const externalExit = (record) => {
132
+ if (record.status === 'running' || record.status === 'aborted') return '';
133
+ if (record.exit === null || record.exit === undefined) return '';
134
+ return record.exit;
135
+ };
136
+
137
+ const progressCount = (progress, key) => {
138
+ if (!progress || typeof progress[key] !== 'number') return 0;
139
+ return progress[key];
140
+ };
141
+
142
+ const progressSame = (left, right) => {
143
+ const done = progressCount(left, 'done') === progressCount(right, 'done');
144
+ const leftFailed = progressCount(left, 'failed');
145
+ const rightFailed = progressCount(right, 'failed');
146
+ const failed = leftFailed === rightFailed;
147
+ const leftExpected = progressCount(left, 'expected');
148
+ const rightExpected = progressCount(right, 'expected');
149
+ const expected = leftExpected === rightExpected;
150
+ return done && failed && expected;
151
+ };
152
+
153
+ const resultSame = (left, right) => {
154
+ if (!left && !right) return true;
155
+ if (!left || !right) return false;
156
+ return (
157
+ left.tests === right.tests &&
158
+ left.passed === right.passed &&
159
+ left.failed === right.failed &&
160
+ left.errors === right.errors
161
+ );
162
+ };
163
+
164
+ const applyExternal = (run, record) => {
165
+ const status = externalStatus(record);
166
+ const exit = externalExit(record);
167
+ const endedAt = Number(record.endedAt) || 0;
168
+ const progress = record.progress ?? null;
169
+ const result = record.result ?? null;
170
+ const same =
171
+ run.status === status &&
172
+ run.exit === exit &&
173
+ (run.endedAt || 0) === endedAt &&
174
+ progressSame(run.progress, progress) &&
175
+ resultSame(run.result, result);
176
+ if (same) return false;
177
+ run.status = status;
178
+ run.exit = exit;
179
+ run.endedAt = endedAt;
180
+ run.progress = progress;
181
+ run.result = result;
182
+ return true;
183
+ };
184
+
185
+ const recordedItem = (record) => {
186
+ const script = trimText(record.script);
187
+ const command = trimText(record.command);
188
+ const name = runName({ command, script }).replace(RESLOP_RUN, '');
189
+ return {
190
+ name,
191
+ kind: script ? 'script' : 'command',
192
+ command,
193
+ status: externalStatus(record),
194
+ exit: externalExit(record),
195
+ startedAt: Number(record.startedAt) || 0,
196
+ endedAt: Number(record.endedAt) || 0,
197
+ logName: logBase(record.log),
198
+ rawName: logBase(record.raw),
199
+ externalId: `${record.id ?? ''}`,
200
+ result: record.result ?? null,
201
+ progress: record.progress ?? null,
202
+ };
203
+ };
204
+
205
+ const whenLabel = (ms) => {
206
+ if (!ms) return '';
207
+ const date = new Date(ms);
208
+ const hh = pad2(date.getHours());
209
+ const mm = pad2(date.getMinutes());
210
+ const time = `${hh}:${mm}`;
211
+ const today = new Date();
212
+ const sameDay =
213
+ date.getFullYear() === today.getFullYear() &&
214
+ date.getMonth() === today.getMonth() &&
215
+ date.getDate() === today.getDate();
216
+ if (sameDay) return time;
217
+ const month = pad2(date.getMonth() + 1);
218
+ const day = pad2(date.getDate());
219
+ return `${month}-${day} ${time}`;
220
+ };
221
+
222
+ const elapsedText = (run, now) => {
223
+ const end = run.endedAt || now;
224
+ const ms = Math.max(0, end - (run.startedAt || end));
225
+ const sec = Math.round(ms / 1000);
226
+ if (sec < 60) return `${sec}s`;
227
+ const min = Math.floor(sec / 60);
228
+ const rest = sec % 60;
229
+ return `${min}m${pad2(rest)}s`;
230
+ };
231
+
232
+ module.exports = {
233
+ filteredOutput,
234
+ runLabel,
235
+ historyMark,
236
+ historyCounts,
237
+ countText,
238
+ historyExit,
239
+ noteTests,
240
+ RUN_KEEP,
241
+ launchedReslop,
242
+ historyName,
243
+ sameWindow,
244
+ logBase,
245
+ applyExternal,
246
+ recordedItem,
247
+ whenLabel,
248
+ elapsedText,
249
+ };