reslop 0.2.7 → 0.3.1

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 +30 -1
  2. package/README.md +16 -14
  3. package/bin/reslop.js +8 -5
  4. package/lib/agents/agents.js +573 -0
  5. package/lib/agents/allow.js +71 -0
  6. package/lib/agents/allow.json +17 -0
  7. package/lib/agents/models.js +467 -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} +151 -95
  34. package/lib/git/git.js +71 -0
  35. package/lib/git/worktree.js +504 -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 +114 -48
  45. package/lib/{editor.js → input/editor.js} +16 -8
  46. package/lib/{keys.js → input/keys.js} +24 -45
  47. package/lib/input/line-edit.js +78 -0
  48. package/lib/input/menu-pick.js +70 -0
  49. package/lib/render/agents.js +642 -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 +385 -0
  59. package/lib/render/frame.js +123 -25
  60. package/lib/render/header.js +176 -0
  61. package/lib/render/log.js +320 -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} +48 -19
  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 +14 -0
  81. package/lib/review/format.js +420 -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 +976 -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 -40
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +91 -52
  100. package/lib/session/compose.js +189 -147
  101. package/lib/session/dashboard.js +23 -22
  102. package/lib/session/dispatch.js +86 -21
  103. package/lib/session/files-pane.js +47 -26
  104. package/lib/session/find.js +16 -29
  105. package/lib/session/import.js +93 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +78 -72
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +6 -3
  110. package/lib/session/npm/history.js +250 -0
  111. package/lib/session/npm/npm.js +836 -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} +158 -63
  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 +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} +4 -3
  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,113 +266,151 @@ 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);
273
+ };
274
+
275
+ const manifestPlan = (top, rel) => {
276
+ const meta = depFileMeta(rel);
277
+ if (!meta || meta.kind !== 'manifest') return null;
278
+ return {
279
+ cwd: meta.dir === '.' ? top : path.join(top, meta.dir),
280
+ npmArgs: ['i'],
281
+ fail: 'npm i failed',
282
+ lockOnly: false,
283
+ lockPath: relOf(meta.dir, LOCKFILE),
284
+ manifest: rel,
285
+ };
286
+ };
287
+
288
+ const syncManifest = async (top, rel, extra = {}) => {
289
+ const plan = manifestPlan(top, rel);
290
+ if (!plan) return;
291
+ const install = extra.run ?? runNpm;
292
+ const result = await install(plan.cwd, plan.npmArgs);
293
+ await finishPlan(top, plan, result, extra.stage);
255
294
  };
256
295
 
257
296
  const restoreOwned = (top, owned) => {
258
297
  for (const saved of owned) restoreFile(top, saved);
259
298
  };
260
299
 
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
- };
300
+ const unlocked = (write) => write();
274
301
 
275
- const applyProposedUpdateAsync = async (top, item) => {
302
+ // The install can run for minutes, so only the file and index steps around
303
+ // it go through the caller's write lock
304
+ const applyProposedUpdate = async (top, item, extra = {}) => {
276
305
  const started = startPlan(top, item);
277
306
  if (!started) return;
278
307
  const { plan, owned } = started;
308
+ const locked = extra.locked ?? unlocked;
279
309
  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));
310
+ if (plan.prepareManifest) {
311
+ await locked(() => applyDepWorktree(top, plan.manifest, item, 'new'));
312
+ }
313
+ const install = extra.run ?? plan.runner ?? runNpm;
314
+ const result = await install(plan.cwd, plan.npmArgs);
315
+ await locked(() => finishPlan(top, plan, result, extra.stage));
283
316
  } catch (error) {
284
317
  restoreOwned(top, owned);
285
318
  throw error;
286
319
  }
287
320
  };
288
321
 
