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,135 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Clears all sessions, agents, events, and token usage from the database.
|
||||
* Destructive — requires explicit confirmation.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/clear-data.js --yes Wipe everything (irrevocable)
|
||||
* node scripts/clear-data.js --yes --backup Snapshot DB to data/backups/ first
|
||||
* node scripts/clear-data.js --demo-only --yes Delete only seed-fixture rows
|
||||
* node scripts/clear-data.js Dry run — print counts, do nothing
|
||||
*
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
let Database;
|
||||
try {
|
||||
Database = require("better-sqlite3");
|
||||
} catch {
|
||||
try {
|
||||
Database = require("../server/compat-sqlite");
|
||||
} catch {
|
||||
console.error(
|
||||
"Error: No SQLite backend available. Upgrade to Node.js 22+ or install build tools."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { getDataDir } = require("../server/lib/claude-home");
|
||||
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const CONFIRMED = args.has("--yes") || args.has("-y");
|
||||
const BACKUP = args.has("--backup");
|
||||
const DEMO_ONLY = args.has("--demo-only");
|
||||
const DRY_RUN = args.has("--dry-run") || !CONFIRMED;
|
||||
|
||||
// Mirror server/db.js resolution so we clear the same shared database the
|
||||
// servers actually use (DASHBOARD_DB_PATH override → shared data dir).
|
||||
const DB_PATH = process.env.DASHBOARD_DB_PATH || path.join(getDataDir(), "dashboard.db");
|
||||
|
||||
if (!fs.existsSync(DB_PATH)) {
|
||||
console.error(`No database at ${DB_PATH} — nothing to clear.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const db = new Database(DB_PATH);
|
||||
db.pragma("foreign_keys = OFF");
|
||||
|
||||
const counts = {
|
||||
token_usage: db.prepare("SELECT COUNT(*) as n FROM token_usage").get()?.n ?? 0,
|
||||
events: db.prepare("SELECT COUNT(*) as n FROM events").get()?.n ?? 0,
|
||||
agents: db.prepare("SELECT COUNT(*) as n FROM agents").get()?.n ?? 0,
|
||||
sessions: db.prepare("SELECT COUNT(*) as n FROM sessions").get()?.n ?? 0,
|
||||
};
|
||||
|
||||
const totalRows = counts.sessions + counts.agents + counts.events + counts.token_usage;
|
||||
|
||||
console.log("");
|
||||
console.log(`Target DB: ${DB_PATH}`);
|
||||
console.log("Current row counts:");
|
||||
console.log(` Sessions: ${counts.sessions.toLocaleString()}`);
|
||||
console.log(` Agents: ${counts.agents.toLocaleString()}`);
|
||||
console.log(` Events: ${counts.events.toLocaleString()}`);
|
||||
console.log(` Tokens: ${counts.token_usage.toLocaleString()}`);
|
||||
console.log("");
|
||||
|
||||
if (DRY_RUN) {
|
||||
db.close();
|
||||
console.log("⚠️ DRY RUN — no data was deleted.");
|
||||
console.log("");
|
||||
console.log("This is a DESTRUCTIVE operation. To actually wipe the database,");
|
||||
console.log("re-run with --yes:");
|
||||
console.log("");
|
||||
console.log(" node scripts/clear-data.js --yes");
|
||||
console.log("");
|
||||
console.log("Strongly recommended: also pass --backup to snapshot the DB first:");
|
||||
console.log("");
|
||||
console.log(" node scripts/clear-data.js --yes --backup");
|
||||
console.log("");
|
||||
if (DEMO_ONLY) {
|
||||
console.log("(--demo-only would delete only rows tagged as seed fixtures.)");
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Confirmed path — actually delete.
|
||||
|
||||
if (BACKUP) {
|
||||
const backupDir = path.join(path.dirname(DB_PATH), "backups");
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const backupPath = path.join(backupDir, `dashboard.${stamp}.db`);
|
||||
// Use SQLite VACUUM INTO for a consistent snapshot
|
||||
db.exec(`VACUUM INTO '${backupPath.replace(/'/g, "''")}'`);
|
||||
console.log(`📦 Backup written: ${backupPath}`);
|
||||
}
|
||||
|
||||
if (DEMO_ONLY) {
|
||||
// Delete only fixture rows. These IDs are stable across seed runs.
|
||||
const FIXTURE_SESSION_IDS = [
|
||||
"demo-solo-0001-0001-0001-000000000001",
|
||||
"demo-nested-0001-0001-0001-000000000001",
|
||||
];
|
||||
const placeholders = FIXTURE_SESSION_IDS.map(() => "?").join(",");
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare(`DELETE FROM events WHERE session_id IN (${placeholders})`).run(
|
||||
...FIXTURE_SESSION_IDS
|
||||
);
|
||||
db.prepare(`DELETE FROM agents WHERE session_id IN (${placeholders})`).run(
|
||||
...FIXTURE_SESSION_IDS
|
||||
);
|
||||
db.prepare(`DELETE FROM token_usage WHERE session_id IN (${placeholders})`).run(
|
||||
...FIXTURE_SESSION_IDS
|
||||
);
|
||||
db.prepare(`DELETE FROM sessions WHERE id IN (${placeholders})`).run(...FIXTURE_SESSION_IDS);
|
||||
});
|
||||
tx();
|
||||
console.log(
|
||||
`Cleared demo fixture rows (${FIXTURE_SESSION_IDS.length} sessions and their children).`
|
||||
);
|
||||
} else {
|
||||
console.log(`⚠️ Wiping ${totalRows.toLocaleString()} rows…`);
|
||||
db.exec("DELETE FROM token_usage; DELETE FROM events; DELETE FROM agents; DELETE FROM sessions;");
|
||||
console.log("Database cleared.");
|
||||
}
|
||||
|
||||
db.pragma("foreign_keys = ON");
|
||||
db.close();
|
||||
|
||||
console.log("");
|
||||
console.log(
|
||||
"Tip: run `npm run import-history` to restore sessions from ~/.claude/ JSONL transcripts."
|
||||
);
|
||||
Reference in New Issue
Block a user