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.
- package/CHANGELOG.md +250 -0
- package/LICENSE +21 -0
- package/README.md +214 -0
- package/bin/icopilot.js +6 -0
- package/dist/acp/router.js +123 -0
- package/dist/acp/schema.js +53 -0
- package/dist/agents/aggregator.js +187 -0
- package/dist/agents/custom-agents.js +97 -0
- package/dist/agents/goal-driven.js +411 -0
- package/dist/agents/multi-repo.js +350 -0
- package/dist/agents/parallel-runner.js +181 -0
- package/dist/agents/router.js +144 -0
- package/dist/agents/self-heal.js +481 -0
- package/dist/agents/tdd-agent.js +278 -0
- package/dist/api/github-models.js +158 -0
- package/dist/bridge/ide-bridge.js +479 -0
- package/dist/cloud/routine-executor.js +34 -0
- package/dist/cloud/routine-scheduler.js +67 -0
- package/dist/cloud/routine-storage.js +297 -0
- package/dist/commands/acp-cmd.js +143 -0
- package/dist/commands/actions-cmd.js +624 -0
- package/dist/commands/agent-cmd.js +144 -0
- package/dist/commands/alias-cmd.js +132 -0
- package/dist/commands/bookmark-cmd.js +77 -0
- package/dist/commands/changelog-cmd.js +99 -0
- package/dist/commands/changes-cmd.js +120 -0
- package/dist/commands/clipboard-cmd.js +217 -0
- package/dist/commands/cloud-routine-cmd.js +265 -0
- package/dist/commands/codegen-cmd.js +544 -0
- package/dist/commands/compare-cmd.js +116 -0
- package/dist/commands/context-cmd.js +247 -0
- package/dist/commands/context-viz-cmd.js +43 -0
- package/dist/commands/conventions-cmd.js +116 -0
- package/dist/commands/cost-cmd.js +51 -0
- package/dist/commands/deps-cmd.js +294 -0
- package/dist/commands/diagram-cmd.js +658 -0
- package/dist/commands/diff-review-cmd.js +92 -0
- package/dist/commands/doc-cmd.js +412 -0
- package/dist/commands/doctor-cmd.js +152 -0
- package/dist/commands/editor-cmd.js +49 -0
- package/dist/commands/env-cmd.js +86 -0
- package/dist/commands/explain-cmd.js +78 -0
- package/dist/commands/explain-shell-cmd.js +22 -0
- package/dist/commands/explore-cmd.js +231 -0
- package/dist/commands/feedback-cmd.js +98 -0
- package/dist/commands/fix-cmd.js +17 -0
- package/dist/commands/generate-cmd.js +38 -0
- package/dist/commands/git-extra.js +197 -0
- package/dist/commands/git-log-cmd.js +98 -0
- package/dist/commands/git-undo-cmd.js +137 -0
- package/dist/commands/git.js +155 -0
- package/dist/commands/history-cmd.js +122 -0
- package/dist/commands/index-cmd.js +65 -0
- package/dist/commands/init-cmd.js +73 -0
- package/dist/commands/lint-cmd.js +133 -0
- package/dist/commands/memory-cmd.js +98 -0
- package/dist/commands/metrics-cmd.js +97 -0
- package/dist/commands/mode-prefix.js +30 -0
- package/dist/commands/multi-cmd.js +44 -0
- package/dist/commands/notify-cmd.js +204 -0
- package/dist/commands/profile-cmd.js +101 -0
- package/dist/commands/prompts.js +17 -0
- package/dist/commands/rag-cmd.js +60 -0
- package/dist/commands/readme-cmd.js +564 -0
- package/dist/commands/reasoning-cmd.js +34 -0
- package/dist/commands/refactor-cmd.js +96 -0
- package/dist/commands/release-cmd.js +450 -0
- package/dist/commands/repo-cmd.js +195 -0
- package/dist/commands/route-cmd.js +21 -0
- package/dist/commands/schedule-cmd.js +109 -0
- package/dist/commands/search-cmd.js +47 -0
- package/dist/commands/security-cmd.js +156 -0
- package/dist/commands/settings-cmd.js +238 -0
- package/dist/commands/skill-cmd.js +338 -0
- package/dist/commands/slash.js +2721 -0
- package/dist/commands/snippets-cmd.js +83 -0
- package/dist/commands/space-cmd.js +92 -0
- package/dist/commands/stash-cmd.js +156 -0
- package/dist/commands/stats-cmd.js +36 -0
- package/dist/commands/style-cmd.js +85 -0
- package/dist/commands/suggest-cmd.js +40 -0
- package/dist/commands/summary-cmd.js +138 -0
- package/dist/commands/task-cmd.js +58 -0
- package/dist/commands/team-memory-cmd.js +97 -0
- package/dist/commands/template-cmd.js +475 -0
- package/dist/commands/test-cmd.js +146 -0
- package/dist/commands/todo-cmd.js +172 -0
- package/dist/commands/tokens-cmd.js +277 -0
- package/dist/commands/trigger-cmd.js +147 -0
- package/dist/commands/undo-cmd.js +18 -0
- package/dist/commands/voice-cmd.js +89 -0
- package/dist/commands/watch-cmd.js +110 -0
- package/dist/commands/web-cmd.js +183 -0
- package/dist/commands/worktree-cmd.js +119 -0
- package/dist/config-profile.js +66 -0
- package/dist/config.js +288 -0
- package/dist/context/compactor.js +53 -0
- package/dist/context/dep-context.js +329 -0
- package/dist/context/file-refs.js +54 -0
- package/dist/context/git-context.js +229 -0
- package/dist/context/image-input.js +66 -0
- package/dist/context/memory.js +55 -0
- package/dist/context/persistent-memory.js +104 -0
- package/dist/context/pinned.js +96 -0
- package/dist/context/priority.js +150 -0
- package/dist/context/read-only.js +48 -0
- package/dist/context/smart-files.js +286 -0
- package/dist/context/team-memory.js +156 -0
- package/dist/extensions/loader.js +149 -0
- package/dist/extensions/marketplace.js +49 -0
- package/dist/extensions/slack-provider.js +181 -0
- package/dist/extensions/team.js +56 -0
- package/dist/extensions/teams-provider.js +222 -0
- package/dist/extensions/voice.js +18 -0
- package/dist/hooks/lifecycle.js +215 -0
- package/dist/hooks/precommit.js +463 -0
- package/dist/index/embeddings.js +23 -0
- package/dist/index/indexer.js +86 -0
- package/dist/index/retrieve.js +20 -0
- package/dist/index/store.js +95 -0
- package/dist/index.js +286 -0
- package/dist/intelligence/dead-code.js +457 -0
- package/dist/intelligence/error-watch.js +263 -0
- package/dist/intelligence/navigation.js +141 -0
- package/dist/intelligence/stack-trace.js +210 -0
- package/dist/intelligence/symbol-index.js +410 -0
- package/dist/knowledge/auto-memory.js +412 -0
- package/dist/knowledge/conventions.js +475 -0
- package/dist/knowledge/corrections.js +213 -0
- package/dist/knowledge/rag.js +450 -0
- package/dist/knowledge/style-learner.js +324 -0
- package/dist/logger.js +35 -0
- package/dist/mcp/client.js +144 -0
- package/dist/mcp/config.js +24 -0
- package/dist/mcp/index.js +89 -0
- package/dist/modes/auto-compact.js +20 -0
- package/dist/modes/autopilot.js +157 -0
- package/dist/modes/background.js +82 -0
- package/dist/modes/interactive.js +187 -0
- package/dist/modes/oneshot.js +36 -0
- package/dist/modes/tui.js +265 -0
- package/dist/modes/turn.js +342 -0
- package/dist/notifications/manager.js +107 -0
- package/dist/plugins/marketplace.js +244 -0
- package/dist/providers/custom-provider.js +298 -0
- package/dist/providers/local-model.js +121 -0
- package/dist/routing/profiles.js +44 -0
- package/dist/routing/router.js +18 -0
- package/dist/sandbox/container.js +151 -0
- package/dist/security/audit.js +237 -0
- package/dist/security/content-filter.js +449 -0
- package/dist/security/proxy.js +301 -0
- package/dist/security/retention.js +281 -0
- package/dist/security/roles.js +252 -0
- package/dist/server/api-server.js +679 -0
- package/dist/session/bookmarks.js +72 -0
- package/dist/session/cloud-session.js +291 -0
- package/dist/session/handoff.js +405 -0
- package/dist/session/manager.js +35 -0
- package/dist/session/session.js +296 -0
- package/dist/session/share.js +313 -0
- package/dist/session/undo-journal.js +91 -0
- package/dist/snippets/store.js +60 -0
- package/dist/spaces/space-config.js +156 -0
- package/dist/spaces/space.js +220 -0
- package/dist/stats/store.js +101 -0
- package/dist/tools/apply-patch.js +134 -0
- package/dist/tools/auto-check.js +218 -0
- package/dist/tools/diff-edit.js +150 -0
- package/dist/tools/diff-prompt.js +36 -0
- package/dist/tools/edit-file.js +66 -0
- package/dist/tools/file-ops.js +205 -0
- package/dist/tools/glob.js +17 -0
- package/dist/tools/grep.js +56 -0
- package/dist/tools/image.js +194 -0
- package/dist/tools/list-directory.js +228 -0
- package/dist/tools/memory.js +17 -0
- package/dist/tools/multi-edit.js +299 -0
- package/dist/tools/policy.js +95 -0
- package/dist/tools/registry.js +484 -0
- package/dist/tools/retry.js +74 -0
- package/dist/tools/run-in-terminal.js +162 -0
- package/dist/tools/safety.js +64 -0
- package/dist/tools/sandbox.js +15 -0
- package/dist/tools/search-symbols.js +212 -0
- package/dist/tools/shell.js +118 -0
- package/dist/tools/web.js +167 -0
- package/dist/ui/prompt.js +37 -0
- package/dist/ui/render.js +96 -0
- package/dist/ui/screen.js +13 -0
- package/dist/ui/theme.js +56 -0
- package/dist/util/browser.js +34 -0
- package/dist/util/completion.js +350 -0
- package/dist/util/cost.js +28 -0
- package/dist/util/keybindings.js +113 -0
- package/dist/util/lazy.js +26 -0
- package/dist/util/perf.js +25 -0
- package/dist/util/token-worker.js +11 -0
- package/dist/util/tokens.js +50 -0
- package/dist/workflows/builtins.js +128 -0
- package/dist/workflows/engine.js +496 -0
- package/dist/workflows/file-trigger.js +197 -0
- package/package.json +79 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { input } from '@inquirer/prompts';
|
|
2
|
+
import { theme } from '../ui/theme.js';
|
|
3
|
+
import { getNotificationManager, initializeNotificationManager } from '../notifications/manager.js';
|
|
4
|
+
const ok = theme.ok;
|
|
5
|
+
const info = theme.hl;
|
|
6
|
+
const warn = theme.warn;
|
|
7
|
+
const error = theme.err;
|
|
8
|
+
function loadNotificationConfig() {
|
|
9
|
+
const slackToken = process.env.ICOPILOT_SLACK_TOKEN;
|
|
10
|
+
const teamsToken = process.env.ICOPILOT_TEAMS_TOKEN;
|
|
11
|
+
const channel = process.env.ICOPILOT_NOTIFY_CHANNEL;
|
|
12
|
+
if (slackToken && channel) {
|
|
13
|
+
return { provider: 'slack', token: slackToken, channel };
|
|
14
|
+
}
|
|
15
|
+
if (teamsToken && channel) {
|
|
16
|
+
return { provider: 'teams', token: teamsToken, channel };
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function saveNotificationConfig(config) {
|
|
21
|
+
if (config.provider === 'slack') {
|
|
22
|
+
process.env.ICOPILOT_SLACK_TOKEN = config.token;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
process.env.ICOPILOT_TEAMS_TOKEN = config.token;
|
|
26
|
+
}
|
|
27
|
+
process.env.ICOPILOT_NOTIFY_CHANNEL = config.channel;
|
|
28
|
+
}
|
|
29
|
+
async function configureSlack() {
|
|
30
|
+
console.log('\n' + info('='.repeat(50)));
|
|
31
|
+
console.log(info('Slack Notification Setup'));
|
|
32
|
+
console.log(info('='.repeat(50)));
|
|
33
|
+
console.log('\nSteps to create a Slack bot:\n' +
|
|
34
|
+
'1. Go to https://api.slack.com/apps\n' +
|
|
35
|
+
'2. Click "Create New App" → "From scratch"\n' +
|
|
36
|
+
'3. Name it "iCopilot" and select your workspace\n' +
|
|
37
|
+
'4. Go to "OAuth & Permissions" (left sidebar)\n' +
|
|
38
|
+
'5. Add these scopes:\n' +
|
|
39
|
+
' - chat:write\n' +
|
|
40
|
+
' - chat:write.public\n' +
|
|
41
|
+
' - channels:read\n' +
|
|
42
|
+
'6. Under "Bot Token Scopes", copy your token (starts with xoxb-)\n' +
|
|
43
|
+
'7. Go to your Slack workspace and find/create a channel for notifications\n' +
|
|
44
|
+
'8. Invite the bot to that channel\n');
|
|
45
|
+
const token = await input({
|
|
46
|
+
message: 'Enter your Slack Bot Token (xoxb-...)',
|
|
47
|
+
validate: (val) => {
|
|
48
|
+
const trimmed = val.trim();
|
|
49
|
+
return trimmed.startsWith('xoxb-') ? true : 'Token must start with xoxb-';
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const channel = await input({
|
|
53
|
+
message: 'Enter the Slack channel name or ID (e.g., #notifications or C0123456789)',
|
|
54
|
+
validate: (val) => (val.trim().length > 0 ? true : 'Channel is required'),
|
|
55
|
+
});
|
|
56
|
+
const config = {
|
|
57
|
+
provider: 'slack',
|
|
58
|
+
token: token.trim(),
|
|
59
|
+
channel: channel.trim(),
|
|
60
|
+
};
|
|
61
|
+
saveNotificationConfig(config);
|
|
62
|
+
return config;
|
|
63
|
+
}
|
|
64
|
+
async function configureTeams() {
|
|
65
|
+
console.log('\n' + info('='.repeat(50)));
|
|
66
|
+
console.log(info('Microsoft Teams Notification Setup'));
|
|
67
|
+
console.log(info('='.repeat(50)));
|
|
68
|
+
console.log('\nSteps to create a Teams bot:\n' +
|
|
69
|
+
'1. Go to https://dev.teams.microsoft.com\n' +
|
|
70
|
+
'2. Click "Create app" → "Build from scratch"\n' +
|
|
71
|
+
'3. Name it "iCopilot"\n' +
|
|
72
|
+
'4. Go to "Build" → "Messaging" (left sidebar)\n' +
|
|
73
|
+
'5. Click "Set up a bot" or find the existing bot configuration\n' +
|
|
74
|
+
'6. Under "Application ID", copy your App ID\n' +
|
|
75
|
+
'7. Click "Add a password key" and copy the secret\n' +
|
|
76
|
+
'8. In Teams, go to the channel where you want notifications\n' +
|
|
77
|
+
'9. Copy the channel ID from the URL\n');
|
|
78
|
+
const token = await input({
|
|
79
|
+
message: 'Enter your Teams App ID',
|
|
80
|
+
validate: (val) => (val.trim().length > 0 ? true : 'App ID is required'),
|
|
81
|
+
});
|
|
82
|
+
const channel = await input({
|
|
83
|
+
message: 'Enter the Teams channel ID',
|
|
84
|
+
validate: (val) => (val.trim().length > 0 ? true : 'Channel ID is required'),
|
|
85
|
+
});
|
|
86
|
+
const config = {
|
|
87
|
+
provider: 'teams',
|
|
88
|
+
token: token.trim(),
|
|
89
|
+
channel: channel.trim(),
|
|
90
|
+
};
|
|
91
|
+
saveNotificationConfig(config);
|
|
92
|
+
return config;
|
|
93
|
+
}
|
|
94
|
+
async function testNotification() {
|
|
95
|
+
const config = loadNotificationConfig();
|
|
96
|
+
if (!config) {
|
|
97
|
+
console.log(error('✗ Notifications not configured. Run `/notify configure slack` or `/notify configure teams` first.'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const manager = getNotificationManager();
|
|
101
|
+
initializeNotificationManager(config);
|
|
102
|
+
console.log(info('Testing notification...'));
|
|
103
|
+
try {
|
|
104
|
+
await manager.sendNotification('🧪 Test message from iCopilot - Notifications working!');
|
|
105
|
+
console.log(ok('✓ Test notification sent successfully'));
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
109
|
+
console.log(error(`✗ Failed to send test notification: ${msg}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function sendMessage(message) {
|
|
113
|
+
const config = loadNotificationConfig();
|
|
114
|
+
if (!config) {
|
|
115
|
+
console.log(error('✗ Notifications not configured. Run `/notify configure slack` or `/notify configure teams` first.'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const manager = getNotificationManager();
|
|
119
|
+
initializeNotificationManager(config);
|
|
120
|
+
try {
|
|
121
|
+
await manager.sendNotification(message);
|
|
122
|
+
console.log(ok('✓ Message sent'));
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
126
|
+
console.log(error(`✗ Failed to send message: ${msg}`));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function showStatus() {
|
|
130
|
+
const config = loadNotificationConfig();
|
|
131
|
+
if (!config) {
|
|
132
|
+
console.log(info('No notification provider configured'));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
console.log('\n' + info('Notification Configuration:'));
|
|
136
|
+
console.log(` Provider: ${config.provider.toUpperCase()}`);
|
|
137
|
+
console.log(` Channel: ${config.channel}`);
|
|
138
|
+
console.log(` Token: ${config.token.substring(0, 10)}...`);
|
|
139
|
+
const manager = getNotificationManager();
|
|
140
|
+
initializeNotificationManager(config);
|
|
141
|
+
try {
|
|
142
|
+
const status = await manager.getStatus();
|
|
143
|
+
if (status.connected) {
|
|
144
|
+
console.log(ok(' Status: ✓ Connected'));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(warn(` Status: ✗ ${status.error || 'Disconnected'}`));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
152
|
+
console.log(warn(` Status: ✗ Error checking connection: ${msg}`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export async function notifyCommand(args) {
|
|
156
|
+
const [subcommand, ...rest] = args;
|
|
157
|
+
if (!subcommand || subcommand === 'help') {
|
|
158
|
+
return ('Usage: /notify <command>\n\n' +
|
|
159
|
+
'Commands:\n' +
|
|
160
|
+
' /notify configure slack - Set up Slack notifications\n' +
|
|
161
|
+
' /notify configure teams - Set up Teams notifications\n' +
|
|
162
|
+
' /notify test - Send a test notification\n' +
|
|
163
|
+
' /notify send <message> - Send a custom message\n' +
|
|
164
|
+
' /notify status - Show current configuration\n' +
|
|
165
|
+
' /notify help - Show this help\n\n' +
|
|
166
|
+
'Examples:\n' +
|
|
167
|
+
' /notify configure slack\n' +
|
|
168
|
+
' /notify test\n' +
|
|
169
|
+
' /notify send Build completed successfully!\n');
|
|
170
|
+
}
|
|
171
|
+
if (subcommand === 'configure') {
|
|
172
|
+
const provider = rest[0];
|
|
173
|
+
if (provider === 'slack') {
|
|
174
|
+
const config = await configureSlack();
|
|
175
|
+
initializeNotificationManager(config);
|
|
176
|
+
console.log(ok('\n✓ Slack configuration saved'));
|
|
177
|
+
return 'Slack notifications configured';
|
|
178
|
+
}
|
|
179
|
+
if (provider === 'teams') {
|
|
180
|
+
const config = await configureTeams();
|
|
181
|
+
initializeNotificationManager(config);
|
|
182
|
+
console.log(ok('\n✓ Teams configuration saved'));
|
|
183
|
+
return 'Teams notifications configured';
|
|
184
|
+
}
|
|
185
|
+
return error('Usage: /notify configure [slack|teams]');
|
|
186
|
+
}
|
|
187
|
+
if (subcommand === 'test') {
|
|
188
|
+
await testNotification();
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
if (subcommand === 'send') {
|
|
192
|
+
const message = rest.join(' ');
|
|
193
|
+
if (!message) {
|
|
194
|
+
return error('Usage: /notify send <message>');
|
|
195
|
+
}
|
|
196
|
+
await sendMessage(message);
|
|
197
|
+
return '';
|
|
198
|
+
}
|
|
199
|
+
if (subcommand === 'status') {
|
|
200
|
+
await showStatus();
|
|
201
|
+
return '';
|
|
202
|
+
}
|
|
203
|
+
return error(`Unknown command: ${subcommand}. Use '/notify help' for usage.`);
|
|
204
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { theme } from '../ui/theme.js';
|
|
2
|
+
import { deleteProfile, getProfile, listProfiles, setProfile, useProfile, } from '../config-profile.js';
|
|
3
|
+
const THEMES = new Set(['auto', 'light', 'dark', 'no-color']);
|
|
4
|
+
export async function profileCommand(rest) {
|
|
5
|
+
const [sub = 'list', ...args] = rest;
|
|
6
|
+
try {
|
|
7
|
+
switch (sub) {
|
|
8
|
+
case 'list':
|
|
9
|
+
return listCommand();
|
|
10
|
+
case 'use':
|
|
11
|
+
return useCommand(args[0]);
|
|
12
|
+
case 'save':
|
|
13
|
+
return saveCommand(args);
|
|
14
|
+
case 'show':
|
|
15
|
+
return showCommand(args[0]);
|
|
16
|
+
case 'delete':
|
|
17
|
+
return deleteCommand(args[0]);
|
|
18
|
+
default:
|
|
19
|
+
return theme.warn('usage: /profile [list|use|save|show|delete]\n');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return theme.err(`profile: ${err?.message || err}\n`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function listCommand() {
|
|
27
|
+
const { active, names } = listProfiles();
|
|
28
|
+
if (names.length === 0)
|
|
29
|
+
return theme.dim('No profiles saved.\n');
|
|
30
|
+
return [
|
|
31
|
+
theme.brand('Profiles'),
|
|
32
|
+
...names.map((name) => `${active === name ? '*' : ' '} ${name}`),
|
|
33
|
+
'',
|
|
34
|
+
].join('\n');
|
|
35
|
+
}
|
|
36
|
+
function useCommand(name) {
|
|
37
|
+
if (!name)
|
|
38
|
+
return theme.warn('usage: /profile use <name>\n');
|
|
39
|
+
const profile = useProfile(name);
|
|
40
|
+
if (!profile)
|
|
41
|
+
return theme.warn(`profile not found: ${name}\n`);
|
|
42
|
+
return theme.ok(`✔ active profile → ${name}\n`);
|
|
43
|
+
}
|
|
44
|
+
function saveCommand(args) {
|
|
45
|
+
const [name, ...pairs] = args;
|
|
46
|
+
if (!name)
|
|
47
|
+
return theme.warn('usage: /profile save <name> [k=v ...]\n');
|
|
48
|
+
const profile = { ...(getProfile(name) ?? { name }), name };
|
|
49
|
+
for (const pair of pairs) {
|
|
50
|
+
const parsed = parsePair(pair);
|
|
51
|
+
if (!parsed)
|
|
52
|
+
return theme.warn(`ignored invalid option: ${pair}\n`);
|
|
53
|
+
Object.assign(profile, parsed);
|
|
54
|
+
}
|
|
55
|
+
setProfile(profile);
|
|
56
|
+
return theme.ok(`✔ saved profile ${name}\n`);
|
|
57
|
+
}
|
|
58
|
+
function showCommand(name) {
|
|
59
|
+
if (!name)
|
|
60
|
+
return theme.warn('usage: /profile show <name>\n');
|
|
61
|
+
const profile = getProfile(name);
|
|
62
|
+
if (!profile)
|
|
63
|
+
return theme.warn(`profile not found: ${name}\n`);
|
|
64
|
+
return theme.hl(JSON.stringify(profile, null, 2)) + '\n';
|
|
65
|
+
}
|
|
66
|
+
function deleteCommand(name) {
|
|
67
|
+
if (!name)
|
|
68
|
+
return theme.warn('usage: /profile delete <name>\n');
|
|
69
|
+
return deleteProfile(name)
|
|
70
|
+
? theme.ok(`✔ deleted profile ${name}\n`)
|
|
71
|
+
: theme.warn(`profile not found: ${name}\n`);
|
|
72
|
+
}
|
|
73
|
+
function parsePair(pair) {
|
|
74
|
+
const eq = pair.indexOf('=');
|
|
75
|
+
if (eq <= 0)
|
|
76
|
+
return null;
|
|
77
|
+
const key = pair.slice(0, eq);
|
|
78
|
+
const value = pair.slice(eq + 1);
|
|
79
|
+
switch (key) {
|
|
80
|
+
case 'model':
|
|
81
|
+
return { model: value };
|
|
82
|
+
case 'theme':
|
|
83
|
+
if (!THEMES.has(value))
|
|
84
|
+
throw new Error('theme must be auto, light, dark, or no-color');
|
|
85
|
+
return { theme: value };
|
|
86
|
+
case 'sandbox':
|
|
87
|
+
if (value !== 'true' && value !== 'false')
|
|
88
|
+
throw new Error('sandbox must be true or false');
|
|
89
|
+
return { sandbox: value === 'true' };
|
|
90
|
+
case 'baseUrl':
|
|
91
|
+
return { baseUrl: value };
|
|
92
|
+
case 'contextWarn': {
|
|
93
|
+
const contextWarn = Number(value);
|
|
94
|
+
if (!Number.isFinite(contextWarn))
|
|
95
|
+
throw new Error('contextWarn must be a number');
|
|
96
|
+
return { contextWarn };
|
|
97
|
+
}
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`unknown profile key: ${key}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { config } from '../config.js';
|
|
2
|
+
import { getEditFormatPrompt, getWholeFileSystemPrompt } from '../tools/diff-prompt.js';
|
|
3
|
+
export const ASK_SYSTEM = getWholeFileSystemPrompt();
|
|
4
|
+
export const PLAN_SYSTEM = `You are iCopilot in **Plan Mode**.
|
|
5
|
+
|
|
6
|
+
Do NOT make tool calls in Plan Mode. Do NOT produce final code patches.
|
|
7
|
+
Instead, produce a concrete implementation plan the user can review and edit before execution:
|
|
8
|
+
|
|
9
|
+
1. A numbered list of steps (one action per step).
|
|
10
|
+
2. For each step: the files to touch and a one-line rationale.
|
|
11
|
+
3. A short "Open questions" section, if any.
|
|
12
|
+
4. A "Validation" section: how the change will be verified (tests, manual checks).
|
|
13
|
+
|
|
14
|
+
Keep the plan tight (≤ 15 steps). End with: "Reply 'go' to execute, or edit the plan above."`;
|
|
15
|
+
export function getAskSystemPrompt() {
|
|
16
|
+
return getEditFormatPrompt(config.editFormat);
|
|
17
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { RAGIndex, defaultRagIndexPath } from '../knowledge/rag.js';
|
|
3
|
+
import { theme } from '../ui/theme.js';
|
|
4
|
+
const DEFAULT_RESULTS = 5;
|
|
5
|
+
const PREVIEW_LENGTH = 220;
|
|
6
|
+
export async function ragCommand(args, cwd) {
|
|
7
|
+
const [subcommand = 'stats', ...rest] = args;
|
|
8
|
+
const indexPath = defaultRagIndexPath(cwd);
|
|
9
|
+
switch (subcommand.toLowerCase()) {
|
|
10
|
+
case 'index': {
|
|
11
|
+
const index = new RAGIndex();
|
|
12
|
+
await index.indexProject(cwd);
|
|
13
|
+
const stats = index.getStats();
|
|
14
|
+
return (theme.ok(`✔ indexed ${stats.documents} documents, ${stats.chunks} chunks (${stats.totalTokens} tokens)\n`) + theme.dim(` ${indexPath}\n`));
|
|
15
|
+
}
|
|
16
|
+
case 'search': {
|
|
17
|
+
const query = rest.join(' ').trim();
|
|
18
|
+
if (!query) {
|
|
19
|
+
return theme.warn('usage: /rag search <query>\n');
|
|
20
|
+
}
|
|
21
|
+
if (!fs.existsSync(indexPath)) {
|
|
22
|
+
return theme.warn('No RAG index found. Run `/rag index` first.\n');
|
|
23
|
+
}
|
|
24
|
+
const index = new RAGIndex();
|
|
25
|
+
index.load(indexPath);
|
|
26
|
+
const matches = index.search(query, DEFAULT_RESULTS);
|
|
27
|
+
if (!matches.length) {
|
|
28
|
+
return theme.dim(`No RAG matches for "${query}".\n`);
|
|
29
|
+
}
|
|
30
|
+
const lines = [`${theme.brand('RAG results')} ${theme.dim(`for "${query}"`)}`, ''];
|
|
31
|
+
for (const match of matches) {
|
|
32
|
+
lines.push(`${theme.hl(match.metadata.file)} ${theme.dim(`(${match.metadata.startLine}-${match.metadata.endLine}, ${match.tokens} tokens)`)}`);
|
|
33
|
+
lines.push(` ${preview(match.text)}`);
|
|
34
|
+
lines.push('');
|
|
35
|
+
}
|
|
36
|
+
return `${lines.join('\n').trimEnd()}\n`;
|
|
37
|
+
}
|
|
38
|
+
case 'stats': {
|
|
39
|
+
if (!fs.existsSync(indexPath)) {
|
|
40
|
+
return theme.warn('No RAG index found. Run `/rag index` first.\n');
|
|
41
|
+
}
|
|
42
|
+
const index = new RAGIndex();
|
|
43
|
+
index.load(indexPath);
|
|
44
|
+
const stats = index.getStats();
|
|
45
|
+
return (`${theme.brand('RAG stats')}\n` +
|
|
46
|
+
` documents: ${theme.hl(String(stats.documents))}\n` +
|
|
47
|
+
` chunks: ${theme.hl(String(stats.chunks))}\n` +
|
|
48
|
+
` tokens: ${theme.hl(String(stats.totalTokens))}\n` +
|
|
49
|
+
` index: ${theme.dim(indexPath)}\n`);
|
|
50
|
+
}
|
|
51
|
+
default:
|
|
52
|
+
return theme.warn('usage: /rag index | /rag search <query> | /rag stats\n');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function preview(text) {
|
|
56
|
+
const normalized = text.replace(/\s+/g, ' ').trim();
|
|
57
|
+
if (normalized.length <= PREVIEW_LENGTH)
|
|
58
|
+
return normalized;
|
|
59
|
+
return `${normalized.slice(0, PREVIEW_LENGTH - 3)}...`;
|
|
60
|
+
}
|