copilot-agent 0.9.0 → 0.10.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 CHANGED
@@ -12,8 +12,13 @@ Autonomous AI agent manager — auto-resume sessions, discover tasks, run overni
12
12
  | **`overnight`** | Run tasks continuously until a deadline (e.g. 07:00) |
13
13
  | **`research`** | Architecture, security, and performance analysis |
14
14
  | **`report`** | Session activity report — tools, commits, files, tokens |
15
- | **`dashboard`** | Real-time TUI dashboard (pure terminal) |
15
+ | **`dashboard`** | htop-style TUI dashboard with blessed (scrollable, keyboard nav) |
16
16
  | **`web`** | Web dashboard with live updates (Hono + htmx + SSE) |
17
+ | **`config`** | Persistent configuration defaults (global + per-project) |
18
+ | **`proxy`** | Manage copilot-api proxy for Claude Code via Copilot |
19
+ | **`diff`** | Show git changes made by an agent session |
20
+ | **`quota`** | Track premium requests, tokens, and usage over time |
21
+ | **`compact`** | Generate context summary for session handoff/resume |
17
22
 
18
23
  All commands support `--agent copilot` or `--agent claude` (auto-detects if omitted).
19
24
 
@@ -43,7 +48,6 @@ copilot-agent status --active
43
48
 
44
49
  # Filter by agent
45
50
  copilot-agent status --agent claude
46
- copilot-agent status --agent copilot
47
51
  ```
48
52
 
49
53
  ### Watch & auto-resume
@@ -83,46 +87,82 @@ copilot-agent overnight ~/my-project
83
87
 
84
88
  # Run with Claude Code
85
89
  copilot-agent overnight --agent claude --until 07 --max-premium 200
86
-
87
- # Use worktree for parallel tasks
88
- copilot-agent overnight --worktree
89
90
  ```
90
91
 
91
- ### Session report
92
+ ### Session report & diff
92
93
 
93
94
  ```bash
94
- # Latest session
95
+ # Latest session report
95
96
  copilot-agent report
96
97
 
97
- # Specific session
98
- copilot-agent report abc12345-...
98
+ # Show git changes from latest session
99
+ copilot-agent diff
100
+
101
+ # Show changes from specific session with diffstat
102
+ copilot-agent diff abc12345-... --stat
103
+ ```
104
+
105
+ ### Dashboards
106
+
107
+ ```bash
108
+ # htop-style TUI (blessed — scrollable, keyboard nav)
109
+ copilot-agent dashboard
99
110
 
100
- # Multiple recent sessions as JSON
101
- copilot-agent report -l 5 --json
111
+ # Simple ANSI fallback (no dependencies)
112
+ copilot-agent dashboard --simple
102
113
 
103
- # Filter by project directory
104
- copilot-agent report --project ~/my-project
114
+ # Web UI (Hono + htmx, opens browser)
115
+ copilot-agent web
105
116
  ```
106
117
 
107
- ### Research
118
+ ### Configuration
108
119
 
109
120
  ```bash
110
- # Analyze current project
111
- copilot-agent research
121
+ # Set persistent defaults
122
+ copilot-agent config set agent claude
123
+ copilot-agent config set steps 50
124
+ copilot-agent config set worktree true
125
+
126
+ # View all config (defaults + global + project)
127
+ copilot-agent config list
112
128
 
113
- # With Claude Code
114
- copilot-agent research --agent claude
129
+ # Per-project config: create .copilot-agent.yaml in project root
115
130
  ```
116
131
 
117
- ### Dashboards
132
+ ### Proxy management (Claude Code via Copilot)
118
133
 
