reslop 0.3.0 → 0.3.2

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 (74) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/README.md +21 -3
  3. package/lib/agents/acp.js +309 -0
  4. package/lib/agents/agents.js +166 -370
  5. package/lib/agents/allow.js +141 -52
  6. package/lib/agents/allow.json +16 -13
  7. package/lib/agents/models.js +250 -376
  8. package/lib/agents/registry.js +437 -0
  9. package/lib/agents/run.js +234 -0
  10. package/lib/agents/transcript.js +143 -0
  11. package/lib/cli.js +2 -1
  12. package/lib/common/process.js +26 -1
  13. package/lib/dashboard/git.js +11 -1
  14. package/lib/dashboard/reports.js +80 -0
  15. package/lib/dashboard/watch.js +43 -8
  16. package/lib/dashboard/workspace.js +19 -20
  17. package/lib/dashboard/worktrees.js +145 -0
  18. package/lib/git/deps.js +29 -0
  19. package/lib/git/dir.js +50 -0
  20. package/lib/git/git.js +1 -1
  21. package/lib/git/linked.js +152 -0
  22. package/lib/git/local.js +179 -0
  23. package/lib/git/status.js +126 -0
  24. package/lib/git/worktree.js +137 -31
  25. package/lib/input/actions.js +83 -2
  26. package/lib/input/editor.js +9 -0
  27. package/lib/input/keys.js +39 -3
  28. package/lib/input/line-edit.js +8 -3
  29. package/lib/render/agents.js +64 -36
  30. package/lib/render/branches.js +55 -27
  31. package/lib/render/field.js +19 -5
  32. package/lib/render/footer.js +14 -4
  33. package/lib/render/frame.js +16 -2
  34. package/lib/render/log.js +5 -4
  35. package/lib/render/menu.js +103 -6
  36. package/lib/render/name-edit.js +31 -0
  37. package/lib/render/repos.js +52 -8
  38. package/lib/render/status.js +22 -4
  39. package/lib/report/render.js +4 -7
  40. package/lib/report/run.js +16 -23
  41. package/lib/review/agent-instructions.md +13 -8
  42. package/lib/review/format.js +0 -6
  43. package/lib/runs/process.js +3 -3
  44. package/lib/runs/runs.js +27 -4
  45. package/lib/session/agents/agents.js +227 -194
  46. package/lib/session/agents/away.js +48 -0
  47. package/lib/session/agents/job.js +23 -86
  48. package/lib/session/agents/jobs.js +14 -27
  49. package/lib/session/agents/options.js +45 -101
  50. package/lib/session/agents/plans.js +53 -0
  51. package/lib/session/agents/view.js +34 -0
  52. package/lib/session/branches.js +18 -2
  53. package/lib/session/compose.js +20 -6
  54. package/lib/session/dispatch.js +43 -3
  55. package/lib/session/editor-pointer.js +98 -0
  56. package/lib/session/files-pane.js +3 -0
  57. package/lib/session/import.js +12 -1
  58. package/lib/session/lifecycle.js +70 -10
  59. package/lib/session/navigation.js +5 -0
  60. package/lib/session/npm/history.js +3 -1
  61. package/lib/session/npm/npm.js +111 -13
  62. package/lib/session/ops.js +3 -0
  63. package/lib/session/pointer.js +304 -46
  64. package/lib/session/session.js +17 -2
  65. package/lib/session/tasks.js +1 -1
  66. package/lib/session/terminal.js +2 -2
  67. package/lib/session/text-menu.js +50 -0
  68. package/lib/session/view.js +19 -2
  69. package/lib/session/workspace.js +179 -38
  70. package/lib/session/worktrees.js +280 -0
  71. package/lib/term/select.js +2 -1
  72. package/lib/term/themes/dark.json +4 -0
  73. package/lib/term/themes/light.json +4 -0
  74. package/package.json +2 -2
