reslop 0.2.3 → 0.2.5

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 (89) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +8 -4
  3. package/bin/reslop.js +4 -2
  4. package/lib/api.js +2 -1
  5. package/lib/capabilities.js +0 -4
  6. package/lib/cli.js +19 -10
  7. package/lib/clipboard.js +45 -4
  8. package/lib/deps-diff.js +61 -118
  9. package/lib/deps.js +166 -235
  10. package/lib/detect.js +4 -2
  11. package/lib/diff/align.js +2 -1
  12. package/lib/diff/diff.js +6 -3
  13. package/lib/diff/display.js +2 -1
  14. package/lib/diff/inline.js +68 -2
  15. package/lib/diff/patch.js +2 -1
  16. package/lib/diff/unit.js +15 -30
  17. package/lib/editor.js +91 -13
  18. package/lib/files.js +54 -27
  19. package/lib/git-branches.js +107 -154
  20. package/lib/git-deps.js +78 -155
  21. package/lib/git-worktree.js +508 -0
  22. package/lib/git.js +23 -580
  23. package/lib/github.js +9 -8
  24. package/lib/gitlab.js +5 -4
  25. package/lib/highlight/bash.js +2 -1
  26. package/lib/highlight/core.js +11 -0
  27. package/lib/highlight/css.js +2 -1
  28. package/lib/highlight/csv.js +2 -1
  29. package/lib/highlight/dot.js +3 -12
  30. package/lib/highlight/html.js +2 -1
  31. package/lib/highlight/json.js +2 -1
  32. package/lib/highlight/log.js +5 -11
  33. package/lib/highlight/txt.js +3 -12
  34. package/lib/highlight.js +2 -1
  35. package/lib/keys.js +22 -12
  36. package/lib/manifest.js +4 -6
  37. package/lib/npm-commands.js +82 -24
  38. package/lib/remote.js +21 -20
  39. package/lib/render/branches.js +9 -27
  40. package/lib/render/chrome.js +109 -35
  41. package/lib/render/commits.js +216 -216
  42. package/lib/render/diff.js +160 -47
  43. package/lib/render/files.js +9 -26
  44. package/lib/render/frame.js +97 -118
  45. package/lib/render/notes.js +74 -26
  46. package/lib/render/npm.js +104 -30
  47. package/lib/render/present.js +2 -1
  48. package/lib/render/primitives.js +128 -52
  49. package/lib/render/render.js +2 -1
  50. package/lib/render/unit.js +17 -11
  51. package/lib/report-model.js +6 -4
  52. package/lib/report-parse.js +8 -14
  53. package/lib/report-render.js +38 -21
  54. package/lib/report-run.js +12 -11
  55. package/lib/report-test.js +45 -51
  56. package/lib/report-tools.js +44 -25
  57. package/lib/review-merge.js +102 -131
  58. package/lib/review.js +69 -78
  59. package/lib/select.js +52 -0
  60. package/lib/session/actions.js +72 -87
  61. package/lib/session/branches.js +18 -52
  62. package/lib/session/change-actions.js +175 -147
  63. package/lib/session/commit.js +65 -61
  64. package/lib/session/compose.js +152 -83
  65. package/lib/session/dispatch.js +53 -46
  66. package/lib/session/edit-click.js +57 -0
  67. package/lib/session/files-pane.js +30 -51
  68. package/lib/session/items.js +73 -90
  69. package/lib/session/lifecycle.js +37 -98
  70. package/lib/session/load.js +52 -57
  71. package/lib/session/navigation.js +105 -95
  72. package/lib/session/npm.js +205 -71
  73. package/lib/session/ops.js +200 -45
  74. package/lib/session/pointer.js +33 -8
  75. package/lib/session/progress.js +9 -6
  76. package/lib/session/review.js +35 -53
  77. package/lib/session/tasks.js +292 -0
  78. package/lib/session/terminal.js +17 -19
  79. package/lib/session/unit.js +3 -4
  80. package/lib/session/update.js +76 -96
  81. package/lib/session/view.js +45 -16
  82. package/lib/session/watch.js +47 -27
  83. package/lib/session.js +66 -97
  84. package/lib/source.js +6 -3
  85. package/lib/update.js +5 -13
  86. package/lib/utilities.js +25 -3
  87. package/lib/wrap.js +18 -1
  88. package/package.json +1 -1
  89. package/lib/session/todos.js +0 -293
