reslop 0.2.7 → 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 +17 -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 +43 -68
- package/lib/{files.js → common/files.js} +48 -55
- package/lib/{find.js → common/find.js} +1 -1
- 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 +90 -49
- package/lib/dashboard/npm.js +17 -13
- package/lib/dashboard/tree.js +27 -30
- package/lib/{session → dashboard}/watch.js +36 -31
- package/lib/{workspace.js → dashboard/workspace.js} +23 -38
- 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 -96
- 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 +111 -48
- package/lib/{editor.js → input/editor.js} +7 -8
- package/lib/{keys.js → input/keys.js} +15 -43
- 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/{dash-blocks.js → dashboard/blocks.js} +131 -137
- 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 +9 -14
- package/lib/render/footer.js +383 -0
- package/lib/render/frame.js +123 -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 +16 -11
- package/lib/render/npm.js +322 -196
- package/lib/render/packages.js +9 -13
- package/lib/render/primitives.js +101 -15
- package/lib/render/render.js +7 -11
- package/lib/render/repos.js +13 -25
- package/lib/render/status.js +370 -0
- package/lib/render/status.json +85 -0
- package/lib/render/{dash-table.js → table.js} +13 -60
- package/lib/render/tiles.js +109 -48
- 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 +33 -39
- package/lib/session/change-actions.js +34 -100
- package/lib/session/commit.js +91 -52
- package/lib/session/compose.js +181 -145
- package/lib/session/dashboard.js +23 -22
- package/lib/session/dispatch.js +85 -21
- package/lib/session/files-pane.js +47 -26
- package/lib/session/find.js +16 -29
- package/lib/session/import.js +82 -0
- package/lib/session/items.js +8 -17
- package/lib/session/lifecycle.js +75 -72
- package/lib/session/load.js +2 -4
- package/lib/session/navigation.js +6 -3
- package/lib/session/npm/history.js +249 -0
- package/lib/session/npm/npm.js +815 -0
- package/lib/session/{packages.js → npm/packages.js} +21 -14
- package/lib/session/ops.js +125 -34
- package/lib/session/pointer.js +202 -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} +156 -63
- package/lib/session/tasks.js +320 -65
- 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 +65 -65
- package/lib/session/workspace.js +39 -78
- 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 -545
- package/lib/detect.js +0 -34
- package/lib/diff/edit.js +0 -33
- package/lib/git-worktree.js +0 -455
- package/lib/git.js +0 -79
- package/lib/npm-commands.js +0 -522
- package/lib/render/chrome.js +0 -806
- package/lib/render/present.js +0 -19
- package/lib/render/report.js +0 -43
- package/lib/render/search.js +0 -25
- package/lib/review.js +0 -745
- 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
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Repo reads the screen needs on every frame: loaded ahead and kept here
|
|
4
|
+
class FileReads {
|
|
5
|
+
constructor(ui) {
|
|
6
|
+
this.ui = ui;
|
|
7
|
+
this.reset();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
reset() {
|
|
11
|
+
this.names = [];
|
|
12
|
+
this.textKey = null;
|
|
13
|
+
this.text = '';
|
|
14
|
+
this.stamp = 0;
|
|
15
|
+
this.wanted = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
keyOf(rel) {
|
|
19
|
+
const ui = this.ui;
|
|
20
|
+
return `${ui.top}\0${ui.rev ?? ''}\0${rel}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async readNames() {
|
|
24
|
+
const ui = this.ui;
|
|
25
|
+
if (!ui.repo.listFiles) return [];
|
|
26
|
+
const options = { commit: ui.rev };
|
|
27
|
+
try {
|
|
28
|
+
return await ui.repo.listFiles(ui.top || ui.cwd, ui.paths ?? [], options);
|
|
29
|
+
} catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async readText(rel) {
|
|
35
|
+
const ui = this.ui;
|
|
36
|
+
const key = this.keyOf(rel);
|
|
37
|
+
const repo = ui.repo;
|
|
38
|
+
const readable = rel && repo && typeof repo.fileText === 'function';
|
|
39
|
+
if (!readable) return { key, text: '' };
|
|
40
|
+
try {
|
|
41
|
+
return { key, text: await repo.fileText(ui.top, rel, ui.rev) };
|
|
42
|
+
} catch {
|
|
43
|
+
return { key, text: '' };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// What a reload needs besides the snapshot, applied together with it
|
|
48
|
+
async read() {
|
|
49
|
+
const nav = this.ui.nav;
|
|
50
|
+
const listed = nav.fileScope === 'file';
|
|
51
|
+
const rel = nav.pane === 'unit' ? nav.reviewPath : null;
|
|
52
|
+
const reads = [
|
|
53
|
+
listed ? this.readNames() : null,
|
|
54
|
+
rel ? this.readText(rel) : null,
|
|
55
|
+
];
|
|
56
|
+
const stamp = this.stamp;
|
|
57
|
+
const [names, viewed] = await Promise.all(reads);
|
|
58
|
+
return { names, viewed, stamp };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Text kept since the read began is newer than what the read found
|
|
62
|
+
apply(found) {
|
|
63
|
+
if (found.names) this.names = found.names;
|
|
64
|
+
if (found.viewed && found.stamp === this.stamp) this.keep(found.viewed);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
keep(viewed) {
|
|
68
|
+
this.textKey = viewed.key;
|
|
69
|
+
this.text = viewed.text;
|
|
70
|
+
this.stamp += 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
keepText(rel, text) {
|
|
74
|
+
this.keep({ key: this.keyOf(rel), text });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async loadNames() {
|
|
78
|
+
this.names = await this.readNames();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async loadText(rel) {
|
|
82
|
+
const viewed = await this.readText(rel);
|
|
83
|
+
this.keep(viewed);
|
|
84
|
+
return viewed.text;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
cached(rel) {
|
|
88
|
+
if (!rel || this.textKey !== this.keyOf(rel)) return null;
|
|
89
|
+
return this.text;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// A path nobody loaded ahead is read now and painted when it arrives
|
|
93
|
+
textOf(rel) {
|
|
94
|
+
const text = this.cached(rel);
|
|
95
|
+
if (text !== null || !rel) return text ?? '';
|
|
96
|
+
const key = this.keyOf(rel);
|
|
97
|
+
if (this.wanted === key) return '';
|
|
98
|
+
this.wanted = key;
|
|
99
|
+
const ui = this.ui;
|
|
100
|
+
const done = () => {
|
|
101
|
+
this.wanted = null;
|
|
102
|
+
ui.paint();
|
|
103
|
+
};
|
|
104
|
+
ui.ops.track(this.loadText(rel).then(done, done));
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = { FileReads };
|
package/lib/session/review.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const { REVIEW_DIR, ISSUE_FILE } = require('../common/files.js');
|
|
7
|
+
const review = require('../review/review.js');
|
|
8
|
+
const { ReviewStore } = review;
|
|
5
9
|
const { listReviewNames, loadTemplates, resolveReviewPath } = review;
|
|
6
10
|
const { loadReview, flushReview } = review;
|
|
7
11
|
|
|
12
|
+
const issueTasks = (imported) => {
|
|
13
|
+
const todos = imported.tasks ?? imported.todos ?? [];
|
|
14
|
+
return todos.filter((todo) => (todo.file || '') === ISSUE_FILE);
|
|
15
|
+
};
|
|
16
|
+
|
|
8
17
|
class ReviewController {
|
|
9
18
|
constructor() {
|
|
10
19
|
this.reset();
|
|
@@ -21,16 +30,17 @@ class ReviewController {
|
|
|
21
30
|
const templates = loadTemplates(dir);
|
|
22
31
|
const resolved = resolveReviewPath(dir, date, names, extra);
|
|
23
32
|
this.didResume = resolved.resume === true;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
const reviewPath = resolved.reviewPath;
|
|
34
|
+
if (this.didResume) this.store = loadReview(reviewPath, templates);
|
|
35
|
+
else this.store = new ReviewStore(reviewPath, templates);
|
|
27
36
|
return this.store;
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
applyImported(imported) {
|
|
31
40
|
if (!imported || !this.store) return false;
|
|
32
|
-
if (this.
|
|
33
|
-
|
|
41
|
+
if (this.importedRemote) return false;
|
|
42
|
+
const notes = this.didResume ? { tasks: issueTasks(imported) } : imported;
|
|
43
|
+
this.store.applyImportedNotes(notes);
|
|
34
44
|
this.importedRemote = true;
|
|
35
45
|
return true;
|
|
36
46
|
}
|
|
@@ -45,9 +55,17 @@ class ReviewController {
|
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
usePlan(dir, name) {
|
|
59
|
+
const store = this.store;
|
|
60
|
+
if (!store || !name) return false;
|
|
61
|
+
const next = path.join(dir, REVIEW_DIR, name);
|
|
62
|
+
if (next === store.reviewPath) return false;
|
|
63
|
+
this.flush();
|
|
64
|
+
const templates = store.templates;
|
|
65
|
+
const loaded = fs.existsSync(next);
|
|
66
|
+
if (loaded) this.store = loadReview(next, templates);
|
|
67
|
+
else this.store = new ReviewStore(next, templates);
|
|
68
|
+
return true;
|
|
51
69
|
}
|
|
52
70
|
}
|
|
53
71
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { clamp } = require('../common/utilities.js');
|
|
4
|
+
const { logScrollMax } = require('../render/log.js');
|
|
5
|
+
|
|
6
|
+
class RunPanel {
|
|
7
|
+
constructor(ui) {
|
|
8
|
+
this.ui = ui;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
barMax() {
|
|
12
|
+
const bar = this.ui.lastFrame && this.ui.lastFrame.scrollBar;
|
|
13
|
+
return bar ? Math.max(0, bar.count - bar.rows) : -1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
setLog(start, max) {
|
|
17
|
+
const next = clamp(start, 0, max);
|
|
18
|
+
this.logScroll = next;
|
|
19
|
+
this.followEnd = next === max;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
placeLog(start) {
|
|
23
|
+
const max = this.barMax();
|
|
24
|
+
if (max >= 0) this.setLog(start, max);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
placeRuns(start) {
|
|
28
|
+
const max = this.barMax();
|
|
29
|
+
if (max >= 0) this.runScroll = clamp(start, 0, max);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
scrollLogBy(delta, text, running) {
|
|
33
|
+
const { lastSize, lastFrame } = this.ui;
|
|
34
|
+
const max = logScrollMax(text, lastSize, lastFrame, running);
|
|
35
|
+
const from = this.followEnd ? max : this.logScroll;
|
|
36
|
+
this.setLog(from + delta, max);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { RunPanel };
|
|
@@ -1,57 +1,58 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
8
|
-
const {
|
|
9
|
-
const
|
|
10
|
-
const {
|
|
11
|
-
const
|
|
12
|
-
const {
|
|
13
|
-
const
|
|
14
|
-
const {
|
|
15
|
-
const
|
|
16
|
-
const {
|
|
17
|
-
const
|
|
18
|
-
const {
|
|
19
|
-
const
|
|
20
|
-
const {
|
|
21
|
-
const
|
|
22
|
-
const {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const progress = require('./session/progress.js');
|
|
28
|
-
const { Progress } = progress;
|
|
29
|
-
const compose = require('./session/compose.js');
|
|
30
|
-
const { Composer } = compose;
|
|
31
|
-
const ops = require('./session/ops.js');
|
|
32
|
-
const { OpsRunner } = ops;
|
|
33
|
-
const commit = require('./session/commit.js');
|
|
34
|
-
const { CommitController } = commit;
|
|
35
|
-
const branches = require('./session/branches.js');
|
|
36
|
-
const { BranchController } = branches;
|
|
37
|
-
const npm = require('./session/npm.js');
|
|
38
|
-
const { NpmController } = npm;
|
|
39
|
-
const packages = require('./session/packages.js');
|
|
40
|
-
const { PackageController } = packages;
|
|
41
|
-
const accessors = require('./session/accessors.js');
|
|
42
|
-
const { bindAccessors, bindGetters } = accessors;
|
|
43
|
-
const view = require('./session/view.js');
|
|
44
|
-
const filesPane = require('./session/files-pane.js');
|
|
45
|
-
const lifecycle = require('./session/lifecycle.js');
|
|
46
|
-
const { Lifecycle } = lifecycle;
|
|
47
|
-
const dispatch = require('./session/dispatch.js');
|
|
3
|
+
const { renderFrame } = require('../render/render.js');
|
|
4
|
+
const { decodeChunk } = require('../input/keys.js');
|
|
5
|
+
const { AUTOSAVE_MS, hasNotes } = require('../review/review.js');
|
|
6
|
+
const { makeTaskItem } = require('../common/files.js');
|
|
7
|
+
const { sessionCapabilities } = require('../source/capabilities.js');
|
|
8
|
+
const { LIST_PANES } = require('../input/actions.js');
|
|
9
|
+
const { Terminal, Progress, LEAVE_TERM } = require('./terminal.js');
|
|
10
|
+
const { LoadCoordinator } = require('./load.js');
|
|
11
|
+
const { ItemCollection } = require('./items.js');
|
|
12
|
+
const { Navigation } = require('./navigation.js');
|
|
13
|
+
const { ReviewController } = require('./review.js');
|
|
14
|
+
const { UpdateCoordinator } = require('./update.js');
|
|
15
|
+
const { Composer } = require('./compose.js');
|
|
16
|
+
const { OpsRunner } = require('./ops.js');
|
|
17
|
+
const { FileReads } = require('./reads.js');
|
|
18
|
+
const { CommitController } = require('./commit.js');
|
|
19
|
+
const { BranchController } = require('./branches.js');
|
|
20
|
+
const { NpmController } = require('./npm/npm.js');
|
|
21
|
+
const { PackageController } = require('./npm/packages.js');
|
|
22
|
+
const { AgentsController } = require('./agents/agents.js');
|
|
23
|
+
const view = require('./view.js');
|
|
24
|
+
const filesPane = require('./files-pane.js');
|
|
25
|
+
const { Lifecycle } = require('./lifecycle.js');
|
|
26
|
+
const dispatch = require('./dispatch.js');
|
|
48
27
|
const { dispatchAction, confirmChoice, handleEvent } = dispatch;
|
|
49
|
-
const
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
const
|
|
28
|
+
const { Dashboard } = require('./dashboard.js');
|
|
29
|
+
const { Workspace } = require('./workspace.js');
|
|
30
|
+
|
|
31
|
+
const bindAccessors = (target, owner, names) => {
|
|
32
|
+
for (const name of names) {
|
|
33
|
+
Object.defineProperty(target, name, {
|
|
34
|
+
configurable: true,
|
|
35
|
+
enumerable: true,
|
|
36
|
+
get: () => owner[name],
|
|
37
|
+
set: (value) => {
|
|
38
|
+
owner[name] = value;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const bindGetters = (target, owner, names) => {
|
|
45
|
+
for (const name of names) {
|
|
46
|
+
Object.defineProperty(target, name, {
|
|
47
|
+
configurable: true,
|
|
48
|
+
enumerable: true,
|
|
49
|
+
get: () => owner[name],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
53
|
|
|
54
54
|
const PROGRESS_MS = 160;
|
|
55
|
+
const QUIT_WAIT_MS = 3000;
|
|
55
56
|
|
|
56
57
|
const NAV_FIELDS = [
|
|
57
58
|
'pane',
|
|
@@ -60,6 +61,8 @@ const NAV_FIELDS = [
|
|
|
60
61
|
'branchCursor',
|
|
61
62
|
'commitCursor',
|
|
62
63
|
'npmCursor',
|
|
64
|
+
'packagesCursor',
|
|
65
|
+
'agentCursor',
|
|
63
66
|
'scroll',
|
|
64
67
|
'listScroll',
|
|
65
68
|
'reviewPath',
|
|
@@ -104,6 +107,11 @@ const assignOptions = (session, options) => {
|
|
|
104
107
|
session.dashboardHome = options.startPane === 'dashboard';
|
|
105
108
|
session.workspaceRoot = options.workspace ? options.workspace.root : '';
|
|
106
109
|
session.workspaceInfo = options.workspace ?? null;
|
|
110
|
+
session.loadPullRequest = options.loadPullRequest ?? null;
|
|
111
|
+
session.loadMergeRequest = options.loadMergeRequest ?? null;
|
|
112
|
+
session.loadGithubIssue = options.loadGithubIssue ?? null;
|
|
113
|
+
session.loadGitlabIssue = options.loadGitlabIssue ?? null;
|
|
114
|
+
session.fetchImpl = options.fetch ?? null;
|
|
107
115
|
};
|
|
108
116
|
|
|
109
117
|
const assignParts = (session, options) => {
|
|
@@ -118,10 +126,12 @@ const assignParts = (session, options) => {
|
|
|
118
126
|
session.updater = new UpdateCoordinator(session);
|
|
119
127
|
session.composer = new Composer(session);
|
|
120
128
|
session.ops = new OpsRunner(session);
|
|
129
|
+
session.reads = new FileReads(session);
|
|
121
130
|
session.commits = new CommitController(session);
|
|
122
131
|
session.gitBranches = new BranchController(session);
|
|
123
132
|
session.npm = new NpmController(session);
|
|
124
133
|
session.packages = new PackageController(session);
|
|
134
|
+
session.agents = new AgentsController(session);
|
|
125
135
|
session.dashboard = new Dashboard(session);
|
|
126
136
|
session.workspace = new Workspace(session);
|
|
127
137
|
session.lifecycle = new Lifecycle(session);
|
|
@@ -164,18 +174,22 @@ class Session {
|
|
|
164
174
|
this.review.reset();
|
|
165
175
|
this.composer.reset();
|
|
166
176
|
this.ops.reset();
|
|
177
|
+
this.reads.reset();
|
|
167
178
|
this.gitBranches.reset();
|
|
168
179
|
this.commits.reset();
|
|
169
180
|
this.npm.reset();
|
|
181
|
+
this.agents.reset();
|
|
170
182
|
this.dashboard.reset();
|
|
171
183
|
this.workspace.reset();
|
|
172
184
|
this.top = this.cwd;
|
|
173
185
|
this.branch = '';
|
|
174
186
|
this.status = '';
|
|
175
187
|
this.layout = 'unified';
|
|
188
|
+
this.lineNumbers = false;
|
|
176
189
|
this.done = false;
|
|
177
190
|
this.exitCode = 0;
|
|
178
191
|
this.mode = 'review';
|
|
192
|
+
this.quitWarning = '';
|
|
179
193
|
this.tasksItem = makeTaskItem();
|
|
180
194
|
this.progressFrame = 0;
|
|
181
195
|
this.emptyReview = false;
|
|
@@ -190,13 +204,13 @@ class Session {
|
|
|
190
204
|
this.progress.clear();
|
|
191
205
|
}
|
|
192
206
|
|
|
193
|
-
ensureRepo() {
|
|
207
|
+
async ensureRepo() {
|
|
194
208
|
if (!this.repo.toplevel) return;
|
|
195
|
-
this.top = this.repo.toplevel(this.cwd);
|
|
209
|
+
this.top = await this.repo.toplevel(this.cwd);
|
|
196
210
|
}
|
|
197
211
|
|
|
198
212
|
load() {
|
|
199
|
-
this.lifecycle.load();
|
|
213
|
+
return this.lifecycle.load();
|
|
200
214
|
}
|
|
201
215
|
|
|
202
216
|
loadReady() {
|
|
@@ -234,6 +248,21 @@ class Session {
|
|
|
234
248
|
if (LIST_PANES.includes(this.pane)) {
|
|
235
249
|
this.nav.listScroll = frame.listScroll;
|
|
236
250
|
}
|
|
251
|
+
const menuScroll = frame.menuScroll;
|
|
252
|
+
if (this.agents.pick && typeof menuScroll === 'number') {
|
|
253
|
+
this.agents.pick.scroll = menuScroll;
|
|
254
|
+
}
|
|
255
|
+
const planPick = this.composer.tasks.planPick;
|
|
256
|
+
if (planPick && typeof menuScroll === 'number') {
|
|
257
|
+
planPick.scroll = menuScroll;
|
|
258
|
+
}
|
|
259
|
+
if (typeof frame.runScroll === 'number') {
|
|
260
|
+
const panel = this.pane === 'npm' ? this.npm : this.agents;
|
|
261
|
+
panel.runScroll = frame.runScroll;
|
|
262
|
+
}
|
|
263
|
+
if (typeof frame.gridCols === 'number') {
|
|
264
|
+
this.workspace.cols = frame.gridCols;
|
|
265
|
+
}
|
|
237
266
|
if (this.nav.tasksOpen) this.nav.scroll = frame.scroll;
|
|
238
267
|
const editScroll = frame.cursor && frame.cursor.scroll;
|
|
239
268
|
if (this.editor && typeof editScroll === 'number') {
|
|
@@ -294,7 +323,11 @@ class Session {
|
|
|
294
323
|
}
|
|
295
324
|
|
|
296
325
|
handleEvent(event) {
|
|
297
|
-
handleEvent(this, event);
|
|
326
|
+
if (!this.ops.defer(event)) handleEvent(this, event);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
finishInput() {
|
|
330
|
+
if (this.finishReadLoop) this.finishReadLoop();
|
|
298
331
|
}
|
|
299
332
|
|
|
300
333
|
pushInput(text) {
|
|
@@ -342,28 +375,83 @@ class Session {
|
|
|
342
375
|
this.composer.autosave();
|
|
343
376
|
}
|
|
344
377
|
|
|
378
|
+
retarget({ cwd, top, repoName, pane }) {
|
|
379
|
+
this.lifecycle.stopWatch();
|
|
380
|
+
this.dashboard.stop();
|
|
381
|
+
this.loader.reset();
|
|
382
|
+
this.cwd = cwd;
|
|
383
|
+
if (top) this.top = top;
|
|
384
|
+
this.paths = [];
|
|
385
|
+
this.repoName = repoName;
|
|
386
|
+
this.branch = '';
|
|
387
|
+
this.rev = null;
|
|
388
|
+
this.revShort = '';
|
|
389
|
+
this.change = null;
|
|
390
|
+
this.dashboardHome = pane === 'dashboard';
|
|
391
|
+
this.collection.reset();
|
|
392
|
+
this.reads.reset();
|
|
393
|
+
this.review.reset();
|
|
394
|
+
this.nav.pane = pane;
|
|
395
|
+
this.nav.tasksOpen = false;
|
|
396
|
+
this.nav.scroll = 0;
|
|
397
|
+
this.nav.clearSelection();
|
|
398
|
+
this.status = '';
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
childWarning() {
|
|
402
|
+
const tests = this.npm && this.npm.running === true;
|
|
403
|
+
const agents = this.agents && this.agents.runningCount() > 0;
|
|
404
|
+
if (tests && agents) return ' exit will terminate tests and agents';
|
|
405
|
+
if (agents) return ' exit will terminate agents';
|
|
406
|
+
if (tests) return ' exit will terminate tests';
|
|
407
|
+
return '';
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
stopChildren() {
|
|
411
|
+
if (this.npm) this.npm.stopAll();
|
|
412
|
+
if (this.agents) this.agents.killAll();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// A quick repo write still running lands first, so it is not left half done
|
|
416
|
+
exitNow() {
|
|
417
|
+
if (this.done || !this.ops.unlanded()) return void this.finishExit();
|
|
418
|
+
const exit = () => this.finishExit();
|
|
419
|
+
this.ops.hold(this.ops.landed(QUIT_WAIT_MS).then(exit));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
finishExit() {
|
|
423
|
+
this.flushReview();
|
|
424
|
+
this.done = true;
|
|
425
|
+
this.exitCode = 0;
|
|
426
|
+
}
|
|
427
|
+
|
|
345
428
|
onQuit() {
|
|
429
|
+
const composer = this.composer;
|
|
430
|
+
if (this.mode === 'compose' && composer.writesDisk()) {
|
|
431
|
+
const quit = () => this.onQuit();
|
|
432
|
+
return void this.ops.hold(composer.writeAndClose().then(quit));
|
|
433
|
+
}
|
|
346
434
|
if (this.mode === 'compose') {
|
|
347
|
-
|
|
348
|
-
|
|
435
|
+
composer.commitCompose();
|
|
436
|
+
composer.closeCompose();
|
|
349
437
|
}
|
|
350
|
-
|
|
438
|
+
const warning = this.childWarning();
|
|
439
|
+
if (warning) {
|
|
440
|
+
this.quitWarning = warning;
|
|
351
441
|
this.mode = 'confirmQuit';
|
|
352
442
|
this.status = '';
|
|
353
443
|
return;
|
|
354
444
|
}
|
|
355
|
-
this.
|
|
356
|
-
this.exitCode = 0;
|
|
445
|
+
this.exitNow();
|
|
357
446
|
}
|
|
358
447
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
this.done = true;
|
|
363
|
-
this.exitCode = 0;
|
|
448
|
+
confirmQuit() {
|
|
449
|
+
this.stopChildren();
|
|
450
|
+
this.exitNow();
|
|
364
451
|
}
|
|
365
452
|
|
|
366
453
|
cancelQuit() {
|
|
454
|
+
this.quitWarning = '';
|
|
367
455
|
this.mode = 'review';
|
|
368
456
|
this.status = '';
|
|
369
457
|
}
|
|
@@ -378,6 +466,7 @@ class Session {
|
|
|
378
466
|
|
|
379
467
|
tickProgress() {
|
|
380
468
|
this.progressFrame += 1;
|
|
469
|
+
if (this.npm.running) this.npm.refreshRecorded();
|
|
381
470
|
this.paint();
|
|
382
471
|
}
|
|
383
472
|
|
|
@@ -398,6 +487,10 @@ class Session {
|
|
|
398
487
|
try {
|
|
399
488
|
if (this.nav.pane === 'repos') this.workspace.start();
|
|
400
489
|
this.openLoad();
|
|
490
|
+
// Populate the agent model cache while the initial screen is loading.
|
|
491
|
+
// Opening the Agents pane can then use the cache instead of being the
|
|
492
|
+
// first place that starts its CLI probes.
|
|
493
|
+
void this.agents.loadModels();
|
|
401
494
|
this.openUpdate();
|
|
402
495
|
this.draw();
|
|
403
496
|
await this.readLoop();
|