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.
Files changed (158) hide show
  1. package/CHANGELOG.md +17 -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 +43 -68
  10. package/lib/{files.js → common/files.js} +48 -55
  11. package/lib/{find.js → common/find.js} +1 -1
  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 +90 -49
  18. package/lib/dashboard/npm.js +17 -13
  19. package/lib/dashboard/tree.js +27 -30
  20. package/lib/{session → dashboard}/watch.js +36 -31
  21. package/lib/{workspace.js → dashboard/workspace.js} +23 -38
  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 -96
  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 +111 -48
  45. package/lib/{editor.js → input/editor.js} +7 -8
  46. package/lib/{keys.js → input/keys.js} +15 -43
  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/{dash-blocks.js → dashboard/blocks.js} +131 -137
  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 +9 -14
  58. package/lib/render/footer.js +383 -0
  59. package/lib/render/frame.js +123 -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 +16 -11
  64. package/lib/render/npm.js +322 -196
  65. package/lib/render/packages.js +9 -13
  66. package/lib/render/primitives.js +101 -15
  67. package/lib/render/render.js +7 -11
  68. package/lib/render/repos.js +13 -25
  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} +13 -60
  72. package/lib/render/tiles.js +109 -48
  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 +33 -39
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +91 -52
  100. package/lib/session/compose.js +181 -145
  101. package/lib/session/dashboard.js +23 -22
  102. package/lib/session/dispatch.js +85 -21
  103. package/lib/session/files-pane.js +47 -26
  104. package/lib/session/find.js +16 -29
  105. package/lib/session/import.js +82 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +75 -72
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +6 -3
  110. package/lib/session/npm/history.js +249 -0
  111. package/lib/session/npm/npm.js +815 -0
  112. package/lib/session/{packages.js → npm/packages.js} +21 -14
  113. package/lib/session/ops.js +125 -34
  114. package/lib/session/pointer.js +202 -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} +156 -63
  120. package/lib/session/tasks.js +320 -65
  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 +65 -65
  126. package/lib/session/workspace.js +39 -78
  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 -545
  142. package/lib/detect.js +0 -34
  143. package/lib/diff/edit.js +0 -33
  144. package/lib/git-worktree.js +0 -455
  145. package/lib/git.js +0 -79
  146. package/lib/npm-commands.js +0 -522
  147. package/lib/render/chrome.js +0 -806
  148. package/lib/render/present.js +0 -19
  149. package/lib/render/report.js +0 -43
  150. package/lib/render/search.js +0 -25
  151. package/lib/review.js +0 -745
  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
@@ -3,22 +3,28 @@
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
 
6
- const deps = require('./deps.js');
6
+ const deps = require('../deps/deps.js');
7
7
  const { foldDepItems, mergeProposedItems } = deps;
8
8
  const { parseAuditReport, parseOutdatedReport } = deps;
9
9
  const { collectUsedNames, collectExportMeta } = deps;
10
- const manifest = require('./manifest.js');
10
+ const { collectGroups } = require('../deps/diff.js');
11
+ const manifest = require('../deps/manifest.js');
11
12
  const { MANIFEST, LOCKFILE, depFileMeta, mergeDepFile, relOf } = manifest;
12
- const files = require('./files.js');
13
- const { itemPath, isPathInScope } = files;
14
- const utilities = require('./utilities.js');
15
- const { IS_WIN, abortError, npmBin, oneLine, runProc, runProcSync } = utilities;
16
- const worktree = require('./git-worktree.js');
17
- const { requireOk, runGit, gitText, worktreeText, readDepSides } = worktree;
13
+ const { itemPath, isPathInScope } = require('../common/files.js');
14
+ const utilities = require('../common/utilities.js');
15
+ const {
16
+ INSTALL_MS,
17
+ IS_WIN,
18
+ abortError,
19
+ npmBin,
20
+ runProc,
21
+ } = require('../common/process.js');
22
+ const { oneLine } = utilities;
23
+ const worktree = require('./worktree.js');
24
+ const { gitOk, indexedText, worktreeText, readDepSides } = worktree;
18
25
  const { revertOnePath, groupByPath, writeFile, writeIndex } = worktree;
