Files
nntrivi2001 57dc91585d 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.
2026-07-30 14:39:03 +07:00

335 lines
13 KiB
JavaScript

/**
* @file cc-discovery-helpers.test.js
* @description Direct unit tests for the exported helpers in
* `server/lib/cc-discovery.js`: parseFrontmatter, redactSettings, isUnder,
* isFileLike, the symlinked skill-directory and agent/command markdown-file
* discovery paths (readSkills / readAgents), and the MAX_FILE_BYTES
* constant. The integration tests in
* cc-config.test.js exercise these indirectly through HTTP routes; this
* file pins down their behavior at the function level so future refactors
* surface regressions immediately.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { describe, it } = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const {
parseFrontmatter,
redactSettings,
isUnder,
isFileLike,
readSkills,
readAgents,
MAX_FILE_BYTES,
HOOK_EVENT_TYPES,
} = require("../lib/cc-discovery");
describe("parseFrontmatter", () => {
it("returns null frontmatter for plain markdown", () => {
const r = parseFrontmatter("# Just a heading\n\nNo frontmatter here.");
assert.equal(r.frontmatter, null);
assert.match(r.body, /Just a heading/);
});
it("parses simple key/value frontmatter", () => {
const r = parseFrontmatter("---\nname: my-skill\ndescription: A simple skill\n---\nbody text");
assert.equal(r.frontmatter.name, "my-skill");
assert.equal(r.frontmatter.description, "A simple skill");
assert.equal(r.body.trim(), "body text");
});
it("strips surrounding double quotes from values", () => {
const r = parseFrontmatter('---\nname: "quoted name"\n---\n');
assert.equal(r.frontmatter.name, "quoted name");
});
it("strips surrounding single quotes from values", () => {
const r = parseFrontmatter("---\nname: 'single quoted'\n---\n");
assert.equal(r.frontmatter.name, "single quoted");
});
it("preserves quotes inside the value", () => {
const r = parseFrontmatter('---\ndescription: text with "inner" quotes\n---\n');
assert.equal(r.frontmatter.description, 'text with "inner" quotes');
});
it("supports multiline indented continuation values", () => {
const r = parseFrontmatter(
"---\ndescription: line one\n line two\n line three\nname: x\n---\n"
);
assert.match(r.frontmatter.description, /line one/);
assert.match(r.frontmatter.description, /line two/);
assert.match(r.frontmatter.description, /line three/);
assert.equal(r.frontmatter.name, "x");
});
it("returns null frontmatter when --- is present but never closed", () => {
const r = parseFrontmatter("---\nname: never closed\nbody text below");
assert.equal(r.frontmatter, null);
// body falls back to the entire input
assert.match(r.body, /never closed/);
});
it("ignores lines that don't match key:value", () => {
const r = parseFrontmatter("---\nname: ok\nthis is not a key value\n---\n");
assert.equal(r.frontmatter.name, "ok");
assert.equal(r.frontmatter["this is not a key value"], undefined);
});
it("handles non-string input gracefully", () => {
const r1 = parseFrontmatter(null);
const r2 = parseFrontmatter(undefined);
const r3 = parseFrontmatter(123);
for (const r of [r1, r2, r3]) {
assert.equal(r.frontmatter, null);
assert.equal(r.body, "");
}
});
it("trims trailing whitespace in values", () => {
const r = parseFrontmatter("---\nname: trailing-space \n---\n");
assert.equal(r.frontmatter.name, "trailing-space");
});
it("body preserves its content verbatim after the closing ---", () => {
const r = parseFrontmatter("---\nname: x\n---\n\n# Heading\n\nParagraph.");
assert.match(r.body, /^# Heading/);
assert.match(r.body, /Paragraph\.$/);
});
});
describe("redactSettings", () => {
it("redacts string values whose key matches token/secret/password/key/auth", () => {
const input = {
apiKey: "sk-abc123",
authToken: "bearer xyz",
password: "hunter2",
secret_key: "shh",
apiSecret: "private",
regularField: "stays",
};
const out = redactSettings(input);
assert.equal(out.apiKey, "<redacted>");
assert.equal(out.authToken, "<redacted>");
assert.equal(out.password, "<redacted>");
assert.equal(out.secret_key, "<redacted>");
assert.equal(out.apiSecret, "<redacted>");
assert.equal(out.regularField, "stays");
});
it("handles api-key and api_key variants", () => {
const out = redactSettings({ "api-key": "x", api_key: "y", apikey: "z" });
assert.equal(out["api-key"], "<redacted>");
assert.equal(out.api_key, "<redacted>");
assert.equal(out.apikey, "<redacted>");
});
it("does not redact non-string values even if key matches", () => {
// The redactor only swaps STRING values; nested objects/arrays/numbers
// pass through unchanged so structure is preserved.
const out = redactSettings({ apiKey: 12345, secrets: { x: "stay" } });
assert.equal(out.apiKey, 12345);
assert.deepEqual(out.secrets, { x: "stay" });
});
it("recurses into nested objects", () => {
const out = redactSettings({
provider: { name: "anthropic", apiKey: "sk-...", endpoint: "https://api" },
});
assert.equal(out.provider.apiKey, "<redacted>");
assert.equal(out.provider.name, "anthropic");
assert.equal(out.provider.endpoint, "https://api");
});
it("recurses into arrays of objects", () => {
const out = redactSettings({
mcpServers: [
{ name: "a", apiKey: "sk-1" },
{ name: "b", token: "tok-2" },
],
});
assert.equal(out.mcpServers[0].apiKey, "<redacted>");
assert.equal(out.mcpServers[1].token, "<redacted>");
assert.equal(out.mcpServers[0].name, "a");
});
it("passes scalars through unchanged", () => {
assert.equal(redactSettings(null), null);
assert.equal(redactSettings(undefined), undefined);
assert.equal(redactSettings(42), 42);
assert.equal(redactSettings(true), true);
assert.equal(redactSettings("plain"), "plain");
});
it("is case-insensitive on key match", () => {
const out = redactSettings({ APIKEY: "x", AuthHeader: "y", ToKeN: "z" });
assert.equal(out.APIKEY, "<redacted>");
assert.equal(out.AuthHeader, "<redacted>");
assert.equal(out.ToKeN, "<redacted>");
});
it("does not match keys that contain unrelated substrings", () => {
// 'kept' contains 'kep' not 'key' - should stay
const out = redactSettings({ kept: "stays", description: "stays too" });
assert.equal(out.kept, "stays");
assert.equal(out.description, "stays too");
});
});
describe("isUnder", () => {
it("returns true when target equals root", () => {
assert.equal(isUnder("/a/b", "/a/b"), true);
});
it("returns true when target is inside root", () => {
assert.equal(isUnder("/a/b", "/a/b/c"), true);
assert.equal(isUnder("/a/b", "/a/b/c/d/e.txt"), true);
});
it("returns false when target is outside root", () => {
assert.equal(isUnder("/a/b", "/a/c"), false);
assert.equal(isUnder("/a/b", "/x/y"), false);
});
it("rejects sibling paths that share a prefix", () => {
// /a/b vs /a/bb - bb starts with b but is not under /a/b
assert.equal(isUnder("/a/b", "/a/bb"), false);
assert.equal(isUnder("/a/b", "/a/b-extra"), false);
});
it("normalises relative segments via path.resolve", () => {
// .. inside the target gets resolved away
assert.equal(isUnder("/a/b", "/a/b/c/../d"), true); // /a/b/d
assert.equal(isUnder("/a/b", "/a/b/../c"), false); // /a/c
});
it("returns true regardless of trailing slash", () => {
assert.equal(isUnder("/a/b/", "/a/b/c"), true);
assert.equal(isUnder("/a/b", "/a/b/c/"), true);
});
it("works with paths produced by path.resolve", () => {
const root = path.resolve("/tmp/cc-test");
const inside = path.resolve("/tmp/cc-test/skills/x/SKILL.md");
const outside = path.resolve("/tmp/elsewhere/file");
assert.equal(isUnder(root, inside), true);
assert.equal(isUnder(root, outside), false);
});
});
describe("readSkills (symlinked skill directories)", () => {
it("includes a skill directory that is a symlink to a real directory", () => {
// Dirent.isDirectory() returns false for a symlink even when it points
// to a directory (Node fs quirk) — readSkillsAt must still follow it.
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cc-discovery-symlink-"));
const realDir = path.join(tmp, "real-skills", "second-brain");
fs.mkdirSync(realDir, { recursive: true });
fs.writeFileSync(
path.join(realDir, "SKILL.md"),
"---\nname: second-brain\n---\n\nBody text.\n"
);
const projectRoot = path.join(tmp, "project");
const skillsDir = path.join(projectRoot, ".claude", "skills");
fs.mkdirSync(skillsDir, { recursive: true });
fs.symlinkSync(realDir, path.join(skillsDir, "second-brain"), "dir");
const items = readSkills({ scope: "project", cwd: projectRoot });
const names = items.map((s) => s.name);
assert.ok(names.includes("second-brain"), `expected symlinked skill in ${names}`);
});
it("skips a broken symlink without throwing", () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cc-discovery-broken-symlink-"));
const projectRoot = path.join(tmp, "project");
const skillsDir = path.join(projectRoot, ".claude", "skills");
fs.mkdirSync(skillsDir, { recursive: true });
fs.symlinkSync(path.join(tmp, "does-not-exist"), path.join(skillsDir, "broken"), "dir");
let items;
assert.doesNotThrow(() => {
items = readSkills({ scope: "project", cwd: projectRoot });
});
assert.ok(!items.map((s) => s.name).includes("broken"));
});
});
describe("readAgents (symlinked markdown files)", () => {
it("includes an agent .md that is a symlink to a real file", () => {
// Same Dirent quirk as symlinked skill directories, but for files:
// ent.isFile() is false for a symlink pointing at a regular file, so a
// version-controlled agent linked into .claude/agents/ was invisible.
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cc-discovery-file-symlink-"));
const realFile = path.join(tmp, "repo", "reviewer.md");
fs.mkdirSync(path.dirname(realFile), { recursive: true });
fs.writeFileSync(realFile, "---\nname: reviewer\ndescription: Reviews diffs\n---\n\nBody.\n");
const projectRoot = path.join(tmp, "project");
const agentsDir = path.join(projectRoot, ".claude", "agents");
fs.mkdirSync(agentsDir, { recursive: true });
fs.symlinkSync(realFile, path.join(agentsDir, "reviewer.md"), "file");
const items = readAgents({ scope: "project", cwd: projectRoot });
const names = items.map((a) => a.name);
assert.ok(names.includes("reviewer"), `expected symlinked agent in ${names}`);
});
it("skips a broken .md symlink without throwing", () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cc-discovery-file-broken-"));
const projectRoot = path.join(tmp, "project");
const agentsDir = path.join(projectRoot, ".claude", "agents");
fs.mkdirSync(agentsDir, { recursive: true });
fs.symlinkSync(path.join(tmp, "gone.md"), path.join(agentsDir, "ghost.md"), "file");
let items;
assert.doesNotThrow(() => {
items = readAgents({ scope: "project", cwd: projectRoot });
});
assert.ok(!items.some((a) => /ghost/.test(a.file || "")));
});
});
describe("isFileLike", () => {
it("true for a regular file dirent", () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cc-isfilelike-"));
fs.writeFileSync(path.join(tmp, "a.md"), "x");
const ent = fs.readdirSync(tmp, { withFileTypes: true })[0];
assert.equal(isFileLike(ent, path.join(tmp, ent.name)), true);
});
it("true for a symlink resolving to a file, false for one resolving to a directory", () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cc-isfilelike-sym-"));
fs.writeFileSync(path.join(tmp, "real.md"), "x");
fs.mkdirSync(path.join(tmp, "realdir"));
fs.symlinkSync(path.join(tmp, "real.md"), path.join(tmp, "file-link.md"), "file");
fs.symlinkSync(path.join(tmp, "realdir"), path.join(tmp, "dir-link"), "dir");
const ents = Object.fromEntries(
fs.readdirSync(tmp, { withFileTypes: true }).map((e) => [e.name, e])
);
assert.equal(isFileLike(ents["file-link.md"], path.join(tmp, "file-link.md")), true);
assert.equal(isFileLike(ents["dir-link"], path.join(tmp, "dir-link")), false);
});
});
describe("module exports", () => {
it("MAX_FILE_BYTES is 256 KB", () => {
assert.equal(MAX_FILE_BYTES, 256 * 1024);
});
it("HOOK_EVENT_TYPES covers all canonical Claude Code events", () => {
const expected = [
"SessionStart",
"SessionEnd",
"UserPromptSubmit",
"PreToolUse",
"PostToolUse",
"Stop",
"SubagentStop",
"Notification",
"PreCompact",
];
for (const t of expected) assert.ok(HOOK_EVENT_TYPES.includes(t), `missing ${t}`);
});
});