copilot-agent 0.11.0 → 0.12.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/README.md +36 -3
- package/dist/index.js +310 -105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ Autonomous AI agent manager — auto-resume sessions, discover tasks, run overni
|
|
|
21
21
|
| **`compact`** | Generate context summary for session handoff/resume |
|
|
22
22
|
| **`hooks`** | Event-driven automation (on_task_complete, on_error, etc.) |
|
|
23
23
|
| **`pr`** | Auto-create GitHub Pull Request from session changes |
|
|
24
|
+
| **`log`** | Search, timeline, and export session history |
|
|
25
|
+
| **`template`** | Manage custom task templates (add/list/remove/import/export) |
|
|
24
26
|
|
|
25
27
|
All commands support `--agent copilot` or `--agent claude` (auto-detects if omitted).
|
|
26
28
|
|
|
@@ -107,11 +109,11 @@ copilot-agent diff abc12345-... --stat
|
|
|
107
109
|
### Dashboards
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
|
-
# htop-style TUI (blessed — scrollable, keyboard nav)
|
|
112
|
+
# htop-style TUI (blessed — scrollable, keyboard nav, cached rendering)
|
|
111
113
|
copilot-agent dashboard
|
|
112
114
|
|
|
113
|
-
#
|
|
114
|
-
copilot-agent dashboard --
|
|
115
|
+
# Custom refresh interval
|
|
116
|
+
copilot-agent dashboard --refresh 3
|
|
115
117
|
|
|
116
118
|
# Web UI (Hono + htmx, opens browser)
|
|
117
119
|
copilot-agent web
|
|
@@ -204,6 +206,37 @@ copilot-agent pr --dry-run
|
|
|
204
206
|
copilot-agent pr --no-draft
|
|
205
207
|
```
|
|
206
208
|
|
|
209
|
+
### Session log (search & export)
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Search sessions by keyword
|
|
213
|
+
copilot-agent log search "auth" --limit 10
|
|
214
|
+
|
|
215
|
+
# View session timeline
|
|
216
|
+
copilot-agent log timeline <session-id>
|
|
217
|
+
|
|
218
|
+
# Export history as JSON or CSV
|
|
219
|
+
copilot-agent log export --format json --output sessions.json
|
|
220
|
+
copilot-agent log export --format csv --limit 50
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Task templates
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# List custom templates
|
|
227
|
+
copilot-agent template list
|
|
228
|
+
|
|
229
|
+
# Add a reusable task template
|
|
230
|
+
copilot-agent template add security-audit --prompt "Run a full security audit"
|
|
231
|
+
|
|
232
|
+
# Remove a template
|
|
233
|
+
copilot-agent template remove security-audit
|
|
234
|
+
|
|
235
|
+
# Export/import templates (YAML)
|
|
236
|
+
copilot-agent template export > my-templates.yaml
|
|
237
|
+
copilot-agent template import team-templates.yaml
|
|
238
|
+
```
|
|
239
|
+
|
|
207
240
|
## How it works
|
|
208
241
|
|
|
209
242
|
1. **Agent abstraction** — Unified interface for both Copilot CLI and Claude Code
|
package/dist/index.js
CHANGED
|
@@ -1779,16 +1779,53 @@ async function loadBlessed() {
|
|
|
1779
1779
|
blessed = mod.default || mod;
|
|
1780
1780
|
}
|
|
1781
1781
|
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").
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
} else {
|
|
1786
|
-
await loadBlessed();
|
|
1787
|
-
runBlessedDashboard(parseInt(opts.refresh, 10), parseInt(opts.limit, 10));
|
|
1788
|
-
}
|
|
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) => {
|
|
1783
|
+
await loadBlessed();
|
|
1784
|
+
runBlessedDashboard(parseInt(opts.refresh, 10), parseInt(opts.limit, 10));
|
|
1789
1785
|
});
|
|
1790
1786
|
}
|
|
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
|
+
}
|
|
1791
1827
|
function runBlessedDashboard(refreshSec, limit) {
|
|
1828
|
+
const cache = createCache();
|
|
1792
1829
|
const screen = blessed.screen({
|
|
1793
1830
|
smartCSR: true,
|
|
1794
1831
|
title: "copilot-agent dashboard",
|
|
@@ -1878,13 +1915,7 @@ function runBlessedDashboard(refreshSec, limit) {
|
|
|
1878
1915
|
let procs = [];
|
|
1879
1916
|
let selectedIdx = 0;
|
|
1880
1917
|
let focusedPanel = "sessions";
|
|
1881
|
-
|
|
1882
|
-
try {
|
|
1883
|
-
procs = findAgentProcesses();
|
|
1884
|
-
sessions = listAllSessions(limit);
|
|
1885
|
-
} catch {
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1918
|
+
let lastDetailId = "";
|
|
1888
1919
|
function renderHeader() {
|
|
1889
1920
|
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB");
|
|
1890
1921
|
const totalPremium = sessions.reduce((s, x) => s + x.premiumRequests, 0);
|
|
@@ -1910,37 +1941,44 @@ ${rows.join("\n")}`);
|
|
|
1910
1941
|
}
|
|
1911
1942
|
sessionList.setLabel(` {cyan-fg}{bold}Sessions (${sessions.length}){/} `);
|
|
1912
1943
|
}
|
|
1913
|
-
function renderDetail2() {
|
|
1944
|
+
function renderDetail2(force = false) {
|
|
1914
1945
|
if (sessions.length === 0 || selectedIdx < 0 || selectedIdx >= sessions.length) {
|
|
1915
1946
|
detailBox.setContent("{gray-fg}No session selected{/}");
|
|
1947
|
+
lastDetailId = "";
|
|
1916
1948
|
return;
|
|
1917
1949
|
}
|
|
1918
1950
|
const s = sessions[selectedIdx];
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
}
|
|
1927
|
-
} catch {
|
|
1928
|
-
detailBox.setContent("{red-fg}Error loading session detail{/}");
|
|
1951
|
+
if (!force && s.id === lastDetailId) return;
|
|
1952
|
+
lastDetailId = s.id;
|
|
1953
|
+
const report = cacheDetail(cache, s);
|
|
1954
|
+
if (report) {
|
|
1955
|
+
detailBox.setContent(detailContent(report));
|
|
1956
|
+
detailBox.setLabel(` {cyan-fg}{bold}Detail \u2014 ${s.id.slice(0, 12)}\u2026{/} `);
|
|
1957
|
+
} else {
|
|
1958
|
+
detailBox.setContent(`{gray-fg}Could not load report for ${s.id.slice(0, 8)}\u2026{/}`);
|
|
1929
1959
|
}
|
|
1930
1960
|
}
|
|
1931
1961
|
function render() {
|
|
1932
|
-
|
|
1962
|
+
sessions = cacheSessions(cache, limit);
|
|
1963
|
+
procs = cacheProcs(cache);
|
|
1933
1964
|
renderHeader();
|
|
1934
1965
|
renderProcesses2();
|
|
1935
1966
|
renderSessions();
|
|
1936
1967
|
renderDetail2();
|
|
1937
1968
|
screen.render();
|
|
1938
1969
|
}
|
|
1970
|
+
function forceRefresh() {
|
|
1971
|
+
cache.sessionsTs = 0;
|
|
1972
|
+
cache.procsTs = 0;
|
|
1973
|
+
cache.details.clear();
|
|
1974
|
+
lastDetailId = "";
|
|
1975
|
+
render();
|
|
1976
|
+
}
|
|
1939
1977
|
screen.key(["q", "C-c"], () => {
|
|
1940
1978
|
screen.destroy();
|
|
1941
1979
|
process.exit(0);
|
|
1942
1980
|
});
|
|
1943
|
-
screen.key(["r"], () =>
|
|
1981
|
+
screen.key(["r"], () => forceRefresh());
|
|
1944
1982
|
screen.key(["tab"], () => {
|
|
1945
1983
|
if (focusedPanel === "sessions") {
|
|
1946
1984
|
focusedPanel = "detail";
|
|
@@ -1977,7 +2015,7 @@ ${rows.join("\n")}`);
|
|
|
1977
2015
|
}
|
|
1978
2016
|
});
|
|
1979
2017
|
sessionList.key(["enter"], () => {
|
|
1980
|
-
renderDetail2();
|
|
2018
|
+
renderDetail2(true);
|
|
1981
2019
|
focusedPanel = "detail";
|
|
1982
2020
|
detailBox.focus();
|
|
1983
2021
|
sessionList.style.border.fg = BORDER_COLOR;
|
|
@@ -1990,82 +2028,6 @@ ${rows.join("\n")}`);
|
|
|
1990
2028
|
const timer = setInterval(render, refreshSec * 1e3);
|
|
1991
2029
|
screen.on("destroy", () => clearInterval(timer));
|
|
1992
2030
|
}
|
|
1993
|
-
var ESC = "\x1B";
|
|
1994
|
-
var CLEAR = `${ESC}[2J${ESC}[H`;
|
|
1995
|
-
var HIDE_CURSOR = `${ESC}[?25l`;
|
|
1996
|
-
var SHOW_CURSOR = `${ESC}[?25h`;
|
|
1997
|
-
function runSimpleDashboard(refreshSec, limit) {
|
|
1998
|
-
process.stdout.write(HIDE_CURSOR);
|
|
1999
|
-
const cleanup = () => {
|
|
2000
|
-
process.stdout.write(SHOW_CURSOR);
|
|
2001
|
-
process.stdout.write(CLEAR);
|
|
2002
|
-
process.exit(0);
|
|
2003
|
-
};
|
|
2004
|
-
process.on("SIGINT", cleanup);
|
|
2005
|
-
process.on("SIGTERM", cleanup);
|
|
2006
|
-
const render = () => {
|
|
2007
|
-
try {
|
|
2008
|
-
const output = buildSimpleScreen(limit);
|
|
2009
|
-
process.stdout.write(CLEAR + output);
|
|
2010
|
-
} catch {
|
|
2011
|
-
}
|
|
2012
|
-
};
|
|
2013
|
-
render();
|
|
2014
|
-
setInterval(render, refreshSec * 1e3);
|
|
2015
|
-
process.stdout.on("resize", render);
|
|
2016
|
-
process.stdin.setRawMode?.(true);
|
|
2017
|
-
process.stdin.resume();
|
|
2018
|
-
process.stdin.on("data", (data) => {
|
|
2019
|
-
const key = data.toString();
|
|
2020
|
-
if (key === "q" || key === "") cleanup();
|
|
2021
|
-
if (key === "r") render();
|
|
2022
|
-
});
|
|
2023
|
-
}
|
|
2024
|
-
function buildSimpleScreen(limit) {
|
|
2025
|
-
const cols = process.stdout.columns || 80;
|
|
2026
|
-
const lines = [];
|
|
2027
|
-
const now = /* @__PURE__ */ new Date();
|
|
2028
|
-
const timeStr = now.toLocaleTimeString("en-GB");
|
|
2029
|
-
lines.push("");
|
|
2030
|
-
lines.push(` ${BOLD}${CYAN}\u250C${"\u2500".repeat(cols - 6)}\u2510${RESET}`);
|
|
2031
|
-
lines.push(` ${BOLD}${CYAN}\u2502${RESET} \u26A1 ${BOLD}Copilot Agent Dashboard${RESET}${" ".repeat(Math.max(0, cols - 37 - timeStr.length))}${DIM}${timeStr}${RESET} ${BOLD}${CYAN}\u2502${RESET}`);
|
|
2032
|
-
lines.push(` ${BOLD}${CYAN}\u2514${"\u2500".repeat(cols - 6)}\u2518${RESET}`);
|
|
2033
|
-
lines.push("");
|
|
2034
|
-
const procs = findAgentProcesses();
|
|
2035
|
-
lines.push(` ${BOLD}${GREEN}\u25CF Active Processes (${procs.length})${RESET}`);
|
|
2036
|
-
lines.push(` ${"\u2500".repeat(Math.min(cols - 4, 70))}`);
|
|
2037
|
-
if (procs.length === 0) {
|
|
2038
|
-
lines.push(` ${DIM}No agent processes running${RESET}`);
|
|
2039
|
-
} else {
|
|
2040
|
-
for (const p of procs) {
|
|
2041
|
-
const agentTag = p.agent === "claude" ? `${YELLOW}[claude]${RESET}` : `${CYAN}[copilot]${RESET}`;
|
|
2042
|
-
const sid = p.sessionId ? p.sessionId.slice(0, 8) + "\u2026" : "\u2014";
|
|
2043
|
-
const cwdShort = p.cwd ? "~/" + p.cwd.split("/").slice(-2).join("/") : "\u2014";
|
|
2044
|
-
lines.push(` ${GREEN}\u2B24${RESET} ${agentTag} PID ${BOLD}${p.pid}${RESET} ${CYAN}${sid}${RESET} ${DIM}${cwdShort}${RESET}`);
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
lines.push("");
|
|
2048
|
-
const sessions = listAllSessions(limit);
|
|
2049
|
-
lines.push(` ${BOLD}${CYAN}\u25CF Recent Sessions (${sessions.length})${RESET}`);
|
|
2050
|
-
lines.push(` ${"\u2500".repeat(Math.min(cols - 4, 70))}`);
|
|
2051
|
-
const hdr = [pad("Status", 10), pad("Agent", 8), pad("Premium", 8), pad("Project", 18), pad("Last Activity", 14)];
|
|
2052
|
-
lines.push(` ${DIM}${hdr.join(" ")}${RESET}`);
|
|
2053
|
-
for (const s of sessions) {
|
|
2054
|
-
const icon = s.complete ? `${GREEN}\u2714 done ${RESET}` : `${YELLOW}\u23F8 stop ${RESET}`;
|
|
2055
|
-
const agent = s.agent === "claude" ? `${YELLOW}claude ${RESET}` : `${CYAN}copilot${RESET}`;
|
|
2056
|
-
const prem = pad(String(s.premiumRequests), 8);
|
|
2057
|
-
const proj = pad(s.cwd.split("/").pop() ?? "\u2014", 18);
|
|
2058
|
-
const ago = pad(fmtTimeAgo(s.mtime), 14);
|
|
2059
|
-
lines.push(` ${icon}${agent} ${prem}${proj}${ago}`);
|
|
2060
|
-
}
|
|
2061
|
-
lines.push("");
|
|
2062
|
-
lines.push(` ${DIM}Press ${BOLD}q${RESET}${DIM} to quit, ${BOLD}r${RESET}${DIM} to refresh${RESET}`);
|
|
2063
|
-
return lines.join("\n");
|
|
2064
|
-
}
|
|
2065
|
-
function pad(s, n) {
|
|
2066
|
-
if (s.length >= n) return s.slice(0, n);
|
|
2067
|
-
return s + " ".repeat(n - s.length);
|
|
2068
|
-
}
|
|
2069
2031
|
|
|
2070
2032
|
// src/commands/web.ts
|
|
2071
2033
|
import { Hono } from "hono";
|
|
@@ -3408,9 +3370,250 @@ function formatDur(ms) {
|
|
|
3408
3370
|
return `${h}h ${m}m`;
|
|
3409
3371
|
}
|
|
3410
3372
|
|
|
3373
|
+
// src/commands/template.ts
|
|
3374
|
+
import chalk8 from "chalk";
|
|
3375
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
|
|
3376
|
+
import { join as join12 } from "path";
|
|
3377
|
+
import { homedir as homedir10 } from "os";
|
|
3378
|
+
import { parse as parseYaml3, stringify as stringifyYaml2 } from "yaml";
|
|
3379
|
+
var CONFIG_DIR2 = join12(homedir10(), ".copilot-agent");
|
|
3380
|
+
var TEMPLATES_FILE = join12(CONFIG_DIR2, "templates.yaml");
|
|
3381
|
+
function ensureDir3() {
|
|
3382
|
+
if (!existsSync10(CONFIG_DIR2)) mkdirSync7(CONFIG_DIR2, { recursive: true });
|
|
3383
|
+
}
|
|
3384
|
+
function loadTemplates() {
|
|
3385
|
+
if (!existsSync10(TEMPLATES_FILE)) return [];
|
|
3386
|
+
try {
|
|
3387
|
+
const data = parseYaml3(readFileSync7(TEMPLATES_FILE, "utf-8"));
|
|
3388
|
+
return Array.isArray(data) ? data : [];
|
|
3389
|
+
} catch {
|
|
3390
|
+
return [];
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
function saveTemplates(templates) {
|
|
3394
|
+
ensureDir3();
|
|
3395
|
+
writeFileSync5(TEMPLATES_FILE, stringifyYaml2(templates), "utf-8");
|
|
3396
|
+
}
|
|
3397
|
+
function registerTemplateCommand(program2) {
|
|
3398
|
+
const cmd = program2.command("template").description("Manage custom task templates");
|
|
3399
|
+
cmd.command("list").description("Show all custom templates").action(() => {
|
|
3400
|
+
const templates = loadTemplates();
|
|
3401
|
+
console.log(chalk8.bold.cyan("\n \u{1F4CB} Task Templates") + chalk8.dim(` (${templates.length})
|
|
3402
|
+
`));
|
|
3403
|
+
if (templates.length === 0) {
|
|
3404
|
+
console.log(chalk8.dim(" No custom templates"));
|
|
3405
|
+
console.log(chalk8.dim('\n Add one: copilot-agent template add <name> --prompt "..."'));
|
|
3406
|
+
console.log();
|
|
3407
|
+
return;
|
|
3408
|
+
}
|
|
3409
|
+
for (const t of templates) {
|
|
3410
|
+
const prio = t.priority !== void 0 ? chalk8.dim(` [priority: ${t.priority}]`) : "";
|
|
3411
|
+
console.log(` ${chalk8.green("\u25CF")} ${chalk8.bold(t.name)}${prio}`);
|
|
3412
|
+
console.log(chalk8.dim(` ${t.prompt.slice(0, 80)}${t.prompt.length > 80 ? "\u2026" : ""}`));
|
|
3413
|
+
}
|
|
3414
|
+
console.log();
|
|
3415
|
+
});
|
|
3416
|
+
cmd.command("add <name>").description("Add a custom template").requiredOption("-p, --prompt <text>", "Task prompt").option("--priority <n>", "Priority (lower = higher priority)").action((name, opts) => {
|
|
3417
|
+
const templates = loadTemplates();
|
|
3418
|
+
const existing = templates.findIndex((t) => t.name === name);
|
|
3419
|
+
const entry = {
|
|
3420
|
+
name,
|
|
3421
|
+
prompt: opts.prompt,
|
|
3422
|
+
priority: opts.priority ? parseInt(opts.priority, 10) : void 0
|
|
3423
|
+
};
|
|
3424
|
+
if (existing >= 0) {
|
|
3425
|
+
templates[existing] = entry;
|
|
3426
|
+
console.log(chalk8.yellow(` \u2714 Updated template: ${chalk8.bold(name)}`));
|
|
3427
|
+
} else {
|
|
3428
|
+
templates.push(entry);
|
|
3429
|
+
console.log(chalk8.green(` \u2714 Added template: ${chalk8.bold(name)}`));
|
|
3430
|
+
}
|
|
3431
|
+
saveTemplates(templates);
|
|
3432
|
+
});
|
|
3433
|
+
cmd.command("remove <name>").description("Remove a custom template").action((name) => {
|
|
3434
|
+
const templates = loadTemplates();
|
|
3435
|
+
const idx = templates.findIndex((t) => t.name === name);
|
|
3436
|
+
if (idx < 0) {
|
|
3437
|
+
console.log(chalk8.red(` \u2717 Template not found: ${name}`));
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
templates.splice(idx, 1);
|
|
3441
|
+
saveTemplates(templates);
|
|
3442
|
+
console.log(chalk8.green(` \u2714 Removed template: ${chalk8.bold(name)}`));
|
|
3443
|
+
});
|
|
3444
|
+
cmd.command("export").description("Export templates to stdout (YAML)").action(() => {
|
|
3445
|
+
const templates = loadTemplates();
|
|
3446
|
+
if (templates.length === 0) {
|
|
3447
|
+
console.log(chalk8.dim(" No templates to export"));
|
|
3448
|
+
return;
|
|
3449
|
+
}
|
|
3450
|
+
console.log(stringifyYaml2(templates));
|
|
3451
|
+
});
|
|
3452
|
+
cmd.command("import <file>").description("Import templates from a YAML file").action((file) => {
|
|
3453
|
+
try {
|
|
3454
|
+
const data = parseYaml3(readFileSync7(file, "utf-8"));
|
|
3455
|
+
if (!Array.isArray(data)) {
|
|
3456
|
+
console.log(chalk8.red(" \u2717 Invalid format: expected YAML array"));
|
|
3457
|
+
return;
|
|
3458
|
+
}
|
|
3459
|
+
const current = loadTemplates();
|
|
3460
|
+
let added = 0;
|
|
3461
|
+
for (const entry of data) {
|
|
3462
|
+
if (!entry.name || !entry.prompt) continue;
|
|
3463
|
+
const idx = current.findIndex((t) => t.name === entry.name);
|
|
3464
|
+
if (idx >= 0) {
|
|
3465
|
+
current[idx] = entry;
|
|
3466
|
+
} else {
|
|
3467
|
+
current.push(entry);
|
|
3468
|
+
added++;
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
saveTemplates(current);
|
|
3472
|
+
console.log(chalk8.green(` \u2714 Imported ${added} new templates (${data.length} total processed)`));
|
|
3473
|
+
} catch (err) {
|
|
3474
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3475
|
+
console.log(chalk8.red(` \u2717 ${msg}`));
|
|
3476
|
+
}
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
// src/commands/log.ts
|
|
3481
|
+
import chalk9 from "chalk";
|
|
3482
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
3483
|
+
function registerLogCommand(program2) {
|
|
3484
|
+
const cmd = program2.command("log").description("Search, browse, and export session history");
|
|
3485
|
+
cmd.command("search <query>").description("Search across all sessions").option("-a, --agent <type>", "Filter: copilot | claude").option("-l, --limit <n>", "Max results", "20").option("--after <date>", "After date (YYYY-MM-DD)").option("--before <date>", "Before date (YYYY-MM-DD)").action((query, opts) => {
|
|
3486
|
+
const limit = parseInt(opts.limit, 10);
|
|
3487
|
+
const sessions = listAllSessions(200);
|
|
3488
|
+
const q = query.toLowerCase();
|
|
3489
|
+
let filtered = sessions;
|
|
3490
|
+
if (opts.agent) filtered = filtered.filter((s) => s.agent === opts.agent);
|
|
3491
|
+
if (opts.after) {
|
|
3492
|
+
const after = new Date(opts.after).getTime();
|
|
3493
|
+
filtered = filtered.filter((s) => s.mtime >= after);
|
|
3494
|
+
}
|
|
3495
|
+
if (opts.before) {
|
|
3496
|
+
const before = new Date(opts.before).getTime();
|
|
3497
|
+
filtered = filtered.filter((s) => s.mtime <= before);
|
|
3498
|
+
}
|
|
3499
|
+
const matches = filtered.filter((s) => {
|
|
3500
|
+
if (s.summary?.toLowerCase().includes(q)) return true;
|
|
3501
|
+
if (s.cwd?.toLowerCase().includes(q)) return true;
|
|
3502
|
+
if (s.lastEvent?.toLowerCase().includes(q)) return true;
|
|
3503
|
+
const report = getAgentSessionReport(s.id, s.agent);
|
|
3504
|
+
if (!report) return false;
|
|
3505
|
+
if (report.gitCommits.some((c) => c.toLowerCase().includes(q))) return true;
|
|
3506
|
+
if (report.filesCreated.some((f) => f.toLowerCase().includes(q))) return true;
|
|
3507
|
+
if (report.filesEdited.some((f) => f.toLowerCase().includes(q))) return true;
|
|
3508
|
+
if (report.taskCompletions.some((t) => t.toLowerCase().includes(q))) return true;
|
|
3509
|
+
return false;
|
|
3510
|
+
}).slice(0, limit);
|
|
3511
|
+
console.log(chalk9.bold.cyan(`
|
|
3512
|
+
\u{1F50D} Search: "${query}"`) + chalk9.dim(` (${matches.length} results)
|
|
3513
|
+
`));
|
|
3514
|
+
if (matches.length === 0) {
|
|
3515
|
+
console.log(chalk9.dim(" No matches found"));
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
for (const s of matches) {
|
|
3519
|
+
const agentTag = s.agent === "claude" ? chalk9.yellow("claude ") : chalk9.cyan("copilot");
|
|
3520
|
+
const icon = s.complete ? chalk9.green("\u2714") : chalk9.yellow("\u23F3");
|
|
3521
|
+
const date = new Date(s.mtime).toLocaleDateString("en-CA");
|
|
3522
|
+
const time = new Date(s.mtime).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
|
|
3523
|
+
const project = s.cwd?.split("/").pop() || "\u2014";
|
|
3524
|
+
const summary = (s.summary || "\u2014").slice(0, 50);
|
|
3525
|
+
console.log(` ${icon} ${agentTag} ${chalk9.dim(date + " " + time)} ${chalk9.bold(project.padEnd(16))} ${summary}`);
|
|
3526
|
+
console.log(chalk9.dim(` ${s.id}`));
|
|
3527
|
+
}
|
|
3528
|
+
console.log();
|
|
3529
|
+
});
|
|
3530
|
+
cmd.command("timeline [session-id]").description("Show chronological timeline of a session").option("-a, --agent <type>", "Agent type").action((sessionId, opts) => {
|
|
3531
|
+
if (!sessionId) {
|
|
3532
|
+
const sessions = listAllSessions(1);
|
|
3533
|
+
if (sessions.length === 0) {
|
|
3534
|
+
console.log(chalk9.dim(" No sessions"));
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
sessionId = sessions[0].id;
|
|
3538
|
+
opts.agent = opts.agent || sessions[0].agent;
|
|
3539
|
+
console.log(chalk9.dim(` Using latest: ${sessionId.slice(0, 12)}\u2026
|
|
3540
|
+
`));
|
|
3541
|
+
}
|
|
3542
|
+
const report = getAgentSessionReport(sessionId, opts.agent);
|
|
3543
|
+
if (!report) {
|
|
3544
|
+
console.log(chalk9.red(` \u2717 Session not found`));
|
|
3545
|
+
return;
|
|
3546
|
+
}
|
|
3547
|
+
const project = report.cwd?.split("/").pop() || "unknown";
|
|
3548
|
+
const agentTag = report.agent === "claude" ? chalk9.yellow("[claude]") : chalk9.cyan("[copilot]");
|
|
3549
|
+
console.log(chalk9.bold.cyan(` \u{1F4C5} Timeline \u2014 ${project}`) + ` ${agentTag}
|
|
3550
|
+
`);
|
|
3551
|
+
const entries = [];
|
|
3552
|
+
if (report.startTime) entries.push(`${chalk9.dim(fmtTime2(report.startTime))} ${chalk9.green("\u25B6")} Session started`);
|
|
3553
|
+
for (const f of report.filesCreated) entries.push(`${chalk9.dim(" ")} ${chalk9.green("+")} Created ${f}`);
|
|
3554
|
+
for (const f of report.filesEdited) entries.push(`${chalk9.dim(" ")} ${chalk9.yellow("~")} Edited ${f}`);
|
|
3555
|
+
for (const c of report.gitCommits) entries.push(`${chalk9.dim(" ")} ${chalk9.cyan("\u25CF")} Commit: ${c.split("\n")[0].slice(0, 60)}`);
|
|
3556
|
+
for (const t of report.taskCompletions) entries.push(`${chalk9.dim(" ")} ${chalk9.green("\u2714")} ${t.split("\n")[0].slice(0, 60)}`);
|
|
3557
|
+
for (const e of report.errors.slice(0, 5)) entries.push(`${chalk9.dim(" ")} ${chalk9.red("\u2717")} ${e.split("\n")[0].slice(0, 60)}`);
|
|
3558
|
+
const statusIcon3 = report.complete ? chalk9.green("\u25A0") : chalk9.yellow("\u23F8");
|
|
3559
|
+
if (report.endTime) entries.push(`${chalk9.dim(fmtTime2(report.endTime))} ${statusIcon3} Session ${report.complete ? "completed" : "stopped"}`);
|
|
3560
|
+
for (const e of entries) console.log(` ${e}`);
|
|
3561
|
+
console.log(chalk9.dim(`
|
|
3562
|
+
Duration: ${fmtDur2(report.durationMs)} | Turns: ${report.assistantTurns} | Premium: \u2B21${report.premiumRequests}
|
|
3563
|
+
`));
|
|
3564
|
+
});
|
|
3565
|
+
cmd.command("export").description("Export session history").option("-f, --format <fmt>", "Format: json | csv", "json").option("-l, --limit <n>", "Max sessions", "50").option("-o, --output <file>", "Output file").action((opts) => {
|
|
3566
|
+
const sessions = listAllSessions(parseInt(opts.limit, 10));
|
|
3567
|
+
const data = sessions.map((s) => {
|
|
3568
|
+
const r = getAgentSessionReport(s.id, s.agent);
|
|
3569
|
+
return {
|
|
3570
|
+
id: s.id,
|
|
3571
|
+
agent: s.agent,
|
|
3572
|
+
project: s.cwd?.split("/").pop() || "",
|
|
3573
|
+
complete: s.complete,
|
|
3574
|
+
premium: s.premiumRequests,
|
|
3575
|
+
tokens: r?.outputTokens || 0,
|
|
3576
|
+
turns: r?.assistantTurns || 0,
|
|
3577
|
+
commits: r?.gitCommits.length || 0,
|
|
3578
|
+
filesCreated: r?.filesCreated.length || 0,
|
|
3579
|
+
filesEdited: r?.filesEdited.length || 0,
|
|
3580
|
+
durationMs: r?.durationMs || 0,
|
|
3581
|
+
summary: s.summary || "",
|
|
3582
|
+
date: new Date(s.mtime).toISOString()
|
|
3583
|
+
};
|
|
3584
|
+
});
|
|
3585
|
+
let output;
|
|
3586
|
+
if (opts.format === "csv") {
|
|
3587
|
+
const headers = Object.keys(data[0] || {}).join(",");
|
|
3588
|
+
const rows = data.map((d) => Object.values(d).map((v) => `"${String(v).replace(/"/g, '""')}"`).join(","));
|
|
3589
|
+
output = [headers, ...rows].join("\n");
|
|
3590
|
+
} else {
|
|
3591
|
+
output = JSON.stringify(data, null, 2);
|
|
3592
|
+
}
|
|
3593
|
+
if (opts.output) {
|
|
3594
|
+
writeFileSync6(opts.output, output, "utf-8");
|
|
3595
|
+
console.log(chalk9.green(` \u2714 Exported ${data.length} sessions to ${opts.output}`));
|
|
3596
|
+
} else {
|
|
3597
|
+
console.log(output);
|
|
3598
|
+
}
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3601
|
+
function fmtTime2(iso) {
|
|
3602
|
+
try {
|
|
3603
|
+
return new Date(iso).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
|
|
3604
|
+
} catch {
|
|
3605
|
+
return " ";
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
function fmtDur2(ms) {
|
|
3609
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
3610
|
+
if (ms < 36e5) return `${Math.round(ms / 6e4)}m`;
|
|
3611
|
+
return `${Math.floor(ms / 36e5)}h ${Math.round(ms % 36e5 / 6e4)}m`;
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3411
3614
|
// src/index.ts
|
|
3412
3615
|
var program = new Command();
|
|
3413
|
-
program.name("copilot-agent").version("0.
|
|
3616
|
+
program.name("copilot-agent").version("0.12.0").description("Autonomous AI agent manager \u2014 auto-resume, task discovery, overnight runs. Supports GitHub Copilot CLI + Claude Code.");
|
|
3414
3617
|
registerStatusCommand(program);
|
|
3415
3618
|
registerWatchCommand(program);
|
|
3416
3619
|
registerRunCommand(program);
|
|
@@ -3426,5 +3629,7 @@ registerQuotaCommand(program);
|
|
|
3426
3629
|
registerCompactCommand(program);
|
|
3427
3630
|
registerHooksCommand(program);
|
|
3428
3631
|
registerPrCommand(program);
|
|
3632
|
+
registerLogCommand(program);
|
|
3633
|
+
registerTemplateCommand(program);
|
|
3429
3634
|
program.parse();
|
|
3430
3635
|
//# sourceMappingURL=index.js.map
|