feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard. Lanes: a durable unit of parallel agent work, one per working directory, tracked across session restarts. Managed lanes are git worktrees the dashboard provisions and can reset or remove behind a three-check destroy guard and a counted preflight; adopted lanes are directories you already own and are never destroyable. Pipelines: a lane moves through pipeline stages. A stage the agent declares with evidence renders green; a stage inferred from the tool-event stream renders dashed amber and never counts as done. Detection is forward-only within a 30-minute window, and never writes the declared stage. Workspace: one page at /run with a lane grid, the selected lane's pipeline, and a full Claude console behind a disclosure.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @file highlight.test.ts
|
||||
* @description Unit tests for the syntax highlighter used by the conversation viewer.
|
||||
* Ensures token classification stays stable for languages we actually render.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { canonicalLang, highlight, tokenClass } from "../highlight";
|
||||
|
||||
function joinByType(tokens: { type: string; text: string }[], type: string): string {
|
||||
return tokens
|
||||
.filter((t) => t.type === type)
|
||||
.map((t) => t.text)
|
||||
.join("|");
|
||||
}
|
||||
|
||||
describe("highlight()", () => {
|
||||
it("preserves the original source verbatim when concatenated", () => {
|
||||
const sources = [
|
||||
"const x = 42;\nconsole.log(x);",
|
||||
"def foo(a, b):\n return a + b\n",
|
||||
'{"a": 1, "b": [true, null]}',
|
||||
"echo $HOME && ls -la",
|
||||
];
|
||||
for (const src of sources) {
|
||||
const langs = ["js", "python", "json", "bash"];
|
||||
for (const lang of langs) {
|
||||
const out = highlight(src, lang)
|
||||
.map((t) => t.text)
|
||||
.join("");
|
||||
expect(out).toBe(src);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("classifies JS keywords, strings, numbers, comments", () => {
|
||||
const tokens = highlight('const x = "hi"; // greet\nreturn 42;', "ts");
|
||||
expect(joinByType(tokens, "keyword")).toContain("const");
|
||||
expect(joinByType(tokens, "keyword")).toContain("return");
|
||||
expect(joinByType(tokens, "string")).toContain('"hi"');
|
||||
expect(joinByType(tokens, "comment")).toContain("// greet");
|
||||
expect(joinByType(tokens, "number")).toContain("42");
|
||||
});
|
||||
|
||||
it("classifies JSON keys as property and bare strings as string", () => {
|
||||
const tokens = highlight('{"name": "Son", "age": 30}', "json");
|
||||
expect(joinByType(tokens, "property")).toContain('"name"');
|
||||
expect(joinByType(tokens, "property")).toContain('"age"');
|
||||
expect(joinByType(tokens, "string")).toContain('"Son"');
|
||||
expect(joinByType(tokens, "number")).toContain("30");
|
||||
});
|
||||
|
||||
it("classifies bash builtins and variables", () => {
|
||||
const tokens = highlight("echo $USER", "bash");
|
||||
expect(joinByType(tokens, "builtin")).toContain("echo");
|
||||
expect(joinByType(tokens, "variable")).toContain("$USER");
|
||||
});
|
||||
|
||||
it("returns plain tokens for unknown languages", () => {
|
||||
const tokens = highlight("anything goes here", "klingon");
|
||||
expect(tokens).toHaveLength(1);
|
||||
expect(tokens[0]!.type).toBe("plain");
|
||||
});
|
||||
|
||||
it("classifies diff lines", () => {
|
||||
const src = "diff --git a b\n+++ b\n--- a\n+added\n-removed\n unchanged";
|
||||
const tokens = highlight(src, "diff");
|
||||
expect(joinByType(tokens, "diff-add")).toContain("+added");
|
||||
expect(joinByType(tokens, "diff-del")).toContain("-removed");
|
||||
expect(joinByType(tokens, "diff-meta")).toContain("diff --git a b");
|
||||
});
|
||||
});
|
||||
|
||||
describe("canonicalLang()", () => {
|
||||
it("maps common aliases to canonical keys", () => {
|
||||
expect(canonicalLang("JavaScript")).toBe("js");
|
||||
expect(canonicalLang("tsx")).toBe("ts");
|
||||
expect(canonicalLang("py")).toBe("python");
|
||||
expect(canonicalLang("zsh")).toBe("bash");
|
||||
expect(canonicalLang("yml")).toBe("yaml");
|
||||
expect(canonicalLang("")).toBe("plain");
|
||||
});
|
||||
});
|
||||
|
||||
describe("tokenClass()", () => {
|
||||
it("returns a non-empty class for every token type", () => {
|
||||
const types = [
|
||||
"plain",
|
||||
"comment",
|
||||
"string",
|
||||
"number",
|
||||
"keyword",
|
||||
"builtin",
|
||||
"function",
|
||||
"operator",
|
||||
"punctuation",
|
||||
"property",
|
||||
"tag",
|
||||
"attr",
|
||||
"variable",
|
||||
"boolean",
|
||||
"diff-add",
|
||||
"diff-del",
|
||||
"diff-meta",
|
||||
] as const;
|
||||
for (const t of types) {
|
||||
expect(tokenClass(t).length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user