@@ -1,10 +1,12 @@
1
1
  'use strict';
2
2
 
3
- const { graphemes } = require('../ansi.js');
3
+ const ansi = require('../ansi.js');
4
+ const { graphemes } = ansi;
4
5
 
5
6
  const LCS_CELL_LIMIT = 250000;
6
7
  const NOISY_TINY_SPANS = 6;
7
8
  const WORD_REFINE_MIN = 0.6;
9
+ const WHOLE_MIN_SHARED = 0.25;
8
10
  const PIECE_RE = /[A-Za-z_$][\w$]*|\d+|\s+|./g;
9
11
  const CAMEL_RE = /[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+|\d+|./g;
10
12
 
@@ -279,6 +281,65 @@ const spansFromWordOps = (ops) => {
279
281
  };
280
282
  };
281
283
 
284
+ const opRuns = (ops) => {
285
+ const runs = [];
286
+ for (const op of ops) {
287
+ const eq = op.type === 'eq';
288
+ let run = runs[runs.length - 1];
289
+ if (!run || run.eq !== eq) {
290
+ run = { eq, del: [], add: [] };
291
+ runs.push(run);
292
+ }
293
+ if (op.type !== 'add') run.del.push(op.text);
294
+ if (op.type !== 'del') run.add.push(op.text);
295
+ }
296
+ return runs;
297
+ };
298
+
299
+ const textSize = (parts) => parts.join('').length;
300
+
301
+ const editSize = (run) => Math.max(textSize(run.del), textSize(run.add));
302
+
303
+ const absorbTinyEquals = (runs) => {
304
+ for (let k = 1; k < runs.length - 1; k++) {
305
+ const [before, eq, after] = runs.slice(k - 1, k + 2);
306
+ if (!eq.eq) continue;
307
+ const size = textSize(eq.del);
308
+ if (size > editSize(before) || size > editSize(after)) continue;
309
+ const del = [...before.del, ...eq.del, ...after.del];
310
+ const add = [...before.add, ...eq.add, ...after.add];
311
+ runs.splice(k - 1, 3, { eq: false, del, add });
312
+ k = 0;
313
+ }
314
+ return runs;
315
+ };
316
+
317
+ const runOps = (runs) => {
318
+ const ops = [];
319
+ for (const run of runs) {
320
+ if (run.eq) {
321
+ for (const text of run.del) ops.push({ type: 'eq', text });
322
+ continue;
323
+ }
324
+ for (const text of run.del) ops.push({ type: 'del', text });
325
+ for (const text of run.add) ops.push({ type: 'add', text });
326
+ }
327
+ return ops;
328
+ };
329
+
330
+ const cleanOps = (ops) => runOps(absorbTinyEquals(opRuns(ops)));
331
+
332
+ const sharedShare = (spans) => {
333
+ let shared = 0;
334
+ let total = 0;
335
+ for (const span of spans) {
336
+ const size = span.text.replace(/\s/g, '').length;
337
+ total += size;
338
+ if (!span.changed) shared += size;
339
+ }
340
+ return total === 0 ? 1 : shared / total;
341
+ };
342
+
282
343
  const diffChars = (oldText, newText) => {
283
344
  const a = splitPieces(oldText);
284
345
  const b = splitPieces(newText);
@@ -295,10 +356,15 @@ const diffChars = (oldText, newText) => {
295
356
  } else if (midA.length * midB.length > LCS_CELL_LIMIT) {
296
357
  mid = wholeChanged(midA, midB);
297
358
  } else {
298
- mid = spansFromWordOps(lcsOps(midA, midB));
359
+ mid = spansFromWordOps(cleanOps(lcsOps(midA, midB)));
299
360
  }
300
361
  const oldSpans = joinAffix(prefix, mid.oldSpans, suffix);
301
362
  const newSpans = joinAffix(prefix, mid.newSpans, suffix);
363
+ const oldShare = sharedShare(oldSpans);
364
+ const newShare = sharedShare(newSpans);
365
+ if (oldShare < WHOLE_MIN_SHARED && newShare < WHOLE_MIN_SHARED) {
366
+ return wholeChanged(a, b);
367
+ }
302
368
  return { oldSpans, newSpans };
303
369
  };
304
370
 
