getgloss 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -10
- package/dist/cli/index.js +302 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/server/daemon.js +110 -45
- package/dist/server/daemon.js.map +1 -1
- package/dist/web/assets/index-DuGSsf8O.js +181 -0
- package/dist/web/assets/index-SRKfUpIg.css +1 -0
- package/dist/web/index.html +2 -2
- package/dist/web/logo-mark.svg +12 -0
- package/dist/web/prompt.md +2 -2
- package/dist/web/setup/index.html +18 -8
- package/dist/web/setup.md +21 -5
- package/package.json +1 -1
- package/skill/SKILL.md +15 -10
- package/dist/web/assets/index-BQXNNRgM.css +0 -1
- package/dist/web/assets/index-xfsP7fu_.js +0 -186
- package/dist/web/skill/SKILL.md +0 -22
package/README.md
CHANGED
|
@@ -6,11 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
Gloss is a local browser review loop for coding agents. It captures your current
|
|
8
8
|
git diff, opens a localhost review UI, lets you attach comments to changed
|
|
9
|
-
lines or ranges, and writes structured feedback
|
|
10
|
-
|
|
9
|
+
lines or ranges, and writes structured feedback under `~/.gloss` for an agent
|
|
10
|
+
to re-ingest.
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
+
```bash
|
|
15
|
+
brew install iamrajjoshi/tap/gloss
|
|
16
|
+
gloss open --json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
With npm:
|
|
20
|
+
|
|
14
21
|
```bash
|
|
15
22
|
npm install -g getgloss
|
|
16
23
|
gloss open --json
|
|
@@ -25,13 +32,13 @@ npx getgloss open --json
|
|
|
25
32
|
For a new agent chat, use:
|
|
26
33
|
|
|
27
34
|
```text
|
|
28
|
-
Install Gloss with npm. Then read https://getgloss.dev/setup.md.
|
|
35
|
+
Install Gloss with Homebrew or npm. Then read https://getgloss.dev/setup.md.
|
|
29
36
|
```
|
|
30
37
|
|
|
31
38
|
### Claude Code Skill
|
|
32
39
|
|
|
33
|
-
Gloss ships a Claude Code skill at `skill/SKILL.md`. Install it with
|
|
34
|
-
[`skills` CLI](https://github.com/vercel-labs/agent-skills):
|
|
40
|
+
Gloss ships a packaged Claude Code skill at `skill/SKILL.md`. Install it with
|
|
41
|
+
the [`skills` CLI](https://github.com/vercel-labs/agent-skills):
|
|
35
42
|
|
|
36
43
|
```bash
|
|
37
44
|
# Global (available across all projects)
|
|
@@ -42,9 +49,12 @@ npx skills add iamrajjoshi/gloss --skill gloss -a claude-code
|
|
|
42
49
|
```
|
|
43
50
|
|
|
44
51
|
`-g` installs to `~/.claude/skills/`, `-a claude-code` targets Claude Code, and
|
|
45
|
-
`--skill gloss` installs only the Gloss skill from the repo.
|
|
52
|
+
`--skill gloss` installs only the Gloss skill folder from the repo. The skill
|
|
53
|
+
teaches agents to run `gloss open --json`, wait for browser submission, read
|
|
54
|
+
`feedbackPath`, apply comments, validate, and mark the review resolved when MCP
|
|
55
|
+
tools are available.
|
|
46
56
|
|
|
47
|
-
The hosted install script
|
|
57
|
+
The hosted install script remains npm-only:
|
|
48
58
|
|
|
49
59
|
```bash
|
|
50
60
|
curl -fsSL https://getgloss.dev/install.sh | sh
|
|
@@ -78,16 +88,17 @@ keeps the old behavior and does not fall back to a branch diff.
|
|
|
78
88
|
Completed reviews are written to:
|
|
79
89
|
|
|
80
90
|
```text
|
|
81
|
-
|
|
91
|
+
~/.gloss/reviews/<reviewId>/
|
|
82
92
|
meta.json
|
|
83
93
|
diff.json
|
|
84
94
|
feedback.json
|
|
85
95
|
feedback.md
|
|
86
|
-
|
|
96
|
+
resolved.json
|
|
87
97
|
```
|
|
88
98
|
|
|
89
99
|
`feedback.json` is the machine-readable payload. `feedback.md` is a readable
|
|
90
|
-
summary ordered by file and line.
|
|
100
|
+
summary ordered by file and line. Set `GLOSS_STATE_DIR` to use an isolated
|
|
101
|
+
state root for tests or development.
|
|
91
102
|
|
|
92
103
|
## MCP
|
|
93
104
|
|
|
@@ -122,10 +133,12 @@ Releases follow Willow's tag-driven shape:
|
|
|
122
133
|
2. GitHub Actions runs checks, tests, and the production build.
|
|
123
134
|
3. The package is published to npm as `getgloss`.
|
|
124
135
|
4. A GitHub release is created with `npm pack` output and checksums.
|
|
136
|
+
5. The Homebrew formula is updated in `iamrajjoshi/homebrew-tap`.
|
|
125
137
|
|
|
126
138
|
Required repository secrets:
|
|
127
139
|
|
|
128
140
|
- `NPM_TOKEN`
|
|
141
|
+
- `HOMEBREW_TAP_GITHUB_TOKEN`
|
|
129
142
|
|
|
130
143
|
## Attribution
|
|
131
144
|
|
package/dist/cli/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import path from "path";
|
|
|
24
24
|
// package.json
|
|
25
25
|
var package_default = {
|
|
26
26
|
name: "getgloss",
|
|
27
|
-
version: "0.
|
|
27
|
+
version: "0.3.0",
|
|
28
28
|
description: "Local browser-based diff review for coding-agent loops.",
|
|
29
29
|
type: "module",
|
|
30
30
|
packageManager: "pnpm@10.33.2",
|
|
@@ -127,6 +127,27 @@ function globalLogDir() {
|
|
|
127
127
|
function globalServerLogFile() {
|
|
128
128
|
return path.join(globalLogDir(), "server.log");
|
|
129
129
|
}
|
|
130
|
+
function globalReviewsDir() {
|
|
131
|
+
return path.join(globalStateDir(), "reviews");
|
|
132
|
+
}
|
|
133
|
+
function globalReviewDir(reviewId) {
|
|
134
|
+
return path.join(globalReviewsDir(), reviewId);
|
|
135
|
+
}
|
|
136
|
+
function globalReviewMetaFile(reviewId) {
|
|
137
|
+
return path.join(globalReviewDir(reviewId), "meta.json");
|
|
138
|
+
}
|
|
139
|
+
function globalReviewDiffFile(reviewId) {
|
|
140
|
+
return path.join(globalReviewDir(reviewId), "diff.json");
|
|
141
|
+
}
|
|
142
|
+
function globalReviewFeedbackFile(reviewId) {
|
|
143
|
+
return path.join(globalReviewDir(reviewId), "feedback.json");
|
|
144
|
+
}
|
|
145
|
+
function globalReviewMarkdownFile(reviewId) {
|
|
146
|
+
return path.join(globalReviewDir(reviewId), "feedback.md");
|
|
147
|
+
}
|
|
148
|
+
function globalReviewResolvedFile(reviewId) {
|
|
149
|
+
return path.join(globalReviewDir(reviewId), "resolved.json");
|
|
150
|
+
}
|
|
130
151
|
async function ensureDir(dir) {
|
|
131
152
|
await mkdir(dir, { recursive: true });
|
|
132
153
|
}
|
|
@@ -717,6 +738,275 @@ async function assertGitAvailable() {
|
|
|
717
738
|
await execa("git", ["--version"]);
|
|
718
739
|
}
|
|
719
740
|
|
|
741
|
+
// src/server/store.ts
|
|
742
|
+
import { readdir, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
743
|
+
import { ulid } from "ulid";
|
|
744
|
+
|
|
745
|
+
// src/shared/markdown.ts
|
|
746
|
+
function formatLineRange(comment) {
|
|
747
|
+
const prefix = comment.side;
|
|
748
|
+
if (comment.startLine === comment.endLine) {
|
|
749
|
+
return `${prefix}${comment.startLine}`;
|
|
750
|
+
}
|
|
751
|
+
return `${prefix}${comment.startLine}-${prefix}${comment.endLine}`;
|
|
752
|
+
}
|
|
753
|
+
function fenceFor(snippet) {
|
|
754
|
+
let fence = "```";
|
|
755
|
+
while (snippet.includes(fence)) {
|
|
756
|
+
fence += "`";
|
|
757
|
+
}
|
|
758
|
+
return fence;
|
|
759
|
+
}
|
|
760
|
+
function languageForPath2(filePath) {
|
|
761
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
762
|
+
const map = {
|
|
763
|
+
cjs: "js",
|
|
764
|
+
css: "css",
|
|
765
|
+
go: "go",
|
|
766
|
+
html: "html",
|
|
767
|
+
js: "js",
|
|
768
|
+
json: "json",
|
|
769
|
+
jsx: "jsx",
|
|
770
|
+
md: "markdown",
|
|
771
|
+
mjs: "js",
|
|
772
|
+
py: "python",
|
|
773
|
+
rb: "ruby",
|
|
774
|
+
rs: "rust",
|
|
775
|
+
sh: "bash",
|
|
776
|
+
swift: "swift",
|
|
777
|
+
ts: "ts",
|
|
778
|
+
tsx: "tsx",
|
|
779
|
+
yaml: "yaml",
|
|
780
|
+
yml: "yaml"
|
|
781
|
+
};
|
|
782
|
+
return ext ? map[ext] ?? ext : "";
|
|
783
|
+
}
|
|
784
|
+
function languageForSnippet(filePath, snippet) {
|
|
785
|
+
const lines = snippet.split("\n").filter((line) => line.length > 0);
|
|
786
|
+
const looksLikeUnifiedDiff = lines.length > 0 && lines.some((line) => line.startsWith("+") || line.startsWith("-")) && lines.every((line) => line.startsWith("+") || line.startsWith("-") || line.startsWith(" "));
|
|
787
|
+
return looksLikeUnifiedDiff ? "diff" : languageForPath2(filePath);
|
|
788
|
+
}
|
|
789
|
+
function byFileThenLine(a, b) {
|
|
790
|
+
return a.filePath.localeCompare(b.filePath) || a.startLine - b.startLine || a.endLine - b.endLine || a.side.localeCompare(b.side);
|
|
791
|
+
}
|
|
792
|
+
function serializeFeedbackMarkdown(bundle) {
|
|
793
|
+
const comments = [...bundle.comments].sort(byFileThenLine);
|
|
794
|
+
const files = [...new Set(comments.map((comment) => comment.filePath))];
|
|
795
|
+
const lines = [
|
|
796
|
+
`# Gloss feedback - ${bundle.timestamp}`,
|
|
797
|
+
`Review: ${bundle.reviewId}`,
|
|
798
|
+
`Base: ${bundle.base.ref} (${bundle.base.sha.slice(0, 7)}) Branch: ${bundle.branch ?? "(detached)"}`,
|
|
799
|
+
`Files: ${files.length} Comments: ${comments.length}`,
|
|
800
|
+
""
|
|
801
|
+
];
|
|
802
|
+
for (const filePath of files) {
|
|
803
|
+
lines.push(`## ${filePath}`, "");
|
|
804
|
+
for (const comment of comments.filter((item) => item.filePath === filePath)) {
|
|
805
|
+
const snippet = comment.originalSnippet.trimEnd();
|
|
806
|
+
const firstSnippetLine = snippet.split("\n").find((line) => line.trim().length > 0);
|
|
807
|
+
const heading = comment.startLine === comment.endLine && firstSnippetLine ? `### ${formatLineRange(comment)} - \`${firstSnippetLine.trim().slice(0, 80)}\`` : `### ${formatLineRange(comment)}`;
|
|
808
|
+
lines.push(heading, comment.body.trim(), "");
|
|
809
|
+
if (snippet) {
|
|
810
|
+
const fence = fenceFor(snippet);
|
|
811
|
+
lines.push(`${fence}${languageForSnippet(comment.filePath, snippet)}`, snippet, fence, "");
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return `${lines.join("\n").trimEnd()}
|
|
816
|
+
`;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/server/store.ts
|
|
820
|
+
var ReviewStore = class {
|
|
821
|
+
reviews = /* @__PURE__ */ new Map();
|
|
822
|
+
listeners = /* @__PURE__ */ new Map();
|
|
823
|
+
async create(diff) {
|
|
824
|
+
const id = ulid();
|
|
825
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
826
|
+
const meta = {
|
|
827
|
+
id,
|
|
828
|
+
cwd: diff.cwd,
|
|
829
|
+
base: diff.base,
|
|
830
|
+
branch: diff.branch,
|
|
831
|
+
status: "pending",
|
|
832
|
+
createdAt,
|
|
833
|
+
artifactDir: globalReviewDir(id)
|
|
834
|
+
};
|
|
835
|
+
const record = { meta, diff };
|
|
836
|
+
this.reviews.set(id, record);
|
|
837
|
+
await this.persistInitial(record);
|
|
838
|
+
this.emit({ type: "review.opened", reviewId: id });
|
|
839
|
+
return record;
|
|
840
|
+
}
|
|
841
|
+
async list() {
|
|
842
|
+
await this.loadAllReviews();
|
|
843
|
+
return [...this.reviews.values()].map((record) => record.meta).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
844
|
+
}
|
|
845
|
+
async get(id) {
|
|
846
|
+
return this.reviews.get(id) ?? await this.loadKnownReview(id);
|
|
847
|
+
}
|
|
848
|
+
async submit(id, comments) {
|
|
849
|
+
const record = await this.get(id);
|
|
850
|
+
if (!record) {
|
|
851
|
+
throw new Error(`Review ${id} not found`);
|
|
852
|
+
}
|
|
853
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
854
|
+
const feedback = {
|
|
855
|
+
version: 1,
|
|
856
|
+
reviewId: id,
|
|
857
|
+
timestamp,
|
|
858
|
+
base: record.diff.base,
|
|
859
|
+
branch: record.diff.branch,
|
|
860
|
+
comments: [...comments].sort(
|
|
861
|
+
(a, b) => a.filePath.localeCompare(b.filePath) || a.startLine - b.startLine || a.endLine - b.endLine || a.side.localeCompare(b.side)
|
|
862
|
+
)
|
|
863
|
+
};
|
|
864
|
+
record.feedback = feedback;
|
|
865
|
+
record.meta = { ...record.meta, status: "completed", completedAt: timestamp };
|
|
866
|
+
this.reviews.set(id, record);
|
|
867
|
+
const artifactDir = globalReviewDir(id);
|
|
868
|
+
const feedbackPath = globalReviewFeedbackFile(id);
|
|
869
|
+
const markdownPath = globalReviewMarkdownFile(id);
|
|
870
|
+
record.meta = {
|
|
871
|
+
...record.meta,
|
|
872
|
+
artifactDir,
|
|
873
|
+
feedbackPath,
|
|
874
|
+
markdownPath
|
|
875
|
+
};
|
|
876
|
+
await ensureDir(artifactDir);
|
|
877
|
+
await Promise.all([
|
|
878
|
+
writeFile2(globalReviewMetaFile(id), `${JSON.stringify(record.meta, null, 2)}
|
|
879
|
+
`),
|
|
880
|
+
writeFile2(feedbackPath, `${JSON.stringify(feedback, null, 2)}
|
|
881
|
+
`),
|
|
882
|
+
writeFile2(markdownPath, serializeFeedbackMarkdown(feedback))
|
|
883
|
+
]);
|
|
884
|
+
this.emit({
|
|
885
|
+
type: "review.completed",
|
|
886
|
+
reviewId: id,
|
|
887
|
+
counts: {
|
|
888
|
+
files: new Set(feedback.comments.map((comment) => comment.filePath)).size,
|
|
889
|
+
comments: feedback.comments.length
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
return { record, feedbackPath, markdownPath };
|
|
893
|
+
}
|
|
894
|
+
async feedback(id) {
|
|
895
|
+
const record = await this.get(id);
|
|
896
|
+
return record?.feedback ?? null;
|
|
897
|
+
}
|
|
898
|
+
async markResolved(id, summary) {
|
|
899
|
+
const record = await this.get(id);
|
|
900
|
+
if (!record) {
|
|
901
|
+
throw new Error(`Review ${id} not found`);
|
|
902
|
+
}
|
|
903
|
+
const resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
904
|
+
const resolvedPath = globalReviewResolvedFile(id);
|
|
905
|
+
record.meta = { ...record.meta, status: "resolved", resolvedAt };
|
|
906
|
+
this.reviews.set(id, record);
|
|
907
|
+
await ensureDir(globalReviewDir(id));
|
|
908
|
+
await writeFile2(
|
|
909
|
+
resolvedPath,
|
|
910
|
+
`${JSON.stringify({ reviewId: id, summary: summary ?? null, resolvedAt }, null, 2)}
|
|
911
|
+
`
|
|
912
|
+
);
|
|
913
|
+
await writeFile2(globalReviewMetaFile(id), `${JSON.stringify(record.meta, null, 2)}
|
|
914
|
+
`);
|
|
915
|
+
return resolvedPath;
|
|
916
|
+
}
|
|
917
|
+
subscribe(reviewId, listener) {
|
|
918
|
+
const listeners = this.listeners.get(reviewId) ?? /* @__PURE__ */ new Set();
|
|
919
|
+
listeners.add(listener);
|
|
920
|
+
this.listeners.set(reviewId, listeners);
|
|
921
|
+
return () => {
|
|
922
|
+
listeners.delete(listener);
|
|
923
|
+
if (listeners.size === 0) {
|
|
924
|
+
this.listeners.delete(reviewId);
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
emit(event) {
|
|
929
|
+
for (const listener of this.listeners.get(event.reviewId) ?? []) {
|
|
930
|
+
listener(event);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
async persistInitial(record) {
|
|
934
|
+
const dir = globalReviewDir(record.meta.id);
|
|
935
|
+
await ensureDir(dir);
|
|
936
|
+
await Promise.all([
|
|
937
|
+
writeFile2(globalReviewMetaFile(record.meta.id), `${JSON.stringify(record.meta, null, 2)}
|
|
938
|
+
`),
|
|
939
|
+
writeFile2(globalReviewDiffFile(record.meta.id), `${JSON.stringify(record.diff, null, 2)}
|
|
940
|
+
`)
|
|
941
|
+
]);
|
|
942
|
+
}
|
|
943
|
+
async loadKnownReview(id) {
|
|
944
|
+
const existing = this.reviews.get(id);
|
|
945
|
+
if (existing) {
|
|
946
|
+
return existing;
|
|
947
|
+
}
|
|
948
|
+
return this.loadReview(id);
|
|
949
|
+
}
|
|
950
|
+
async loadAllReviews() {
|
|
951
|
+
let entries;
|
|
952
|
+
try {
|
|
953
|
+
entries = await readdir(globalReviewsDir(), { withFileTypes: true });
|
|
954
|
+
} catch {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
await Promise.all(
|
|
958
|
+
entries.filter((entry) => entry.isDirectory()).map((entry) => this.loadReview(entry.name))
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
async loadReview(id) {
|
|
962
|
+
try {
|
|
963
|
+
const [metaRaw, diffRaw] = await Promise.all([
|
|
964
|
+
readFile2(globalReviewMetaFile(id), "utf8"),
|
|
965
|
+
readFile2(globalReviewDiffFile(id), "utf8")
|
|
966
|
+
]);
|
|
967
|
+
const meta = JSON.parse(metaRaw);
|
|
968
|
+
const diff = JSON.parse(diffRaw);
|
|
969
|
+
let feedback;
|
|
970
|
+
try {
|
|
971
|
+
feedback = JSON.parse(
|
|
972
|
+
await readFile2(globalReviewFeedbackFile(id), "utf8")
|
|
973
|
+
);
|
|
974
|
+
} catch {
|
|
975
|
+
feedback = void 0;
|
|
976
|
+
}
|
|
977
|
+
const record = {
|
|
978
|
+
meta: {
|
|
979
|
+
...meta,
|
|
980
|
+
artifactDir: meta.artifactDir ?? globalReviewDir(id),
|
|
981
|
+
feedbackPath: meta.feedbackPath ?? (feedback ? globalReviewFeedbackFile(id) : void 0),
|
|
982
|
+
markdownPath: meta.markdownPath ?? (feedback ? globalReviewMarkdownFile(id) : void 0)
|
|
983
|
+
},
|
|
984
|
+
diff,
|
|
985
|
+
feedback
|
|
986
|
+
};
|
|
987
|
+
this.reviews.set(id, record);
|
|
988
|
+
return record;
|
|
989
|
+
} catch {
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
var reviewStore = new ReviewStore();
|
|
995
|
+
|
|
996
|
+
// src/cli/status.ts
|
|
997
|
+
async function listReviewsForStatus({
|
|
998
|
+
responsive,
|
|
999
|
+
server
|
|
1000
|
+
}) {
|
|
1001
|
+
if (server && responsive) {
|
|
1002
|
+
try {
|
|
1003
|
+
return (await new ServerClient(serverUrl(server)).listReviews()).reviews;
|
|
1004
|
+
} catch {
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return new ReviewStore().list();
|
|
1008
|
+
}
|
|
1009
|
+
|
|
720
1010
|
// src/cli/index.ts
|
|
721
1011
|
function printJson(value) {
|
|
722
1012
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
@@ -742,7 +1032,13 @@ program.command("open").description("Capture local changes and open them for rev
|
|
|
742
1032
|
await openBrowser(url);
|
|
743
1033
|
}
|
|
744
1034
|
if (options.watch === false) {
|
|
745
|
-
const result2 = {
|
|
1035
|
+
const result2 = {
|
|
1036
|
+
reviewId: meta.id,
|
|
1037
|
+
url,
|
|
1038
|
+
files: diff.files.length,
|
|
1039
|
+
scope: diff.scope.mode,
|
|
1040
|
+
artifactDir: meta.artifactDir
|
|
1041
|
+
};
|
|
746
1042
|
globals.json ? printJson(result2) : printPlain(`Review ${meta.id}: ${url}`);
|
|
747
1043
|
return;
|
|
748
1044
|
}
|
|
@@ -761,8 +1057,9 @@ program.command("open").description("Capture local changes and open them for rev
|
|
|
761
1057
|
url,
|
|
762
1058
|
files: event.counts.files,
|
|
763
1059
|
comments: event.counts.comments,
|
|
764
|
-
feedbackPath:
|
|
765
|
-
markdownPath:
|
|
1060
|
+
feedbackPath: globalReviewFeedbackFile(meta.id),
|
|
1061
|
+
markdownPath: globalReviewMarkdownFile(meta.id),
|
|
1062
|
+
artifactDir: globalReviewDir(meta.id),
|
|
766
1063
|
feedback
|
|
767
1064
|
};
|
|
768
1065
|
globals.json ? printJson(result) : printPlain(`Review ${meta.id} completed with ${event.counts.comments} comments`);
|
|
@@ -784,10 +1081,7 @@ program.command("status").description("Show server and active reviews").action(a
|
|
|
784
1081
|
const globals = program.opts();
|
|
785
1082
|
const info = await readServerInfo();
|
|
786
1083
|
const responsive = info ? await isServerResponsive(info) : false;
|
|
787
|
-
|
|
788
|
-
if (info && responsive) {
|
|
789
|
-
reviews = (await new ServerClient(serverUrl(info)).listReviews()).reviews;
|
|
790
|
-
}
|
|
1084
|
+
const reviews = await listReviewsForStatus({ responsive, server: info });
|
|
791
1085
|
const status = { running: responsive, server: info, reviews };
|
|
792
1086
|
globals.json ? printJson(status) : printPlain(
|
|
793
1087
|
responsive && info ? `Gloss server running at ${serverUrl(info)} with ${reviews.length} active review(s)` : "Gloss server is not running"
|