19
26
 
20
27
  const AUDIT_MS = 15000;
21
- const INSTALL_MS = 120000;
22
28
 
23
29
  const NPM_REPORT = {
24
30
  audit: {
@@ -41,21 +47,11 @@ const npmOptions = (cwd, timeout, signal) => ({
41
47
  });
42
48
 
43
49
  const runNpm = (cwd, args) =>
44
- runProcSync(npmBin(), args, npmOptions(cwd, INSTALL_MS));
45
-
46
- const runNpmAsync = (cwd, args) =>
47
50
  runProc(npmBin(), args, npmOptions(cwd, INSTALL_MS));
48
51
 
49
52
  const npmOutput = (result) => (result.error ? '' : result.stdout);
50
53
 
51
- const collectReport = (top, kind) => {
52
- const report = NPM_REPORT[kind];
53
- if (!fs.existsSync(path.join(top, report.file))) return null;
54
- const options = npmOptions(top, AUDIT_MS);
55
- return report.parse(npmOutput(runProcSync(npmBin(), report.args, options)));
56
- };
57
-
58
- const collectReportAsync = async (top, kind, signal) => {
54
+ const collectReport = async (top, kind, signal) => {
59
55
  const report = NPM_REPORT[kind];
60
56
  if (!fs.existsSync(path.join(top, report.file))) return null;
61
57
  const options = npmOptions(top, AUDIT_MS, signal);
@@ -67,13 +63,16 @@ const depPaths = (item) => item.dep?.files ?? [];
67
63
 
68
64
  const depChange = (item) => item.dep?.change ?? null;
69
65
 
70
- const hasProposed = (item) => !!depChange(item)?.propose;
71
-
72
- const isStagedOrCommit = (item) =>
73
- item.origin === 'staged' || item.origin === 'commit';
66
+ const isStagedOrCommit = (item) => {
67
+ const staged = item.origin === 'staged';
68
+ const commit = item.origin === 'commit';
69
+ return staged || commit;
70
+ };
74
71
 
75
- const firstDepRel = (rels, kind) =>
76
- rels.find((rel) => depFileMeta(rel)?.kind === kind) ?? '';
72
+ const firstDepRel = (rels, kind) => {
73
+ const match = (rel) => depFileMeta(rel)?.kind === kind;
74
+ return rels.find(match) ?? '';
75
+ };
77
76
 
78
77
  const proposedNpmPlan = (top, item) => {
79
78
  const change = depChange(item);
@@ -118,8 +117,25 @@ const extrasNeeds = (items, rev, extra) => {
118
117
  return { live, scan: live || hasDepDiff, audit };
119
118
  };
120
119
 
121
- const foldLoaded = (items, top, rev, extra) => {
122
- const readSides = (origin, rel) => readDepSides(top, rev, origin, rel);
120
+ const NO_SIDES = { oldText: '', newText: '' };
121
+
122
+ const sidesReader = async (items, top, rev) => {
123
+ const sides = new Map();
124
+ const reads = [];
125
+ for (const group of collectGroups(items).values()) {
126
+ for (const name of [MANIFEST, LOCKFILE]) {
127
+ const rel = relOf(group.dir, name);
128
+ const key = `${group.origin}\0${rel}`;
129
+ const keep = (found) => sides.set(key, found);
130
+ reads.push(readDepSides(top, rev, group.origin, rel).then(keep));
131
+ }
132
+ }
133
+ await Promise.all(reads);
134
+ return (origin, rel) => sides.get(`${origin}\0${rel}`) ?? NO_SIDES;
135
+ };
136
+
137
+ const foldLoaded = async (items, top, rev, extra) => {
138
+ const readSides = await sidesReader(items, top, rev);
123
139
  const needs = extrasNeeds(items, rev, extra);
124
140
  const collect = extra.collect !== false;
125
141
  const scan = collect && needs.scan;
@@ -129,10 +145,10 @@ const foldLoaded = (items, top, rev, extra) => {
129
145
  exportEntries = collectExportMeta(top);
130
146
  }
131
147
  if (collect && needs.audit && audit === undefined) {
132
- audit = collectReport(top, 'audit');
148
+ audit = await collectReport(top, 'audit', extra.signal);
133
149
  }
134
150
  if (collect && needs.live && outdated === undefined) {
135
- outdated = collectReport(top, 'outdated');
151
+ outdated = await collectReport(top, 'outdated', extra.signal);
136
152
  }
137
153
  audit ??= null;
138
154
  outdated ??= null;
@@ -141,16 +157,16 @@ const foldLoaded = (items, top, rev, extra) => {
141
157
  exportEntries,
142
158
  });
143
159
  if (!needs.live || !isPathInScope(MANIFEST, extra.paths)) return folded;
144
- const pkgText = worktreeText(top, MANIFEST);
145
- const lockText = worktreeText(top, LOCKFILE);
160
+ const texts = [worktreeText(top, MANIFEST), worktreeText(top, LOCKFILE)];
161
+ const [pkgText, lockText] = await Promise.all(texts);
146
162
  const options = { lockText, usedNames, root: top, exportEntries };
147
163
  return mergeProposedItems(folded, pkgText, outdated, audit, options);
148
164
  };
149
165
 
150
- const finishLoad = (snapshot, extra) => {
166
+ const finishLoad = async (snapshot, extra) => {
151
167
  const { top, rev, parsed } = snapshot;
152
168
  const options = { ...extra, collect: !extra.deferExtras };
153
- const items = foldLoaded(parsed, top, rev, options);
169
+ const items = await foldLoaded(parsed, top, rev, options);
154
170
  const pending = extra.deferExtras && extrasNeeds(parsed, rev, extra).audit;
155
171
  return { ...snapshot, items, pending };
156
172
  };
@@ -167,21 +183,23 @@ const loadExtras = async (snapshot, extra = {}) => {
167
183
  const { signal } = extra;
168
184
  const needs = extrasNeeds(parsed, rev, extra);
169
185
  const known = extra.exportEntries;
186
+ let exportMeta = known;
187
+ if (known === undefined && needs.scan) {
188
+ exportMeta = later(() => collectExportMeta(top));
189
+ }
170
190
  const [usedNames, audit, outdated, exportEntries] = await Promise.all([
171
191
  needs.scan ? later(() => collectUsedNames(top)) : null,
172
192
  reportTask(needs.audit, extra.auditMap, () =>
173
- collectReportAsync(top, 'audit', signal),
193
+ collectReport(top, 'audit', signal),
174
194
  ),
175
195
  reportTask(needs.live, extra.outdatedMap, () =>
176
- collectReportAsync(top, 'outdated', signal),
196
+ collectReport(top, 'outdated', signal),
177
197
  ),
178
- known === undefined && needs.scan
179
- ? later(() => collectExportMeta(top))
180
- : known,
198
+ exportMeta,
181
199
  ]);
182
200
  if (signal && signal.aborted) throw abortError();
183
201
  const found = { usedNames, auditMap: audit, outdatedMap: outdated };
184
- const items = foldLoaded(parsed, top, rev, {
202
+ const items = await foldLoaded(parsed, top, rev, {
185
203
  ...extra,
186
204
  ...found,
187
205
  paths: extra.paths ?? [],
@@ -207,23 +225,23 @@ const restoreFile = (top, saved) => {
207
225
  else fs.rmSync(abs, { force: true });
208
226
  };
209
227
 
210
- const mergeLive = (top, rel, item, base, side) => {
211
- const sides = readDepSides(top, null, item.origin, rel);
228
+ const mergeLive = async (top, rel, item, base, side) => {
229
+ const sides = await readDepSides(top, null, item.origin, rel);
212
230
  const { kind } = depFileMeta(rel);
213
231
  const change = depChange(item);
214
232
  return mergeDepFile(base, sides.oldText, sides.newText, change, side, kind);
215
233
  };
216
234
 
217
- const applyDepIndex = (top, rel, item, side) => {
235
+ const applyDepIndex = async (top, rel, item, side) => {
218
236
  if (!depFileMeta(rel)) return;
219
- const indexed = gitText(top, `:${rel}`) || gitText(top, `HEAD:${rel}`);
220
- writeIndex(top, rel, mergeLive(top, rel, item, indexed, side));
237
+ const indexed = await indexedText(top, rel);
238
+ await writeIndex(top, rel, await mergeLive(top, rel, item, indexed, side));
221
239
  };
222
240
 
223
- const applyDepWorktree = (top, rel, item, side) => {
241
+ const applyDepWorktree = async (top, rel, item, side) => {
224
242
  if (!depFileMeta(rel)) return;
225
- const text = mergeLive(top, rel, item, worktreeText(top, rel), side);
226
- writeFile(top, rel, text);
243
+ const live = await worktreeText(top, rel);
244
+ await writeFile(top, rel, await mergeLive(top, rel, item, live, side));
227
245
  };
228
246
 
229
247
  const npmError = (result, fail) => {
@@ -232,12 +250,12 @@ const npmError = (result, fail) => {
232
250
  return new Error(oneLine(msg || fail, fail));
233
251
  };
234
252
 
235
- const stagePlan = (top, plan) => {
253
+ const stagePlan = async (top, plan) => {
236
254
  const toAdd = [];
237
255
  if (!plan.lockOnly && plan.manifest) toAdd.push(plan.manifest);
238
256
  if (fs.existsSync(path.join(top, plan.lockPath))) toAdd.push(plan.lockPath);
239
257
  if (!toAdd.length) return;
240
- requireOk(runGit(['add', '--', ...toAdd], top));
258
+ await gitOk(['add', '--', ...toAdd], top);
241
259
  };
242
260
 
243
261
  const startPlan = (top, item) => {
@@ -248,105 +266,115 @@ const startPlan = (top, item) => {
248
266
  return { plan, owned };
249
267
  };
250
268
 
251
- const finishPlan = (top, plan, result, stage = stagePlan) => {
269
+ const finishPlan = async (top, plan, result, stage = stagePlan) => {
252
270
  const failed = result && (result.error || result.status !== 0);
253
271
  if (failed) throw npmError(result, plan.fail);
254
- stage(top, plan);
272
+ await stage(top, plan);
255
273
  };
256
274
 
257
275
  const restoreOwned = (top, owned) => {
258
276
  for (const saved of owned) restoreFile(top, saved);
259
277
  };
260
278
 
261
- const applyProposedUpdate = (top, item, extra = {}) => {
262
- const started = startPlan(top, item);
263
- if (!started) return;
264
- const { plan, owned } = started;
265
- try {
266
- if (plan.prepareManifest) applyDepWorktree(top, plan.manifest, item, 'new');
267
- const install = extra.run ?? plan.runner ?? runNpm;
268
- finishPlan(top, plan, install(plan.cwd, plan.npmArgs), extra.stage);
269
- } catch (error) {
270
- restoreOwned(top, owned);
271
- throw error;
272
- }
273
- };
279
+ const unlocked = (write) => write();
274
280
 
275
- const applyProposedUpdateAsync = async (top, item) => {
281
+ // The install can run for minutes, so only the file and index steps around
282
+ // it go through the caller's write lock
283
+ const applyProposedUpdate = async (top, item, extra = {}) => {
276
284
  const started = startPlan(top, item);
277
285
  if (!started) return;
278
286
  const { plan, owned } = started;
287
+ const locked = extra.locked ?? unlocked;
279
288
  try {
280
- if (plan.prepareManifest) applyDepWorktree(top, plan.manifest, item, 'new');
281
- const install = plan.runner ?? runNpmAsync;
282
- finishPlan(top, plan, await install(plan.cwd, plan.npmArgs));
289
+ if (plan.prepareManifest) {
290
+ await locked(() => applyDepWorktree(top, plan.manifest, item, 'new'));
291
+ }
292
+ const install = extra.run ?? plan.runner ?? runNpm;
293
+ const result = await install(plan.cwd, plan.npmArgs);
294
+ await locked(() => finishPlan(top, plan, result, extra.stage));
283
295
  } catch (error) {
284
296
  restoreOwned(top, owned);
285
297
  throw error;
286
298
  }
287
299
  };
288
300
 
289
- const applyDepChange = (top, item, mode) => {
301
+ const applyDepChange = async (top, item, mode, extra) => {
290
302
  const change = depChange(item);
291
303
  if (change.propose) {
292
- if (mode === 'add') return void applyProposedUpdate(top, item);
304
+ if (mode === 'add') {
305
+ await applyProposedUpdate(top, item, extra);
306
+ return;
307
+ }
293
308
  if (!change.unused) return;
294
309
  }
295
310
  for (const rel of depPaths(item)) {
296
311
  if (mode !== 'revert') {
297
- applyDepIndex(top, rel, item, mode === 'add' ? 'new' : 'old');
312
+ await applyDepIndex(top, rel, item, mode === 'add' ? 'new' : 'old');
298
313
  continue;
299
314
  }
300
- if (item.origin === 'staged') applyDepIndex(top, rel, item, 'old');
301
- applyDepWorktree(top, rel, item, 'old');
315
+ if (item.origin === 'staged') await applyDepIndex(top, rel, item, 'old');
316
+ await applyDepWorktree(top, rel, item, 'old');
302
317
  }
303
318
  };
304
319
 
305
- const addItem = (top, item) => {
306
- if (!item.dep) return void worktree.addItem(top, item);
320
+ const addItem = async (top, item, extra) => {
321
+ if (!item.dep) {
322
+ await worktree.addItem(top, item);
323
+ return;
324
+ }
307
325
  if (isStagedOrCommit(item)) return;
308
- if (depChange(item)) return void applyDepChange(top, item, 'add');
326
+ if (depChange(item)) {
327
+ await applyDepChange(top, item, 'add', extra);
328
+ return;
329
+ }
309
330
  const rels = depPaths(item);
310
- if (rels.length) requireOk(runGit(['add', '--', ...rels], top));
331
+ if (rels.length) await gitOk(['add', '--', ...rels], top);
311
332
  };
312
333
 
313
- const addItemAsync = async (top, item) => {
314
- if (isStagedOrCommit(item) || !hasProposed(item)) {
315
- return void addItem(top, item);
334
+ const unstageItem = async (top, item) => {
335
+ if (!item.dep) {
336
+ await worktree.unstageItem(top, item);
337
+ return;
316
338
  }
317
- await applyProposedUpdateAsync(top, item);
318
- };
319
-
320
- const unstageItem = (top, item) => {
321
- if (!item.dep) return void worktree.unstageItem(top, item);
322
339
  if (item.origin !== 'staged') return;
323
- if (depChange(item)) return void applyDepChange(top, item, 'unstage');
340
+ if (depChange(item)) {
341
+ await applyDepChange(top, item, 'unstage');
342
+ return;
343
+ }
324
344
  const rels = depPaths(item);
325
345
  if (!rels.length) return;
326
- requireOk(runGit(['restore', '--staged', '--', ...rels], top));
346
+ await gitOk(['restore', '--staged', '--', ...rels], top);
327
347
  };
328
348
 
329
- const revertItem = (top, item) => {
349
+ const revertItem = async (top, item) => {
330
350
  if (item.origin === 'commit') return;
331
- if (!item.dep) return void worktree.revertItem(top, item);
332
- if (depChange(item)) return void applyDepChange(top, item, 'revert');
351
+ if (!item.dep) {
352
+ await worktree.revertItem(top, item);
353
+ return;
354
+ }
355
+ if (depChange(item)) {
356
+ await applyDepChange(top, item, 'revert');
357
+ return;
358
+ }
333
359
  const byPath = groupByPath(item.dep.items ?? []);
334
- for (const rel of byPath.keys()) revertOnePath(top, rel, byPath.get(rel));
360
+ for (const rel of byPath.keys()) {
361
+ await revertOnePath(top, rel, byPath.get(rel));
362
+ }
335
363
  };
336
364
 
337
- const revertFile = (top, rel, items) => {
365
+ const revertFile = async (top, rel, items) => {
338
366
  const rels = new Set([rel]);
339
367
  for (const item of items) {
340
368
  for (const file of depPaths(item)) rels.add(file);
341
369
  }
342
370
  for (const file of rels) {
343
371
  const group = itemsForPath(file, items);
344
- revertOnePath(top, file, group.length ? group : items);
372
+ await revertOnePath(top, file, group.length ? group : items);
345
373
  }
346
374
  };
347
375
 
348
376
  module.exports = {
349
- collectReportAsync,
377
+ collectReport,
350
378
  finishLoad,
351
379
  loadExtras,
352
380
  captureFile,
@@ -354,7 +382,6 @@ module.exports = {
354
382
  proposedNpmPlan,
355
383
  applyProposedUpdate,
356
384
  addItem,
357
- addItemAsync,
358
385
  unstageItem,
359
386
  revertItem,
360
387
  revertFile,
package/lib/git/git.js ADDED
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ capabilitiesFor,
5
+ attachCapabilities,
6
+ } = require('../source/capabilities.js');
7
+ const worktree = require('./worktree.js');
8
+ const branches = require('./branches.js');
9
+ const gitDeps = require('./deps.js');
10
+
11
+ const load = async (cwd, paths = [], options = {}) => {
12
+ const snapshot = await worktree.readSnapshot(cwd, paths, options);
13
+ const deferExtras = options.deferExtras ?? true;
14
+ return gitDeps.finishLoad(snapshot, { ...options, paths, deferExtras });
15
+ };
16
+
17
+ const REPO_METHODS = {
18
+ load,
19
+ loadExtras: gitDeps.loadExtras,
20
+ toplevel: worktree.toplevel,
21
+ resolveRev: worktree.resolveRev,
22
+ listFiles: worktree.listScopeFiles,
23
+ fileText: worktree.fileText,
24
+ add: gitDeps.addItem,
25
+ unstage: gitDeps.unstageItem,
26
+ revert: gitDeps.revertItem,
27
+ revertFile: gitDeps.revertFile,
28
+ edit: worktree.editItem,
29
+ writeFile: worktree.writeFile,
30
+ stagePath: worktree.stagePath,
31
+ commit: branches.commitChanges,
32
+ updateCommit: branches.updateCommit,
33
+ hasStaged: branches.hasStaged,
34
+ lastMessage: branches.lastMessage,
35
+ commitMessage: branches.commitMessage,
36
+ listBranches: branches.listBranches,
37
+ listCommits: branches.listCommits,
38
+ currentBranch: worktree.currentBranch,
39
+ checkout: branches.checkoutBranch,
40
+ createBranch: branches.createBranch,
41
+ rebase: branches.rebaseBranch,
42
+ drop: branches.dropBranch,
43
+ dropCommit: branches.dropCommit,
44
+ applyFixup: branches.applyFixup,
45
+ reword: branches.rewordCommit,
46
+ pull: branches.pullChanges,
47
+ push: branches.pushChanges,
48
+ };
49
+
50
+ const createGitRepo = () =>
51
+ attachCapabilities(REPO_METHODS, capabilitiesFor('local'));
52
+
53
+ const listed = createGitRepo();
54
+ const unlisted = Object.keys(REPO_METHODS).filter(
55
+ (name) => !Object.hasOwn(listed, name),
56
+ );
57
+ if (unlisted.length) {
58
+ throw new Error(`git methods without a capability: ${unlisted.join(', ')}`);
59
+ }
60
+
61
+ module.exports = {
62
+ ...branches,
63
+ currentBranch: worktree.currentBranch,
64
+ editItem: worktree.editItem,
65
+ load,
66
+ loadExtras: gitDeps.loadExtras,
67
+ addItem: gitDeps.addItem,
68
+ unstageItem: gitDeps.unstageItem,
69
+ revertItem: gitDeps.revertItem,
70
+ createGitRepo,
71
+ };