package/lib/diff/patch.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { isCtxType } = require('./align.js');
3
+ const align = require('./align.js');
4
+ const { isCtxType } = align;
4
5
 
5
6
  const LINE_MARK = {
6
7
  add: '+',
package/lib/diff/unit.js CHANGED
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const { attachInline } = require('./align.js');
4
- const { listPath } = require('../files.js');
3
+ const align = require('./align.js');
4
+ const { attachInline } = align;
5
+ const files = require('../files.js');
6
+ const { listPath } = files;
5
7
 
6
8
  const splitEditor = (text) => {
7
9
  const lines = `${text}`.split('\n');
@@ -126,43 +128,26 @@ const sameBlock = (line, item) => {
126
128
  return line.blockId === item.blockId;
127
129
  };
128
130
 
129
- const blockLineRange = (lines, item) => {
130
- let start = 0;
131
+ const changedLineRange = (lines, item, belongs) => {
132
+ let start = -1;
131
133
  let end = 0;
132
- let found = false;
133
- if (!item) return { start, end };
134
+ if (!item) return { start: 0, end };
134
135
  for (let i = 0; i < lines.length; i++) {
135
136
  const line = lines[i];
136
- if (!sameBlock(line, item)) continue;
137
- if (line.type === 'ctx') continue;
138
- if (!found) {
139
- start = i;
140
- found = true;
141
- }
137
+ if (line.type === 'ctx' || !belongs(line, item)) continue;
138
+ if (start < 0) start = i;
142
139
  end = i;
143
140
  }
144
- return { start, end };
141
+ return { start: Math.max(start, 0), end };
145
142
  };
146
143
 
144
+ const blockLineRange = (lines, item) =>
145
+ changedLineRange(lines, item, sameBlock);
146
+
147
147
  const blockLineIndex = (lines, item) => blockLineRange(lines, item).start;
148
148
 
149
- const hunkLineRange = (lines, item) => {
150
- let start = 0;
151
- let end = 0;
152
- let found = false;
153
- if (!item) return { start, end };
154
- for (let i = 0; i < lines.length; i++) {
155
- const line = lines[i];
156
- if (!sameHunk(line.item, item)) continue;
157
- if (line.type === 'ctx') continue;
158
- if (!found) {
159
- start = i;
160
- found = true;
161
- }
162
- end = i;
163
- }
164
- return { start, end };
165
- };
149
+ const hunkLineRange = (lines, item) =>
150
+ changedLineRange(lines, item, (line) => sameHunk(line.item, item));
166
151
 
167
152
  const fileOffset = (lines, at) => {
168
153
  let offset = 0;
package/lib/editor.js CHANGED
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const { graphemes } = require('./ansi.js');
4
- const { wrapMove } = require('./wrap.js');
3
+ const ansi = require('./ansi.js');
4
+ const { graphemes, visibleWidth } = ansi;
5
+ const wrap = require('./wrap.js');
6
+ const { wrapMove } = wrap;
5
7
 
6
8
  const UNDO_CAP = 100;
7
9
 
@@ -59,15 +61,53 @@ class Editor {
59
61
  this.undo = [];
60
62
  this.redo = [];
61
63
  this.wantCol = null;
64
+ this.scrollCol = 0;
65
+ this.anchor = null;
62
66
  }
63
67
 
64
68
  snapshot() {
65
- return { text: this.text, cursor: this.cursor };
69
+ return { text: this.text, cursor: this.cursor, anchor: this.anchor };
66
70
  }
67
71
 
68
72
  restore(snap) {
69
73
  this.text = snap.text;
70
74
  this.cursor = snap.cursor;
75
+ this.anchor = snap.anchor ?? null;
76
+ }
77
+
78
+ hasSelect() {
79
+ return this.anchor !== null && this.anchor !== this.cursor;
80
+ }
81
+
82
+ selectedText() {
83
+ if (!this.hasSelect()) return '';
84
+ const lo = Math.min(this.anchor, this.cursor);
85
+ const hi = Math.max(this.anchor, this.cursor);
86
+ return this.text.slice(lo, hi);
87
+ }
88
+
89
+ beginSelect() {
90
+ if (this.anchor === null) this.anchor = this.cursor;
91
+ }
92
+
93
+ clearSelect() {
94
+ this.anchor = null;
95
+ }
96
+
97
+ collapse(delta) {
98
+ const lo = Math.min(this.anchor, this.cursor);
99
+ const hi = Math.max(this.anchor, this.cursor);
100
+ this.anchor = null;
101
+ this.cursor = delta < 0 ? lo : hi;
102
+ }
103
+
104
+ removeSpan() {
105
+ if (!this.hasSelect()) return false;
106
+ const lo = Math.min(this.anchor, this.cursor);
107
+ const hi = Math.max(this.anchor, this.cursor);
108
+ const next = this.text.slice(0, lo) + this.text.slice(hi);
109
+ this.edit(next, lo);
110
+ return true;
71
111
  }
72
112
 
73
113
  edit(nextText, nextCursor) {
@@ -78,13 +118,17 @@ class Editor {
78
118
  this.redo = [];
79
119
  this.text = nextText;
80
120
  this.cursor = nextCursor;
121
+ this.anchor = null;
81
122
  }
82
123
 
83
124
  insert(chunk) {
84
125
  this.wantCol = null;
85
- const before = this.text.slice(0, this.cursor);
86
- const after = this.text.slice(this.cursor);
87
- this.edit(before + chunk + after, this.cursor + chunk.length);
126
+ const selected = this.hasSelect();
127
+ const lo = selected ? Math.min(this.anchor, this.cursor) : this.cursor;
128
+ const hi = selected ? Math.max(this.anchor, this.cursor) : this.cursor;
129
+ const before = this.text.slice(0, lo);
130
+ const after = this.text.slice(hi);
131
+ this.edit(before + chunk + after, lo + chunk.length);
88
132
  }
89
133
 
90
134
  replace(text) {
@@ -92,8 +136,16 @@ class Editor {
92
136
  this.edit(text, text.length);
93
137
  }
94
138
 
139
+ place(offset) {
140
+ const next = Math.max(0, Math.min(offset, this.text.length));
141
+ this.wantCol = null;
142
+ this.anchor = null;
143
+ this.cursor = next;
144
+ }
145
+
95
146
  backspace() {
96
147
  this.wantCol = null;
148
+ if (this.removeSpan()) return;
97
149
  const { parts, i } = graphemeIndex(this.text, this.cursor);
98
150
  if (i === 0) return;
99
151
  const drop = parts[i - 1];
@@ -103,22 +155,27 @@ class Editor {
103
155
 
104
156
  delete() {
105
157
  this.wantCol = null;
158
+ if (this.removeSpan()) return;
106
159
  const { parts, i } = graphemeIndex(this.text, this.cursor);
107
160
  if (i >= parts.length) return;
108
161
  const nextParts = [...parts.slice(0, i), ...parts.slice(i + 1)];
109
162
  this.edit(nextParts.join(''), this.cursor);
110
163
  }
111
164
 
112
- move(delta) {
165
+ move(delta, extend) {
113
166
  this.wantCol = null;
167
+ if (!extend && this.hasSelect()) return void this.collapse(delta);
168
+ if (extend) this.beginSelect();
114
169
  const { parts, i } = graphemeIndex(this.text, this.cursor);
115
170
  const next = i + delta;
116
171
  if (next < 0 || next > parts.length) return;
117
172
  this.cursor = offsetAt(parts, next);
118
173
  }
119
174
 
120
- moveWord(delta) {
175
+ moveWord(delta, extend) {
121
176
  this.wantCol = null;
177
+ if (!extend && this.hasSelect()) return void this.collapse(delta);
178
+ if (extend) this.beginSelect();
122
179
  const dir = delta < 0 ? -1 : 1;
123
180
  const { parts, i } = graphemeIndex(this.text, this.cursor);
124
181
  this.cursor = offsetAt(parts, wordIndex(parts, i, dir));
@@ -132,7 +189,22 @@ class Editor {
132
189
  return { line, col };
133
190
  }
134
191
 
135
- moveLine(delta, width) {
192
+ visibleLineCol() {
193
+ const { col, line } = this.linePos();
194
+ const row = this.text.split('\n')[line] ?? '';
195
+ return visibleWidth(row.slice(0, col));
196
+ }
197
+
198
+ reveal(width) {
199
+ const col = this.visibleLineCol();
200
+ const room = Math.max(1, width);
201
+ if (col < this.scrollCol) this.scrollCol = col;
202
+ if (col >= this.scrollCol + room) this.scrollCol = col - room + 1;
203
+ }
204
+
205
+ moveLine(delta, width, extend) {
206
+ if (!extend) this.clearSelect();
207
+ else this.beginSelect();
136
208
  if (width !== undefined) {
137
209
  const col = this.wantCol;
138
210
  const moved = wrapMove(this.text, this.cursor, width, delta, col);
@@ -142,8 +214,10 @@ class Editor {
142
214
  }
143
215
  const rows = this.text.split('\n');
144
216
  const { line, col } = this.linePos();
145
- const next = line + delta;
146
- if (next < 0 || next >= rows.length) return;
217
+ let next = line + delta;
218
+ if (next < 0) next = 0;
219
+ if (next >= rows.length) next = rows.length - 1;
220
+ if (next === line) return;
147
221
  const row = rows[next];
148
222
  const c = Math.min(col, row.length);
149
223
  let cursor = 0;
@@ -151,14 +225,18 @@ class Editor {
151
225
  this.cursor = cursor + c;
152
226
  }
153
227
 
154
- home() {
228
+ home(extend) {
155
229
  this.wantCol = null;
230
+ if (!extend) this.clearSelect();
231
+ else this.beginSelect();
156
232
  const { col } = this.linePos();
157
233
  this.cursor -= col;
158
234
  }
159
235
 
160
- end() {
236
+ end(extend) {
161
237
  this.wantCol = null;
238
+ if (!extend) this.clearSelect();
239
+ else this.beginSelect();
162
240
  const { line, col } = this.linePos();
163
241
  const rows = this.text.split('\n');
164
242
  const row = rows[line] ?? '';
package/lib/files.js CHANGED
@@ -1,11 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const { listFilePath } = require('./manifest.js');
3
+ const manifest = require('./manifest.js');
4
+ const { listFilePath } = manifest;
4
5
 
5
6
  const REVIEW_DIR = '.review';
6
7
  const ORIGIN_ORDER = ['staged', 'unstaged', 'untracked', 'commit', 'pr'];
7
- const TODO_FILE = 'TODOs';
8
- const REPO_TODOS_LABEL = 'Repository TODOs and Issues';
8
+ const TASKS_FILE = 'TODOs';
9
+ const REPO_TASKS_LABEL =
10
+ 'Project Tasks: Backlog, Issues, Bug Reports, Feature Requests';
9
11
  const TOTAL_LABEL = 'total';
10
12
 
11
13
  const isPathInScope = (rel, paths) => {
@@ -25,19 +27,19 @@ const itemPath = (item) => {
25
27
 
26
28
  const listPath = (item) => listFilePath(itemPath(item));
27
29
 
28
- const isTodoItem = (item) => !!(item && item.origin === 'todo');
30
+ const isTaskItem = (item) => !!(item && item.origin === 'task');
29
31
 
30
32
  const isReadOnlyOrigin = (origin) => origin === 'commit' || origin === 'pr';
31
33
 
32
- const isTodosEntry = (entry) => !!(entry && entry.kind === 'todos');
34
+ const isTasksEntry = (entry) => !!(entry && entry.kind === 'tasks');
33
35
 
34
36
  const isTotalEntry = (entry) => !!(entry && entry.kind === 'total');
35
37
 
36
- const makeTodoItem = () => ({
37
- origin: 'todo',
38
+ const makeTaskItem = () => ({
39
+ origin: 'task',
38
40
  file: {
39
- oldPath: TODO_FILE,
40
- newPath: TODO_FILE,
41
+ oldPath: TASKS_FILE,
42
+ newPath: TASKS_FILE,
41
43
  isNew: false,
42
44
  isDeleted: false,
43
45
  isBinary: false,
@@ -45,7 +47,7 @@ const makeTodoItem = () => ({
45
47
  hunks: [],
46
48
  },
47
49
  hunk: null,
48
- blockId: 'todo',
50
+ blockId: 'task',
49
51
  patchAdd: '',
50
52
  patchRevert: '',
51
53
  });
@@ -73,23 +75,47 @@ const lineDelta = ({ hunk, blockId }) => {
73
75
  return delta;
74
76
  };
75
77
 
76
- const countAgo = (n, unit) => {
77
- const label = n === 1 ? unit : `${unit}s`;
78
- return `${n} ${label} ago`;
78
+ const AGE_UNIT = {
79
+ second: 's',
80
+ minute: 'm',
81
+ hour: 'h',
82
+ day: 'd',
83
+ week: 'w',
84
+ month: 'mo',
85
+ year: 'y',
86
+ };
87
+
88
+ const AGE_AGO = /^(\d+)\s+(second|minute|hour|day|week|month|year)s?\s+ago$/i;
89
+ const AGE_SHORT = /^(\d+)(mo|[smhdwy])(?: ago)?$/i;
90
+
91
+ const shortUnit = (n, unit) => `${n}${AGE_UNIT[unit]} ago`;
92
+
93
+ const shortAge = (text) => {
94
+ const raw = `${text ?? ''}`.trim();
95
+ if (!raw) return '';
96
+ if (/^yesterday$/i.test(raw)) return '1d ago';
97
+ const match = AGE_AGO.exec(raw);
98
+ if (match) {
99
+ const unit = AGE_UNIT[match[2].toLowerCase()];
100
+ if (unit) return `${match[1]}${unit} ago`;
101
+ }
102
+ const brief = AGE_SHORT.exec(raw);
103
+ if (brief) return `${brief[1]}${brief[2].toLowerCase()} ago`;
104
+ return raw;
79
105
  };
80
106
 
81
107
  const relativeAge = (thenMs, nowMs = Date.now()) => {
82
108
  const diff = Math.max(0, Math.round((nowMs - thenMs) / 1000));
83
- if (diff < 90) return countAgo(diff, 'second');
109
+ if (diff < 90) return shortUnit(diff, 'second');
84
110
  const minutes = Math.round(diff / 60);
85
- if (minutes < 90) return countAgo(minutes, 'minute');
111
+ if (minutes < 90) return shortUnit(minutes, 'minute');
86
112
  const hours = Math.round(minutes / 60);
87
- if (hours < 36) return countAgo(hours, 'hour');
113
+ if (hours < 36) return shortUnit(hours, 'hour');
88
114
  const days = Math.round(hours / 24);
89
- if (days < 14) return countAgo(days, 'day');
90
- if (days < 70) return countAgo(Math.round(days / 7), 'week');
91
- if (days < 365) return countAgo(Math.round(days / 30), 'month');
92
- return countAgo(Math.round(days / 365), 'year');
115
+ if (days < 14) return shortUnit(days, 'day');
116
+ if (days < 70) return shortUnit(Math.round(days / 7), 'week');
117
+ if (days < 365) return shortUnit(Math.round(days / 30), 'month');
118
+ return shortUnit(Math.round(days / 365), 'year');
93
119
  };
94
120
 
95
121
  const addLineDelta = (group, origin, delta) => {
@@ -135,7 +161,7 @@ const fileEntries = (items) => {
135
161
  const groups = new Map();
136
162
  for (let i = 0; i < items.length; i++) {
137
163
  const item = items[i];
138
- if (isTodoItem(item)) continue;
164
+ if (isTaskItem(item)) continue;
139
165
  const rel = listPath(item);
140
166
  if (!rel) continue;
141
167
  if (!groups.has(rel)) {
@@ -192,7 +218,7 @@ const fileTotals = (files) => {
192
218
  unstaged: 0,
193
219
  };
194
220
  for (const entry of files) {
195
- if (isTodosEntry(entry) || isTotalEntry(entry)) continue;
221
+ if (isTasksEntry(entry) || isTotalEntry(entry)) continue;
196
222
  const remaining = entry.remaining ?? 0;
197
223
  const staged = entry.staged ?? 0;
198
224
  const added = entry.added ?? 0;
@@ -216,19 +242,20 @@ const fileTotals = (files) => {
216
242
 
217
243
  module.exports = {
218
244
  REVIEW_DIR,
219
- TODO_FILE,
220
- REPO_TODOS_LABEL,
245
+ TASKS_FILE,
246
+ REPO_TASKS_LABEL,
221
247
  TOTAL_LABEL,
222
248
  isPathInScope,
223
249
  itemPath,
224
250
  listPath,
225
- isTodoItem,
251
+ isTaskItem,
226
252
  isReadOnlyOrigin,
227
- isTodosEntry,
253
+ isTasksEntry,
228
254
  isTotalEntry,
229
- makeTodoItem,
255
+ makeTaskItem,
230
256
  fileStatus,
231
257
  fileEntries,
232
258
  fileTotals,
233
259
  relativeAge,
260
+ shortAge,
234
261
  };