copilot-agent 1.0.1 → 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,71 +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
- let detailDebounce = null;
1925
- function showQuickPreview(s) {
1926
- const project = s.cwd?.split("/").pop() || s.id.slice(0, 12);
1927
- const agent = s.agent === "claude" ? "{yellow-fg}claude{/}" : "{cyan-fg}copilot{/}";
1928
- const status = s.complete ? "{green-fg}\u2714 complete{/}" : "{yellow-fg}\u23F3 incomplete{/}";
1929
- const lines = [
1930
- `{bold}${project}{/} ${agent}`,
1931
- `{gray-fg}${s.id}{/}`,
1932
- "",
1933
- ` Status ${status}`,
1934
- ` Premium {yellow-fg}${s.premiumRequests}{/}`,
1935
- ` Activity ${fmtTimeAgo(s.mtime)}`,
1936
- ` Directory {gray-fg}${s.cwd || "\u2014"}{/}`,
1937
- "",
1938
- "{gray-fg}Loading full report\u2026{/}"
1939
- ];
1940
- detailBox.setContent(lines.join("\n"));
1941
- detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${s.id.slice(0, 12)}\u2026{/} `);
1942
- }
1943
- function scheduleDetailRender(force = false) {
1944
- if (detailDebounce) clearTimeout(detailDebounce);
1945
- if (sessions.length === 0 || selectedIdx < 0 || selectedIdx >= sessions.length) {
1946
- detailBox.setContent("{gray-fg}No session selected{/}");
1947
- lastDetailId = "";
1948
- return;
1949
- }
1950
- const s = sessions[selectedIdx];
1951
- const cached = cache.details.get(s.id);
1952
- if (!force && cached && Date.now() - cached.ts < 1e4 && cached.report) {
1953
- lastDetailId = s.id;
1954
- detailBox.setContent(detailContent(cached.report));
1955
- detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${s.id.slice(0, 12)}\u2026{/} `);
1956
- return;
1957
- }
1958
- if (s.id !== lastDetailId) {
1959
- showQuickPreview(s);
1960
- }
1961
- detailDebounce = setTimeout(() => {
1962
- detailDebounce = null;
1963
- if (selectedIdx < 0 || selectedIdx >= sessions.length) return;
1964
- const current = sessions[selectedIdx];
1965
- lastDetailId = current.id;
1966
- const report = cacheDetail(cache, current);
1967
- if (report) {
1968
- detailBox.setContent(detailContent(report));
1969
- detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${current.id.slice(0, 12)}\u2026{/} `);
1970
- } else {
1971
- detailBox.setContent(`{gray-fg}No report data for ${current.id.slice(0, 8)}\u2026{/}`);
1972
- }
1973
- screen.render();
1974
- }, 150);
1975
- }
1976
2300
  function renderHeader() {
1977
2301
  const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB");
2302
+ const sessions = store.sessions;
1978
2303
  const totalPremium = sessions.reduce((s, x) => s + x.premiumRequests, 0);
1979
2304
  const completedCount = sessions.filter((s) => s.complete).length;
1980
- const stats = headerStats(procs.length, sessions.length, totalPremium, completedCount);
2305
+ const stats = headerStats(store.processes.length, sessions.length, totalPremium, completedCount);
1981
2306
  headerBox.setContent(` {bold}{cyan-fg}\u26A1 copilot-agent{/} ${stats} {gray-fg}${time}{/}`);
1982
2307
  }
1983
2308
  function renderProcesses2() {
2309
+ const procs = store.processes;
1984
2310
  if (procs.length === 0) {
1985
2311
  processBox.setContent(" {gray-fg}No agent processes running{/}");
1986
2312
  return;
@@ -1991,35 +2317,65 @@ function runBlessedDashboard(refreshSec, limit) {
1991
2317
  ${rows.join("\n")}`);
1992
2318
  }
1993
2319
  function renderSessions() {
1994
- const items = sessions.map((s) => sessionRow(s));
2320
+ const items = store.sessions.map((s) => sessionRow(s));
1995
2321
  sessionList.setItems(items);
1996
2322
  if (selectedIdx >= 0 && selectedIdx < items.length) {
1997
2323
  sessionList.select(selectedIdx);
1998
2324
  }
1999
- sessionList.setLabel(` {cyan-fg}{bold}Sessions (${sessions.length}){/} `);
2325
+ sessionList.setLabel(` {cyan-fg}{bold}Sessions (${store.sessions.length}){/} `);
2000
2326
  }
2001
- function render() {
2002
- sessions = cacheSessions(cache, limit);
2003
- procs = cacheProcs(cache);
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", () => {
2004
2345
  renderHeader();
2005
- renderProcesses2();
2006
2346
  renderSessions();
2007
- scheduleDetailRender();
2347
+ const s = store.sessions[selectedIdx];
2348
+ if (s) store.requestDetail(s.id, s.agent);
2008
2349
  screen.render();
2009
- }
2010
- function forceRefresh() {
2011
- cache.sessionsTs = 0;
2012
- cache.procsTs = 0;
2013
- cache.details.clear();
2014
- lastDetailId = "";
2015
- render();
2016
- }
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);
2360
+ if (report) {
2361
+ detailBox.setContent(detailContent(report));
2362
+ detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${sessionId.slice(0, 12)}\u2026{/} `);
2363
+ } else {
2364
+ detailBox.setContent(`{gray-fg}No report data for ${sessionId.slice(0, 8)}\u2026{/}`);
2365
+ }
2366
+ screen.render();
2367
+ });
2368
+ const clockTimer = setInterval(() => {
2369
+ renderHeader();
2370
+ screen.render();
2371
+ }, 5e3);
2017
2372
  screen.key(["q", "C-c"], () => {
2018
- if (detailDebounce) clearTimeout(detailDebounce);
2373
+ store.stop();
2374
+ clearInterval(clockTimer);
2019
2375
  screen.destroy();
2020
2376
  process.exit(0);
2021
2377
  });
2022
- screen.key(["r"], () => forceRefresh());
2378
+ screen.key(["r"], () => store.forceRefresh());
2023
2379
  screen.key(["tab"], () => {
2024
2380
  if (focusedPanel === "sessions") {
2025
2381
  focusedPanel = "detail";
@@ -2034,29 +2390,40 @@ ${rows.join("\n")}`);
2034
2390
  }