119
134
  ```bash
120
- # Terminal UI (pure ANSI, no deps)
121
- copilot-agent dashboard
135
+ # Start copilot-api proxy (auto-detects Copilot OAuth token)
136
+ copilot-agent proxy start
122
137
 
123
- # Web UI (Hono + htmx, opens browser)
124
- copilot-agent web
125
- copilot-agent web --port 8080
138
+ # Check status (PID, port, token, model count)
139
+ copilot-agent proxy status
140
+ ```
141
+
142
+ ### Usage tracking
143
+
144
+ ```bash
145
+ # Show last 7 days of premium/token usage
146
+ copilot-agent quota
147
+
148
+ # All-time usage
149
+ copilot-agent quota --all
150
+
151
+ # Last 30 days
152
+ copilot-agent quota --days 30
153
+ ```
154
+
155
+ ### Context handoff (compact)
156
+
157
+ ```bash
158
+ # Generate context summary from latest session
159
+ copilot-agent compact
160
+
161
+ # Save compact to file
162
+ copilot-agent compact --save
163
+
164
+ # Get a resume prompt to continue the work
165
+ copilot-agent compact --resume-prompt
126
166
  ```
127
167
 
128
168
  ## How it works
@@ -133,6 +173,7 @@ copilot-agent web --port 8080
133
173
  4. **Task discovery** — Detects project type and generates relevant maintenance tasks
134
174
  5. **Race prevention** — File locking + process tracking prevents concurrent agents in the same directory
135
175
  6. **Worktree isolation** — Optional `--worktree` flag for parallel task execution via `git worktree`
176
+ 7. **Config layering** — Defaults → `~/.copilot-agent/config.yaml` → `.copilot-agent.yaml` → CLI flags
136
177
 
137
178
  ## Supported project types
138
179
 
package/dist/index.js CHANGED
@@ -2830,9 +2830,313 @@ function showDiff(sessionId, opts) {
2830
2830
  console.log();
2831
2831
  }
2832
2832
 
