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.
- package/CHANGELOG.md +27 -1
- package/README.md +16 -14
- package/bin/reslop.js +8 -5
- package/lib/agents/agents.js +504 -0
- package/lib/agents/allow.js +71 -0
- package/lib/agents/allow.json +16 -0
- package/lib/agents/models.js +460 -0
- package/lib/api.js +15 -12
- package/lib/cli.js +61 -66
- package/lib/{files.js → common/files.js} +51 -52
- package/lib/common/find.js +61 -0
- package/lib/common/format.js +101 -0
- package/lib/{utilities.js → common/process.js} +7 -95
- package/lib/common/tasks.json +27 -0
- package/lib/common/utilities.js +112 -0
- package/lib/dashboard/git.js +9 -16
- package/lib/dashboard/model.js +117 -51
- package/lib/dashboard/npm.js +133 -32
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +41 -31
- package/lib/dashboard/workspace.js +151 -0
- package/lib/{deps.js → deps/deps.js} +31 -45
- package/lib/{deps-diff.js → deps/diff.js} +35 -25
- package/lib/{manifest.js → deps/manifest.js} +47 -30
- package/lib/diff/align.js +1 -2
- package/lib/diff/diff.js +2 -2
- package/lib/diff/display.js +29 -5
- package/lib/diff/inline.js +10 -14
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +39 -3
- package/lib/diff/unit.js +45 -16
- package/lib/{git-branches.js → git/branches.js} +53 -48
- package/lib/{git-deps.js → git/deps.js} +123 -95
- package/lib/git/git.js +71 -0
- package/lib/git/worktree.js +475 -0
- package/lib/highlight/bash.js +1 -2
- package/lib/highlight/csv.js +1 -2
- package/lib/highlight/dot.js +1 -2
- package/lib/{highlight.js → highlight/highlight.js} +42 -11
- package/lib/highlight/html.js +20 -18
- package/lib/highlight/json.js +1 -2
- package/lib/highlight/log.js +1 -2
- package/lib/highlight/txt.js +1 -2
- package/lib/{session → input}/actions.js +240 -71
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +36 -39
- package/lib/input/line-edit.js +77 -0
- package/lib/input/menu-pick.js +70 -0
- package/lib/render/agents.js +640 -0
- package/lib/render/branches.js +4 -5
- package/lib/render/commits.js +54 -64
- package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
- package/lib/render/dashboard/blocks.js +490 -0
- package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
- package/lib/render/diff.js +166 -45
- package/lib/render/field.js +131 -0
- package/lib/render/files.js +22 -8
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +127 -25
- package/lib/render/header.js +176 -0
- package/lib/render/log.js +319 -0
- package/lib/render/menu.js +60 -0
- package/lib/render/notes.js +29 -13
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +89 -0
- package/lib/render/primitives.js +109 -16
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +214 -0
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +19 -61
- package/lib/render/tiles.js +203 -47
- package/lib/render/unit.js +32 -13
- package/lib/{report-model.js → report/model.js} +4 -6
- package/lib/{report-parse.js → report/parse.js} +9 -14
- package/lib/{report-render.js → report/render.js} +46 -14
- package/lib/{report-run.js → report/run.js} +42 -30
- package/lib/{report-test.js → report/test.js} +56 -55
- package/lib/{report-tools.js → report/tools.js} +24 -33
- package/lib/review/agent-instructions.md +9 -0
- package/lib/review/format.js +426 -0
- package/lib/{review-merge.js → review/merge.js} +9 -8
- package/lib/review/review.js +190 -0
- package/lib/review/store.js +281 -0
- package/lib/review/templates.js +83 -0
- package/lib/runs/commands.js +14 -0
- package/lib/runs/logs.js +299 -0
- package/lib/runs/output.js +174 -0
- package/lib/runs/process.js +113 -0
- package/lib/{runs.js → runs/runs.js} +84 -36
- package/lib/runs/scripts.js +150 -0
- package/lib/session/agents/agents.js +958 -0
- package/lib/session/agents/job.js +320 -0
- package/lib/session/agents/jobs.js +355 -0
- package/lib/session/agents/options.js +153 -0
- package/lib/session/agents/plans.js +83 -0
- package/lib/session/branches.js +36 -44
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +122 -56
- package/lib/session/compose.js +197 -146
- package/lib/session/dashboard.js +183 -37
- package/lib/session/dispatch.js +120 -22
- package/lib/session/files-pane.js +60 -24
- package/lib/session/find.js +81 -0
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +96 -66
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +8 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/npm/packages.js +150 -0
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +216 -10
- package/lib/session/prefs.js +112 -0
- package/lib/session/reads.js +109 -0
- package/lib/session/review.js +28 -10
- package/lib/session/run-panel.js +40 -0
- package/lib/{session.js → session/session.js} +166 -59
- package/lib/session/tasks.js +321 -66
- package/lib/session/templates.js +1 -1
- package/lib/session/terminal.js +94 -12
- package/lib/session/unit.js +35 -30
- package/lib/session/update.js +194 -6
- package/lib/session/view.js +72 -59
- package/lib/session/workspace.js +416 -0
- package/lib/{capabilities.js → source/capabilities.js} +3 -12
- package/lib/{github.js → source/github.js} +102 -32
- package/lib/{gitlab.js → source/gitlab.js} +87 -10
- package/lib/{remote.js → source/remote.js} +144 -40
- package/lib/source/review-threads.graphql +20 -0
- package/lib/source/source.js +74 -0
- package/lib/term/ansi.js +396 -0
- package/lib/{clipboard.js → term/clipboard.js} +3 -6
- package/lib/{select.js → term/select.js} +2 -2
- package/lib/term/themes/dark.json +122 -0
- package/lib/term/themes/light.json +122 -0
- package/lib/term/wide.txt +21 -0
- package/lib/{wrap.js → term/wrap.js} +4 -4
- package/package.json +2 -2
- package/lib/ansi.js +0 -505
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -437
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -628
- package/lib/render/dash-blocks.js +0 -245
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/review.js +0 -744
- package/lib/session/accessors.js +0 -26
- package/lib/session/edit-click.js +0 -57
- package/lib/session/npm.js +0 -452
- package/lib/session/progress.js +0 -41
- package/lib/source.js +0 -36
- package/lib/update.js +0 -192
- /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
package/lib/session/dashboard.js
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
9
|
-
const tree = require('../dashboard/tree.js');
|
|
10
|
-
const { FileIndex, HEAT_MS } = tree;
|
|
11
|
-
const gitInfo = require('../dashboard/git.js');
|
|
12
|
-
const { readGitSummary } = gitInfo;
|
|
13
|
-
const npmInfo = require('../dashboard/npm.js');
|
|
14
|
-
const { readNpmSummary } = npmInfo;
|
|
3
|
+
const { fileEntries, fileTotals } = require('../common/files.js');
|
|
4
|
+
const { noteCounts } = require('../review/review.js');
|
|
5
|
+
const { readRuns, settleRecord, isRunsRel } = require('../runs/runs.js');
|
|
6
|
+
const { FileIndex, HEAT_MS } = require('../dashboard/tree.js');
|
|
7
|
+
const { readGitSummary } = require('../dashboard/git.js');
|
|
8
|
+
const { readNpmSummary } = require('../dashboard/npm.js');
|
|
15
9
|
const model = require('../dashboard/model.js');
|
|
16
|
-
const { buildModel } = model;
|
|
10
|
+
const { buildModel, minuteOf, MINUTE_MS, npmModel } = model;
|
|
17
11
|
const filesPane = require('./files-pane.js');
|
|
18
|
-
const
|
|
19
|
-
const { UNKNOWN_PATH, MODULES_STAMP } = watch;
|
|
12
|
+
const { UNKNOWN_PATH, MODULES_STAMP } = require('../dashboard/watch.js');
|
|
20
13
|
|
|
21
14
|
const TICK_ID = 'dashboard';
|
|
22
15
|
const HEAT_TIMER = 'dashboard-heat';
|
|
23
16
|
const AGE_TIMER = 'dashboard-age';
|
|
24
|
-
const
|
|
17
|
+
const MINUTE_TIMER = 'dashboard-minute';
|
|
18
|
+
const BLINK_TIMER = 'dashboard-blink';
|
|
19
|
+
const BLINK_MS = 3000;
|
|
20
|
+
const MINUTE_KEEP = 360;
|
|
25
21
|
const SWITCHES = 4;
|
|
26
|
-
const
|
|
22
|
+
const RUN_MARK = { failed: 'fail', passed: 'pass', aborted: 'abort' };
|
|
23
|
+
const GIT_NAMES = [
|
|
27
24
|
'HEAD',
|
|
28
25
|
'packed-refs',
|
|
29
26
|
'FETCH_HEAD',
|
|
@@ -32,8 +29,8 @@ const GIT_NAMES = new Set([
|
|
|
32
29
|
'logs',
|
|
33
30
|
'rebase-merge',
|
|
34
31
|
'rebase-apply',
|
|
35
|
-
]
|
|
36
|
-
const NPM_FILES =
|
|
32
|
+
];
|
|
33
|
+
const NPM_FILES = ['package.json', 'package-lock.json', MODULES_STAMP];
|
|
37
34
|
const AGE_FAST_MS = 1000;
|
|
38
35
|
const AGE_MID_MS = 30000;
|
|
39
36
|
const AGE_SLOW_MS = 600000;
|
|
@@ -81,11 +78,12 @@ const classify = (paths) => {
|
|
|
81
78
|
if (rel === UNKNOWN_PATH) {
|
|
82
79
|
found.all = true;
|
|
83
80
|
} else if (rel.startsWith('.git/')) {
|
|
84
|
-
|
|
81
|
+
const gitName = rel.split('/')[1];
|
|
82
|
+
if (GIT_NAMES.includes(gitName)) found.git = true;
|
|
85
83
|
} else if (isRunsRel(rel)) {
|
|
86
84
|
found.runs = true;
|
|
87
85
|
} else {
|
|
88
|
-
if (NPM_FILES.
|
|
86
|
+
if (NPM_FILES.includes(rel)) found.npm = true;
|
|
89
87
|
found.files.push(rel);
|
|
90
88
|
}
|
|
91
89
|
}
|
|
@@ -124,7 +122,9 @@ class Dashboard {
|
|
|
124
122
|
this.totals = null;
|
|
125
123
|
this.totalsKey = '';
|
|
126
124
|
this.diffDelta = null;
|
|
127
|
-
this.
|
|
125
|
+
this.minutes = new Map();
|
|
126
|
+
this.activityAt = 0;
|
|
127
|
+
this.rebaseOn = false;
|
|
128
128
|
this.switches = [];
|
|
129
129
|
this.branchAt = new Map();
|
|
130
130
|
this.branchSha = new Map();
|
|
@@ -177,6 +177,101 @@ class Dashboard {
|
|
|
177
177
|
progress.stop(TICK_ID);
|
|
178
178
|
term.cancel(HEAT_TIMER);
|
|
179
179
|
term.cancel(AGE_TIMER);
|
|
180
|
+
term.cancel(MINUTE_TIMER);
|
|
181
|
+
term.cancel(BLINK_TIMER);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
bucket(at) {
|
|
185
|
+
const minute = minuteOf(at);
|
|
186
|
+
let newest = minute;
|
|
187
|
+
for (const key of this.minutes.keys()) {
|
|
188
|
+
if (key > newest) newest = key;
|
|
189
|
+
}
|
|
190
|
+
if (minute < newest - MINUTE_KEEP) return null;
|
|
191
|
+
let row = this.minutes.get(minute);
|
|
192
|
+
if (!row) {
|
|
193
|
+
row = { level: 0, added: 0, removed: 0, marks: [] };
|
|
194
|
+
this.minutes.set(minute, row);
|
|
195
|
+
}
|
|
196
|
+
const oldest = newest - MINUTE_KEEP;
|
|
197
|
+
for (const key of this.minutes.keys()) {
|
|
198
|
+
if (key < oldest) this.minutes.delete(key);
|
|
199
|
+
}
|
|
200
|
+
return row;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
touchActivity(at) {
|
|
204
|
+
if (at > this.activityAt) this.activityAt = at;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
heat(at, added, removed = 0) {
|
|
208
|
+
const plus = Math.max(0, added || 0);
|
|
209
|
+
const minus = Math.max(0, removed || 0);
|
|
210
|
+
if (!at || plus + minus <= 0) return;
|
|
211
|
+
const row = this.bucket(at);
|
|
212
|
+
if (!row) return;
|
|
213
|
+
row.added += plus;
|
|
214
|
+
row.removed += minus;
|
|
215
|
+
row.level += plus + minus;
|
|
216
|
+
this.touchActivity(at);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
mark(at, kind) {
|
|
220
|
+
if (!at || !kind) return;
|
|
221
|
+
const row = this.bucket(at);
|
|
222
|
+
if (!row || row.marks.includes(kind)) return;
|
|
223
|
+
row.marks.push(kind);
|
|
224
|
+
this.touchActivity(at);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
noteCommits(next) {
|
|
228
|
+
const recent = next.commits.recent;
|
|
229
|
+
for (const entry of recent) this.mark(entry.at, 'commit');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
noteRebase(next, now) {
|
|
233
|
+
const rebasing = Boolean(next.rebase);
|
|
234
|
+
if (rebasing && !this.rebaseOn) this.mark(now, 'rebase');
|
|
235
|
+
this.rebaseOn = rebasing;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
noteRuns(runs) {
|
|
239
|
+
for (const run of runs) {
|
|
240
|
+
const kind = RUN_MARK[run.status];
|
|
241
|
+
if (kind) this.mark(run.endedAt || run.startedAt, kind);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
isRunLive() {
|
|
246
|
+
for (const run of this.liveRuns()) {
|
|
247
|
+
if (run.status === 'running') return true;
|
|
248
|
+
}
|
|
249
|
+
const npm = this.ui.npm.lastRun;
|
|
250
|
+
return Boolean(npm && npm.running);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
isBusy() {
|
|
254
|
+
if (this.git && this.git.rebase) return true;
|
|
255
|
+
return this.isRunLive();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
liveActivity(now) {
|
|
259
|
+
if (this.isBusy()) return true;
|
|
260
|
+
return now - this.activityAt < BLINK_MS;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
activityView(now) {
|
|
264
|
+
const minutes = [];
|
|
265
|
+
for (const [at, row] of this.minutes) {
|
|
266
|
+
minutes.push({
|
|
267
|
+
at,
|
|
268
|
+
level: row.level,
|
|
269
|
+
added: row.added,
|
|
270
|
+
removed: row.removed,
|
|
271
|
+
marks: row.marks.slice(),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return { minutes, live: this.liveActivity(now) };
|
|
180
275
|
}
|
|
181
276
|
|
|
182
277
|
afterDraw() {
|
|
@@ -184,17 +279,21 @@ class Dashboard {
|
|
|
184
279
|
if (!this.visible()) return void this.cancelTimers();
|
|
185
280
|
const { progress, term } = this.ui;
|
|
186
281
|
const now = Date.now();
|
|
282
|
+
if (this.isBusy()) this.touchActivity(now);
|
|
187
283
|
const until = this.hotUntil();
|
|
188
284
|
const hot = now < until;
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
if (running || hot) progress.start(TICK_ID);
|
|
285
|
+
const live = this.liveActivity(now);
|
|
286
|
+
if (live || hot) progress.start(TICK_ID);
|
|
192
287
|
else progress.stop(TICK_ID);
|
|
193
288
|
const repaint = () => this.paintIfVisible();
|
|
194
289
|
if (hot) term.later(HEAT_TIMER, repaint, until - now + 20);
|
|
195
|
-
const at = this.youngest(
|
|
290
|
+
const at = this.youngest(this.liveRuns());
|
|
196
291
|
const delay = at ? ageDelay(now - at) : AGE_SLOW_MS;
|
|
197
292
|
term.later(AGE_TIMER, repaint, delay);
|
|
293
|
+
const minuteLeft = MINUTE_MS - (now % MINUTE_MS) + 20;
|
|
294
|
+
term.later(MINUTE_TIMER, repaint, minuteLeft);
|
|
295
|
+
const blinkLeft = this.activityAt + BLINK_MS - now;
|
|
296
|
+
if (blinkLeft > 0) term.later(BLINK_TIMER, repaint, blinkLeft + 20);
|
|
198
297
|
}
|
|
199
298
|
|
|
200
299
|
async runFiles() {
|
|
@@ -209,7 +308,12 @@ class Dashboard {
|
|
|
209
308
|
const after = index.summary().total;
|
|
210
309
|
const lines = after.lines - before.lines;
|
|
211
310
|
const bytes = after.bytes - before.bytes;
|
|
212
|
-
if (lines
|
|
311
|
+
if (!lines && !bytes) return;
|
|
312
|
+
const at = Date.now();
|
|
313
|
+
this.fileDelta = { lines, at };
|
|
314
|
+
const plus = lines > 0 ? lines : 0;
|
|
315
|
+
const minus = lines < 0 ? -lines : 0;
|
|
316
|
+
this.heat(at, plus || (minus ? 0 : 1), minus);
|
|
213
317
|
}
|
|
214
318
|
|
|
215
319
|
noteShas(next, now) {
|
|
@@ -243,9 +347,12 @@ class Dashboard {
|
|
|
243
347
|
const moved = this.noteShas(next, now);
|
|
244
348
|
const switched = this.noteSwitch(next, now);
|
|
245
349
|
if (moved || switched) this.marks.branches = now;
|
|
350
|
+
if (switched) this.mark(now, 'branch');
|
|
246
351
|
const sha = next.commits.last ? next.commits.last.sha : '';
|
|
247
352
|
if (this.lastSha && sha !== this.lastSha) this.marks.commit = now;
|
|
248
353
|
this.lastSha = sha;
|
|
354
|
+
this.noteCommits(next);
|
|
355
|
+
this.noteRebase(next, now);
|
|
249
356
|
this.git = next;
|
|
250
357
|
}
|
|
251
358
|
|
|
@@ -259,12 +366,17 @@ class Dashboard {
|
|
|
259
366
|
(prev.deps !== next.deps ||
|
|
260
367
|
prev.dev !== next.dev ||
|
|
261
368
|
prev.modules.stamp !== next.modules.stamp);
|
|
262
|
-
if (changed)
|
|
369
|
+
if (changed) {
|
|
370
|
+
const now = Date.now();
|
|
371
|
+
this.marks.npm = now;
|
|
372
|
+
this.mark(now, 'npm');
|
|
373
|
+
}
|
|
263
374
|
this.npm = next;
|
|
264
375
|
}
|
|
265
376
|
|
|
266
377
|
runRuns() {
|
|
267
378
|
this.runs = readRuns(this.root());
|
|
379
|
+
this.noteRuns(this.runs);
|
|
268
380
|
}
|
|
269
381
|
|
|
270
382
|
noteLoaded() {
|
|
@@ -276,12 +388,14 @@ class Dashboard {
|
|
|
276
388
|
if (prev) {
|
|
277
389
|
const added = totals.added - prev.added;
|
|
278
390
|
const removed = totals.removed - prev.removed;
|
|
279
|
-
|
|
391
|
+
const at = Date.now();
|
|
392
|
+
this.diffDelta = { added, removed, at };
|
|
393
|
+
const plus = Math.max(0, added);
|
|
394
|
+
const minus = Math.max(0, removed);
|
|
395
|
+
this.heat(at, plus || (minus ? 0 : 1), minus);
|
|
280
396
|
}
|
|
281
397
|
this.totals = totals;
|
|
282
398
|
this.totalsKey = key;
|
|
283
|
-
this.samples.push(totals.added + totals.removed);
|
|
284
|
-
if (this.samples.length > SAMPLES) this.samples.shift();
|
|
285
399
|
}
|
|
286
400
|
|
|
287
401
|
requestAll() {
|
|
@@ -290,8 +404,12 @@ class Dashboard {
|
|
|
290
404
|
}
|
|
291
405
|
|
|
292
406
|
noteDisk(paths) {
|
|
293
|
-
if (!this.active) return;
|
|
294
407
|
const found = classify(paths);
|
|
408
|
+
const npm = this.ui.npm;
|
|
409
|
+
if ((found.runs || found.all) && npm && npm.refreshRecorded) {
|
|
410
|
+
npm.refreshRecorded();
|
|
411
|
+
}
|
|
412
|
+
if (!this.active) return;
|
|
295
413
|
if (found.all) return void this.requestAll();
|
|
296
414
|
const tasks = this.tasks;
|
|
297
415
|
if (found.files.length) {
|
|
@@ -325,17 +443,18 @@ class Dashboard {
|
|
|
325
443
|
|
|
326
444
|
view() {
|
|
327
445
|
const ui = this.ui;
|
|
446
|
+
const now = Date.now();
|
|
328
447
|
const entries = fileEntries(ui.items);
|
|
329
448
|
const store = ui.review.store;
|
|
330
449
|
return buildModel({
|
|
331
|
-
now
|
|
450
|
+
now,
|
|
332
451
|
frame: ui.progressFrame,
|
|
333
452
|
busy: ui.ops.busy,
|
|
334
453
|
index: this.index,
|
|
335
454
|
fileDelta: this.fileDelta,
|
|
336
455
|
entries,
|
|
337
456
|
totals: this.totals ?? fileTotals(entries),
|
|
338
|
-
|
|
457
|
+
activity: this.activityView(now),
|
|
339
458
|
diffDelta: this.diffDelta,
|
|
340
459
|
git: this.git,
|
|
341
460
|
switches: this.switches,
|
|
@@ -344,17 +463,43 @@ class Dashboard {
|
|
|
344
463
|
npm: this.npm,
|
|
345
464
|
npmExtras: ui.lifecycle.npmExtras(),
|
|
346
465
|
npmRun: ui.npm.lastRun,
|
|
466
|
+
npmRuns: ui.npm.dashboardRuns(),
|
|
347
467
|
runs: this.liveRuns(),
|
|
348
468
|
notes: noteCounts(store),
|
|
349
469
|
store,
|
|
470
|
+
agents: ui.agents.summary(),
|
|
350
471
|
});
|
|
351
472
|
}
|
|
352
473
|
|
|
474
|
+
packagesView() {
|
|
475
|
+
return npmModel({
|
|
476
|
+
npm: this.npm,
|
|
477
|
+
npmExtras: this.ui.lifecycle.npmExtras(),
|
|
478
|
+
npmRun: this.ui.npm.lastRun,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
packageCount() {
|
|
483
|
+
const data = this.packagesView();
|
|
484
|
+
if (!data.ready || !data.hasManifest) return 1;
|
|
485
|
+
const listed = data.modules && data.modules.packages;
|
|
486
|
+
return listed ? listed.length : 0;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
openPackages() {
|
|
490
|
+
const ui = this.ui;
|
|
491
|
+
ui.nav.pane = 'packages';
|
|
492
|
+
ui.nav.packagesCursor = 0;
|
|
493
|
+
ui.nav.resetListScroll('packages');
|
|
494
|
+
ui.nav.clearSelection();
|
|
495
|
+
ui.status = '';
|
|
496
|
+
}
|
|
497
|
+
|
|
353
498
|
showFiles(scope) {
|
|
354
499
|
const ui = this.ui;
|
|
355
500
|
ui.showFiles();
|
|
356
|
-
if (scope === 'file') filesPane.onFileScope(ui);
|
|
357
|
-
else filesPane.onDiffScope(ui);
|
|
501
|
+
if (scope === 'file') filesPane.onFileScope(ui, true);
|
|
502
|
+
else filesPane.onDiffScope(ui, true);
|
|
358
503
|
ui.status = '';
|
|
359
504
|
}
|
|
360
505
|
|
|
@@ -365,9 +510,10 @@ class Dashboard {
|
|
|
365
510
|
diffs: () => this.showFiles('diff'),
|
|
366
511
|
commits: () => ui.commits.onCommit(),
|
|
367
512
|
branches: () => ui.gitBranches.onBranch(),
|
|
368
|
-
npm: () =>
|
|
513
|
+
npm: () => this.openPackages(),
|
|
369
514
|
run: () => ui.npm.open(),
|
|
370
515
|
tasks: () => ui.composer.tasks.openTasksPage(),
|
|
516
|
+
agents: () => ui.agents.open(),
|
|
371
517
|
};
|
|
372
518
|
const opener = openers[id];
|
|
373
519
|
if (!opener || ui.mode !== 'review') return;
|
package/lib/session/dispatch.js
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
const { hasStagedChanges, listedCommits } = commit;
|
|
7
|
-
const actions = require('./actions.js');
|
|
3
|
+
const { COMPOSE_LEAVE } = require('./compose.js');
|
|
4
|
+
const { listedCommits } = require('./commit.js');
|
|
5
|
+
const actions = require('../input/actions.js');
|
|
8
6
|
const { LIST_PANES, DASH_BLOCKS, DASH_ALLOWED, CONFIRM_MODE } = actions;
|
|
9
|
-
const {
|
|
10
|
-
const
|
|
11
|
-
const { onCommand } =
|
|
7
|
+
const { REPOS_ALLOWED } = actions;
|
|
8
|
+
const { disabledActions, actionFromKey, confirmKey, lineHasDiff } = actions;
|
|
9
|
+
const { onCommand } = require('./change-actions.js');
|
|
12
10
|
const filesPane = require('./files-pane.js');
|
|
13
|
-
const
|
|
14
|
-
const {
|
|
11
|
+
const { handlePointer } = require('./pointer.js');
|
|
12
|
+
const { FIND_PANES, startFind, onFindKey } = require('./find.js');
|
|
13
|
+
const { startImport, stopImport, onImportKey } = require('./import.js');
|
|
14
|
+
const { unitBodyLines } = require('./view.js');
|
|
15
15
|
|
|
16
16
|
const currentTarget = (ui) => {
|
|
17
17
|
if (ui.pane === 'files') return ui.fileCursorEntry();
|
|
18
18
|
if (ui.pane === 'branches') return ui.branches[ui.branchCursor];
|
|
19
19
|
if (ui.pane === 'commits') {
|
|
20
20
|
const entry = listedCommits(ui.commits.commits)[ui.commitCursor];
|
|
21
|
-
const canCommit =
|
|
21
|
+
const canCommit = ui.commits.staged;
|
|
22
22
|
const unstagedCurrent = !ui.rev && !ui.revShort;
|
|
23
23
|
return { ...entry, canCommit, unstagedCurrent };
|
|
24
24
|
}
|
|
25
|
-
return ui.current();
|
|
25
|
+
if (ui.pane !== 'unit') return ui.current();
|
|
26
|
+
const item = ui.current();
|
|
27
|
+
const lines = unitBodyLines(ui);
|
|
28
|
+
if (!lines.length) return item;
|
|
29
|
+
const line = lines[ui.nav.unitLine] ?? null;
|
|
30
|
+
const lineDiff = lineHasDiff(line);
|
|
31
|
+
if (!item) return { lineDiff };
|
|
32
|
+
return { ...item, lineDiff };
|
|
26
33
|
};
|
|
27
34
|
|
|
28
35
|
const scrollBy = (fraction) => (ui) =>
|
|
@@ -43,9 +50,13 @@ const HANDLERS = {
|
|
|
43
50
|
next: filesPane.onNext,
|
|
44
51
|
prev: filesPane.onPrev,
|
|
45
52
|
layout: filesPane.onLayout,
|
|
53
|
+
lines: filesPane.onLineNumbers,
|
|
46
54
|
theme: filesPane.onTheme,
|
|
47
55
|
files: filesPane.onFiles,
|
|
48
|
-
open:
|
|
56
|
+
open: (ui) => {
|
|
57
|
+
if (ui.pane === 'repos') return void ui.workspace.openSelected();
|
|
58
|
+
filesPane.onOpenFile(ui);
|
|
59
|
+
},
|
|
49
60
|
file: filesPane.onFileScope,
|
|
50
61
|
diff: filesPane.onDiffScope,
|
|
51
62
|
back: filesPane.onEscape,
|
|
@@ -63,10 +74,12 @@ const HANDLERS = {
|
|
|
63
74
|
check: (ui) => ui.composer.tasks.toggleFocusedTask(),
|
|
64
75
|
removeTask: (ui) => {
|
|
65
76
|
if (ui.pane === 'npm') return void ui.npm.askDrop();
|
|
77
|
+
if (ui.pane === 'packages') return;
|
|
66
78
|
ui.composer.tasks.removeFocusedTask();
|
|
67
79
|
},
|
|
68
80
|
reload: (ui) => ui.lifecycle.onReload(),
|
|
69
81
|
commit: (ui) => ui.commits.onCommit(),
|
|
82
|
+
newCommit: (ui) => ui.commits.onInsert(),
|
|
70
83
|
amend: (ui) => ui.commits.onAmend(),
|
|
71
84
|
reword: (ui) => ui.commits.onReword(),
|
|
72
85
|
apply: (ui) => ui.commits.onApply(),
|
|
@@ -82,26 +95,51 @@ const HANDLERS = {
|
|
|
82
95
|
if (ui.pane === 'tasks') return void ui.composer.tasks.removeFocusedTask();
|
|
83
96
|
ui.gitBranches.onDropBranch();
|
|
84
97
|
},
|
|
98
|
+
taskUp: (ui) => ui.composer.tasks.moveIssue(-1),
|
|
99
|
+
taskDown: (ui) => ui.composer.tasks.moveIssue(1),
|
|
100
|
+
plan: (ui) => ui.composer.tasks.togglePlanMenu(),
|
|
101
|
+
import: (ui) => startImport(ui),
|
|
85
102
|
npm: (ui) => ui.npm.open(),
|
|
86
103
|
npmEdit: (ui) => ui.npm.edit(),
|
|
87
104
|
npmNew: (ui) => ui.npm.create(),
|
|
88
105
|
npmDrop: (ui) => ui.npm.askDrop(),
|
|
106
|
+
packageNew: (ui) => ui.packages.create(),
|
|
107
|
+
packageDrop: (ui) => ui.packages.askDrop(),
|
|
108
|
+
packageWanted: (ui) => ui.packages.updateWanted(),
|
|
109
|
+
packageLatest: (ui) => ui.packages.updateLatest(),
|
|
89
110
|
npmLogs: (ui) => ui.npm.askLogs(),
|
|
90
|
-
npmStop: (ui) => ui.npm.
|
|
111
|
+
npmStop: (ui) => ui.npm.stop(),
|
|
91
112
|
npmVerbose: (ui) => ui.npm.toggleVerbose(),
|
|
92
113
|
npmRerun: (ui) => ui.npm.rerun(),
|
|
114
|
+
agentModel: (ui) => ui.agents.openPick('model'),
|
|
115
|
+
agentEffort: (ui) => ui.agents.openPick('effort'),
|
|
116
|
+
agentFast: (ui) => ui.agents.toggleFast(),
|
|
117
|
+
agentContext: (ui) => ui.agents.openPick('context'),
|
|
118
|
+
agentReview: (ui) => ui.agents.openPick('plan'),
|
|
119
|
+
agentStop: (ui) => ui.agents.stop(),
|
|
120
|
+
agentRerun: (ui) => ui.agents.rerun(),
|
|
93
121
|
quit: (ui) => ui.onQuit(),
|
|
122
|
+
repoLeft: (ui) => ui.workspace.move(-1, 0),
|
|
123
|
+
repoRight: (ui) => ui.workspace.move(1, 0),
|
|
124
|
+
repoUp: (ui) => ui.workspace.move(0, -1),
|
|
125
|
+
repoDown: (ui) => ui.workspace.move(0, 1),
|
|
94
126
|
...DASH_HANDLERS,
|
|
95
127
|
};
|
|
96
128
|
|
|
97
129
|
const dispatchAction = (ui, action) => {
|
|
98
130
|
if (CONFIRM_MODE.includes(ui.mode)) return;
|
|
131
|
+
if (ui.mode === 'import') stopImport(ui);
|
|
132
|
+
if (action === 'lines' && (ui.pane === 'diff' || ui.pane === 'unit')) {
|
|
133
|
+
return void filesPane.onLineNumbers(ui);
|
|
134
|
+
}
|
|
99
135
|
if (ui.mode === 'compose') {
|
|
100
136
|
if (!COMPOSE_LEAVE.includes(action)) return;
|
|
101
|
-
const
|
|
102
|
-
|
|
137
|
+
const after = () => dispatchAction(ui, action);
|
|
138
|
+
const result = ui.composer.saveCompose({ after });
|
|
139
|
+
if (result.kind === 'saving') return;
|
|
103
140
|
}
|
|
104
141
|
const pane = ui.pane;
|
|
142
|
+
if (pane === 'repos' && !REPOS_ALLOWED.includes(action)) return;
|
|
105
143
|
if (pane === 'dashboard' && !DASH_ALLOWED.includes(action)) return;
|
|
106
144
|
if (LIST_PANES.includes(pane) && (action === 'next' || action === 'prev')) {
|
|
107
145
|
return void filesPane.onScroll(ui, action === 'next' ? 1 : -1);
|
|
@@ -113,8 +151,7 @@ const dispatchAction = (ui, action) => {
|
|
|
113
151
|
|
|
114
152
|
const CONFIRMATIONS = {
|
|
115
153
|
confirmQuit: {
|
|
116
|
-
|
|
117
|
-
editing: (ui) => ui.finishQuit('editing'),
|
|
154
|
+
confirm: (ui) => ui.confirmQuit(),
|
|
118
155
|
cancel: (ui) => ui.cancelQuit(),
|
|
119
156
|
},
|
|
120
157
|
confirmUpdate: {
|
|
@@ -123,11 +160,13 @@ const CONFIRMATIONS = {
|
|
|
123
160
|
},
|
|
124
161
|
confirmDrop: {
|
|
125
162
|
confirm: (ui) => {
|
|
163
|
+
if (ui.packages.dropName) return void ui.packages.confirmDrop();
|
|
126
164
|
if (ui.npm.dropName) return void ui.npm.confirmDrop();
|
|
127
165
|
if (ui.commits.dropName) return void ui.commits.confirmDropCommit();
|
|
128
166
|
ui.gitBranches.confirmDropBranch();
|
|
129
167
|
},
|
|
130
168
|
cancel: (ui) => {
|
|
169
|
+
if (ui.packages.dropName) return void ui.packages.cancelDrop();
|
|
131
170
|
if (ui.npm.dropName) return void ui.npm.cancelDrop();
|
|
132
171
|
if (ui.commits.dropName) return void ui.commits.cancelDropCommit();
|
|
133
172
|
ui.gitBranches.cancelDropBranch();
|
|
@@ -137,6 +176,13 @@ const CONFIRMATIONS = {
|
|
|
137
176
|
force: (ui) => ui.gitBranches.onForcePush(),
|
|
138
177
|
cancel: (ui) => ui.gitBranches.cancelPush(),
|
|
139
178
|
},
|
|
179
|
+
confirmCommit: {
|
|
180
|
+
update: (ui) => ui.commits.chooseCommit('update'),
|
|
181
|
+
commit: (ui) => ui.commits.chooseCommit('commit'),
|
|
182
|
+
amend: (ui) => ui.commits.chooseCommit('amend'),
|
|
183
|
+
fixup: (ui) => ui.commits.chooseCommit('fixup'),
|
|
184
|
+
cancel: (ui) => ui.commits.cancelCommitChoice(),
|
|
185
|
+
},
|
|
140
186
|
};
|
|
141
187
|
|
|
142
188
|
const confirmChoice = (ui, key) => {
|
|
@@ -147,6 +193,7 @@ const confirmChoice = (ui, key) => {
|
|
|
147
193
|
|
|
148
194
|
const NPM_VIEW_KEYS = {
|
|
149
195
|
escape: (ui) => ui.npm.closeView(),
|
|
196
|
+
s: (ui) => ui.npm.stop(),
|
|
150
197
|
r: (ui) => ui.npm.rerun(),
|
|
151
198
|
v: (ui) => ui.npm.toggleVerbose(),
|
|
152
199
|
e: () => {},
|
|
@@ -158,29 +205,80 @@ const NPM_LIST_KEYS = {
|
|
|
158
205
|
'ctrl-down': (ui) => ui.npm.reorder(1),
|
|
159
206
|
up: (ui) => ui.npm.move(-1),
|
|
160
207
|
down: (ui) => ui.npm.move(1),
|
|
208
|
+
right: (ui) => ui.npm.focusRuns(),
|
|
209
|
+
left: (ui) => ui.npm.focusCommands(),
|
|
210
|
+
tab: (ui) => ui.npm.toggleFocus(),
|
|
211
|
+
'shift-tab': (ui) => ui.npm.toggleFocus(),
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const AGENT_VIEW_KEYS = {
|
|
215
|
+
escape: (ui) => ui.agents.closeView(),
|
|
216
|
+
s: (ui) => ui.agents.stop(),
|
|
217
|
+
r: (ui) => ui.agents.rerun(),
|
|
218
|
+
e: () => {},
|
|
219
|
+
m: () => {},
|
|
220
|
+
a: () => {},
|
|
221
|
+
t: () => {},
|
|
222
|
+
f: () => {},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const AGENT_LIST_KEYS = {
|
|
226
|
+
right: (ui) => ui.agents.focusRuns(),
|
|
227
|
+
left: (ui) => ui.agents.focusCli(),
|
|
228
|
+
tab: (ui) => ui.agents.toggleFocus(),
|
|
229
|
+
'shift-tab': (ui) => ui.agents.toggleFocus(),
|
|
161
230
|
};
|
|
162
231
|
|
|
163
232
|
const npmKey = (ui, key) => {
|
|
164
233
|
if (ui.pane !== 'npm') return null;
|
|
165
|
-
if (ui.npm.viewing
|
|
166
|
-
return NPM_VIEW_KEYS[key];
|
|
234
|
+
if (ui.npm.viewing) {
|
|
235
|
+
if (Object.hasOwn(NPM_VIEW_KEYS, key)) return NPM_VIEW_KEYS[key];
|
|
236
|
+
if (key === 'up' || key === 'down') return NPM_LIST_KEYS[key];
|
|
237
|
+
return null;
|
|
167
238
|
}
|
|
168
239
|
return Object.hasOwn(NPM_LIST_KEYS, key) ? NPM_LIST_KEYS[key] : null;
|
|
169
240
|
};
|
|
170
241
|
|
|
242
|
+
const agentKey = (ui, key) => {
|
|
243
|
+
if (ui.pane !== 'agents') return null;
|
|
244
|
+
if (ui.agents.pick) return () => ui.agents.onPickKey(key);
|
|
245
|
+
const keys = ui.agents.viewing ? AGENT_VIEW_KEYS : AGENT_LIST_KEYS;
|
|
246
|
+
return Object.hasOwn(keys, key) ? keys[key] : null;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const planMenuKey = (ui, key) => {
|
|
250
|
+
if (!ui.composer.tasks.planOpen || ui.pane !== 'tasks') return false;
|
|
251
|
+
ui.composer.tasks.onPlanKey(key);
|
|
252
|
+
return true;
|
|
253
|
+
};
|
|
254
|
+
|
|
171
255
|
const handleKey = (ui, key) => {
|
|
172
256
|
if (CONFIRM_MODE.includes(ui.mode)) return void confirmChoice(ui, key);
|
|
257
|
+
if (planMenuKey(ui, key)) return;
|
|
258
|
+
if (ui.pane === 'tasks' && (key === 'ctrl-up' || key === 'ctrl-down')) {
|
|
259
|
+
if (ui.mode === 'import') stopImport(ui);
|
|
260
|
+
const delta = key === 'ctrl-up' ? -1 : 1;
|
|
261
|
+
return void ui.composer.tasks.moveIssue(delta);
|
|
262
|
+
}
|
|
263
|
+
if (key === 'ctrl-l' && (ui.pane === 'diff' || ui.pane === 'unit')) {
|
|
264
|
+
return void filesPane.onLineNumbers(ui);
|
|
265
|
+
}
|
|
173
266
|
if (ui.mode === 'compose') {
|
|
174
267
|
return void ui.runComposeCommand(ui.composer.handleKey(key));
|
|
175
268
|
}
|
|
269
|
+
if (ui.mode === 'find') return void onFindKey(ui, key);
|
|
270
|
+
if (ui.mode === 'import') return void onImportKey(ui, key);
|
|
271
|
+
if (key === '/' && FIND_PANES.includes(ui.pane)) return void startFind(ui);
|
|
176
272
|
const onNpmKey = npmKey(ui, key);
|
|
177
273
|
if (onNpmKey) return void onNpmKey(ui);
|
|
274
|
+
const onAgentKey = agentKey(ui, key);
|
|
275
|
+
if (onAgentKey) return void onAgentKey(ui);
|
|
178
276
|
if (key === 'escape') return void filesPane.onEscape(ui);
|
|
179
277
|
const tasks = ui.composer.tasks;
|
|
180
|
-
const toggle = key === '
|
|
278
|
+
const toggle = key === ' ';
|
|
181
279
|
if (toggle && tasks.toggleFocusedTask()) return;
|
|
182
280
|
if (tasks.typeIntoTask(key)) return;
|
|
183
|
-
const action = actionFromKey(key, ui.pane
|
|
281
|
+
const action = actionFromKey(key, ui.pane);
|
|
184
282
|
if (action) ui.dispatch(action);
|
|
185
283
|
};
|
|
186
284
|
|