289
- const applyDepChange = (top, item, mode) => {
322
+ const applyDepChange = async (top, item, mode, extra) => {
290
323
  const change = depChange(item);
291
324
  if (change.propose) {
292
- if (mode === 'add') return void applyProposedUpdate(top, item);
325
+ if (mode === 'add') {
326
+ await applyProposedUpdate(top, item, extra);
327
+ return;
328
+ }
293
329
  if (!change.unused) return;
294
330
  }
295
331
  for (const rel of depPaths(item)) {
296
332
  if (mode !== 'revert') {
297
- applyDepIndex(top, rel, item, mode === 'add' ? 'new' : 'old');
333
+ await applyDepIndex(top, rel, item, mode === 'add' ? 'new' : 'old');
298
334
  continue;
299
335
  }
300
- if (item.origin === 'staged') applyDepIndex(top, rel, item, 'old');
301
- applyDepWorktree(top, rel, item, 'old');
336
+ if (item.origin === 'staged') await applyDepIndex(top, rel, item, 'old');
337
+ await applyDepWorktree(top, rel, item, 'old');
302
338
  }
303
339
  };
304
340
 
305
- const addItem = (top, item) => {
306
- if (!item.dep) return void worktree.addItem(top, item);
341
+ const addItem = async (top, item, extra) => {
342
+ if (!item.dep) {
343
+ await worktree.addItem(top, item);
344
+ await syncManifest(top, itemPath(item), extra);
345
+ return;
346
+ }
307
347
  if (isStagedOrCommit(item)) return;
308
- if (depChange(item)) return void applyDepChange(top, item, 'add');
348
+ if (depChange(item)) {
349
+ await applyDepChange(top, item, 'add', extra);
350
+ return;
351
+ }
309
352
  const rels = depPaths(item);
310
- if (rels.length) requireOk(runGit(['add', '--', ...rels], top));
353
+ if (rels.length) await gitOk(['add', '--', ...rels], top);
311
354
  };
312
355
 
313
- const addItemAsync = async (top, item) => {
314
- if (isStagedOrCommit(item) || !hasProposed(item)) {
315
- return void addItem(top, item);
316
- }
317
- await applyProposedUpdateAsync(top, item);
356
+ const stagePath = async (top, rel, extra) => {
357
+ await worktree.stagePath(top, rel);
358
+ await syncManifest(top, rel, extra);
318
359
  };
319
360
 
320
- const unstageItem = (top, item) => {
321
- if (!item.dep) return void worktree.unstageItem(top, item);
361
+ const unstageItem = async (top, item) => {
362
+ if (!item.dep) {
363
+ await worktree.unstageItem(top, item);
364
+ return;
365
+ }
322
366
  if (item.origin !== 'staged') return;
323
- if (depChange(item)) return void applyDepChange(top, item, 'unstage');
367
+ if (depChange(item)) {
368
+ await applyDepChange(top, item, 'unstage');
369
+ return;
370
+ }
324
371
  const rels = depPaths(item);
325
372
  if (!rels.length) return;
326
- requireOk(runGit(['restore', '--staged', '--', ...rels], top));
373
+ await gitOk(['restore', '--staged', '--', ...rels], top);
327
374
  };
328
375
 
329
- const revertItem = (top, item) => {
376
+ const revertItem = async (top, item) => {
330
377
  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');
378
+ if (!item.dep) {
379
+ await worktree.revertItem(top, item);
380
+ return;
381
+ }
382
+ if (depChange(item)) {
383
+ await applyDepChange(top, item, 'revert');
384
+ return;
385
+ }
333
386
  const byPath = groupByPath(item.dep.items ?? []);
334
- for (const rel of byPath.keys()) revertOnePath(top, rel, byPath.get(rel));
387
+ for (const rel of byPath.keys()) {
388
+ await revertOnePath(top, rel, byPath.get(rel));
389
+ }
335
390
  };
336
391
 
337
- const revertFile = (top, rel, items) => {
392
+ const revertFile = async (top, rel, items) => {
338
393
  const rels = new Set([rel]);
339
394
  for (const item of items) {
340
395
  for (const file of depPaths(item)) rels.add(file);
341
396
  }
342
397
  for (const file of rels) {
343
398
  const group = itemsForPath(file, items);
344
- revertOnePath(top, file, group.length ? group : items);
399
+ await revertOnePath(top, file, group.length ? group : items);
345
400
  }
346
401
  };
347
402
 
348
403
  module.exports = {
349
- collectReportAsync,
404
+ collectReport,
350
405
  finishLoad,
351
406
  loadExtras,
352
407
  captureFile,
353
408
  restoreFile,
354
409
  proposedNpmPlan,
355
410
  applyProposedUpdate,
411
+ syncManifest,
356
412
  addItem,
357
- addItemAsync,
413
+ stagePath,
358
414
  unstageItem,
359
415
  revertItem,
360
416
  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: gitDeps.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
+ };