2833
+ // src/commands/quota.ts
2834
+ import chalk4 from "chalk";
2835
+
2836
+ // src/lib/quota.ts
2837
+ import { join as join9 } from "path";
2838
+ import { homedir as homedir7 } from "os";
2839
+ var DATA_DIR = join9(homedir7(), ".copilot-agent");
2840
+ var USAGE_FILE = join9(DATA_DIR, "usage.jsonl");
2841
+ function buildUsageSummary(days) {
2842
+ const sessions = listAllSessions(500);
2843
+ const cutoff = days ? Date.now() - days * 864e5 : 0;
2844
+ const filtered = sessions.filter((s) => s.mtime >= cutoff);
2845
+ const summary = {
2846
+ total: { sessions: 0, premium: 0, tokens: 0, turns: 0, durationMs: 0 },
2847
+ copilot: { sessions: 0, premium: 0, tokens: 0 },
2848
+ claude: { sessions: 0, premium: 0, tokens: 0 },
2849
+ byDay: {}
2850
+ };
2851
+ for (const s of filtered) {
2852
+ const report = getAgentSessionReport(s.id, s.agent);
2853
+ if (!report) continue;
2854
+ summary.total.sessions++;
2855
+ summary.total.premium += report.premiumRequests;
2856
+ summary.total.tokens += report.outputTokens;
2857
+ summary.total.turns += report.assistantTurns;
2858
+ summary.total.durationMs += report.durationMs;
2859
+ const bucket = s.agent === "claude" ? summary.claude : summary.copilot;
2860
+ bucket.sessions++;
2861
+ bucket.premium += report.premiumRequests;
2862
+ bucket.tokens += report.outputTokens;
2863
+ const day = new Date(s.mtime).toISOString().slice(0, 10);
2864
+ if (!summary.byDay[day]) summary.byDay[day] = { premium: 0, tokens: 0, sessions: 0 };
2865
+ summary.byDay[day].premium += report.premiumRequests;
2866
+ summary.byDay[day].tokens += report.outputTokens;
2867
+ summary.byDay[day].sessions++;
2868
+ }
2869
+ return summary;
2870
+ }
2871
+ function formatTokens(n) {
2872
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
2873
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
2874
+ return String(n);
2875
+ }
2876
+ function formatDurationShort(ms) {
2877
+ if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
2878
+ if (ms < 36e5) return `${Math.round(ms / 6e4)}m`;
2879
+ const h = Math.floor(ms / 36e5);
2880
+ const m = Math.round(ms % 36e5 / 6e4);
2881
+ return `${h}h ${m}m`;
2882
+ }
2883
+
2884
+ // src/commands/quota.ts
2885
+ function registerQuotaCommand(program2) {
2886
+ program2.command("quota").description("Track premium requests, tokens, and usage over time").option("-d, --days <n>", "Number of days to show", "7").option("--all", "Show all-time usage").action((opts) => {
2887
+ const days = opts.all ? void 0 : parseInt(opts.days, 10);
2888
+ const label = days ? `Last ${days} days` : "All time";
2889
+ const summary = buildUsageSummary(days);
2890
+ console.log();
2891
+ console.log(chalk4.bold.cyan(` \u2B21 Usage Summary \u2014 ${label}`));
2892
+ console.log(chalk4.dim(` ${"\u2500".repeat(50)}`));
2893
+ console.log();
2894
+ const t = summary.total;
2895
+ console.log(` ${chalk4.bold("Sessions")} ${chalk4.white(String(t.sessions))}`);
2896
+ console.log(` ${chalk4.bold("Premium")} ${chalk4.yellow("\u2B21 " + String(t.premium))}`);
2897
+ console.log(` ${chalk4.bold("Tokens")} ${chalk4.green(formatTokens(t.tokens))}`);
2898
+ console.log(` ${chalk4.bold("Turns")} ${chalk4.white(String(t.turns))}`);
2899
+ console.log(` ${chalk4.bold("Total time")} ${chalk4.white(formatDurationShort(t.durationMs))}`);
2900
+ console.log();
2901
+ if (summary.copilot.sessions > 0 || summary.claude.sessions > 0) {
2902
+ console.log(chalk4.bold.cyan(" Per Agent"));
2903
+ console.log(chalk4.dim(` ${"\u2500".repeat(50)}`));
2904
+ if (summary.copilot.sessions > 0) {
2905
+ const c = summary.copilot;
2906
+ console.log(` ${chalk4.cyan("copilot")} ${String(c.sessions).padStart(4)} sessions ${chalk4.yellow("\u2B21" + String(c.premium).padStart(5))} ${chalk4.green(formatTokens(c.tokens).padStart(7))} tokens`);
2907
+ }
2908
+ if (summary.claude.sessions > 0) {
2909
+ const c = summary.claude;
2910
+ console.log(` ${chalk4.yellow("claude ")} ${String(c.sessions).padStart(4)} sessions ${chalk4.yellow("\u2B21" + String(c.premium).padStart(5))} ${chalk4.green(formatTokens(c.tokens).padStart(7))} tokens`);
2911
+ }
2912
+ console.log();
2913
+ }
2914
+ const dayEntries = Object.entries(summary.byDay).sort((a, b) => a[0].localeCompare(b[0]));
2915
+ if (dayEntries.length > 0) {
2916
+ console.log(chalk4.bold.cyan(" Daily Usage"));
2917
+ console.log(chalk4.dim(` ${"\u2500".repeat(50)}`));
2918
+ const maxPremium = Math.max(...dayEntries.map(([, d]) => d.premium), 1);
2919
+ const barWidth = 24;
2920
+ for (const [day, data] of dayEntries.slice(-14)) {
2921
+ const shortDay = day.slice(5);
2922
+ const filled = Math.round(data.premium / maxPremium * barWidth);
2923
+ const bar2 = chalk4.cyan("\u2588".repeat(filled)) + chalk4.dim("\u2591".repeat(barWidth - filled));
2924
+ console.log(` ${chalk4.dim(shortDay)} ${bar2} ${chalk4.yellow("\u2B21" + String(data.premium).padStart(4))} ${chalk4.dim(String(data.sessions) + " sess")}`);
2925
+ }
2926
+ console.log();
2927
+ }
2928
+ });
2929
+ }
2930
+
2931
+ // src/commands/compact.ts
2932
+ import chalk5 from "chalk";
2933
+
2934
+ // src/lib/compact.ts
2935
+ import { writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
2936
+ import { join as join10 } from "path";
2937
+ import { homedir as homedir8 } from "os";
2938
+ var COMPACT_DIR = join10(homedir8(), ".copilot-agent", "compacts");
2939
+ function ensureDir2() {
2940
+ if (!existsSync8(COMPACT_DIR)) mkdirSync6(COMPACT_DIR, { recursive: true });
2941
+ }
2942
+ function compactSession(sessionId, agent) {
2943
+ const report = getAgentSessionReport(sessionId, agent);
2944
+ if (!report) return null;
2945
+ const project = report.cwd?.split("/").pop() || "unknown";
2946
+ const done = [];
2947
+ for (const task of report.taskCompletions) {
2948
+ done.push(task.split("\n")[0].slice(0, 100));
2949
+ }
2950
+ for (const file of report.filesCreated) {
2951
+ done.push(`Created ${file}`);
2952
+ }
2953
+ for (const file of report.filesEdited) {
2954
+ done.push(`Edited ${file}`);
2955
+ }
2956
+ const remaining = [];
2957
+ if (!report.complete) {
2958
+ remaining.push("Session was interrupted before completion");
2959
+ }
2960
+ for (const err of report.errors) {
2961
+ remaining.push(`Fix: ${err.split("\n")[0].slice(0, 100)}`);
2962
+ }
2963
+ const filesChanged = [
2964
+ ...report.filesCreated.map((f) => `+ ${f}`),
2965
+ ...report.filesEdited.map((f) => `~ ${f}`)
2966
+ ];
2967
+ const commits = report.gitCommits.map((c) => c.split("\n")[0].slice(0, 100));
2968
+ const durationMs = report.durationMs;
2969
+ const durationStr = durationMs < 6e4 ? `${Math.round(durationMs / 1e3)}s` : durationMs < 36e5 ? `${Math.round(durationMs / 6e4)}m` : `${Math.floor(durationMs / 36e5)}h ${Math.round(durationMs % 36e5 / 6e4)}m`;
2970
+ const lines = [];
2971
+ lines.push(`## Session Context (auto-generated)`);
2972
+ lines.push(`**Project:** ${project} | **Agent:** ${report.agent} | **Duration:** ${durationStr}`);
2973
+ lines.push(`**Turns:** ${report.assistantTurns} | **Tokens:** ${report.outputTokens.toLocaleString()} | **Premium:** ${report.premiumRequests}`);
2974
+ lines.push("");
2975
+ if (report.summary) {
2976
+ lines.push(`**Task:** ${report.summary}`);
2977
+ lines.push("");
2978
+ }
2979
+ if (done.length > 0) {
2980
+ lines.push("### \u2705 Completed");
2981
+ for (const d of done) lines.push(`- ${d}`);
2982
+ lines.push("");
2983
+ }
2984
+ if (remaining.length > 0) {
2985
+ lines.push("### \u23F3 Remaining");
2986
+ for (const r of remaining) lines.push(`- ${r}`);
2987
+ lines.push("");
2988
+ }
2989
+ if (commits.length > 0) {
2990
+ lines.push("### Git Commits");
2991
+ for (const c of commits) lines.push(`- ${c}`);
2992
+ lines.push("");
2993
+ }
2994
+ if (filesChanged.length > 0) {
2995
+ lines.push("### Files Changed");
2996
+ for (const f of filesChanged) lines.push(`- ${f}`);
2997
+ lines.push("");
2998
+ }
2999
+ if (report.errors.length > 0) {
3000
+ lines.push("### \u26A0\uFE0F Errors Encountered");
3001
+ for (const e of report.errors.slice(0, 5)) lines.push(`- ${e.split("\n")[0]}`);
3002
+ lines.push("");
3003
+ }
3004
+ const topTools = Object.entries(report.toolUsage).sort((a, b) => b[1] - a[1]).slice(0, 5);
3005
+ if (topTools.length > 0) {
3006
+ lines.push("### Tools Used");
3007
+ for (const [tool, count] of topTools) lines.push(`- ${tool}: ${count}x`);
3008
+ lines.push("");
3009
+ }
3010
+ const markdown = lines.join("\n");
3011
+ return {
3012
+ sessionId,
3013
+ agent: report.agent,
3014
+ project,
3015
+ summary: report.summary || "",
3016
+ done,
3017
+ remaining,
3018
+ filesChanged,
3019
+ commits,
3020
+ errors: report.errors.map((e) => e.split("\n")[0]),
3021
+ stats: {
3022
+ turns: report.assistantTurns,
3023
+ tokens: report.outputTokens,
3024
+ premium: report.premiumRequests,
3025
+ duration: durationStr
3026
+ },
3027
+ markdown
3028
+ };
3029
+ }
3030
+ function saveCompact(compact) {
3031
+ ensureDir2();
3032
+ const filename = `${compact.sessionId.slice(0, 12)}.md`;
3033
+ const filepath = join10(COMPACT_DIR, filename);
3034
+ writeFileSync4(filepath, compact.markdown, "utf-8");
3035
+ return filepath;
3036
+ }
3037
+ function buildResumePrompt(compact) {
3038
+ const parts = [];
3039
+ parts.push("Continue the previous task. Here is the context from the interrupted session:");
3040
+ parts.push("");
3041
+ if (compact.summary) parts.push(`Task: ${compact.summary}`);
3042
+ if (compact.done.length > 0) {
3043
+ parts.push("Already completed:");
3044
+ for (const d of compact.done.slice(0, 10)) parts.push(`- ${d}`);
3045
+ }
3046
+ if (compact.remaining.length > 0) {
3047
+ parts.push("Still needs to be done:");
3048
+ for (const r of compact.remaining) parts.push(`- ${r}`);
3049
+ }
3050
+ if (compact.errors.length > 0) {
3051
+ parts.push("Errors to address:");
3052
+ for (const e of compact.errors.slice(0, 3)) parts.push(`- ${e}`);
3053
+ }
3054
+ parts.push("");
3055
+ parts.push("Please continue where the previous session left off.");
3056
+ return parts.join("\n");
3057
+ }
3058
+
3059
+ // src/commands/compact.ts
3060
+ function registerCompactCommand(program2) {
3061
+ program2.command("compact [session-id]").description("Generate context summary from a session for handoff/resume").option("--save", "Save compact to ~/.copilot-agent/compacts/").option("--resume-prompt", "Output a resume prompt for the next session").option("-a, --agent <type>", "Agent type: copilot | claude").action((sessionId, opts) => {
3062
+ try {
3063
+ showCompact(sessionId, opts);
3064
+ } catch (err) {
3065
+ const msg = err instanceof Error ? err.message : String(err);
3066
+ console.error(chalk5.red(` \u2717 ${msg}`));
3067
+ }
3068
+ });
3069
+ }
3070
+ function showCompact(sessionId, opts) {
3071
+ if (!sessionId) {
3072
+ const sessions = listAllSessions(10);
3073
+ if (sessions.length === 0) {
3074
+ console.log(chalk5.dim(" No sessions found"));
3075
+ return;
3076
+ }
3077
+ sessionId = sessions[0].id;
3078
+ console.log(chalk5.dim(` Using latest session: ${sessionId.slice(0, 12)}\u2026
3079
+ `));
3080
+ }
3081
+ const compact = compactSession(sessionId, opts.agent);
3082
+ if (!compact) {
3083
+ console.log(chalk5.red(` \u2717 Session not found: ${sessionId}`));
3084
+ return;
3085
+ }
3086
+ if (opts.resumePrompt) {
3087
+ console.log(buildResumePrompt(compact));
3088
+ return;
3089
+ }
3090
+ const agentTag = compact.agent === "claude" ? chalk5.yellow("[claude]") : chalk5.cyan("[copilot]");
3091
+ console.log(chalk5.bold.cyan(` \u{1F4CB} Session Compact \u2014 ${compact.project}`) + ` ${agentTag}`);
3092
+ console.log(chalk5.dim(` ${compact.sessionId}`));
3093
+ console.log(chalk5.dim(` ${"\u2500".repeat(50)}`));
3094
+ console.log();
3095
+ console.log(` ${chalk5.bold("Duration")} ${compact.stats.duration} ${chalk5.bold("Turns")} ${compact.stats.turns} ${chalk5.bold("Tokens")} ${compact.stats.tokens.toLocaleString()} ${chalk5.bold("Premium")} ${chalk5.yellow("\u2B21" + compact.stats.premium)}`);
3096
+ console.log();
3097
+ if (compact.summary) {
3098
+ console.log(chalk5.bold(" Task:") + ` ${compact.summary}`);
3099
+ console.log();
3100
+ }
3101
+ if (compact.done.length > 0) {
3102
+ console.log(chalk5.bold.green(" \u2705 Completed:"));
3103
+ for (const d of compact.done.slice(0, 15)) {
3104
+ console.log(chalk5.green(` \u25CF ${d}`));
3105
+ }
3106
+ if (compact.done.length > 15) console.log(chalk5.dim(` ... +${compact.done.length - 15} more`));
3107
+ console.log();
3108
+ }
3109
+ if (compact.remaining.length > 0) {
3110
+ console.log(chalk5.bold.yellow(" \u23F3 Remaining:"));
3111
+ for (const r of compact.remaining) {
3112
+ console.log(chalk5.yellow(` \u25CB ${r}`));
3113
+ }
3114
+ console.log();
3115
+ }
3116
+ if (compact.commits.length > 0) {
3117
+ console.log(chalk5.bold.cyan(" Commits:"));
3118
+ for (const c of compact.commits.slice(0, 8)) {
3119
+ console.log(chalk5.cyan(` \u25CF ${c}`));
3120
+ }
3121
+ console.log();
3122
+ }
3123
+ if (compact.errors.length > 0) {
3124
+ console.log(chalk5.bold.red(" \u26A0\uFE0F Errors:"));
3125
+ for (const e of compact.errors.slice(0, 5)) {
3126
+ console.log(chalk5.red(` \u2717 ${e}`));
3127
+ }
3128
+ console.log();
3129
+ }
3130
+ if (opts.save) {
3131
+ const path = saveCompact(compact);
3132
+ console.log(chalk5.green(` \u2714 Saved to ${path}`));
3133
+ console.log();
3134
+ }
3135
+ }
3136
+
2833
3137
  // src/index.ts
2834
3138
  var program = new Command();
2835
- program.name("copilot-agent").version("0.9.0").description("Autonomous AI agent manager \u2014 auto-resume, task discovery, overnight runs. Supports GitHub Copilot CLI + Claude Code.");
3139
+ program.name("copilot-agent").version("0.10.0").description("Autonomous AI agent manager \u2014 auto-resume, task discovery, overnight runs. Supports GitHub Copilot CLI + Claude Code.");
2836
3140
  registerStatusCommand(program);
2837
3141
  registerWatchCommand(program);
2838
3142
  registerRunCommand(program);
@@ -2844,5 +3148,7 @@ registerWebCommand(program);
2844
3148
  registerConfigCommand(program);
2845
3149
  registerProxyCommand(program);
2846
3150
  registerDiffCommand(program);
3151
+ registerQuotaCommand(program);
3152
+ registerCompactCommand(program);
2847
3153
  program.parse();
2848
3154
  //# sourceMappingURL=index.js.map