icopilot 2.2.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.
Files changed (203) hide show
  1. package/CHANGELOG.md +250 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/bin/icopilot.js +6 -0
  5. package/dist/acp/router.js +123 -0
  6. package/dist/acp/schema.js +53 -0
  7. package/dist/agents/aggregator.js +187 -0
  8. package/dist/agents/custom-agents.js +97 -0
  9. package/dist/agents/goal-driven.js +411 -0
  10. package/dist/agents/multi-repo.js +350 -0
  11. package/dist/agents/parallel-runner.js +181 -0
  12. package/dist/agents/router.js +144 -0
  13. package/dist/agents/self-heal.js +481 -0
  14. package/dist/agents/tdd-agent.js +278 -0
  15. package/dist/api/github-models.js +158 -0
  16. package/dist/bridge/ide-bridge.js +479 -0
  17. package/dist/cloud/routine-executor.js +34 -0
  18. package/dist/cloud/routine-scheduler.js +67 -0
  19. package/dist/cloud/routine-storage.js +297 -0
  20. package/dist/commands/acp-cmd.js +143 -0
  21. package/dist/commands/actions-cmd.js +624 -0
  22. package/dist/commands/agent-cmd.js +144 -0
  23. package/dist/commands/alias-cmd.js +132 -0
  24. package/dist/commands/bookmark-cmd.js +77 -0
  25. package/dist/commands/changelog-cmd.js +99 -0
  26. package/dist/commands/changes-cmd.js +120 -0
  27. package/dist/commands/clipboard-cmd.js +217 -0
  28. package/dist/commands/cloud-routine-cmd.js +265 -0
  29. package/dist/commands/codegen-cmd.js +544 -0
  30. package/dist/commands/compare-cmd.js +116 -0
  31. package/dist/commands/context-cmd.js +247 -0
  32. package/dist/commands/context-viz-cmd.js +43 -0
  33. package/dist/commands/conventions-cmd.js +116 -0
  34. package/dist/commands/cost-cmd.js +51 -0
  35. package/dist/commands/deps-cmd.js +294 -0
  36. package/dist/commands/diagram-cmd.js +658 -0
  37. package/dist/commands/diff-review-cmd.js +92 -0
  38. package/dist/commands/doc-cmd.js +412 -0
  39. package/dist/commands/doctor-cmd.js +152 -0
  40. package/dist/commands/editor-cmd.js +49 -0
  41. package/dist/commands/env-cmd.js +86 -0
  42. package/dist/commands/explain-cmd.js +78 -0
  43. package/dist/commands/explain-shell-cmd.js +22 -0
  44. package/dist/commands/explore-cmd.js +231 -0
  45. package/dist/commands/feedback-cmd.js +98 -0
  46. package/dist/commands/fix-cmd.js +17 -0
  47. package/dist/commands/generate-cmd.js +38 -0
  48. package/dist/commands/git-extra.js +197 -0
  49. package/dist/commands/git-log-cmd.js +98 -0
  50. package/dist/commands/git-undo-cmd.js +137 -0
  51. package/dist/commands/git.js +155 -0
  52. package/dist/commands/history-cmd.js +122 -0
  53. package/dist/commands/index-cmd.js +65 -0
  54. package/dist/commands/init-cmd.js +73 -0
  55. package/dist/commands/lint-cmd.js +133 -0
  56. package/dist/commands/memory-cmd.js +98 -0
  57. package/dist/commands/metrics-cmd.js +97 -0
  58. package/dist/commands/mode-prefix.js +30 -0
  59. package/dist/commands/multi-cmd.js +44 -0
  60. package/dist/commands/notify-cmd.js +204 -0
  61. package/dist/commands/profile-cmd.js +101 -0
  62. package/dist/commands/prompts.js +17 -0
  63. package/dist/commands/rag-cmd.js +60 -0
  64. package/dist/commands/readme-cmd.js +564 -0
  65. package/dist/commands/reasoning-cmd.js +34 -0
  66. package/dist/commands/refactor-cmd.js +96 -0
  67. package/dist/commands/release-cmd.js +450 -0
  68. package/dist/commands/repo-cmd.js +195 -0
  69. package/dist/commands/route-cmd.js +21 -0
  70. package/dist/commands/schedule-cmd.js +109 -0
  71. package/dist/commands/search-cmd.js +47 -0
  72. package/dist/commands/security-cmd.js +156 -0
  73. package/dist/commands/settings-cmd.js +238 -0
  74. package/dist/commands/skill-cmd.js +338 -0
  75. package/dist/commands/slash.js +2721 -0
  76. package/dist/commands/snippets-cmd.js +83 -0
  77. package/dist/commands/space-cmd.js +92 -0
  78. package/dist/commands/stash-cmd.js +156 -0
  79. package/dist/commands/stats-cmd.js +36 -0
  80. package/dist/commands/style-cmd.js +85 -0
  81. package/dist/commands/suggest-cmd.js +40 -0
  82. package/dist/commands/summary-cmd.js +138 -0
  83. package/dist/commands/task-cmd.js +58 -0
  84. package/dist/commands/team-memory-cmd.js +97 -0
  85. package/dist/commands/template-cmd.js +475 -0
  86. package/dist/commands/test-cmd.js +146 -0
  87. package/dist/commands/todo-cmd.js +172 -0
  88. package/dist/commands/tokens-cmd.js +277 -0
  89. package/dist/commands/trigger-cmd.js +147 -0
  90. package/dist/commands/undo-cmd.js +18 -0
  91. package/dist/commands/voice-cmd.js +89 -0
  92. package/dist/commands/watch-cmd.js +110 -0
  93. package/dist/commands/web-cmd.js +183 -0
  94. package/dist/commands/worktree-cmd.js +119 -0
  95. package/dist/config-profile.js +66 -0
  96. package/dist/config.js +288 -0
  97. package/dist/context/compactor.js +53 -0
  98. package/dist/context/dep-context.js +329 -0
  99. package/dist/context/file-refs.js +54 -0
  100. package/dist/context/git-context.js +229 -0
  101. package/dist/context/image-input.js +66 -0
  102. package/dist/context/memory.js +55 -0
  103. package/dist/context/persistent-memory.js +104 -0
  104. package/dist/context/pinned.js +96 -0
  105. package/dist/context/priority.js +150 -0
  106. package/dist/context/read-only.js +48 -0
  107. package/dist/context/smart-files.js +286 -0
  108. package/dist/context/team-memory.js +156 -0
  109. package/dist/extensions/loader.js +149 -0
  110. package/dist/extensions/marketplace.js +49 -0
  111. package/dist/extensions/slack-provider.js +181 -0
  112. package/dist/extensions/team.js +56 -0
  113. package/dist/extensions/teams-provider.js +222 -0
  114. package/dist/extensions/voice.js +18 -0
  115. package/dist/hooks/lifecycle.js +215 -0
  116. package/dist/hooks/precommit.js +463 -0
  117. package/dist/index/embeddings.js +23 -0
  118. package/dist/index/indexer.js +86 -0
  119. package/dist/index/retrieve.js +20 -0
  120. package/dist/index/store.js +95 -0
  121. package/dist/index.js +286 -0
  122. package/dist/intelligence/dead-code.js +457 -0
  123. package/dist/intelligence/error-watch.js +263 -0
  124. package/dist/intelligence/navigation.js +141 -0
  125. package/dist/intelligence/stack-trace.js +210 -0
  126. package/dist/intelligence/symbol-index.js +410 -0
  127. package/dist/knowledge/auto-memory.js +412 -0
  128. package/dist/knowledge/conventions.js +475 -0
  129. package/dist/knowledge/corrections.js +213 -0
  130. package/dist/knowledge/rag.js +450 -0
  131. package/dist/knowledge/style-learner.js +324 -0
  132. package/dist/logger.js +35 -0
  133. package/dist/mcp/client.js +144 -0
  134. package/dist/mcp/config.js +24 -0
  135. package/dist/mcp/index.js +89 -0
  136. package/dist/modes/auto-compact.js +20 -0
  137. package/dist/modes/autopilot.js +157 -0
  138. package/dist/modes/background.js +82 -0
  139. package/dist/modes/interactive.js +187 -0
  140. package/dist/modes/oneshot.js +36 -0
  141. package/dist/modes/tui.js +265 -0
  142. package/dist/modes/turn.js +342 -0
  143. package/dist/notifications/manager.js +107 -0
  144. package/dist/plugins/marketplace.js +244 -0
  145. package/dist/providers/custom-provider.js +298 -0
  146. package/dist/providers/local-model.js +121 -0
  147. package/dist/routing/profiles.js +44 -0
  148. package/dist/routing/router.js +18 -0
  149. package/dist/sandbox/container.js +151 -0
  150. package/dist/security/audit.js +237 -0
  151. package/dist/security/content-filter.js +449 -0
  152. package/dist/security/proxy.js +301 -0
  153. package/dist/security/retention.js +281 -0
  154. package/dist/security/roles.js +252 -0
  155. package/dist/server/api-server.js +679 -0
  156. package/dist/session/bookmarks.js +72 -0
  157. package/dist/session/cloud-session.js +291 -0
  158. package/dist/session/handoff.js +405 -0
  159. package/dist/session/manager.js +35 -0
  160. package/dist/session/session.js +296 -0
  161. package/dist/session/share.js +313 -0
  162. package/dist/session/undo-journal.js +91 -0
  163. package/dist/snippets/store.js +60 -0
  164. package/dist/spaces/space-config.js +156 -0
  165. package/dist/spaces/space.js +220 -0
  166. package/dist/stats/store.js +101 -0
  167. package/dist/tools/apply-patch.js +134 -0
  168. package/dist/tools/auto-check.js +218 -0
  169. package/dist/tools/diff-edit.js +150 -0
  170. package/dist/tools/diff-prompt.js +36 -0
  171. package/dist/tools/edit-file.js +66 -0
  172. package/dist/tools/file-ops.js +205 -0
  173. package/dist/tools/glob.js +17 -0
  174. package/dist/tools/grep.js +56 -0
  175. package/dist/tools/image.js +194 -0
  176. package/dist/tools/list-directory.js +228 -0
  177. package/dist/tools/memory.js +17 -0
  178. package/dist/tools/multi-edit.js +299 -0
  179. package/dist/tools/policy.js +95 -0
  180. package/dist/tools/registry.js +484 -0
  181. package/dist/tools/retry.js +74 -0
  182. package/dist/tools/run-in-terminal.js +162 -0
  183. package/dist/tools/safety.js +64 -0
  184. package/dist/tools/sandbox.js +15 -0
  185. package/dist/tools/search-symbols.js +212 -0
  186. package/dist/tools/shell.js +118 -0
  187. package/dist/tools/web.js +167 -0
  188. package/dist/ui/prompt.js +37 -0
  189. package/dist/ui/render.js +96 -0
  190. package/dist/ui/screen.js +13 -0
  191. package/dist/ui/theme.js +56 -0
  192. package/dist/util/browser.js +34 -0
  193. package/dist/util/completion.js +350 -0
  194. package/dist/util/cost.js +28 -0
  195. package/dist/util/keybindings.js +113 -0
  196. package/dist/util/lazy.js +26 -0
  197. package/dist/util/perf.js +25 -0
  198. package/dist/util/token-worker.js +11 -0
  199. package/dist/util/tokens.js +50 -0
  200. package/dist/workflows/builtins.js +128 -0
  201. package/dist/workflows/engine.js +496 -0
  202. package/dist/workflows/file-trigger.js +197 -0
  203. package/package.json +79 -0