2035
2391
  screen.render();
2036
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
+ }
2037
2408
  sessionList.on("select item", (_item, index) => {
2038
2409
  selectedIdx = index;
2039
- scheduleDetailRender();
2040
- screen.render();
2410
+ onNavigate();
2041
2411
  });
2042
2412
  sessionList.key(["up", "k"], () => {
2043
2413
  if (selectedIdx > 0) {
2044
2414
  selectedIdx--;
2045
- sessionList.select(selectedIdx);
2046
- scheduleDetailRender();
2047
- screen.render();
2415
+ onNavigate();
2048
2416
  }
2049
2417
  });
2050
2418
  sessionList.key(["down", "j"], () => {
2051
- if (selectedIdx < sessions.length - 1) {
2419
+ if (selectedIdx < store.sessions.length - 1) {
2052
2420
  selectedIdx++;
2053
- sessionList.select(selectedIdx);
2054
- scheduleDetailRender();
2055
- screen.render();
2421
+ onNavigate();
2056
2422
  }
2057
2423
  });
2058
2424
  sessionList.key(["enter"], () => {
2059
- scheduleDetailRender(true);
2425
+ const s = store.sessions[selectedIdx];
2426
+ if (s) store.requestDetailNow(s.id, s.agent);
2060
2427
  focusedPanel = "detail";
2061
2428
  detailBox.focus();
2062
2429
  sessionList.style.border.fg = BORDER_COLOR;
@@ -2065,9 +2432,7 @@ ${rows.join("\n")}`);
2065
2432
  });
2066
2433
  sessionList.focus();
2067
2434
  sessionList.style.border.fg = ACCENT;
2068
- render();
2069
- const timer = setInterval(render, refreshSec * 1e3);
2070
- screen.on("destroy", () => clearInterval(timer));
2435
+ store.start(refreshSec * 1e3, 3e3);
2071
2436
  }
2072
2437
 
2073
2438
  // src/commands/web.ts
@@ -2470,19 +2835,19 @@ ${layoutFoot}`);
2470
2835
  import chalk from "chalk";
2471
2836
 
2472
2837
  // src/lib/config.ts
2473
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
2474
- import { join as join7 } from "path";
2475
- 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";
2476
2841
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2477
2842
  import { findUpSync } from "find-up";
2478
- var CONFIG_DIR = join7(homedir5(), ".copilot-agent");
2479
- 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");
2480
2845
  var PROJECT_CONFIG_NAME = ".copilot-agent.yaml";
2481
2846
  function ensureConfigDir() {
2482
- if (!existsSync6(CONFIG_DIR)) mkdirSync4(CONFIG_DIR, { recursive: true });
2847
+ if (!existsSync7(CONFIG_DIR)) mkdirSync4(CONFIG_DIR, { recursive: true });
2483
2848
  }
2484
2849
  function loadGlobalConfig() {
2485
- if (!existsSync6(GLOBAL_CONFIG)) return {};
2850
+ if (!existsSync7(GLOBAL_CONFIG)) return {};
2486
2851
  try {
2487
2852
  return parseYaml(readFileSync4(GLOBAL_CONFIG, "utf-8")) || {};
2488
2853
  } catch {
@@ -2529,7 +2894,7 @@ function deleteConfigValue(key) {
2529
2894
  saveGlobalConfig(config);
2530
2895
  }
2531
2896
  function resetConfig() {
2532
- if (existsSync6(GLOBAL_CONFIG)) {
2897
+ if (existsSync7(GLOBAL_CONFIG)) {
2533
2898
  writeFileSync2(GLOBAL_CONFIG, "", "utf-8");
2534
2899
  }
2535
2900
  }
@@ -2586,18 +2951,18 @@ function registerConfigCommand(program2) {
2586
2951
  // src/commands/proxy.ts
2587
2952
  import chalk2 from "chalk";
2588
2953
  import { execa } from "execa";
2589
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync5, unlinkSync } from "fs";
2590
- import { join as join8 } from "path";
2591
- import { homedir as homedir6 } from "os";
2592
- 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");
2593
2958
  var DEFAULT_PORT = 4141;
2594
2959
  function ensureDir() {
2595
- const dir = join8(homedir6(), ".copilot-agent");
2596
- if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
2960
+ const dir = join9(homedir7(), ".copilot-agent");
2961
+ if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
2597
2962
  }
2598
2963
  function findCopilotToken() {
2599
- const appsPath = join8(homedir6(), ".config", "github-copilot", "apps.json");
2600
- if (!existsSync7(appsPath)) return null;
2964
+ const appsPath = join9(homedir7(), ".config", "github-copilot", "apps.json");
2965
+ if (!existsSync8(appsPath)) return null;
2601
2966
  try {
2602
2967
  const apps = JSON.parse(readFileSync5(appsPath, "utf-8"));
2603
2968
  for (const key of Object.keys(apps)) {
@@ -2610,7 +2975,7 @@ function findCopilotToken() {
2610
2975
  }
2611
2976
  }
2612
2977
  function readPid() {
2613
- if (!existsSync7(PID_FILE)) return null;
2978
+ if (!existsSync8(PID_FILE)) return null;
2614
2979
  try {
2615
2980
  const pid = parseInt(readFileSync5(PID_FILE, "utf-8").trim(), 10);
2616
2981
  return isNaN(pid) ? null : pid;
@@ -2698,12 +3063,12 @@ function registerProxyCommand(program2) {
2698
3063
  }
2699
3064
  if (!isProcessRunning(pid)) {
2700
3065
  console.log(chalk2.dim(` Proxy (PID ${pid}) is not running`));
2701
- if (existsSync7(PID_FILE)) unlinkSync(PID_FILE);
3066
+ if (existsSync8(PID_FILE)) unlinkSync(PID_FILE);
2702
3067
  return;
2703
3068
  }
2704
3069
  try {
2705
3070
  process.kill(pid, "SIGTERM");
2706
- if (existsSync7(PID_FILE)) unlinkSync(PID_FILE);
3071
+ if (existsSync8(PID_FILE)) unlinkSync(PID_FILE);
2707
3072
  console.log(chalk2.green(` \u2714 Proxy stopped (PID ${pid})`));
2708
3073
  } catch (err) {
2709
3074
  const message = err instanceof Error ? err.message : String(err);
@@ -2837,10 +3202,10 @@ function showDiff(sessionId, opts) {
2837
3202
  import chalk4 from "chalk";
2838
3203
 
2839
3204
  // src/lib/quota.ts
2840
- import { join as join9 } from "path";
2841
- import { homedir as homedir7 } from "os";
2842
- var DATA_DIR = join9(homedir7(), ".copilot-agent");
2843
- 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");
2844
3209
  function buildUsageSummary(days) {
2845
3210
  const sessions = listAllSessions(500);
2846
3211
  const cutoff = days ? Date.now() - days * 864e5 : 0;
@@ -2935,12 +3300,12 @@ function registerQuotaCommand(program2) {
2935
3300
  import chalk5 from "chalk";
2936
3301
 
2937
3302
  // src/lib/compact.ts
2938
- import { writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
2939
- import { join as join10 } from "path";
2940
- import { homedir as homedir8 } from "os";
2941
- 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");
2942
3307
  function ensureDir2() {
2943
- if (!existsSync8(COMPACT_DIR)) mkdirSync6(COMPACT_DIR, { recursive: true });
3308
+ if (!existsSync9(COMPACT_DIR)) mkdirSync6(COMPACT_DIR, { recursive: true });
2944
3309
  }
2945
3310
  function compactSession(sessionId, agent) {
2946
3311
  const report = getAgentSessionReport(sessionId, agent);
@@ -3033,7 +3398,7 @@ function compactSession(sessionId, agent) {
3033
3398
  function saveCompact(compact) {
3034
3399
  ensureDir2();
3035
3400
  const filename = `${compact.sessionId.slice(0, 12)}.md`;
3036
- const filepath = join10(COMPACT_DIR, filename);
3401
+ const filepath = join11(COMPACT_DIR, filename);
3037
3402
  writeFileSync4(filepath, compact.markdown, "utf-8");
3038
3403
  return filepath;
3039
3404
  }
@@ -3141,17 +3506,17 @@ function showCompact(sessionId, opts) {
3141
3506
  import chalk6 from "chalk";
3142
3507
 
3143
3508
  // src/lib/hooks.ts
3144
- import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
3145
- import { join as join11 } from "path";
3146
- 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";
3147
3512
  import { parse as parseYaml2 } from "yaml";
3148
3513
  import { findUpSync as findUpSync2 } from "find-up";
3149
3514
  import { execaCommand } from "execa";
3150
- var GLOBAL_HOOKS = join11(homedir9(), ".copilot-agent", "hooks.yaml");
3515
+ var GLOBAL_HOOKS = join12(homedir10(), ".copilot-agent", "hooks.yaml");
3151
3516
  var PROJECT_HOOKS = ".copilot-agent/hooks.yaml";
3152
3517
  function loadHooksConfig(cwd) {
3153
3518
  const configs = [];
3154
- if (existsSync9(GLOBAL_HOOKS)) {
3519
+ if (existsSync10(GLOBAL_HOOKS)) {
3155
3520
  try {
3156
3521
  const parsed = parseYaml2(readFileSync6(GLOBAL_HOOKS, "utf-8"));
3157
3522
  if (parsed) configs.push(parsed);
@@ -3413,17 +3778,17 @@ function formatDur(ms) {
3413
3778
 
3414
3779
  // src/commands/template.ts
3415
3780
  import chalk8 from "chalk";
3416
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
3417
- import { join as join12 } from "path";
3418
- 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";
3419
3784
  import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
3420
- var CONFIG_DIR2 = join12(homedir10(), ".copilot-agent");
3421
- 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");
3422
3787
  function ensureDir3() {
3423
- if (!existsSync10(CONFIG_DIR2)) mkdirSync7(CONFIG_DIR2, { recursive: true });
3788
+ if (!existsSync11(CONFIG_DIR2)) mkdirSync7(CONFIG_DIR2, { recursive: true });
3424
3789
  }
3425
3790
  function loadTemplates() {
3426
- if (!existsSync10(TEMPLATES_FILE)) return [];
3791
+ if (!existsSync11(TEMPLATES_FILE)) return [];
3427
3792
  try {
3428
3793
  const data = parseYaml3(readFileSync7(TEMPLATES_FILE, "utf-8"));
3429
3794
  return Array.isArray(data) ? data : [];
@@ -3653,15 +4018,15 @@ function fmtDur2(ms) {
3653
4018
  }
3654
4019
 
3655
4020
  // src/commands/multi.ts
3656
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
3657
- import { join as join13, resolve as resolve7, basename as basename3 } from "path";
3658
- 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";
3659
4024
  import chalk10 from "chalk";
3660
4025
  import { parse as parseYaml4, stringify as stringifyYaml3 } from "yaml";
3661
- var CONFIG_DIR3 = join13(homedir11(), ".copilot-agent");
3662
- var PROJECTS_FILE = join13(CONFIG_DIR3, "multi-projects.txt");
3663
- var STATUS_FILE = join13(CONFIG_DIR3, "multi-status.yaml");
3664
- 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");
3665
4030
  var MAX_CONCURRENCY = 3;
3666
4031
  function registerMultiCommand(program2) {
3667
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) => {
@@ -3704,7 +4069,7 @@ async function multiCommand(action, args, opts) {
3704
4069
  function ensureFiles() {
3705
4070
  mkdirSync8(LOG_DIR, { recursive: true });
3706
4071
  mkdirSync8(CONFIG_DIR3, { recursive: true });
3707
- if (!existsSync11(PROJECTS_FILE)) writeFileSync7(PROJECTS_FILE, "");
4072
+ if (!existsSync12(PROJECTS_FILE)) writeFileSync7(PROJECTS_FILE, "");
3708
4073
  }
3709
4074
  function readProjects() {
3710
4075
  return readFileSync8(PROJECTS_FILE, "utf-8").split("\n").map((l) => l.trim()).filter(Boolean);
@@ -3713,7 +4078,7 @@ function writeProjects(projects) {
3713
4078
  writeFileSync7(PROJECTS_FILE, projects.join("\n") + "\n");
3714
4079
  }
3715
4080
  function readStatusFile() {
3716
- if (!existsSync11(STATUS_FILE)) return [];
4081
+ if (!existsSync12(STATUS_FILE)) return [];
3717
4082
  try {
3718
4083
  const raw = readFileSync8(STATUS_FILE, "utf-8");
3719
4084
  const parsed = parseYaml4(raw);
@@ -3747,7 +4112,7 @@ async function addProject(path) {
3747
4112
  process.exit(1);
3748
4113
  }
3749
4114
  const resolved = resolve7(path);
3750
- if (!existsSync11(resolved)) {
4115
+ if (!existsSync12(resolved)) {
3751
4116
  fail(`Not found: ${resolved}`);
3752
4117
  process.exit(1);
3753
4118
  }
@@ -3789,7 +4154,7 @@ function listProjects() {
3789
4154
  console.log(chalk10.bold("\nRegistered projects:"));
3790
4155
  for (let i = 0; i < projects.length; i++) {
3791
4156
  const p = projects[i];
3792
- const exists = existsSync11(p);
4157
+ const exists = existsSync12(p);
3793
4158
  const icon = exists ? chalk10.green("\u2705") : chalk10.red("\u274C");
3794
4159
  const type = exists ? detectProjectType(p) : "?";
3795
4160
  const st = statuses.find((s) => s.project === p);
@@ -3808,7 +4173,7 @@ function showStatus() {
3808
4173
  for (const s of statuses) {
3809
4174
  const icon = s.status === "success" ? chalk10.green("\u2705") : s.status === "failed" ? chalk10.red("\u274C") : chalk10.yellow("\u{1F504}");
3810
4175
  console.log(
3811
- ` ${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)}`
3812
4177
  );
3813
4178
  }
3814
4179
  console.log();
@@ -3838,7 +4203,7 @@ function createSemaphore(max) {
3838
4203
  async function runAll(mode, opts) {
3839
4204
  assertAgent(opts.agent);
3840
4205
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 15);
3841
- setLogFile(join13(LOG_DIR, `multi-${mode}-${ts}.log`));
4206
+ setLogFile(join14(LOG_DIR, `multi-${mode}-${ts}.log`));
3842
4207
  const projects = readProjects();
3843
4208
  if (projects.length === 0) {
3844
4209
  fail("No projects registered. Add: copilot-agent multi add <path>");
@@ -3847,10 +4212,10 @@ async function runAll(mode, opts) {
3847
4212
  log(`\u{1F3ED} Multi-project ${mode} \u2014 ${projects.length} projects \u2014 agent: ${opts.agent}${opts.parallel ? " (parallel)" : ""}`);
3848
4213
  if (opts.dryRun) {
3849
4214
  for (const p of projects) {
3850
- const type = existsSync11(p) ? detectProjectType(p) : "unknown";
4215
+ const type = existsSync12(p) ? detectProjectType(p) : "unknown";
3851
4216
  const tasks = getTasksForProject(type);
3852
4217
  console.log(`
3853
- ${chalk10.bold(basename3(p))} (${type})`);
4218
+ ${chalk10.bold(basename4(p))} (${type})`);
3854
4219
  for (const t of tasks.slice(0, 3)) {
3855
4220
  console.log(` ${chalk10.dim("\u2022")} ${t.title}`);
3856
4221
  }
@@ -3896,8 +4261,8 @@ ${"\u2550".repeat(50)}`);
3896
4261
  notify(`Multi-${mode}: ${success}/${total} succeeded`, "copilot-agent");
3897
4262
  }
3898
4263
  async function runSingleProject(project, mode, opts) {
3899
- const name = existsSync11(project) ? detectProjectName(project) : basename3(project);
3900
- if (!existsSync11(project)) {
4264
+ const name = existsSync12(project) ? detectProjectName(project) : basename4(project);
4265
+ if (!existsSync12(project)) {
3901
4266
  warn(`Skipping (not found): ${project}`);
3902
4267
  return { name, success: false, skipped: true };
3903
4268
  }
@@ -4077,17 +4442,17 @@ function registerReviewCommand(program2) {
4077
4442
  }
4078
4443
 
4079
4444
  // src/commands/schedule.ts
4080
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "fs";
4081
- import { join as join14 } from "path";
4082
- 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";
4083
4448
  import { parse as parseYaml5, stringify as stringifyYaml4 } from "yaml";
4084
- var CONFIG_DIR4 = join14(homedir12(), ".copilot-agent");
4085
- 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");
4086
4451
  function ensureConfigDir2() {
4087
- if (!existsSync12(CONFIG_DIR4)) mkdirSync9(CONFIG_DIR4, { recursive: true });
4452
+ if (!existsSync13(CONFIG_DIR4)) mkdirSync9(CONFIG_DIR4, { recursive: true });
4088
4453
  }
4089
4454
  function loadSchedules() {
4090
- if (!existsSync12(SCHEDULES_FILE)) return [];
4455
+ if (!existsSync13(SCHEDULES_FILE)) return [];
4091
4456
  try {
4092
4457
  const raw = parseYaml5(readFileSync9(SCHEDULES_FILE, "utf-8"));
4093
4458
  return Array.isArray(raw) ? raw : [];
@@ -4326,7 +4691,7 @@ function registerScheduleCommand(program2) {
4326
4691
 
4327
4692
  // src/index.ts
4328
4693
  var program = new Command();
4329
- program.name("copilot-agent").version("1.0.1").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.");
4330
4695
  registerStatusCommand(program);
4331
4696
  registerWatchCommand(program);
4332
4697
  registerRunCommand(program);