copilot-agent 1.0.0 → 1.1.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/dist/index.js CHANGED
@@ -31,8 +31,8 @@ function listSessions(limit = 20) {
31
31
  const dirPath = join(SESSION_DIR, entry.name);
32
32
  if (!existsSync(join(dirPath, "events.jsonl"))) continue;
33
33
  try {
34
- const stat = statSync(dirPath);
35
- dirs.push({ id: entry.name, dir: dirPath, mtime: stat.mtimeMs });
34
+ const stat2 = statSync(dirPath);
35
+ dirs.push({ id: entry.name, dir: dirPath, mtime: stat2.mtimeMs });
36
36
  } catch {
37
37
  }
38
38
  }
@@ -55,9 +55,9 @@ function getLatestSessionId() {
55
55
  let latest = null;
56
56
  for (const entry of entries) {
57
57
  try {
58
- const stat = statSync(join(SESSION_DIR, entry.name));
59
- if (!latest || stat.mtimeMs > latest.mtime) {
60
- latest = { id: entry.name, mtime: stat.mtimeMs };
58
+ const stat2 = statSync(join(SESSION_DIR, entry.name));
59
+ if (!latest || stat2.mtimeMs > latest.mtime) {
60
+ latest = { id: entry.name, mtime: stat2.mtimeMs };
61
61
  }
62
62
  } catch {
63
63
  }
@@ -244,12 +244,12 @@ function listClaudeSessions(limit = 20) {
244
244
  const filePath = join(projPath, file);
245
245
  const sid = basename(file, ".jsonl");
246
246
  try {
247
- const stat = statSync(filePath);
247
+ const stat2 = statSync(filePath);
248
248
  const { lastEvent, complete, summary } = parseClaudeSessionMeta(filePath);
249
249
  sessions.push({
250
250
  id: sid,
251
251
  dir: projPath,
252
- mtime: stat.mtimeMs,
252
+ mtime: stat2.mtimeMs,
253
253
  lastEvent,
254
254
  premiumRequests: 0,
255
255
  summary,
@@ -319,9 +319,9 @@ function getLatestClaudeSessionId(projectDir) {
319
319
  try {
320
320
  const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
321
321
  for (const file of files) {
322
- const stat = statSync(join(dir, file));
323
- if (!latest || stat.mtimeMs > latest.mtime) {
324
- latest = { id: basename(file, ".jsonl"), mtime: stat.mtimeMs };
322
+ const stat2 = statSync(join(dir, file));
323
+ if (!latest || stat2.mtimeMs > latest.mtime) {
324
+ latest = { id: basename(file, ".jsonl"), mtime: stat2.mtimeMs };
325
325
  }
326
326
  }
327
327
  } catch {
@@ -1658,6 +1658,427 @@ function renderReport(r) {
1658
1658
  log("");
1659
1659
  }
1660
1660
 
1661
+ // src/lib/reactive.ts
1662
+ import { EventEmitter } from "events";
1663
+ import { readFile, readdir, stat } from "fs/promises";
1664
+ import { existsSync as existsSync6 } from "fs";
1665
+ import { exec } from "child_process";
1666
+ import { join as join7, basename as basename3 } from "path";
1667
+ import { homedir as homedir5 } from "os";
1668
+ var SESSION_DIR2 = join7(homedir5(), ".copilot", "session-state");
1669
+ var CLAUDE_PROJECTS_DIR2 = join7(homedir5(), ".claude", "projects");
1670
+ function execAsync(cmd) {
1671
+ return new Promise((resolve8, reject) => {
1672
+ exec(cmd, { encoding: "utf-8", maxBuffer: 512 * 1024 }, (err, stdout) => {
1673
+ if (err) reject(err);
1674
+ else resolve8(stdout);
1675
+ });
1676
+ });
1677
+ }
1678
+ function parseSimpleYaml2(content) {
1679
+ const result = {};
1680
+ for (const line of content.split("\n")) {
1681
+ const idx = line.indexOf(": ");
1682
+ if (idx === -1) continue;
1683
+ const key = line.substring(0, idx).trim();
1684
+ const value = line.substring(idx + 2).trim();
1685
+ if (key) result[key] = value;
1686
+ }
1687
+ return result;
1688
+ }
1689
+ async function parseJsonlChunked(content, handler) {
1690
+ const lines = content.trimEnd().split("\n");
1691
+ const CHUNK = 200;
1692
+ for (let i = 0; i < lines.length; i++) {
1693
+ try {
1694
+ handler(JSON.parse(lines[i]));
1695
+ } catch {
1696
+ }
1697
+ if (i % CHUNK === 0 && i > 0) {
1698
+ await new Promise((r) => setImmediate(r));
1699
+ }
1700
+ }
1701
+ }
1702
+ async function readWorkspaceAsync(sid) {
1703
+ const wsPath = join7(SESSION_DIR2, sid, "workspace.yaml");
1704
+ try {
1705
+ const content = await readFile(wsPath, "utf-8");
1706
+ return parseSimpleYaml2(content);
1707
+ } catch {
1708
+ return {};
1709
+ }
1710
+ }
1711
+ async function listCopilotSessionsAsync(limit) {
1712
+ if (!existsSync6(SESSION_DIR2)) return [];
1713
+ try {
1714
+ const entries = await readdir(SESSION_DIR2, { withFileTypes: true });
1715
+ const dirs = [];
1716
+ for (const entry of entries) {
1717
+ if (!entry.isDirectory()) continue;
1718
+ const dirPath = join7(SESSION_DIR2, entry.name);
1719
+ const eventsPath = join7(dirPath, "events.jsonl");
1720
+ if (!existsSync6(eventsPath)) continue;
1721
+ try {
1722
+ const s = await stat(dirPath);
1723
+ dirs.push({ id: entry.name, dir: dirPath, mtime: s.mtimeMs });
1724
+ } catch {
1725
+ }
1726
+ }
1727
+ dirs.sort((a, b) => b.mtime - a.mtime);
1728
+ const top = dirs.slice(0, limit);
1729
+ return Promise.all(top.map(async (s) => {
1730
+ const ws = await readWorkspaceAsync(s.id);
1731
+ let premiumRequests = 0;
1732
+ let complete = false;
1733
+ let lastEvent = "unknown";
1734
+ try {
1735
+ const evPath = join7(SESSION_DIR2, s.id, "events.jsonl");
1736
+ const fileStat = await stat(evPath);
1737
+ const fullContent = await readFile(evPath, "utf-8");
1738
+ const lines = fullContent.trimEnd().split("\n");
1739
+ for (let i = lines.length - 1; i >= Math.max(0, lines.length - 10); i--) {
1740
+ try {
1741
+ const ev = JSON.parse(lines[i]);
1742
+ if (i === lines.length - 1) lastEvent = ev.type ?? "unknown";
1743
+ if (ev.type === "session.shutdown" && ev.data?.totalPremiumRequests != null) {
1744
+ premiumRequests = ev.data.totalPremiumRequests;
1745
+ }
1746
+ if (ev.type === "session.task_complete") complete = true;
1747
+ } catch {
1748
+ }
1749
+ }
1750
+ if (!complete && fullContent.includes('"session.task_complete"')) complete = true;
1751
+ } catch {
1752
+ }
1753
+ return {
1754
+ id: s.id,
1755
+ dir: s.dir,
1756
+ mtime: s.mtime,
1757
+ lastEvent,
1758
+ premiumRequests,
1759
+ summary: ws.summary ?? "",
1760
+ cwd: ws.cwd ?? "",
1761
+ complete,
1762
+ agent: "copilot"
1763
+ };
1764
+ }));
1765
+ } catch {
1766
+ return [];
1767
+ }
1768
+ }
1769
+ async function listClaudeSessionsAsync(limit) {
1770
+ if (!existsSync6(CLAUDE_PROJECTS_DIR2)) return [];
1771
+ const sessions = [];
1772
+ try {
1773
+ const projectDirs = await readdir(CLAUDE_PROJECTS_DIR2, { withFileTypes: true });
1774
+ for (const projDir of projectDirs.filter((d) => d.isDirectory())) {
1775
+ const projPath = join7(CLAUDE_PROJECTS_DIR2, projDir.name);
1776
+ const cwd = projDir.name.replace(/^-/, "/").replace(/-/g, "/");
1777
+ const files = (await readdir(projPath)).filter((f) => f.endsWith(".jsonl"));
1778
+ for (const file of files) {
1779
+ const filePath = join7(projPath, file);
1780
+ const sid = basename3(file, ".jsonl");
1781
+ try {
1782
+ const s = await stat(filePath);
1783
+ sessions.push({
1784
+ id: sid,
1785
+ dir: projPath,
1786
+ mtime: s.mtimeMs,
1787
+ lastEvent: "unknown",
1788
+ premiumRequests: 0,
1789
+ summary: "",
1790
+ cwd,
1791
+ complete: false,
1792
+ agent: "claude"
1793
+ });
1794
+ } catch {
1795
+ }
1796
+ }
1797
+ }
1798
+ } catch {
1799
+ }
1800
+ sessions.sort((a, b) => b.mtime - a.mtime);
1801
+ return sessions.slice(0, limit);
1802
+ }
1803
+ async function findProcessesAsync() {
1804
+ try {
1805
+ const output = await execAsync("ps -eo pid,command");
1806
+ const results = [];
1807
+ const myPid = process.pid;
1808
+ const parentPid = process.ppid;
1809
+ for (const line of output.split("\n")) {
1810
+ const trimmed = line.trim();
1811
+ if (!trimmed) continue;
1812
+ const isCopilot = (trimmed.includes("copilot") || trimmed.includes("@githubnext/copilot")) && !trimmed.includes("copilot-agent") && !trimmed.includes("copilot-api");
1813
+ const isClaude = trimmed.includes("claude") && !trimmed.includes("claude-code") && !trimmed.includes("copilot-agent");
1814
+ if (!isCopilot && !isClaude) continue;
1815
+ if (trimmed.includes("ps -eo") || trimmed.includes("grep")) continue;
1816
+ const agent = isClaude ? "claude" : "copilot";
1817
+ const match = trimmed.match(/^(\d+)\s+(.+)$/);
1818
+ if (!match) continue;
1819
+ const pid = parseInt(match[1], 10);
1820
+ if (pid === myPid || pid === parentPid) continue;
1821
+ const cmd = match[2];
1822
+ const sidMatch = agent === "copilot" ? cmd.match(/resume[= ]+([a-f0-9-]{36})/) : cmd.match(/(?:--resume|--session-id)[= ]+([a-f0-9-]{36})/);
1823
+ results.push({ pid, command: cmd, sessionId: sidMatch?.[1], agent });
1824
+ }
1825
+ return results;
1826
+ } catch {
1827
+ return [];
1828
+ }
1829
+ }
1830
+ async function loadReportAsync(sid, agent) {
1831
+ if (agent === "claude") return loadClaudeReportAsync(sid);
1832
+ return loadCopilotReportAsync(sid);
1833
+ }
1834
+ async function loadCopilotReportAsync(sid) {
1835
+ const eventsPath = join7(SESSION_DIR2, sid, "events.jsonl");
1836
+ if (!existsSync6(eventsPath)) return null;
1837
+ const ws = await readWorkspaceAsync(sid);
1838
+ let content;
1839
+ try {
1840
+ content = await readFile(eventsPath, "utf-8");
1841
+ } catch {
1842
+ return null;
1843
+ }
1844
+ const report = {
1845
+ id: sid,
1846
+ cwd: ws.cwd ?? "",
1847
+ summary: ws.summary ?? "",
1848
+ startTime: "",
1849
+ endTime: "",
1850
+ durationMs: 0,
1851
+ complete: false,
1852
+ userMessages: 0,
1853
+ assistantTurns: 0,
1854
+ outputTokens: 0,
1855
+ premiumRequests: 0,
1856
+ toolUsage: {},
1857
+ gitCommits: [],
1858
+ filesCreated: [],
1859
+ filesEdited: [],
1860
+ errors: [],
1861
+ taskCompletions: [],
1862
+ agent: "copilot"
1863
+ };
1864
+ await parseJsonlChunked(content, (event) => {
1865
+ const type = event.type;
1866
+ const ts = event.timestamp;
1867
+ const data = event.data ?? {};
1868
+ if (ts && !report.startTime) report.startTime = ts;
1869
+ if (ts) report.endTime = ts;
1870
+ switch (type) {
1871
+ case "user.message":
1872
+ report.userMessages++;
1873
+ break;
1874
+ case "assistant.message":
1875
+ report.assistantTurns++;
1876
+ report.outputTokens += data.outputTokens ?? 0;
1877
+ break;
1878
+ case "tool.execution_start": {
1879
+ const toolName = data.toolName;
1880
+ if (toolName) report.toolUsage[toolName] = (report.toolUsage[toolName] ?? 0) + 1;
1881
+ if (toolName === "bash") {
1882
+ const args = data.arguments;
1883
+ const cmd = args?.command ?? "";
1884
+ if (cmd.includes("git") && cmd.includes("commit") && cmd.includes("-m")) {
1885
+ const msgMatch = cmd.match(/-m\s+"([^"]{1,120})/);
1886
+ if (msgMatch) report.gitCommits.push(msgMatch[1]);
1887
+ }
1888
+ }
1889
+ if (toolName === "create") {
1890
+ const args = data.arguments;
1891
+ if (args?.path) report.filesCreated.push(args.path);
1892
+ }
1893
+ if (toolName === "edit") {
1894
+ const args = data.arguments;
1895
+ if (args?.path && !report.filesEdited.includes(args.path)) report.filesEdited.push(args.path);
1896
+ }
1897
+ break;
1898
+ }
1899
+ case "session.task_complete": {
1900
+ report.taskCompletions.push(data.summary ?? "(task completed)");
1901
+ report.complete = true;
1902
+ break;
1903
+ }
1904
+ case "session.error": {
1905
+ const msg = data.message;
1906
+ if (msg) report.errors.push(msg);
1907
+ break;
1908
+ }
1909
+ case "session.shutdown": {
1910
+ const prem = data.totalPremiumRequests;
1911
+ if (prem != null) report.premiumRequests = prem;
1912
+ break;
1913
+ }
1914
+ }
1915
+ });
1916
+ if (report.startTime && report.endTime) {
1917
+ report.durationMs = new Date(report.endTime).getTime() - new Date(report.startTime).getTime();
1918
+ }
1919
+ return report;
1920
+ }
1921
+ async function loadClaudeReportAsync(sid) {
1922
+ if (!existsSync6(CLAUDE_PROJECTS_DIR2)) return null;
1923
+ let filePath = null;
1924
+ let cwd = "";
1925
+ try {
1926
+ const projectDirs = await readdir(CLAUDE_PROJECTS_DIR2, { withFileTypes: true });
1927
+ for (const projDir of projectDirs.filter((d) => d.isDirectory())) {
1928
+ const candidate = join7(CLAUDE_PROJECTS_DIR2, projDir.name, `${sid}.jsonl`);
1929
+ if (existsSync6(candidate)) {
1930
+ filePath = candidate;
1931
+ cwd = projDir.name.replace(/^-/, "/").replace(/-/g, "/");
1932
+ break;
1933
+ }
1934
+ }
1935
+ } catch {
1936
+ }
1937
+ if (!filePath) return null;
1938
+ let content;
1939
+ try {
1940
+ content = await readFile(filePath, "utf-8");
1941
+ } catch {
1942
+ return null;
1943
+ }
1944
+ const report = {
1945
+ id: sid,
1946
+ cwd,
1947
+ summary: "",
1948
+ startTime: "",
1949
+ endTime: "",
1950
+ durationMs: 0,
1951
+ complete: false,
1952
+ userMessages: 0,
1953
+ assistantTurns: 0,
1954
+ outputTokens: 0,
1955
+ premiumRequests: 0,
1956
+ toolUsage: {},
1957
+ gitCommits: [],
1958
+ filesCreated: [],
1959
+ filesEdited: [],
1960
+ errors: [],
1961
+ taskCompletions: [],
1962
+ agent: "claude"
1963
+ };
1964
+ await parseJsonlChunked(content, (event) => {
1965
+ const type = event.type ?? event.role ?? "";
1966
+ const ts = event.timestamp;
1967
+ if (ts && !report.startTime) report.startTime = ts;
1968
+ if (ts) report.endTime = ts;
1969
+ if (type === "human" || type === "user") report.userMessages++;
1970
+ if (type === "assistant") {
1971
+ report.assistantTurns++;
1972
+ if (event.stop_reason === "end_turn") report.complete = true;
1973
+ }
1974
+ if (type === "result") report.complete = true;
1975
+ if (type === "tool_use" || type === "tool_result") {
1976
+ const name = event.name ?? "tool";
1977
+ report.toolUsage[name] = (report.toolUsage[name] ?? 0) + 1;
1978
+ }
1979
+ });
1980
+ if (report.startTime && report.endTime) {
1981
+ report.durationMs = new Date(report.endTime).getTime() - new Date(report.startTime).getTime();
1982
+ }
1983
+ return report;
1984
+ }
1985
+ var DashboardStore = class extends EventEmitter {
1986
+ sessions = [];
1987
+ processes = [];
1988
+ details = /* @__PURE__ */ new Map();
1989
+ limit;
1990
+ sessionsTimer = null;
1991
+ procsTimer = null;
1992
+ detailPending = null;
1993
+ detailDebounce = null;
1994
+ refreshing = { sessions: false, procs: false, detail: false };
1995
+ constructor(limit = 20) {
1996
+ super();
1997
+ this.limit = limit;
1998
+ }
1999
+ // ── Start auto-refresh loops ───────────────────────────────────
2000
+ start(sessionIntervalMs = 2e3, procsIntervalMs = 3e3) {
2001
+ this.refreshSessions();
2002
+ this.refreshProcesses();
2003
+ this.sessionsTimer = setInterval(() => this.refreshSessions(), sessionIntervalMs);
2004
+ this.procsTimer = setInterval(() => this.refreshProcesses(), procsIntervalMs);
2005
+ }
2006
+ stop() {
2007
+ if (this.sessionsTimer) clearInterval(this.sessionsTimer);
2008
+ if (this.procsTimer) clearInterval(this.procsTimer);
2009
+ if (this.detailDebounce) clearTimeout(this.detailDebounce);
2010
+ }
2011
+ // ── Async session refresh (non-blocking) ───────────────────────
2012
+ async refreshSessions() {
2013
+ if (this.refreshing.sessions) return;
2014
+ this.refreshing.sessions = true;
2015
+ try {
2016
+ const [copilot, claude] = await Promise.all([
2017
+ listCopilotSessionsAsync(this.limit),
2018
+ listClaudeSessionsAsync(this.limit)
2019
+ ]);
2020
+ const merged = [...copilot, ...claude].sort((a, b) => b.mtime - a.mtime).slice(0, this.limit);
2021
+ this.sessions = merged;
2022
+ this.emit("sessions");
2023
+ } catch {
2024
+ }
2025
+ this.refreshing.sessions = false;
2026
+ }
2027
+ // ── Async process refresh (non-blocking) ───────────────────────
2028
+ async refreshProcesses() {
2029
+ if (this.refreshing.procs) return;
2030
+ this.refreshing.procs = true;
2031
+ try {
2032
+ this.processes = await findProcessesAsync();
2033
+ this.emit("processes");
2034
+ } catch {
2035
+ }
2036
+ this.refreshing.procs = false;
2037
+ }
2038
+ // ── Request detail for a session (debounced 150ms) ─────────────
2039
+ requestDetail(sessionId, agent, force = false) {
2040
+ if (!force && this.details.has(sessionId)) {
2041
+ this.emit("detail", sessionId);
2042
+ return;
2043
+ }
2044
+ this.detailPending = sessionId;
2045
+ if (this.detailDebounce) clearTimeout(this.detailDebounce);
2046
+ this.detailDebounce = setTimeout(() => {
2047
+ this.detailDebounce = null;
2048
+ if (this.detailPending !== sessionId) return;
2049
+ this.loadDetail(sessionId, agent);
2050
+ }, 150);
2051
+ }
2052
+ // ── Force-load detail immediately (Enter key) ──────────────────
2053
+ requestDetailNow(sessionId, agent) {
2054
+ if (this.detailDebounce) clearTimeout(this.detailDebounce);
2055
+ this.detailPending = sessionId;
2056
+ this.loadDetail(sessionId, agent);
2057
+ }
2058
+ // ── Async detail loading (non-blocking JSONL parse) ────────────
2059
+ async loadDetail(sessionId, agent) {
2060
+ if (this.refreshing.detail) return;
2061
+ this.refreshing.detail = true;
2062
+ try {
2063
+ const report = await loadReportAsync(sessionId, agent);
2064
+ this.details.set(sessionId, report);
2065
+ if (this.details.size > 15) {
2066
+ const first = this.details.keys().next().value;
2067
+ if (first && first !== sessionId) this.details.delete(first);
2068
+ }
2069
+ this.emit("detail", sessionId);
2070
+ } catch {
2071
+ }
2072
+ this.refreshing.detail = false;
2073
+ }
2074
+ // ── Force full refresh (r key) ─────────────────────────────────
2075
+ forceRefresh() {
2076
+ this.details.clear();
2077
+ this.refreshSessions();
2078
+ this.refreshProcesses();
2079
+ }
2080
+ };
2081
+
1661
2082
  // src/tui/widgets.ts
1662
2083
  function agentLabel(agent) {
1663
2084
  return agent === "claude" ? "{yellow-fg}claude{/}" : "{cyan-fg}copilot{/}";
@@ -1779,53 +2200,13 @@ async function loadBlessed() {
1779
2200
  blessed = mod.default || mod;
1780
2201
  }
1781
2202
  function registerDashboardCommand(program2) {
1782
- program2.command("dashboard").alias("tui").description("Real-time terminal dashboard for copilot sessions (htop-style)").option("-r, --refresh <n>", "Refresh interval in seconds", "5").option("-l, --limit <n>", "Number of sessions to show", "20").action(async (opts) => {
2203
+ program2.command("dashboard").alias("tui").description("Real-time terminal dashboard for copilot sessions (htop-style)").option("-r, --refresh <n>", "Session refresh interval in seconds", "2").option("-l, --limit <n>", "Number of sessions to show", "20").action(async (opts) => {
1783
2204
  await loadBlessed();
1784
- runBlessedDashboard(parseInt(opts.refresh, 10), parseInt(opts.limit, 10));
2205
+ runReactiveDashboard(parseInt(opts.refresh, 10), parseInt(opts.limit, 10));
1785
2206
  });
1786
2207
  }
1787
- function createCache() {
1788
- return { sessions: [], sessionsTs: 0, procs: [], procsTs: 0, details: /* @__PURE__ */ new Map() };
1789
- }
1790
- function cacheSessions(cache, limit) {
1791
- const now = Date.now();
1792
- if (now - cache.sessionsTs > 2e3) {
1793
- try {
1794
- cache.sessions = listAllSessions(limit);
1795
- } catch {
1796
- }
1797
- cache.sessionsTs = now;
1798
- }
1799
- return cache.sessions;
1800
- }
1801
- function cacheProcs(cache) {
1802
- const now = Date.now();
1803
- if (now - cache.procsTs > 3e3) {
1804
- try {
1805
- cache.procs = findAgentProcesses();
1806
- } catch {
1807
- }
1808
- cache.procsTs = now;
1809
- }
1810
- return cache.procs;
1811
- }
1812
- function cacheDetail(cache, s) {
1813
- const entry = cache.details.get(s.id);
1814
- if (entry && Date.now() - entry.ts < 1e4) return entry.report;
1815
- let report = null;
1816
- try {
1817
- report = getAgentSessionReport(s.id, s.agent);
1818
- } catch {
1819
- }
1820
- cache.details.set(s.id, { report, ts: Date.now() });
1821
- if (cache.details.size > 15) {
1822
- const oldest = [...cache.details.entries()].sort((a, b) => a[1].ts - b[1].ts)[0];
1823
- if (oldest) cache.details.delete(oldest[0]);
1824
- }
1825
- return report;
1826
- }
1827
- function runBlessedDashboard(refreshSec, limit) {
1828
- const cache = createCache();
2208
+ function runReactiveDashboard(refreshSec, limit) {
2209
+ const store = new DashboardStore(limit);
1829
2210
  const origTerm = process.env.TERM;
1830
2211
  if (origTerm?.includes("256color")) {
1831
2212
  process.env.TERM = "xterm";
@@ -1849,8 +2230,7 @@ function runBlessedDashboard(refreshSec, limit) {
1849
2230
  height: 3,
1850
2231
  tags: true,
1851
2232
  style: { fg: FG, bg: "#161b22", border: { fg: BORDER_COLOR } },
1852
- border: { type: "line" },
1853
- content: ""
2233
+ border: { type: "line" }
1854
2234
  });
1855
2235
  const processBox = blessed.box({
1856
2236
  parent: screen,
@@ -1862,8 +2242,7 @@ function runBlessedDashboard(refreshSec, limit) {
1862
2242
  label: " {cyan-fg}{bold}Active Processes{/} ",
1863
2243
  scrollable: true,
1864
2244
  style: { fg: FG, bg: BG, border: { fg: BORDER_COLOR }, label: { fg: ACCENT } },
1865
- border: { type: "line" },
1866
- content: ""
2245
+ border: { type: "line" }
1867
2246
  });
1868
2247
  const sessionList = blessed.list({
1869
2248
  parent: screen,
@@ -1905,7 +2284,7 @@ function runBlessedDashboard(refreshSec, limit) {
1905
2284
  scrollbar: { ch: "\u2502", style: { fg: ACCENT } },
1906
2285
  content: "{gray-fg}Select a session with \u2191\u2193 keys{/}"
1907
2286
  });
1908
- const footer = blessed.box({
2287
+ blessed.box({
1909
2288
  parent: screen,
1910
2289
  bottom: 0,
1911
2290
  left: 0,
@@ -1916,19 +2295,18 @@ function runBlessedDashboard(refreshSec, limit) {
1916
2295
  border: { type: "line" },
1917
2296
  content: " {bold}\u2191\u2193{/} Navigate {bold}Enter{/} Detail {bold}Tab{/} Switch panel {bold}r{/} Refresh {bold}q{/} Quit"
1918
2297
  });
1919
- let sessions = [];
1920
- let procs = [];
1921
2298
  let selectedIdx = 0;
1922
2299
  let focusedPanel = "sessions";
1923
- let lastDetailId = "";
1924
2300
  function renderHeader() {
1925
2301
  const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB");
2302
+ const sessions = store.sessions;
1926
2303
  const totalPremium = sessions.reduce((s, x) => s + x.premiumRequests, 0);
1927
2304
  const completedCount = sessions.filter((s) => s.complete).length;
1928
- const stats = headerStats(procs.length, sessions.length, totalPremium, completedCount);
2305
+ const stats = headerStats(store.processes.length, sessions.length, totalPremium, completedCount);
1929
2306
  headerBox.setContent(` {bold}{cyan-fg}\u26A1 copilot-agent{/} ${stats} {gray-fg}${time}{/}`);
1930
2307
  }
1931
2308
  function renderProcesses2() {
2309
+ const procs = store.processes;
1932
2310
  if (procs.length === 0) {
1933
2311
  processBox.setContent(" {gray-fg}No agent processes running{/}");
1934
2312
  return;
@@ -1939,51 +2317,65 @@ function runBlessedDashboard(refreshSec, limit) {
1939
2317
  ${rows.join("\n")}`);
1940
2318
  }
1941
2319
  function renderSessions() {
1942
- const items = sessions.map((s) => sessionRow(s));
2320
+ const items = store.sessions.map((s) => sessionRow(s));
1943
2321
  sessionList.setItems(items);
1944
2322
  if (selectedIdx >= 0 && selectedIdx < items.length) {
1945
2323
  sessionList.select(selectedIdx);
1946
2324
  }
1947
- sessionList.setLabel(` {cyan-fg}{bold}Sessions (${sessions.length}){/} `);
1948
- }
1949
- function renderDetail2(force = false) {
1950
- if (sessions.length === 0 || selectedIdx < 0 || selectedIdx >= sessions.length) {
1951
- detailBox.setContent("{gray-fg}No session selected{/}");
1952
- lastDetailId = "";
1953
- return;
1954
- }
1955
- const s = sessions[selectedIdx];
1956
- if (!force && s.id === lastDetailId) return;
1957
- lastDetailId = s.id;
1958
- const report = cacheDetail(cache, s);
2325
+ sessionList.setLabel(` {cyan-fg}{bold}Sessions (${store.sessions.length}){/} `);
2326
+ }
2327
+ function showQuickPreview(s) {
2328
+ const project = s.cwd?.split("/").pop() || s.id.slice(0, 12);
2329
+ const agent = s.agent === "claude" ? "{yellow-fg}claude{/}" : "{cyan-fg}copilot{/}";
2330
+ const status = s.complete ? "{green-fg}\u2714 complete{/}" : "{yellow-fg}\u23F3 incomplete{/}";
2331
+ detailBox.setContent([
2332
+ `{bold}${project}{/} ${agent}`,
2333
+ `{gray-fg}${s.id}{/}`,
2334
+ "",
2335
+ ` Status ${status}`,
2336
+ ` Premium {yellow-fg}${s.premiumRequests}{/}`,
2337
+ ` Activity ${fmtTimeAgo(s.mtime)}`,
2338
+ ` Directory {gray-fg}${s.cwd || "\u2014"}{/}`,
2339
+ "",
2340
+ "{gray-fg}Loading\u2026{/}"
2341
+ ].join("\n"));
2342
+ detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${s.id.slice(0, 12)}\u2026{/} `);
2343
+ }
2344
+ store.on("sessions", () => {
2345
+ renderHeader();
2346
+ renderSessions();
2347
+ const s = store.sessions[selectedIdx];
2348
+ if (s) store.requestDetail(s.id, s.agent);
2349
+ screen.render();
2350
+ });
2351
+ store.on("processes", () => {
2352
+ renderHeader();
2353
+ renderProcesses2();
2354
+ screen.render();
2355
+ });
2356
+ store.on("detail", (sessionId) => {
2357
+ const currentSession = store.sessions[selectedIdx];
2358
+ if (!currentSession || currentSession.id !== sessionId) return;
2359
+ const report = store.details.get(sessionId);
1959
2360
  if (report) {
1960
2361
  detailBox.setContent(detailContent(report));
1961
- detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${s.id.slice(0, 12)}\u2026{/} `);
2362
+ detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${sessionId.slice(0, 12)}\u2026{/} `);
1962
2363
  } else {
1963
- detailBox.setContent(`{gray-fg}Could not load report for ${s.id.slice(0, 8)}\u2026{/}`);
2364
+ detailBox.setContent(`{gray-fg}No report data for ${sessionId.slice(0, 8)}\u2026{/}`);
1964
2365
  }
1965
- }
1966
- function render() {
1967
- sessions = cacheSessions(cache, limit);
1968
- procs = cacheProcs(cache);
2366
+ screen.render();
2367
+ });
2368
+ const clockTimer = setInterval(() => {
1969
2369
  renderHeader();
1970
- renderProcesses2();
1971
- renderSessions();
1972
- renderDetail2();
1973
2370
  screen.render();
1974
- }
1975
- function forceRefresh() {
1976
- cache.sessionsTs = 0;
1977
- cache.procsTs = 0;
1978
- cache.details.clear();
1979
- lastDetailId = "";
1980
- render();
1981
- }
2371
+ }, 5e3);
1982
2372
  screen.key(["q", "C-c"], () => {
2373
+ store.stop();
2374
+ clearInterval(clockTimer);
1983
2375
  screen.destroy();
1984
2376
  process.exit(0);
1985
2377
  });
1986
- screen.key(["r"], () => forceRefresh());
2378
+ screen.key(["r"], () => store.forceRefresh());
1987
2379
  screen.key(["tab"], () => {
1988
2380
  if (focusedPanel === "sessions") {
1989
2381
  focusedPanel = "detail";
@@ -1998,29 +2390,40 @@ ${rows.join("\n")}`);
1998
2390
  }
1999
2391
  screen.render();
2000
2392
  });
2393
+ function onNavigate() {
2394
+ sessionList.select(selectedIdx);
2395
+ const s = store.sessions[selectedIdx];
2396
+ if (s) {
2397
+ const cached = store.details.get(s.id);
2398
+ if (cached) {
2399
+ detailBox.setContent(detailContent(cached));
2400
+ detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${s.id.slice(0, 12)}\u2026{/} `);
2401
+ } else {
2402
+ showQuickPreview(s);
2403
+ }
2404
+ store.requestDetail(s.id, s.agent);
2405
+ }
2406
+ screen.render();
2407
+ }
2001
2408
  sessionList.on("select item", (_item, index) => {
2002
2409
  selectedIdx = index;
2003
- renderDetail2();
2004
- screen.render();
2410
+ onNavigate();
2005
2411
  });
2006
2412
  sessionList.key(["up", "k"], () => {
2007
2413
  if (selectedIdx > 0) {
2008
2414
  selectedIdx--;
2009
- sessionList.select(selectedIdx);
2010
- renderDetail2();
2011
- screen.render();
2415
+ onNavigate();
2012
2416
  }
2013
2417
  });
2014
2418
  sessionList.key(["down", "j"], () => {
2015
- if (selectedIdx < sessions.length - 1) {
2419
+ if (selectedIdx < store.sessions.length - 1) {
2016
2420
  selectedIdx++;
2017
- sessionList.select(selectedIdx);
2018
- renderDetail2();
2019
- screen.render();
2421
+ onNavigate();
2020
2422
  }
2021
2423
  });
2022
2424
  sessionList.key(["enter"], () => {
2023
- renderDetail2(true);
2425
+ const s = store.sessions[selectedIdx];
2426
+ if (s) store.requestDetailNow(s.id, s.agent);
2024
2427
  focusedPanel = "detail";
2025
2428
  detailBox.focus();
2026
2429
  sessionList.style.border.fg = BORDER_COLOR;
@@ -2029,9 +2432,7 @@ ${rows.join("\n")}`);
2029
2432
  });
2030
2433
  sessionList.focus();
2031
2434
  sessionList.style.border.fg = ACCENT;
2032
- render();
2033
- const timer = setInterval(render, refreshSec * 1e3);
2034
- screen.on("destroy", () => clearInterval(timer));
2435
+ store.start(refreshSec * 1e3, 3e3);
2035
2436
  }
2036
2437
 
2037
2438
  // src/commands/web.ts
@@ -2434,19 +2835,19 @@ ${layoutFoot}`);
2434
2835
  import chalk from "chalk";
2435
2836
 
2436
2837
  // src/lib/config.ts
2437
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
2438
- import { join as join7 } from "path";
2439
- import { homedir as homedir5 } from "os";
2838
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2839
+ import { join as join8 } from "path";
2840
+ import { homedir as homedir6 } from "os";
2440
2841
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2441
2842
  import { findUpSync } from "find-up";
2442
- var CONFIG_DIR = join7(homedir5(), ".copilot-agent");
2443
- var GLOBAL_CONFIG = join7(CONFIG_DIR, "config.yaml");
2843
+ var CONFIG_DIR = join8(homedir6(), ".copilot-agent");
2844
+ var GLOBAL_CONFIG = join8(CONFIG_DIR, "config.yaml");
2444
2845
  var PROJECT_CONFIG_NAME = ".copilot-agent.yaml";
2445
2846
  function ensureConfigDir() {
2446
- if (!existsSync6(CONFIG_DIR)) mkdirSync4(CONFIG_DIR, { recursive: true });
2847
+ if (!existsSync7(CONFIG_DIR)) mkdirSync4(CONFIG_DIR, { recursive: true });
2447
2848
  }
2448
2849
  function loadGlobalConfig() {
2449
- if (!existsSync6(GLOBAL_CONFIG)) return {};
2850
+ if (!existsSync7(GLOBAL_CONFIG)) return {};
2450
2851
  try {
2451
2852
  return parseYaml(readFileSync4(GLOBAL_CONFIG, "utf-8")) || {};
2452
2853
  } catch {
@@ -2493,7 +2894,7 @@ function deleteConfigValue(key) {
2493
2894
  saveGlobalConfig(config);
2494
2895
  }
2495
2896
  function resetConfig() {
2496
- if (existsSync6(GLOBAL_CONFIG)) {
2897
+ if (existsSync7(GLOBAL_CONFIG)) {
2497
2898
  writeFileSync2(GLOBAL_CONFIG, "", "utf-8");
2498
2899
  }
2499
2900
  }
@@ -2550,18 +2951,18 @@ function registerConfigCommand(program2) {
2550
2951
  // src/commands/proxy.ts
2551
2952
  import chalk2 from "chalk";
2552
2953
  import { execa } from "execa";
2553
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync5, unlinkSync } from "fs";
2554
- import { join as join8 } from "path";
2555
- import { homedir as homedir6 } from "os";
2556
- var PID_FILE = join8(homedir6(), ".copilot-agent", "proxy.pid");
2954
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync8, mkdirSync as mkdirSync5, unlinkSync } from "fs";
2955
+ import { join as join9 } from "path";
2956
+ import { homedir as homedir7 } from "os";
2957
+ var PID_FILE = join9(homedir7(), ".copilot-agent", "proxy.pid");
2557
2958
  var DEFAULT_PORT = 4141;
2558
2959
  function ensureDir() {
2559
- const dir = join8(homedir6(), ".copilot-agent");
2560
- if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
2960
+ const dir = join9(homedir7(), ".copilot-agent");
2961
+ if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
2561
2962
  }
2562
2963
  function findCopilotToken() {
2563
- const appsPath = join8(homedir6(), ".config", "github-copilot", "apps.json");
2564
- if (!existsSync7(appsPath)) return null;
2964
+ const appsPath = join9(homedir7(), ".config", "github-copilot", "apps.json");
2965
+ if (!existsSync8(appsPath)) return null;
2565
2966
  try {
2566
2967
  const apps = JSON.parse(readFileSync5(appsPath, "utf-8"));
2567
2968
  for (const key of Object.keys(apps)) {
@@ -2574,7 +2975,7 @@ function findCopilotToken() {
2574
2975
  }
2575
2976
  }
2576
2977
  function readPid() {
2577
- if (!existsSync7(PID_FILE)) return null;
2978
+ if (!existsSync8(PID_FILE)) return null;
2578
2979
  try {
2579
2980
  const pid = parseInt(readFileSync5(PID_FILE, "utf-8").trim(), 10);
2580
2981
  return isNaN(pid) ? null : pid;
@@ -2662,12 +3063,12 @@ function registerProxyCommand(program2) {
2662
3063
  }
2663
3064
  if (!isProcessRunning(pid)) {
2664
3065
  console.log(chalk2.dim(` Proxy (PID ${pid}) is not running`));
2665
- if (existsSync7(PID_FILE)) unlinkSync(PID_FILE);
3066
+ if (existsSync8(PID_FILE)) unlinkSync(PID_FILE);
2666
3067
  return;
2667
3068
  }
2668
3069
  try {
2669
3070
  process.kill(pid, "SIGTERM");
2670
- if (existsSync7(PID_FILE)) unlinkSync(PID_FILE);
3071
+ if (existsSync8(PID_FILE)) unlinkSync(PID_FILE);
2671
3072
  console.log(chalk2.green(` \u2714 Proxy stopped (PID ${pid})`));
2672
3073
  } catch (err) {
2673
3074
  const message = err instanceof Error ? err.message : String(err);
@@ -2801,10 +3202,10 @@ function showDiff(sessionId, opts) {
2801
3202
  import chalk4 from "chalk";
2802
3203
 
2803
3204
  // src/lib/quota.ts
2804
- import { join as join9 } from "path";
2805
- import { homedir as homedir7 } from "os";
2806
- var DATA_DIR = join9(homedir7(), ".copilot-agent");
2807
- var USAGE_FILE = join9(DATA_DIR, "usage.jsonl");
3205
+ import { join as join10 } from "path";
3206
+ import { homedir as homedir8 } from "os";
3207
+ var DATA_DIR = join10(homedir8(), ".copilot-agent");
3208
+ var USAGE_FILE = join10(DATA_DIR, "usage.jsonl");
2808
3209
  function buildUsageSummary(days) {
2809
3210
  const sessions = listAllSessions(500);
2810
3211
  const cutoff = days ? Date.now() - days * 864e5 : 0;
@@ -2899,12 +3300,12 @@ function registerQuotaCommand(program2) {
2899
3300
  import chalk5 from "chalk";
2900
3301
 
2901
3302
  // src/lib/compact.ts
2902
- import { writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
2903
- import { join as join10 } from "path";
2904
- import { homedir as homedir8 } from "os";
2905
- var COMPACT_DIR = join10(homedir8(), ".copilot-agent", "compacts");
3303
+ import { writeFileSync as writeFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
3304
+ import { join as join11 } from "path";
3305
+ import { homedir as homedir9 } from "os";
3306
+ var COMPACT_DIR = join11(homedir9(), ".copilot-agent", "compacts");
2906
3307
  function ensureDir2() {
2907
- if (!existsSync8(COMPACT_DIR)) mkdirSync6(COMPACT_DIR, { recursive: true });
3308
+ if (!existsSync9(COMPACT_DIR)) mkdirSync6(COMPACT_DIR, { recursive: true });
2908
3309
  }
2909
3310
  function compactSession(sessionId, agent) {
2910
3311
  const report = getAgentSessionReport(sessionId, agent);
@@ -2997,7 +3398,7 @@ function compactSession(sessionId, agent) {
2997
3398
  function saveCompact(compact) {
2998
3399
  ensureDir2();
2999
3400
  const filename = `${compact.sessionId.slice(0, 12)}.md`;
3000
- const filepath = join10(COMPACT_DIR, filename);
3401
+ const filepath = join11(COMPACT_DIR, filename);
3001
3402
  writeFileSync4(filepath, compact.markdown, "utf-8");
3002
3403
  return filepath;
3003
3404
  }
@@ -3105,17 +3506,17 @@ function showCompact(sessionId, opts) {
3105
3506
  import chalk6 from "chalk";
3106
3507
 
3107
3508
  // src/lib/hooks.ts
3108
- import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
3109
- import { join as join11 } from "path";
3110
- import { homedir as homedir9 } from "os";
3509
+ import { readFileSync as readFileSync6, existsSync as existsSync10 } from "fs";
3510
+ import { join as join12 } from "path";
3511
+ import { homedir as homedir10 } from "os";
3111
3512
  import { parse as parseYaml2 } from "yaml";
3112
3513
  import { findUpSync as findUpSync2 } from "find-up";
3113
3514
  import { execaCommand } from "execa";
3114
- var GLOBAL_HOOKS = join11(homedir9(), ".copilot-agent", "hooks.yaml");
3515
+ var GLOBAL_HOOKS = join12(homedir10(), ".copilot-agent", "hooks.yaml");
3115
3516
  var PROJECT_HOOKS = ".copilot-agent/hooks.yaml";
3116
3517
  function loadHooksConfig(cwd) {
3117
3518
  const configs = [];
3118
- if (existsSync9(GLOBAL_HOOKS)) {
3519
+ if (existsSync10(GLOBAL_HOOKS)) {
3119
3520
  try {
3120
3521
  const parsed = parseYaml2(readFileSync6(GLOBAL_HOOKS, "utf-8"));
3121
3522
  if (parsed) configs.push(parsed);
@@ -3377,17 +3778,17 @@ function formatDur(ms) {
3377
3778
 
3378
3779
  // src/commands/template.ts
3379
3780
  import chalk8 from "chalk";
3380
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
3381
- import { join as join12 } from "path";
3382
- import { homedir as homedir10 } from "os";
3781
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync11, mkdirSync as mkdirSync7 } from "fs";
3782
+ import { join as join13 } from "path";
3783
+ import { homedir as homedir11 } from "os";
3383
3784
  import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
3384
- var CONFIG_DIR2 = join12(homedir10(), ".copilot-agent");
3385
- var TEMPLATES_FILE = join12(CONFIG_DIR2, "templates.yaml");
3785
+ var CONFIG_DIR2 = join13(homedir11(), ".copilot-agent");
3786
+ var TEMPLATES_FILE = join13(CONFIG_DIR2, "templates.yaml");
3386
3787
  function ensureDir3() {
3387
- if (!existsSync10(CONFIG_DIR2)) mkdirSync7(CONFIG_DIR2, { recursive: true });
3788
+ if (!existsSync11(CONFIG_DIR2)) mkdirSync7(CONFIG_DIR2, { recursive: true });
3388
3789
  }
3389
3790
  function loadTemplates() {
3390
- if (!existsSync10(TEMPLATES_FILE)) return [];
3791
+ if (!existsSync11(TEMPLATES_FILE)) return [];
3391
3792
  try {
3392
3793
  const data = parseYaml3(readFileSync7(TEMPLATES_FILE, "utf-8"));
3393
3794
  return Array.isArray(data) ? data : [];
@@ -3617,15 +4018,15 @@ function fmtDur2(ms) {
3617
4018
  }
3618
4019
 
3619
4020
  // src/commands/multi.ts
3620
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
3621
- import { join as join13, resolve as resolve7, basename as basename3 } from "path";
3622
- import { homedir as homedir11 } from "os";
4021
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
4022
+ import { join as join14, resolve as resolve7, basename as basename4 } from "path";
4023
+ import { homedir as homedir12 } from "os";
3623
4024
  import chalk10 from "chalk";
3624
4025
  import { parse as parseYaml4, stringify as stringifyYaml3 } from "yaml";
3625
- var CONFIG_DIR3 = join13(homedir11(), ".copilot-agent");
3626
- var PROJECTS_FILE = join13(CONFIG_DIR3, "multi-projects.txt");
3627
- var STATUS_FILE = join13(CONFIG_DIR3, "multi-status.yaml");
3628
- var LOG_DIR = join13(CONFIG_DIR3, "multi-logs");
4026
+ var CONFIG_DIR3 = join14(homedir12(), ".copilot-agent");
4027
+ var PROJECTS_FILE = join14(CONFIG_DIR3, "multi-projects.txt");
4028
+ var STATUS_FILE = join14(CONFIG_DIR3, "multi-status.yaml");
4029
+ var LOG_DIR = join14(CONFIG_DIR3, "multi-logs");
3629
4030
  var MAX_CONCURRENCY = 3;
3630
4031
  function registerMultiCommand(program2) {
3631
4032
  program2.command("multi <action> [args...]").description("Multi-project orchestration \u2014 add/remove/list/run/status/research").option("-a, --agent <type>", "Agent to use (copilot or claude)", "copilot").option("--parallel", "Run projects in parallel (max 3 concurrent)").option("--cooldown <n>", "Cooldown between projects in seconds", "30").option("-s, --steps <n>", "Max steps per task", "10").option("--max-premium <n>", "Premium budget per project", "50").option("--dry-run", "Preview without executing").action(async (action, args, opts) => {
@@ -3668,7 +4069,7 @@ async function multiCommand(action, args, opts) {
3668
4069
  function ensureFiles() {
3669
4070
  mkdirSync8(LOG_DIR, { recursive: true });
3670
4071
  mkdirSync8(CONFIG_DIR3, { recursive: true });
3671
- if (!existsSync11(PROJECTS_FILE)) writeFileSync7(PROJECTS_FILE, "");
4072
+ if (!existsSync12(PROJECTS_FILE)) writeFileSync7(PROJECTS_FILE, "");
3672
4073
  }
3673
4074
  function readProjects() {
3674
4075
  return readFileSync8(PROJECTS_FILE, "utf-8").split("\n").map((l) => l.trim()).filter(Boolean);
@@ -3677,7 +4078,7 @@ function writeProjects(projects) {
3677
4078
  writeFileSync7(PROJECTS_FILE, projects.join("\n") + "\n");
3678
4079
  }
3679
4080
  function readStatusFile() {
3680
- if (!existsSync11(STATUS_FILE)) return [];
4081
+ if (!existsSync12(STATUS_FILE)) return [];
3681
4082
  try {
3682
4083
  const raw = readFileSync8(STATUS_FILE, "utf-8");
3683
4084
  const parsed = parseYaml4(raw);
@@ -3711,7 +4112,7 @@ async function addProject(path) {
3711
4112
  process.exit(1);
3712
4113
  }
3713
4114
  const resolved = resolve7(path);
3714
- if (!existsSync11(resolved)) {
4115
+ if (!existsSync12(resolved)) {
3715
4116
  fail(`Not found: ${resolved}`);
3716
4117
  process.exit(1);
3717
4118
  }
@@ -3753,7 +4154,7 @@ function listProjects() {
3753
4154
  console.log(chalk10.bold("\nRegistered projects:"));
3754
4155
  for (let i = 0; i < projects.length; i++) {
3755
4156
  const p = projects[i];
3756
- const exists = existsSync11(p);
4157
+ const exists = existsSync12(p);
3757
4158
  const icon = exists ? chalk10.green("\u2705") : chalk10.red("\u274C");
3758
4159
  const type = exists ? detectProjectType(p) : "?";
3759
4160
  const st = statuses.find((s) => s.project === p);
@@ -3772,7 +4173,7 @@ function showStatus() {
3772
4173
  for (const s of statuses) {
3773
4174
  const icon = s.status === "success" ? chalk10.green("\u2705") : s.status === "failed" ? chalk10.red("\u274C") : chalk10.yellow("\u{1F504}");
3774
4175
  console.log(
3775
- ` ${icon} ${chalk10.bold(basename3(s.project))} \u2014 ${s.agent} \u2014 ${s.tasks} tasks \u2014 ${s.duration} \u2014 ${chalk10.dim(s.lastRun)}`
4176
+ ` ${icon} ${chalk10.bold(basename4(s.project))} \u2014 ${s.agent} \u2014 ${s.tasks} tasks \u2014 ${s.duration} \u2014 ${chalk10.dim(s.lastRun)}`
3776
4177
  );
3777
4178
  }
3778
4179
  console.log();
@@ -3802,7 +4203,7 @@ function createSemaphore(max) {
3802
4203
  async function runAll(mode, opts) {
3803
4204
  assertAgent(opts.agent);
3804
4205
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 15);
3805
- setLogFile(join13(LOG_DIR, `multi-${mode}-${ts}.log`));
4206
+ setLogFile(join14(LOG_DIR, `multi-${mode}-${ts}.log`));
3806
4207
  const projects = readProjects();
3807
4208
  if (projects.length === 0) {
3808
4209
  fail("No projects registered. Add: copilot-agent multi add <path>");
@@ -3811,10 +4212,10 @@ async function runAll(mode, opts) {
3811
4212
  log(`\u{1F3ED} Multi-project ${mode} \u2014 ${projects.length} projects \u2014 agent: ${opts.agent}${opts.parallel ? " (parallel)" : ""}`);
3812
4213
  if (opts.dryRun) {
3813
4214
  for (const p of projects) {
3814
- const type = existsSync11(p) ? detectProjectType(p) : "unknown";
4215
+ const type = existsSync12(p) ? detectProjectType(p) : "unknown";
3815
4216
  const tasks = getTasksForProject(type);
3816
4217
  console.log(`
3817
- ${chalk10.bold(basename3(p))} (${type})`);
4218
+ ${chalk10.bold(basename4(p))} (${type})`);
3818
4219
  for (const t of tasks.slice(0, 3)) {
3819
4220
  console.log(` ${chalk10.dim("\u2022")} ${t.title}`);
3820
4221
  }
@@ -3860,8 +4261,8 @@ ${"\u2550".repeat(50)}`);
3860
4261
  notify(`Multi-${mode}: ${success}/${total} succeeded`, "copilot-agent");
3861
4262
  }
3862
4263
  async function runSingleProject(project, mode, opts) {
3863
- const name = existsSync11(project) ? detectProjectName(project) : basename3(project);
3864
- if (!existsSync11(project)) {
4264
+ const name = existsSync12(project) ? detectProjectName(project) : basename4(project);
4265
+ if (!existsSync12(project)) {
3865
4266
  warn(`Skipping (not found): ${project}`);
3866
4267
  return { name, success: false, skipped: true };
3867
4268
  }
@@ -4041,17 +4442,17 @@ function registerReviewCommand(program2) {
4041
4442
  }
4042
4443
 
4043
4444
  // src/commands/schedule.ts
4044
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
4045
- import { join as join14 } from "path";
4046
- import { homedir as homedir12 } from "os";
4445
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, existsSync as existsSync13, mkdirSync as mkdirSync9 } from "fs";
4446
+ import { join as join15 } from "path";
4447
+ import { homedir as homedir13 } from "os";
4047
4448
  import { parse as parseYaml5, stringify as stringifyYaml4 } from "yaml";
4048
- var CONFIG_DIR4 = join14(homedir12(), ".copilot-agent");
4049
- var SCHEDULES_FILE = join14(CONFIG_DIR4, "schedules.yaml");
4449
+ var CONFIG_DIR4 = join15(homedir13(), ".copilot-agent");
4450
+ var SCHEDULES_FILE = join15(CONFIG_DIR4, "schedules.yaml");
4050
4451
  function ensureConfigDir2() {
4051
- if (!existsSync12(CONFIG_DIR4)) mkdirSync9(CONFIG_DIR4, { recursive: true });
4452
+ if (!existsSync13(CONFIG_DIR4)) mkdirSync9(CONFIG_DIR4, { recursive: true });
4052
4453
  }
4053
4454
  function loadSchedules() {
4054
- if (!existsSync12(SCHEDULES_FILE)) return [];
4455
+ if (!existsSync13(SCHEDULES_FILE)) return [];
4055
4456
  try {
4056
4457
  const raw = parseYaml5(readFileSync9(SCHEDULES_FILE, "utf-8"));
4057
4458
  return Array.isArray(raw) ? raw : [];
@@ -4290,7 +4691,7 @@ function registerScheduleCommand(program2) {
4290
4691
 
4291
4692
  // src/index.ts
4292
4693
  var program = new Command();
4293
- program.name("copilot-agent").version("1.0.0").description("Autonomous AI agent manager \u2014 auto-resume, task discovery, overnight runs. Supports GitHub Copilot CLI + Claude Code.");
4694
+ program.name("copilot-agent").version("1.1.0").description("Autonomous AI agent manager \u2014 auto-resume, task discovery, overnight runs. Supports GitHub Copilot CLI + Claude Code.");
4294
4695
  registerStatusCommand(program);
4295
4696
  registerWatchCommand(program);
4296
4697
  registerRunCommand(program);