package/lib/git/dir.js ADDED
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ const GITDIR_PREFIX = 'gitdir:';
7
+
8
+ const statOf = (file) => {
9
+ try {
10
+ return fs.statSync(file);
11
+ } catch {
12
+ return null;
13
+ }
14
+ };
15
+
16
+ const readFirstLine = (file) => {
17
+ try {
18
+ return fs.readFileSync(file, 'utf8').split('\n')[0] ?? '';
19
+ } catch {
20
+ return '';
21
+ }
22
+ };
23
+
24
+ // A linked worktree keeps `.git` as a file that points into the main one
25
+ const gitDirOf = (dot) => {
26
+ const line = readFirstLine(dot).trim();
27
+ if (!line.startsWith(GITDIR_PREFIX)) return '';
28
+ const raw = line.slice(GITDIR_PREFIX.length).trim();
29
+ if (!raw) return '';
30
+ return path.resolve(path.dirname(dot), raw);
31
+ };
32
+
33
+ const commonGitDir = (gitDir) => {
34
+ const raw = readFirstLine(path.join(gitDir, 'commondir')).trim();
35
+ if (!raw) return gitDir;
36
+ return path.resolve(gitDir, raw);
37
+ };
38
+
39
+ // The directory with HEAD and the index, and the one with refs and objects
40
+ const resolveGitDirs = (root) => {
41
+ const dot = path.join(path.resolve(root), '.git');
42
+ const stat = statOf(dot);
43
+ if (!stat) return null;
44
+ if (stat.isDirectory()) return { gitDir: dot, commonDir: dot, linked: false };
45
+ const gitDir = stat.isFile() ? gitDirOf(dot) : '';
46
+ if (!gitDir) return null;
47
+ return { gitDir, commonDir: commonGitDir(gitDir), linked: true };
48
+ };
49
+
50
+ module.exports = { resolveGitDirs };
package/lib/git/git.js CHANGED
@@ -27,7 +27,7 @@ const REPO_METHODS = {
27
27
  revertFile: gitDeps.revertFile,
28
28
  edit: worktree.editItem,
29
29
  writeFile: worktree.writeFile,
30
- stagePath: worktree.stagePath,
30
+ stagePath: gitDeps.stagePath,
31
31
  commit: branches.commitChanges,
32
32
  updateCommit: branches.updateCommit,
33
33
  hasStaged: branches.hasStaged,
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ const { oneLine } = require('../common/utilities.js');
7
+ const { REVIEW_DIR, LOG_DIR } = require('../common/files.js');
8
+ const { runGit, requireOk } = require('./worktree.js');
9
+
10
+ const HEADS = 'refs/heads/';
11
+
12
+ const FIELDS = {
13
+ worktree: (entry, value) => ({ path: path.resolve(value) }),
14
+ HEAD: (entry, value) => ({ head: value }),
15
+ branch: (entry, value) => ({ branch: value.replace(HEADS, '') }),
16
+ detached: () => ({ detached: true }),
17
+ bare: () => ({ bare: true }),
18
+ prunable: () => ({ prunable: true }),
19
+ locked: (entry, value) => ({ locked: true, lockReason: value }),
20
+ };
21
+
22
+ const parseEntry = (block) => {
23
+ let entry = {
24
+ path: '',
25
+ head: '',
26
+ branch: '',
27
+ detached: false,
28
+ bare: false,
29
+ locked: false,
30
+ lockReason: '',
31
+ prunable: false,
32
+ };
33
+ for (const line of block.split('\n')) {
34
+ const space = line.indexOf(' ');
35
+ const key = space < 0 ? line : line.slice(0, space);
36
+ const value = space < 0 ? '' : line.slice(space + 1).trim();
37
+ const field = Object.hasOwn(FIELDS, key) ? FIELDS[key] : null;
38
+ if (field) entry = { ...entry, ...field(entry, value) };
39
+ }
40
+ return entry;
41
+ };
42
+
43
+ // The first entry is the main worktree, the others are linked to it
44
+ const parseWorktrees = (text) =>
45
+ `${text ?? ''}`
46
+ .split(/\r?\n\r?\n/)
47
+ .filter((block) => block.trim())
48
+ .map(parseEntry)
49
+ .filter((entry) => entry.path)
50
+ .map((entry, index) => ({ ...entry, main: index === 0 }));
51
+
52
+ const listWorktrees = async (dir) => {
53
+ const args = ['worktree', 'list', '--porcelain'];
54
+ const result = await runGit(args, dir);
55
+ if (result.status !== 0) return [];
56
+ return parseWorktrees(result.stdout);
57
+ };
58
+
59
+ // Git reports real paths, the others can go through links
60
+ const dirKey = (dir) => {
61
+ try {
62
+ return fs.realpathSync.native(dir);
63
+ } catch {
64
+ return path.resolve(dir);
65
+ }
66
+ };
67
+
68
+ const sameDir = (left, right) => dirKey(left) === dirKey(right);
69
+
70
+ const findWorktree = (list, dir) => list.find((it) => sameDir(it.path, dir));
71
+
72
+ const isOwnFile = (rel) => {
73
+ const head = rel.split(/[/\\]/)[0];
74
+ return head === REVIEW_DIR || head === LOG_DIR;
75
+ };
76
+
77
+ // The plan and the logs of reslop are not the work of the user
78
+ const changedFiles = async (dir) => {
79
+ const args = ['status', '--porcelain', '-z', '--untracked-files=all'];
80
+ const result = await runGit([...args, '--no-renames'], dir);
81
+ requireOk(result, 'git status failed');
82
+ const names = result.stdout.split('\0').filter(Boolean);
83
+ return names.map((line) => line.slice(3)).filter((rel) => !isOwnFile(rel));
84
+ };
85
+
86
+ const isDirty = async (dir) => (await changedFiles(dir)).length > 0;
87
+
88
+ const branchExists = async (top, branch) => {
89
+ const args = ['show-ref', '--verify', '--quiet', `${HEADS}${branch}`];
90
+ return (await runGit(args, top)).status === 0;
91
+ };
92
+
93
+ const branchError = (branch) => {
94
+ const name = `${branch ?? ''}`.trim();
95
+ if (!name) return 'empty branch name';
96
+ if (name.startsWith('-')) return `bad branch name ${name}`;
97
+ return '';
98
+ };
99
+
100
+ // Git tells what happened after a line of progress: the last complaint
101
+ const requireDone = (result, fallback) => {
102
+ if (result.status === 0) return result;
103
+ const text = `${result.stderr}\n${result.stdout}`;
104
+ const lines = text.split(/\r?\n/).map((line) => line.trim());
105
+ const filled = lines.filter(Boolean);
106
+ const named = filled.filter((line) => /^(fatal|error):/.test(line));
107
+ const line = named.at(-1) ?? filled.at(-1) ?? fallback;
108
+ throw new Error(oneLine(line.replace(/^(fatal|error):\s*/, ''), fallback));
109
+ };
110
+
111
+ // Git itself refuses a branch that is checked out in another worktree
112
+ const addWorktree = async (top, target, branch) => {
113
+ const failed = branchError(branch);
114
+ if (failed) throw new Error(failed);
115
+ const name = branch.trim();
116
+ const dir = path.resolve(top, target);
117
+ const exists = await branchExists(top, name);
118
+ const args = exists
119
+ ? ['worktree', 'add', '--', dir, name]
120
+ : ['worktree', 'add', '-b', name, '--', dir];
121
+ requireDone(await runGit(args, top), 'git worktree add failed');
122
+ return dir;
123
+ };
124
+
125
+ // Never forced: uncommitted work or a lock keeps the worktree
126
+ const removeWorktree = async (top, target) => {
127
+ const list = await listWorktrees(top);
128
+ const found = findWorktree(list, target);
129
+ if (!found) throw new Error('not a worktree of this repository');
130
+ if (found.main) throw new Error('the main worktree cannot be removed');
131
+ if (found.locked) {
132
+ const why = found.lockReason ? `: ${found.lockReason}` : '';
133
+ throw new Error(oneLine(`worktree is locked${why}`, 'worktree is locked'));
134
+ }
135
+ if (await isDirty(found.path)) {
136
+ throw new Error('worktree has uncommitted changes');
137
+ }
138
+ const args = ['worktree', 'remove', '--', found.path];
139
+ requireDone(await runGit(args, top), 'git worktree remove failed');
140
+ };
141
+
142
+ module.exports = {
143
+ parseWorktrees,
144
+ listWorktrees,
145
+ findWorktree,
146
+ dirKey,
147
+ sameDir,
148
+ changedFiles,
149
+ isDirty,
150
+ addWorktree,
151
+ removeWorktree,
152
+ };
@@ -0,0 +1,179 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const zlib = require('node:zlib');
6
+
7
+ const { resolveGitDirs } = require('./dir.js');
8
+
9
+ const HEAD_REF = 'ref: refs/heads/';
10
+ const SHA = /^[0-9a-f]{40}$/i;
11
+
12
+ const readText = (file) => {
13
+ try {
14
+ return fs.readFileSync(file, 'utf8');
15
+ } catch {
16
+ return '';
17
+ }
18
+ };
19
+
20
+ const readFirstLine = (file) => readText(file).split('\n')[0] ?? '';
21
+
22
+ const isWorktreeRoot = (dir) => {
23
+ const dirs = resolveGitDirs(dir);
24
+ if (!dirs) return false;
25
+ try {
26
+ const head = path.join(dirs.gitDir, 'HEAD');
27
+ const objects = path.join(dirs.commonDir, 'objects');
28
+ const refs = path.join(dirs.commonDir, 'refs');
29
+ return (
30
+ fs.statSync(head).isFile() &&
31
+ fs.statSync(objects).isDirectory() &&
32
+ fs.statSync(refs).isDirectory()
33
+ );
34
+ } catch {
35
+ return false;
36
+ }
37
+ };
38
+
39
+ // The worktree that contains `start`, without asking git. A linked checkout
40
+ // is the directory whose `.git` file points at the common repository.
41
+ const worktreeRoot = (start) => {
42
+ let dir = path.resolve(start);
43
+ let stat;
44
+ try {
45
+ stat = fs.statSync(dir);
46
+ } catch {
47
+ return '';
48
+ }
49
+ if (!stat.isDirectory()) dir = path.dirname(dir);
50
+ for (;;) {
51
+ if (isWorktreeRoot(dir)) return dir;
52
+ const parent = path.dirname(dir);
53
+ if (parent === dir) return '';
54
+ dir = parent;
55
+ }
56
+ };
57
+
58
+ const headLine = (top) => {
59
+ const dirs = resolveGitDirs(top);
60
+ if (!dirs) return '';
61
+ return readFirstLine(path.join(dirs.gitDir, 'HEAD')).trim();
62
+ };
63
+
64
+ // Empty when HEAD is detached or the checkout has no branch yet
65
+ const branchOf = (top) => {
66
+ const line = headLine(top);
67
+ if (!line.startsWith(HEAD_REF)) return '';
68
+ return line.slice(HEAD_REF.length);
69
+ };
70
+
71
+ const packedSha = (commonDir, ref) => {
72
+ const text = readText(path.join(commonDir, 'packed-refs'));
73
+ for (const line of text.split('\n')) {
74
+ if (!line || line.startsWith('#') || line.startsWith('^')) continue;
75
+ const space = line.indexOf(' ');
76
+ if (space < 0) continue;
77
+ if (line.slice(space + 1).trim() === ref) return line.slice(0, space);
78
+ }
79
+ return '';
80
+ };
81
+
82
+ const refSha = (commonDir, ref) => {
83
+ const direct = readFirstLine(path.join(commonDir, ref)).trim();
84
+ if (SHA.test(direct)) return direct;
85
+ return packedSha(commonDir, ref);
86
+ };
87
+
88
+ const headSha = (dirs) => {
89
+ const line = readFirstLine(path.join(dirs.gitDir, 'HEAD')).trim();
90
+ if (SHA.test(line)) return line;
91
+ if (!line.startsWith('ref: ')) return '';
92
+ return refSha(dirs.commonDir, line.slice('ref: '.length).trim());
93
+ };
94
+
95
+ const looseCommit = (commonDir, sha) => {
96
+ if (!SHA.test(sha)) return null;
97
+ const file = path.join(commonDir, 'objects', sha.slice(0, 2), sha.slice(2));
98
+ let raw;
99
+ try {
100
+ raw = zlib.inflateSync(fs.readFileSync(file));
101
+ } catch {
102
+ return null;
103
+ }
104
+ const text = raw.toString('utf8');
105
+ const nul = text.indexOf('\0');
106
+ if (nul < 0 || !text.slice(0, nul).startsWith('commit ')) return null;
107
+ return text.slice(nul + 1);
108
+ };
109
+
110
+ const parentsOf = (body) => {
111
+ const parents = [];
112
+ for (const line of body.split('\n')) {
113
+ if (line.startsWith('parent ')) parents.push(line.slice('parent '.length));
114
+ else if (line === '') break;
115
+ }
116
+ return parents;
117
+ };
118
+
119
+ // Null when an object lives in a pack: the caller asks git then
120
+ const countLoose = (commonDir, sha) => {
121
+ const seen = new Set();
122
+ const queue = [sha];
123
+ while (queue.length) {
124
+ const id = queue.pop();
125
+ if (!id || seen.has(id)) continue;
126
+ const body = looseCommit(commonDir, id);
127
+ if (body === null) return null;
128
+ seen.add(id);
129
+ for (const parent of parentsOf(body)) queue.push(parent);
130
+ }
131
+ return seen.size;
132
+ };
133
+
134
+ // '' when the history is not a set of loose objects
135
+ const commitCount = (top) => {
136
+ const dirs = resolveGitDirs(top);
137
+ if (!dirs) return '';
138
+ const sha = headSha(dirs);
139
+ if (!sha) return '0';
140
+ const count = countLoose(dirs.commonDir, sha);
141
+ if (count === null) return '';
142
+ return `${count}`;
143
+ };
144
+
145
+ const originUrl = (text) => {
146
+ let section = '';
147
+ for (const line of text.split('\n')) {
148
+ const trimmed = line.trim();
149
+ if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
150
+ section = trimmed.slice(1, -1).trim();
151
+ continue;
152
+ }
153
+ if (section !== 'remote "origin"') continue;
154
+ const eq = trimmed.indexOf('=');
155
+ if (eq < 0) continue;
156
+ if (trimmed.slice(0, eq).trim() !== 'url') continue;
157
+ return trimmed.slice(eq + 1).trim();
158
+ }
159
+ return '';
160
+ };
161
+
162
+ const remoteOrigin = (top) => {
163
+ const dirs = resolveGitDirs(top);
164
+ if (!dirs) return '';
165
+ return originUrl(readText(path.join(dirs.commonDir, 'config')));
166
+ };
167
+
168
+ const gitDirPath = (top) => {
169
+ const dirs = resolveGitDirs(top);
170
+ return dirs ? dirs.gitDir : '';
171
+ };
172
+
173
+ module.exports = {
174
+ worktreeRoot,
175
+ branchOf,
176
+ commitCount,
177
+ remoteOrigin,
178
+ gitDirPath,
179
+ };
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ const STAGED_TITLE = 'Changes to be committed:\n';
4
+ const WORK_TITLE = 'Changes not staged for commit:\n';
5
+ const UNTRACKED_TITLE = 'Untracked files:\n';
6
+ const PREFIX = {
7
+ staged: { left: 'c/', right: 'i/' },
8
+ work: { left: 'i/', right: 'w/' },
9
+ };
10
+
11
+ const DIFF_HEADS = [
12
+ 'diff --git ',
13
+ 'index ',
14
+ '@@ ',
15
+ 'new file ',
16
+ 'deleted file ',
17
+ 'old mode ',
18
+ 'new mode ',
19
+ 'similarity ',
20
+ 'rename ',
21
+ 'Binary files ',
22
+ ];
23
+
24
+ const isDiffHead = (line) => DIFF_HEADS.some((head) => line.startsWith(head));
25
+
26
+ const isDiffLine = (line) => {
27
+ if (/^-{10,}$/.test(line)) return false;
28
+ if (line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
29
+ return true;
30
+ }
31
+ if (line.startsWith('\\')) return true;
32
+ return isDiffHead(line);
33
+ };
34
+
35
+ const sectionDiff = (text, title) => {
36
+ const at = text.lastIndexOf(title);
37
+ if (at < 0) return '';
38
+ const rest = text.slice(at + title.length);
39
+ if (!rest.startsWith('diff --git ')) return '';
40
+ const lines = [];
41
+ for (const line of rest.split('\n')) {
42
+ if (lines.length && !isDiffLine(line)) break;
43
+ lines.push(line);
44
+ }
45
+ return lines.join('\n').replace(/\s+$/, '');
46
+ };
47
+
48
+ const untrackedOf = (text) => {
49
+ const at = text.indexOf(UNTRACKED_TITLE);
50
+ if (at < 0) return [];
51
+ const rest = text.slice(at + UNTRACKED_TITLE.length);
52
+ const stop = rest.indexOf('\ndiff --git ');
53
+ const block = stop < 0 ? rest : rest.slice(0, stop);
54
+ const names = [];
55
+ for (const line of block.split('\n')) {
56
+ if (line.startsWith('\t')) names.push(line.slice(1));
57
+ }
58
+ return names;
59
+ };
60
+
61
+ const rewritePath = (value, from, to) => {
62
+ if (value === '/dev/null') return value;
63
+ if (!value.startsWith(from)) return '';
64
+ return `${to}${value.slice(from.length)}`;
65
+ };
66
+
67
+ const rewriteGit = (line, left, right) => {
68
+ const rest = line.slice('diff --git '.length);
69
+ const mid = ` ${right}`;
70
+ const at = rest.indexOf(mid);
71
+ if (!rest.startsWith(left) || at < 0) return '';
72
+ const oldPath = rest.slice(left.length, at);
73
+ const newPath = rest.slice(at + mid.length);
74
+ return `diff --git a/${oldPath} b/${newPath}`;
75
+ };
76
+
77
+ const rewriteBinary = (line, left, right) => {
78
+ const match = /^Binary files (.+) and (.+) differ$/.exec(line);
79
+ if (!match) return line;
80
+ const oldPath = rewritePath(match[1], left, 'a/');
81
+ const newPath = rewritePath(match[2], right, 'b/');
82
+ if (!oldPath || !newPath) return '';
83
+ return `Binary files ${oldPath} and ${newPath} differ`;
84
+ };
85
+
86
+ const rewriteMarker = (line, mark, from, to) => {
87
+ if (!line.startsWith(mark)) return line;
88
+ const value = line.slice(mark.length);
89
+ if (value === '/dev/null') return line;
90
+ const next = rewritePath(value, from, to);
91
+ if (!next) return '';
92
+ return `${mark}${next}`;
93
+ };
94
+
95
+ const rewritten = (line, left, right) => {
96
+ if (line.startsWith('diff --git ')) return rewriteGit(line, left, right);
97
+ if (line.startsWith('Binary files ')) return rewriteBinary(line, left, right);
98
+ if (line.startsWith('--- ')) return rewriteMarker(line, '--- ', left, 'a/');
99
+ if (line.startsWith('+++ ')) return rewriteMarker(line, '+++ ', right, 'b/');
100
+ return line;
101
+ };
102
+
103
+ // git status -vv labels the sides c/ (HEAD), i/ (index) and w/ (worktree).
104
+ // The diff parser wants the a/ and b/ labels of git diff.
105
+ const rewriteDiff = (text, side) => {
106
+ if (!text) return '';
107
+ const { left, right } = PREFIX[side];
108
+ const lines = [];
109
+ for (const line of text.split('\n')) {
110
+ const next = rewritten(line, left, right);
111
+ if (!next) return null;
112
+ lines.push(next);
113
+ }
114
+ return `${lines.join('\n')}\n`;
115
+ };
116
+
117
+ // Null when this git prints diffs in a shape we do not rewrite
118
+ const parseStatus = (text) => {
119
+ const raw = `${text ?? ''}`.replaceAll('\r\n', '\n');
120
+ const staged = rewriteDiff(sectionDiff(raw, STAGED_TITLE), 'staged');
121
+ const work = rewriteDiff(sectionDiff(raw, WORK_TITLE), 'work');
122
+ if (staged === null || work === null) return null;
123
+ return { staged, work, untracked: untrackedOf(raw) };
124
+ };
125
+
126
+ module.exports = { parseStatus };