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,252 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parseDocument, stringify } from 'yaml';
4
+ import { config } from '../config.js';
5
+ export const DEFAULT_ROLE_NAME = 'developer';
6
+ export const ROLES_CONFIG_FILE = '.icopilot/roles.yaml';
7
+ export const BUILTIN_ROLES = [
8
+ {
9
+ name: 'admin',
10
+ permissions: ['shell:execute', 'file:read', 'file:write', 'file:delete', 'tool:*', 'command:*'],
11
+ },
12
+ {
13
+ name: 'developer',
14
+ permissions: ['shell:execute', 'file:read', 'file:write', 'tool:*', 'command:*'],
15
+ },
16
+ {
17
+ name: 'reviewer',
18
+ permissions: [
19
+ 'file:read',
20
+ 'tool:read_file',
21
+ 'tool:grep',
22
+ 'tool:glob',
23
+ 'tool:list_directory',
24
+ 'tool:search_symbols',
25
+ 'tool:web_fetch',
26
+ 'tool:describe_image',
27
+ 'command:*',
28
+ ],
29
+ },
30
+ {
31
+ name: 'viewer',
32
+ permissions: [
33
+ 'file:read',
34
+ 'tool:read_file',
35
+ 'tool:grep',
36
+ 'tool:glob',
37
+ 'tool:list_directory',
38
+ 'tool:search_symbols',
39
+ 'tool:describe_image',
40
+ 'command:*',
41
+ ],
42
+ },
43
+ ];
44
+ const TOOL_PERMISSION_MAP = {
45
+ run_shell: 'shell:execute',
46
+ run_in_terminal: 'shell:execute',
47
+ read_file: 'file:read',
48
+ grep: 'file:read',
49
+ glob: 'file:read',
50
+ list_directory: 'file:read',
51
+ search_symbols: 'file:read',
52
+ describe_image: 'file:read',
53
+ write_file: 'file:write',
54
+ write_files: 'file:write',
55
+ edit_file: 'file:write',
56
+ multi_edit: 'file:write',
57
+ apply_patch: 'file:write',
58
+ };
59
+ export class RoleManager {
60
+ configPath;
61
+ roles = cloneRoles(BUILTIN_ROLES);
62
+ currentRoleName = DEFAULT_ROLE_NAME;
63
+ constructor(configPath = defaultRolesConfigPath()) {
64
+ this.configPath = configPath;
65
+ }
66
+ loadRoles(configPath = this.configPath) {
67
+ this.configPath = path.resolve(configPath);
68
+ this.roles = cloneRoles(BUILTIN_ROLES);
69
+ this.currentRoleName = DEFAULT_ROLE_NAME;
70
+ try {
71
+ if (!fs.existsSync(this.configPath))
72
+ return this.listRoles();
73
+ const raw = fs.readFileSync(this.configPath, 'utf8');
74
+ const document = parseDocument(raw);
75
+ if (document.errors.length > 0) {
76
+ throw document.errors[0] ?? new Error('Invalid roles YAML');
77
+ }
78
+ const parsed = parseRolesConfig(document.toJSON());
79
+ this.roles = mergeRoles(BUILTIN_ROLES, parsed.roles ?? []);
80
+ this.currentRoleName = resolveRoleName(parsed.currentRole, this.roles) ?? DEFAULT_ROLE_NAME;
81
+ }
82
+ catch {
83
+ this.roles = cloneRoles(BUILTIN_ROLES);
84
+ this.currentRoleName = DEFAULT_ROLE_NAME;
85
+ }
86
+ return this.listRoles();
87
+ }
88
+ getCurrentRole() {
89
+ this.ensureLoaded();
90
+ return cloneRole(this.findCurrentRole());
91
+ }
92
+ setRole(roleName) {
93
+ this.ensureLoaded();
94
+ const resolved = resolveRoleName(roleName, this.roles);
95
+ if (!resolved) {
96
+ throw new Error(`Unknown role: ${roleName}`);
97
+ }
98
+ this.currentRoleName = resolved;
99
+ this.persist();
100
+ }
101
+ hasPermission(permission) {
102
+ this.ensureLoaded();
103
+ const permissions = new Set(this.findCurrentRole().permissions);
104
+ if (permissions.has(permission))
105
+ return true;
106
+ if (permission.startsWith('tool:') && permissions.has('tool:*'))
107
+ return true;
108
+ if (permission.startsWith('command:') && permissions.has('command:*'))
109
+ return true;
110
+ return false;
111
+ }
112
+ checkAccess(tool) {
113
+ this.ensureLoaded();
114
+ const normalized = normalizeTarget(tool);
115
+ const role = this.findCurrentRole();
116
+ if (normalized.kind === 'command') {
117
+ const permission = `command:${normalized.name}`;
118
+ if (this.hasPermission(permission))
119
+ return { allowed: true };
120
+ return {
121
+ allowed: false,
122
+ reason: `role "${role.name}" does not permit command "${normalized.name}"`,
123
+ };
124
+ }
125
+ const specificPermission = `tool:${normalized.name}`;
126
+ const impliedPermission = inferToolPermission(normalized.name);
127
+ if (this.hasPermission(specificPermission) ||
128
+ (impliedPermission && this.hasPermission(impliedPermission))) {
129
+ return { allowed: true };
130
+ }
131
+ return {
132
+ allowed: false,
133
+ reason: `role "${role.name}" does not permit tool "${normalized.name}"`,
134
+ };
135
+ }
136
+ listRoles() {
137
+ this.ensureLoaded();
138
+ return cloneRoles(this.roles);
139
+ }
140
+ ensureLoaded() {
141
+ if (this.roles.length === 0) {
142
+ this.loadRoles();
143
+ return;
144
+ }
145
+ if (!this.findRole(this.currentRoleName)) {
146
+ this.currentRoleName = DEFAULT_ROLE_NAME;
147
+ }
148
+ }
149
+ findCurrentRole() {
150
+ return this.findRole(this.currentRoleName) ?? cloneRole(BUILTIN_ROLES[1]);
151
+ }
152
+ findRole(roleName) {
153
+ return this.roles.find((role) => role.name.toLowerCase() === roleName.toLowerCase());
154
+ }
155
+ persist() {
156
+ fs.mkdirSync(path.dirname(this.configPath), { recursive: true });
157
+ fs.writeFileSync(this.configPath, renderRolesConfig(this.currentRoleName, this.roles), 'utf8');
158
+ }
159
+ }
160
+ export function defaultRolesConfigPath(cwd = config.cwd) {
161
+ return path.join(cwd, ROLES_CONFIG_FILE);
162
+ }
163
+ export function renderRolesConfig(currentRole = DEFAULT_ROLE_NAME, roles = BUILTIN_ROLES) {
164
+ return stringify({
165
+ currentRole,
166
+ roles: cloneRoles(roles),
167
+ });
168
+ }
169
+ function parseRolesConfig(value) {
170
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
171
+ throw new Error('roles config must contain a YAML object');
172
+ }
173
+ const record = value;
174
+ const currentRole = typeof record.currentRole === 'string' && record.currentRole.trim().length > 0
175
+ ? record.currentRole.trim()
176
+ : undefined;
177
+ if (record.roles !== undefined && !Array.isArray(record.roles)) {
178
+ throw new Error('roles field must be an array');
179
+ }
180
+ return {
181
+ currentRole,
182
+ roles: Array.isArray(record.roles) ? record.roles.map(validateRole) : undefined,
183
+ };
184
+ }
185
+ function validateRole(value) {
186
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
187
+ throw new Error('role entries must be objects');
188
+ }
189
+ const record = value;
190
+ if (typeof record.name !== 'string' || record.name.trim().length === 0) {
191
+ throw new Error('role name must be a non-empty string');
192
+ }
193
+ if (!Array.isArray(record.permissions) ||
194
+ record.permissions.some((permission) => typeof permission !== 'string' || permission.trim().length === 0)) {
195
+ throw new Error(`role "${record.name}" permissions must be an array of non-empty strings`);
196
+ }
197
+ return {
198
+ name: record.name.trim(),
199
+ permissions: [
200
+ ...new Set(record.permissions.map((permission) => permission.trim())),
201
+ ],
202
+ };
203
+ }
204
+ function mergeRoles(base, overrides) {
205
+ const merged = new Map();
206
+ for (const role of base)
207
+ merged.set(role.name.toLowerCase(), cloneRole(role));
208
+ for (const role of overrides)
209
+ merged.set(role.name.toLowerCase(), cloneRole(role));
210
+ const ordered = [];
211
+ for (const role of base) {
212
+ const next = merged.get(role.name.toLowerCase());
213
+ if (next) {
214
+ ordered.push(next);
215
+ merged.delete(role.name.toLowerCase());
216
+ }
217
+ }
218
+ const custom = [...merged.values()].sort((left, right) => left.name.localeCompare(right.name));
219
+ return [...ordered, ...custom];
220
+ }
221
+ function resolveRoleName(roleName, roles) {
222
+ if (!roleName)
223
+ return undefined;
224
+ const match = roles.find((role) => role.name.toLowerCase() === roleName.toLowerCase());
225
+ return match?.name;
226
+ }
227
+ function normalizeTarget(target) {
228
+ const trimmed = target.trim();
229
+ if (trimmed.startsWith('command:')) {
230
+ return { kind: 'command', name: trimmed.slice('command:'.length).trim() };
231
+ }
232
+ if (trimmed.startsWith('/')) {
233
+ return { kind: 'command', name: trimmed.slice(1).trim() };
234
+ }
235
+ if (trimmed.startsWith('tool:')) {
236
+ return { kind: 'tool', name: trimmed.slice('tool:'.length).trim() };
237
+ }
238
+ return { kind: 'tool', name: trimmed };
239
+ }
240
+ function inferToolPermission(toolName) {
241
+ if (toolName in TOOL_PERMISSION_MAP)
242
+ return TOOL_PERMISSION_MAP[toolName];
243
+ if (/(^|[_-])(delete|remove|unlink)([_-]|$)/i.test(toolName))
244
+ return 'file:delete';
245
+ return undefined;
246
+ }
247
+ function cloneRole(role) {
248
+ return { name: role.name, permissions: [...role.permissions] };
249
+ }
250
+ function cloneRoles(roles) {
251
+ return roles.map(cloneRole);
252
+ }