@@ -0,0 +1,297 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ const ROUTINES_ENV = 'ICOPILOT_CLOUD_ROUTINES_PATH';
6
+ const LOGS_ENV = 'ICOPILOT_CLOUD_ROUTINES_LOGS_PATH';
7
+ function routinesPath() {
8
+ return process.env[ROUTINES_ENV] || path.join(os.homedir(), '.icopilot', 'cloud-routines.json');
9
+ }
10
+ function logsPath() {
11
+ return process.env[LOGS_ENV] || path.join(os.homedir(), '.icopilot', 'cloud-routines-logs.json');
12
+ }
13
+ export class CloudRoutineStore {
14
+ routines = new Map();
15
+ logs = [];
16
+ constructor() {
17
+ this.load();
18
+ }
19
+ load() {
20
+ this.loadRoutines();
21
+ this.loadLogs();
22
+ }
23
+ loadRoutines() {
24
+ const file = routinesPath();
25
+ if (!fs.existsSync(file)) {
26
+ this.routines = new Map();
27
+ return;
28
+ }
29
+ try {
30
+ const data = fs.readFileSync(file, 'utf8');
31
+ const parsed = JSON.parse(data);
32
+ if (Array.isArray(parsed)) {
33
+ this.routines.clear();
34
+ for (const item of parsed) {
35
+ if (isCloudRoutine(item)) {
36
+ this.routines.set(item.id, item);
37
+ }
38
+ }
39
+ }
40
+ }
41
+ catch {
42
+ this.routines = new Map();
43
+ }
44
+ }
45
+ loadLogs() {
46
+ const file = logsPath();
47
+ if (!fs.existsSync(file)) {
48
+ this.logs = [];
49
+ return;
50
+ }
51
+ try {
52
+ const data = fs.readFileSync(file, 'utf8');
53
+ const parsed = JSON.parse(data);
54
+ if (Array.isArray(parsed)) {
55
+ this.logs = parsed.filter(isRoutineExecutionLog);
56
+ }
57
+ }
58
+ catch {
59
+ this.logs = [];
60
+ }
61
+ }
62
+ saveRoutines() {
63
+ const dir = path.dirname(routinesPath());
64
+ if (!fs.existsSync(dir)) {
65
+ fs.mkdirSync(dir, { recursive: true });
66
+ }
67
+ const data = Array.from(this.routines.values());
68
+ fs.writeFileSync(routinesPath(), JSON.stringify(data, null, 2), 'utf8');
69
+ }
70
+ saveLogs() {
71
+ const dir = path.dirname(logsPath());
72
+ if (!fs.existsSync(dir)) {
73
+ fs.mkdirSync(dir, { recursive: true });
74
+ }
75
+ fs.writeFileSync(logsPath(), JSON.stringify(this.logs, null, 2), 'utf8');
76
+ }
77
+ create(name, schedule, prompt, enabled = true) {
78
+ const trimmedName = name.trim();
79
+ if (!trimmedName) {
80
+ throw new Error('routine name is required');
81
+ }
82
+ if (!isValidSchedule(schedule)) {
83
+ throw new Error('invalid schedule configuration');
84
+ }
85
+ const trimmedPrompt = prompt.trim();
86
+ if (!trimmedPrompt) {
87
+ throw new Error('routine prompt is required');
88
+ }
89
+ const routine = {
90
+ id: randomUUID(),
91
+ name: trimmedName,
92
+ prompt: trimmedPrompt,
93
+ schedule,
94
+ enabled,
95
+ createdAt: new Date().toISOString(),
96
+ nextRun: calculateNextRun(schedule),
97
+ };
98
+ this.routines.set(routine.id, routine);
99
+ this.saveRoutines();
100
+ return routine;
101
+ }
102
+ list() {
103
+ return Array.from(this.routines.values()).sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
104
+ }
105
+ get(id) {
106
+ return this.routines.get(id);
107
+ }
108
+ update(id, updates) {
109
+ const routine = this.routines.get(id);
110
+ if (!routine) {
111
+ throw new Error(`routine with id ${id} not found`);
112
+ }
113
+ if (updates.name !== undefined && !updates.name.trim()) {
114
+ throw new Error('routine name cannot be empty');
115
+ }
116
+ if (updates.prompt !== undefined && !updates.prompt.trim()) {
117
+ throw new Error('routine prompt cannot be empty');
118
+ }
119
+ if (updates.schedule !== undefined && !isValidSchedule(updates.schedule)) {
120
+ throw new Error('invalid schedule configuration');
121
+ }
122
+ const updated = {
123
+ ...routine,
124
+ ...updates,
125
+ id: routine.id,
126
+ createdAt: routine.createdAt,
127
+ };
128
+ if (updates.schedule) {
129
+ updated.nextRun = calculateNextRun(updates.schedule);
130
+ }
131
+ this.routines.set(id, updated);
132
+ this.saveRoutines();
133
+ return updated;
134
+ }
135
+ delete(id) {
136
+ if (!this.routines.has(id)) {
137
+ return false;
138
+ }
139
+ this.routines.delete(id);
140
+ this.saveRoutines();
141
+ return true;
142
+ }
143
+ updateLastRun(id, timestamp) {
144
+ const routine = this.routines.get(id);
145
+ if (routine) {
146
+ routine.lastRun = timestamp;
147
+ routine.nextRun = calculateNextRun(routine.schedule);
148
+ this.routines.set(id, routine);
149
+ this.saveRoutines();
150
+ }
151
+ }
152
+ getNextRun(id) {
153
+ const routine = this.routines.get(id);
154
+ return routine?.nextRun;
155
+ }
156
+ findDueRoutines() {
157
+ const now = new Date();
158
+ return this.list().filter((r) => {
159
+ if (!r.enabled || !r.nextRun)
160
+ return false;
161
+ return new Date(r.nextRun) <= now;
162
+ });
163
+ }
164
+ logExecution(routineId, status, output, error, duration = 0) {
165
+ const log = {
166
+ id: randomUUID(),
167
+ routineId,
168
+ timestamp: new Date().toISOString(),
169
+ status,
170
+ output,
171
+ error,
172
+ duration,
173
+ };
174
+ this.logs.push(log);
175
+ this.saveLogs();
176
+ return log;
177
+ }
178
+ getLogs(routineId, limit = 20) {
179
+ return this.logs
180
+ .filter((log) => log.routineId === routineId)
181
+ .slice(-limit)
182
+ .reverse();
183
+ }
184
+ getAllLogs(limit = 100) {
185
+ return this.logs.slice(-limit).reverse();
186
+ }
187
+ }
188
+ function isValidSchedule(schedule) {
189
+ if (!schedule || typeof schedule !== 'object')
190
+ return false;
191
+ const { type } = schedule;
192
+ if (!type || !['once', 'daily', 'weekly', 'monthly', 'custom'].includes(type)) {
193
+ return false;
194
+ }
195
+ if (type === 'daily' && !schedule.time)
196
+ return false;
197
+ if (type === 'weekly' && (schedule.dayOfWeek === undefined || !schedule.time))
198
+ return false;
199
+ if (type === 'monthly' && (schedule.dayOfMonth === undefined || !schedule.time))
200
+ return false;
201
+ if (type === 'custom' && !schedule.expression)
202
+ return false;
203
+ return true;
204
+ }
205
+ function calculateNextRun(schedule) {
206
+ const now = new Date();
207
+ switch (schedule.type) {
208
+ case 'once': {
209
+ return now.toISOString();
210
+ }
211
+ case 'daily': {
212
+ const time = schedule.time || '09:00';
213
+ const [hours, minutes] = parseTime(time);
214
+ const next = new Date(now);
215
+ next.setHours(hours, minutes, 0, 0);
216
+ if (next <= now) {
217
+ next.setDate(next.getDate() + 1);
218
+ }
219
+ return next.toISOString();
220
+ }
221
+ case 'weekly': {
222
+ const time = schedule.time || '09:00';
223
+ const dayOfWeek = schedule.dayOfWeek ?? 0;
224
+ const [hours, minutes] = parseTime(time);
225
+ const next = new Date(now);
226
+ next.setHours(hours, minutes, 0, 0);
227
+ const daysUntilTarget = (dayOfWeek - next.getDay() + 7) % 7;
228
+ if (daysUntilTarget === 0 && next <= now) {
229
+ next.setDate(next.getDate() + 7);
230
+ }
231
+ else if (daysUntilTarget > 0) {
232
+ next.setDate(next.getDate() + daysUntilTarget);
233
+ }
234
+ return next.toISOString();
235
+ }
236
+ case 'monthly': {
237
+ const time = schedule.time || '09:00';
238
+ const dayOfMonth = schedule.dayOfMonth ?? 1;
239
+ const [hours, minutes] = parseTime(time);
240
+ const next = new Date(now);
241
+ next.setHours(hours, minutes, 0, 0);
242
+ next.setDate(dayOfMonth);
243
+ if (next <= now) {
244
+ next.setMonth(next.getMonth() + 1);
245
+ next.setDate(dayOfMonth);
246
+ }
247
+ return next.toISOString();
248
+ }
249
+ case 'custom': {
250
+ return now.toISOString();
251
+ }
252
+ default: {
253
+ return now.toISOString();
254
+ }
255
+ }
256
+ }
257
+ function parseTime(timeStr) {
258
+ const trimmed = timeStr.trim();
259
+ const match = trimmed.match(/^(\d{1,2}):?(\d{2})?/);
260
+ if (!match) {
261
+ throw new Error(`invalid time format: ${timeStr}`);
262
+ }
263
+ const hours = parseInt(match[1], 10);
264
+ const minutes = match[2] ? parseInt(match[2], 10) : 0;
265
+ if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
266
+ throw new Error(`invalid time values: ${timeStr}`);
267
+ }
268
+ return [hours, minutes];
269
+ }
270
+ function isCloudRoutine(value) {
271
+ if (!value || typeof value !== 'object')
272
+ return false;
273
+ const obj = value;
274
+ return (typeof obj.id === 'string' &&
275
+ typeof obj.name === 'string' &&
276
+ typeof obj.prompt === 'string' &&
277
+ isValidSchedule(obj.schedule) &&
278
+ typeof obj.enabled === 'boolean' &&
279
+ typeof obj.createdAt === 'string');
280
+ }
281
+ function isRoutineExecutionLog(value) {
282
+ if (!value || typeof value !== 'object')
283
+ return false;
284
+ const obj = value;
285
+ return (typeof obj.id === 'string' &&
286
+ typeof obj.routineId === 'string' &&
287
+ typeof obj.timestamp === 'string' &&
288
+ (obj.status === 'success' || obj.status === 'error') &&
289
+ typeof obj.duration === 'number');
290
+ }
291
+ let storeInstance = null;
292
+ export function getCloudRoutineStore() {
293
+ if (!storeInstance) {
294
+ storeInstance = new CloudRoutineStore();
295
+ }
296
+ return storeInstance;
297
+ }
@@ -0,0 +1,143 @@
1
+ import { getGlobalAPIServer } from '../server/api-server.js';
2
+ import { theme } from '../ui/theme.js';
3
+ export async function acpCommand(options) {
4
+ const subcommand = (options.subcommand || 'status').toLowerCase();
5
+ switch (subcommand) {
6
+ case 'status':
7
+ return formatAcpStatus();
8
+ case 'enable':
9
+ return await enableAcp(options.args);
10
+ case 'disable':
11
+ return await disableAcp();
12
+ case 'test':
13
+ return await testAcpMethod(options.args);
14
+ case 'help':
15
+ return formatAcpHelp();
16
+ default:
17
+ return `${theme.error('Unknown ACP subcommand:')} ${subcommand}\n\n${formatAcpHelp()}`;
18
+ }
19
+ }
20
+ function formatAcpStatus() {
21
+ const server = getGlobalAPIServer();
22
+ const isRunning = server.isRunning();
23
+ const port = server.getPort();
24
+ if (!isRunning) {
25
+ return theme.info('ACP Server is currently disabled.');
26
+ }
27
+ return `${theme.success('ACP Server is running')}
28
+ ${theme.muted('Protocol:')} Agent Client Protocol (ACP)
29
+ ${theme.muted('Endpoint:')} http://localhost:${port}/acp
30
+ ${theme.muted('Methods:')}
31
+ • tools/list - list available tools
32
+ • tool/call - execute a tool
33
+ • capabilities/get - get server capabilities
34
+
35
+ ${theme.muted('Test request:')}
36
+ curl -X POST http://localhost:${port}/acp \\
37
+ -H 'Content-Type: application/json' \\
38
+ -d '{"jsonrpc":"2.0","method":"capabilities/get","id":1}'
39
+ `;
40
+ }
41
+ async function enableAcp(args) {
42
+ const portStr = args?.[0];
43
+ const port = portStr ? parseInt(portStr, 10) : 5173;
44
+ if (isNaN(port) || port < 1024 || port > 65535) {
45
+ return theme.error(`Invalid port: ${portStr || 'default'}`);
46
+ }
47
+ try {
48
+ const server = getGlobalAPIServer();
49
+ const actualPort = await server.start(port);
50
+ return `${theme.success('ACP Server enabled')}
51
+ ${theme.muted('Port:')} ${actualPort}
52
+ ${theme.muted('Endpoint:')} http://localhost:${actualPort}/acp
53
+
54
+ ${theme.info('Test with:')}
55
+ curl -X POST http://localhost:${actualPort}/acp \\
56
+ -H 'Content-Type: application/json' \\
57
+ -d '{"jsonrpc":"2.0","method":"capabilities/get","id":1}'
58
+ `;
59
+ }
60
+ catch (error) {
61
+ return theme.error(`Failed to start ACP server: ${error instanceof Error ? error.message : String(error)}`);
62
+ }
63
+ }
64
+ async function disableAcp() {
65
+ try {
66
+ const server = getGlobalAPIServer();
67
+ if (!server.isRunning()) {
68
+ return theme.info('ACP Server is already disabled.');
69
+ }
70
+ await server.stop();
71
+ return theme.success('ACP Server disabled');
72
+ }
73
+ catch (error) {
74
+ return theme.error(`Failed to stop ACP server: ${error instanceof Error ? error.message : String(error)}`);
75
+ }
76
+ }
77
+ async function testAcpMethod(args) {
78
+ const method = args?.[0];
79
+ if (!method) {
80
+ return theme.error('Usage: /acp test <method> [params]');
81
+ }
82
+ const server = getGlobalAPIServer();
83
+ if (!server.isRunning()) {
84
+ return theme.error('ACP Server is not running. Use /acp enable [port] first.');
85
+ }
86
+ const port = server.getPort();
87
+ if (!port) {
88
+ return theme.error('Could not determine ACP server port.');
89
+ }
90
+ const paramsStr = args?.slice(1).join(' ') || '';
91
+ let params = undefined;
92
+ if (paramsStr) {
93
+ try {
94
+ params = JSON.parse(paramsStr);
95
+ }
96
+ catch {
97
+ return theme.error(`Invalid JSON params: ${paramsStr}`);
98
+ }
99
+ }
100
+ const request = {
101
+ jsonrpc: '2.0',
102
+ method,
103
+ params,
104
+ id: Math.floor(Math.random() * 1000000),
105
+ };
106
+ return `${theme.info('Test request:')}
107
+ ${JSON.stringify(request, null, 2)}
108
+
109
+ ${theme.info('Execute with:')}
110
+ curl -X POST http://localhost:${port}/acp \\
111
+ -H 'Content-Type: application/json' \\
112
+ -d '${JSON.stringify(request)}'
113
+
114
+ ${theme.muted('Expected responses:')}
115
+ • tools/list → array of available tools
116
+ • tool/call → requires toolName and args in params
117
+ • capabilities/get → server capabilities and version
118
+ `;
119
+ }
120
+ function formatAcpHelp() {
121
+ return `${theme.brand('ACP (Agent Client Protocol) Commands')}
122
+
123
+ ${theme.muted('Usage:')} /acp [command] [options]
124
+
125
+ ${theme.muted('Commands:')}
126
+ status show ACP server status and configuration
127
+ enable [port] start ACP server on specified port (default 5173)
128
+ disable stop the ACP server
129
+ test <method> test an ACP method with sample request
130
+ help show this help message
131
+
132
+ ${theme.muted('Examples:')}
133
+ /acp status
134
+ /acp enable 5173
135
+ /acp test tools/list
136
+ /acp test capabilities/get
137
+
138
+ ${theme.muted('ACP allows external agents to:')}
139
+ • List available iCopilot tools
140
+ • Execute iCopilot tools with parameters
141
+ • Query server capabilities and version
142
+ `;
143
+ }