Official
git
Built-in git UI (JavaScript UI plugin).
The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/git@0.2.0
Permissions in 0.2.0
Files
status.test.ts4.1 KB
// Git status sections and menu/picker decisions, independent of the host.
import assert from "node:assert/strict";
import { test } from "node:test";
import type { GitEntry, GitStatus } from "convergence";
import {
branchOptions,
discardPaths,
groups,
initialState,
isOpen,
MENU,
pathsOf,
primaryAction,
runFinished,
TONE,
} from "./status.ts";
const status = (entries: GitEntry[] = []): GitStatus => ({
entries,
branch: "main",
upstream: "origin/main",
ahead: 0,
behind: 0,
isRepo: true,
});
test("all seven conflict codes occupy only the merge section", () => {
const entries = ["DD", "AU", "UD", "UA", "DU", "AA", "UU"].map((code) => ({
path: code,
staged: code[0],
unstaged: code[1],
}));
const grouped = groups(status(entries));
assert.equal(grouped.merge.length, 7);
assert.deepEqual(grouped.staged, []);
assert.deepEqual(grouped.changes, []);
assert.ok(grouped.merge.every((entry) => entry.side === "merge" && entry.letter === "!"));
});
test("staged and unstaged edits both appear, untracked is U, and null means unchanged", () => {
const entries = [
{ path: "both", staged: "M", unstaged: "D" },
{ path: "new", staged: "?", unstaged: "?" },
{ path: "clean", staged: null, unstaged: null },
];
const grouped = groups(status(entries));
assert.deepEqual(
grouped.staged.map((entry) => [entry.path, entry.letter]),
[["both", "M"]],
);
assert.deepEqual(
grouped.changes.map((entry) => [entry.path, entry.letter]),
[
["both", "D"],
["new", "U"],
],
);
assert.deepEqual(entries[0], { path: "both", staged: "M", unstaged: "D" });
});
test("rename commands include old and new paths; discard separates tracked and untracked", () => {
const grouped = groups(
status([
{ path: "new", origPath: "old", staged: "R", unstaged: "M" },
{ path: "fresh", staged: "?", unstaged: "?" },
]),
);
assert.deepEqual(pathsOf(grouped.staged), ["old", "new"]);
assert.deepEqual(discardPaths(grouped.changes), { tracked: ["old", "new"], untracked: ["fresh"] });
});
test("primary action prioritizes commit, then publish, then sync, then idle", () => {
const s = status();
assert.equal(primaryAction(s, 1), "commit");
assert.equal(primaryAction(s, 0), "idle");
assert.equal(primaryAction({ ...s, upstream: null, ahead: 1 }, 0), "publish");
assert.equal(primaryAction({ ...s, ahead: 1 }, 0), "sync");
assert.equal(primaryAction({ ...s, behind: 1 }, 0), "sync");
assert.equal(primaryAction({ ...s, branch: "HEAD (no branch)", upstream: null }, 0), "idle");
assert.equal(primaryAction({ ...s, branch: null, upstream: null }, 0), "idle");
});
test("branch filtering is case-insensitive but creation retains exact-name semantics", () => {
assert.deepEqual(branchOptions(["main", "Feature"], " fea "), { query: "fea", shown: ["Feature"], create: true });
assert.equal(branchOptions(["main"], "main").create, false);
assert.equal(branchOptions(["main"], "MAIN").create, true);
assert.equal(branchOptions([], " ").create, false);
});
test("menu ids, labels and order remain the original source-control menu", () => {
assert.deepEqual(
MENU.map((item) => item.action),
["pull", "push", "fetch", "amend", "undo", "stage-all", "unstage-all", "stash", "pop"],
);
assert.deepEqual(
MENU.map((item) => item.label),
[
"Pull",
"Push",
"Fetch",
"Commit (Amend)",
"Undo Last Commit",
"Stage All Changes",
"Unstage All Changes",
"Stash",
"Pop Latest Stash",
],
);
assert.equal(TONE.D, "danger");
assert.equal(TONE.R, "info");
assert.equal(TONE.U, "success");
});
test("panel visibility, fresh fold state and turn-finished selection", () => {
assert.equal(isOpen({ shared: { rightPanelPreview: "git" } }), true);
assert.equal(isOpen({ shared: { rightPanel: "git" } }), true);
assert.equal(isOpen({}), false);
const a = initialState(),
b = initialState();
a.collapsed.merge = true;
assert.equal(b.collapsed.merge, false);
assert.equal(runFinished({ event: "run_finished" }), true);
assert.equal(runFinished(null), false);
});Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 13.4 KB | 4 permissions | Listed |
No comments yet.