@stan-chen/simple-cli 0.2.1
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 +287 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +259 -0
- package/dist/commands/add.d.ts +9 -0
- package/dist/commands/add.js +50 -0
- package/dist/commands/git/commit.d.ts +12 -0
- package/dist/commands/git/commit.js +97 -0
- package/dist/commands/git/status.d.ts +6 -0
- package/dist/commands/git/status.js +42 -0
- package/dist/commands/index.d.ts +16 -0
- package/dist/commands/index.js +376 -0
- package/dist/commands/mcp/status.d.ts +6 -0
- package/dist/commands/mcp/status.js +31 -0
- package/dist/commands/swarm.d.ts +36 -0
- package/dist/commands/swarm.js +236 -0
- package/dist/commands.d.ts +32 -0
- package/dist/commands.js +427 -0
- package/dist/context.d.ts +116 -0
- package/dist/context.js +327 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +109 -0
- package/dist/lib/agent.d.ts +98 -0
- package/dist/lib/agent.js +281 -0
- package/dist/lib/editor.d.ts +74 -0
- package/dist/lib/editor.js +441 -0
- package/dist/lib/git.d.ts +164 -0
- package/dist/lib/git.js +351 -0
- package/dist/lib/ui.d.ts +159 -0
- package/dist/lib/ui.js +252 -0
- package/dist/mcp/client.d.ts +22 -0
- package/dist/mcp/client.js +81 -0
- package/dist/mcp/manager.d.ts +186 -0
- package/dist/mcp/manager.js +442 -0
- package/dist/prompts/provider.d.ts +22 -0
- package/dist/prompts/provider.js +78 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.js +82 -0
- package/dist/providers/multi.d.ts +11 -0
- package/dist/providers/multi.js +28 -0
- package/dist/registry.d.ts +24 -0
- package/dist/registry.js +379 -0
- package/dist/repoMap.d.ts +5 -0
- package/dist/repoMap.js +79 -0
- package/dist/router.d.ts +41 -0
- package/dist/router.js +108 -0
- package/dist/skills.d.ts +25 -0
- package/dist/skills.js +288 -0
- package/dist/swarm/coordinator.d.ts +86 -0
- package/dist/swarm/coordinator.js +257 -0
- package/dist/swarm/index.d.ts +28 -0
- package/dist/swarm/index.js +29 -0
- package/dist/swarm/task.d.ts +104 -0
- package/dist/swarm/task.js +221 -0
- package/dist/swarm/types.d.ts +132 -0
- package/dist/swarm/types.js +37 -0
- package/dist/swarm/worker.d.ts +107 -0
- package/dist/swarm/worker.js +299 -0
- package/dist/tools/analyzeFile.d.ts +16 -0
- package/dist/tools/analyzeFile.js +43 -0
- package/dist/tools/git.d.ts +40 -0
- package/dist/tools/git.js +236 -0
- package/dist/tools/glob.d.ts +34 -0
- package/dist/tools/glob.js +165 -0
- package/dist/tools/grep.d.ts +53 -0
- package/dist/tools/grep.js +296 -0
- package/dist/tools/linter.d.ts +35 -0
- package/dist/tools/linter.js +349 -0
- package/dist/tools/listDir.d.ts +29 -0
- package/dist/tools/listDir.js +50 -0
- package/dist/tools/memory.d.ts +34 -0
- package/dist/tools/memory.js +215 -0
- package/dist/tools/readFiles.d.ts +25 -0
- package/dist/tools/readFiles.js +31 -0
- package/dist/tools/reloadTools.d.ts +11 -0
- package/dist/tools/reloadTools.js +22 -0
- package/dist/tools/runCommand.d.ts +32 -0
- package/dist/tools/runCommand.js +79 -0
- package/dist/tools/scraper.d.ts +31 -0
- package/dist/tools/scraper.js +211 -0
- package/dist/tools/writeFiles.d.ts +63 -0
- package/dist/tools/writeFiles.js +87 -0
- package/dist/ui/server.d.ts +5 -0
- package/dist/ui/server.js +74 -0
- package/dist/watcher.d.ts +35 -0
- package/dist/watcher.js +164 -0
- package/docs/assets/logo.jpeg +0 -0
- package/package.json +78 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import * as ui from '../../lib/ui.js';
|
|
3
|
+
import { getGitManager, generateCommitMessage } from '../../lib/git.js';
|
|
4
|
+
import { createProvider } from '../../providers/index.js';
|
|
5
|
+
import 'dotenv/config';
|
|
6
|
+
export default class GitCommit extends Command {
|
|
7
|
+
static description = 'Commit changes with optional AI-generated message';
|
|
8
|
+
static flags = {
|
|
9
|
+
message: Flags.string({
|
|
10
|
+
char: 'm',
|
|
11
|
+
description: 'Commit message',
|
|
12
|
+
}),
|
|
13
|
+
ai: Flags.boolean({
|
|
14
|
+
description: 'Generate commit message using AI',
|
|
15
|
+
default: false,
|
|
16
|
+
}),
|
|
17
|
+
all: Flags.boolean({
|
|
18
|
+
char: 'a',
|
|
19
|
+
description: 'Stage all changes',
|
|
20
|
+
default: false,
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
static examples = [
|
|
24
|
+
'<%= config.bin %> git commit -m "feat: add new feature"',
|
|
25
|
+
'<%= config.bin %> git commit --ai',
|
|
26
|
+
'<%= config.bin %> git commit -a --ai',
|
|
27
|
+
];
|
|
28
|
+
async run() {
|
|
29
|
+
const { flags } = await this.parse(GitCommit);
|
|
30
|
+
const git = getGitManager();
|
|
31
|
+
if (!(await git.isRepo())) {
|
|
32
|
+
ui.error('Not a git repository');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Stage all if requested
|
|
36
|
+
if (flags.all) {
|
|
37
|
+
await git.addAll();
|
|
38
|
+
}
|
|
39
|
+
// Get diff
|
|
40
|
+
const diff = await git.stagedDiff();
|
|
41
|
+
if (!diff) {
|
|
42
|
+
const status = await git.status();
|
|
43
|
+
if (status.modified.length === 0 && status.created.length === 0) {
|
|
44
|
+
ui.error('Nothing to commit');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Auto-stage if no staged changes
|
|
48
|
+
await git.addAll();
|
|
49
|
+
}
|
|
50
|
+
// Get or generate message
|
|
51
|
+
let message = flags.message;
|
|
52
|
+
if (!message && flags.ai) {
|
|
53
|
+
const provider = createProvider();
|
|
54
|
+
const currentDiff = await git.stagedDiff() || await git.diff();
|
|
55
|
+
message = await ui.spin('Generating commit message...', async () => {
|
|
56
|
+
return generateCommitMessage(currentDiff, async (prompt) => {
|
|
57
|
+
return provider.generateResponse(prompt, []);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
ui.log(`Generated message: ${message}`);
|
|
61
|
+
const confirmed = await ui.confirm({
|
|
62
|
+
message: 'Use this message?',
|
|
63
|
+
initialValue: true,
|
|
64
|
+
});
|
|
65
|
+
if (ui.isCancel(confirmed) || !confirmed) {
|
|
66
|
+
const custom = await ui.text({
|
|
67
|
+
message: 'Enter commit message:',
|
|
68
|
+
placeholder: message,
|
|
69
|
+
});
|
|
70
|
+
if (ui.isCancel(custom)) {
|
|
71
|
+
ui.cancel('Commit cancelled');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
message = custom;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (!message) {
|
|
78
|
+
const input = await ui.text({
|
|
79
|
+
message: 'Enter commit message:',
|
|
80
|
+
validate: (v) => (v.trim() ? undefined : 'Message required'),
|
|
81
|
+
});
|
|
82
|
+
if (ui.isCancel(input)) {
|
|
83
|
+
ui.cancel('Commit cancelled');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
message = input;
|
|
87
|
+
}
|
|
88
|
+
// Commit
|
|
89
|
+
const result = await git.commit({ message });
|
|
90
|
+
if (result) {
|
|
91
|
+
ui.success(`Committed: ${result.hash} ${result.message}`);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
ui.error('Commit failed');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import * as ui from '../../lib/ui.js';
|
|
3
|
+
import { getGitManager } from '../../lib/git.js';
|
|
4
|
+
export default class GitStatus extends Command {
|
|
5
|
+
static description = 'Show git repository status';
|
|
6
|
+
static examples = ['<%= config.bin %> git status'];
|
|
7
|
+
async run() {
|
|
8
|
+
const git = getGitManager();
|
|
9
|
+
if (!(await git.isRepo())) {
|
|
10
|
+
ui.error('Not a git repository');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const status = await git.status();
|
|
14
|
+
ui.log(`Branch: ${status.current || 'detached HEAD'}`);
|
|
15
|
+
if (status.ahead > 0) {
|
|
16
|
+
ui.log(`Ahead of origin by ${status.ahead} commit(s)`);
|
|
17
|
+
}
|
|
18
|
+
if (status.behind > 0) {
|
|
19
|
+
ui.log(`Behind origin by ${status.behind} commit(s)`);
|
|
20
|
+
}
|
|
21
|
+
const changes = [];
|
|
22
|
+
for (const file of status.created) {
|
|
23
|
+
changes.push({ path: file, status: 'added' });
|
|
24
|
+
}
|
|
25
|
+
for (const file of status.modified) {
|
|
26
|
+
changes.push({ path: file, status: 'modified' });
|
|
27
|
+
}
|
|
28
|
+
for (const file of status.deleted) {
|
|
29
|
+
changes.push({ path: file, status: 'deleted' });
|
|
30
|
+
}
|
|
31
|
+
for (const file of status.not_added) {
|
|
32
|
+
changes.push({ path: file, status: 'added' });
|
|
33
|
+
}
|
|
34
|
+
if (changes.length > 0) {
|
|
35
|
+
ui.log('\nChanges:');
|
|
36
|
+
ui.showFileStatus(changes);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
ui.success('Working tree clean');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
export default class Chat extends Command {
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
yolo: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
moe: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
watch: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
skill: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'auto-commit': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
'auto-lint': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
'auto-test': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
'test-cmd': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import * as ui from '../lib/ui.js';
|
|
3
|
+
import { Agent, summarizeHistory } from '../lib/agent.js';
|
|
4
|
+
import { getGitManager } from '../lib/git.js';
|
|
5
|
+
import { getContextManager } from '../context.js';
|
|
6
|
+
import { createProvider } from '../providers/index.js';
|
|
7
|
+
import { createMultiProvider } from '../providers/multi.js';
|
|
8
|
+
import { routeTask, loadTierConfig } from '../router.js';
|
|
9
|
+
import { getMCPManager } from '../mcp/manager.js';
|
|
10
|
+
import { getActiveSkill, setActiveSkill, listSkills } from '../skills.js';
|
|
11
|
+
import { createFileWatcher } from '../watcher.js';
|
|
12
|
+
import 'dotenv/config';
|
|
13
|
+
export default class Chat extends Command {
|
|
14
|
+
static description = 'Start an interactive coding session';
|
|
15
|
+
static flags = {
|
|
16
|
+
yolo: Flags.boolean({
|
|
17
|
+
description: 'Auto-approve all tool executions',
|
|
18
|
+
default: false,
|
|
19
|
+
}),
|
|
20
|
+
moe: Flags.boolean({
|
|
21
|
+
description: 'Enable Mix of Experts routing',
|
|
22
|
+
default: false,
|
|
23
|
+
}),
|
|
24
|
+
watch: Flags.boolean({
|
|
25
|
+
description: 'Watch files for AI comments',
|
|
26
|
+
default: false,
|
|
27
|
+
}),
|
|
28
|
+
skill: Flags.string({
|
|
29
|
+
description: 'Initial skill/mode (code, architect, test, etc.)',
|
|
30
|
+
default: 'code',
|
|
31
|
+
}),
|
|
32
|
+
'auto-commit': Flags.boolean({
|
|
33
|
+
description: 'Auto-commit after successful changes',
|
|
34
|
+
default: false,
|
|
35
|
+
}),
|
|
36
|
+
'auto-lint': Flags.boolean({
|
|
37
|
+
description: 'Auto-lint after file changes',
|
|
38
|
+
default: true,
|
|
39
|
+
}),
|
|
40
|
+
'auto-test': Flags.boolean({
|
|
41
|
+
description: 'Auto-run tests after changes',
|
|
42
|
+
default: false,
|
|
43
|
+
}),
|
|
44
|
+
'test-cmd': Flags.string({
|
|
45
|
+
description: 'Test command to run',
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
async run() {
|
|
49
|
+
const { flags } = await this.parse(Chat);
|
|
50
|
+
// Show banner
|
|
51
|
+
ui.intro('Simple-CLI v0.2.0');
|
|
52
|
+
ui.log(`Mode: ${flags.moe ? 'MoE' : 'Single'} | ${flags.yolo ? 'YOLO' : 'Safe'} | Skill: @${flags.skill}`);
|
|
53
|
+
// Initialize context
|
|
54
|
+
const ctx = getContextManager();
|
|
55
|
+
await ctx.initialize();
|
|
56
|
+
ui.success(`Loaded ${ctx.getTools().size} tools`);
|
|
57
|
+
// Set initial skill
|
|
58
|
+
const skill = setActiveSkill(flags.skill);
|
|
59
|
+
if (skill) {
|
|
60
|
+
ctx.setSkill(skill);
|
|
61
|
+
}
|
|
62
|
+
// Initialize git
|
|
63
|
+
const git = getGitManager();
|
|
64
|
+
const isGitRepo = await git.isRepo();
|
|
65
|
+
if (isGitRepo) {
|
|
66
|
+
const branch = await git.currentBranch();
|
|
67
|
+
ui.log(`Git: ${branch || 'detached HEAD'}`);
|
|
68
|
+
}
|
|
69
|
+
// Initialize MCP
|
|
70
|
+
const mcpManager = getMCPManager();
|
|
71
|
+
try {
|
|
72
|
+
const configs = await mcpManager.loadConfig();
|
|
73
|
+
if (configs.length > 0) {
|
|
74
|
+
await ui.spin(`Connecting to ${configs.length} MCP server(s)`, async () => {
|
|
75
|
+
await mcpManager.connectAll(configs);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// MCP not configured
|
|
81
|
+
}
|
|
82
|
+
// Initialize providers
|
|
83
|
+
const tierConfigs = flags.moe ? loadTierConfig() : null;
|
|
84
|
+
const multiProvider = tierConfigs ? createMultiProvider(tierConfigs) : null;
|
|
85
|
+
const singleProvider = !flags.moe ? createProvider() : null;
|
|
86
|
+
// Initialize file watcher
|
|
87
|
+
let watcher = null;
|
|
88
|
+
if (flags.watch) {
|
|
89
|
+
watcher = createFileWatcher({
|
|
90
|
+
root: ctx.getCwd(),
|
|
91
|
+
onAIComment: (path, comments) => {
|
|
92
|
+
ui.note(comments.map(c => `Line ${c.line}: ${c.text}`).join('\n'), `AI Comments in ${path}`);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
ui.success('File watcher active');
|
|
96
|
+
}
|
|
97
|
+
// Generate function
|
|
98
|
+
const generate = async (messages) => {
|
|
99
|
+
const fullPrompt = await ctx.buildSystemPrompt();
|
|
100
|
+
const llmMessages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
101
|
+
if (flags.moe && multiProvider && tierConfigs) {
|
|
102
|
+
const userMsg = messages.find(m => m.role === 'user')?.content || '';
|
|
103
|
+
const routing = await routeTask(userMsg, async (prompt) => {
|
|
104
|
+
return multiProvider.generateWithTier(1, prompt, [{ role: 'user', content: userMsg }]);
|
|
105
|
+
});
|
|
106
|
+
return multiProvider.generateWithTier(routing.tier, fullPrompt, llmMessages);
|
|
107
|
+
}
|
|
108
|
+
return singleProvider.generateResponse(fullPrompt, llmMessages);
|
|
109
|
+
};
|
|
110
|
+
// Tool execution
|
|
111
|
+
const executeTool = async (name, args) => {
|
|
112
|
+
const tools = ctx.getTools();
|
|
113
|
+
const tool = tools.get(name);
|
|
114
|
+
if (!tool) {
|
|
115
|
+
throw new Error(`Tool not found: ${name}`);
|
|
116
|
+
}
|
|
117
|
+
// Permission check
|
|
118
|
+
if (!flags.yolo && tool.permission !== 'read') {
|
|
119
|
+
const confirmed = await ui.confirm({
|
|
120
|
+
message: `Execute ${name}?`,
|
|
121
|
+
initialValue: false,
|
|
122
|
+
});
|
|
123
|
+
if (ui.isCancel(confirmed) || !confirmed) {
|
|
124
|
+
return 'Cancelled by user';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return tool.execute(args);
|
|
128
|
+
};
|
|
129
|
+
// Create agent
|
|
130
|
+
const agent = new Agent({
|
|
131
|
+
config: {
|
|
132
|
+
maxReflections: 3,
|
|
133
|
+
autoLint: flags['auto-lint'],
|
|
134
|
+
autoTest: flags['auto-test'],
|
|
135
|
+
autoCommit: flags['auto-commit'],
|
|
136
|
+
testCommand: flags['test-cmd'],
|
|
137
|
+
},
|
|
138
|
+
git,
|
|
139
|
+
generateFn: generate,
|
|
140
|
+
executeTool,
|
|
141
|
+
lintFn: flags['auto-lint'] ? async (file) => {
|
|
142
|
+
const { execute } = await import('../tools/linter.js');
|
|
143
|
+
const result = await execute({ path: file, fix: false });
|
|
144
|
+
return { passed: result.passed, output: result.output };
|
|
145
|
+
} : undefined,
|
|
146
|
+
testFn: flags['auto-test'] && flags['test-cmd'] ? async () => {
|
|
147
|
+
const { execute } = await import('../tools/runCommand.js');
|
|
148
|
+
const result = await execute({ command: flags['test-cmd'] });
|
|
149
|
+
return { passed: result.exitCode === 0, output: result.stdout + result.stderr };
|
|
150
|
+
} : undefined,
|
|
151
|
+
});
|
|
152
|
+
// Main loop
|
|
153
|
+
while (true) {
|
|
154
|
+
const currentSkill = getActiveSkill();
|
|
155
|
+
const input = await ui.text({
|
|
156
|
+
message: `[${currentSkill.name}]`,
|
|
157
|
+
placeholder: 'Type a message or /help...',
|
|
158
|
+
});
|
|
159
|
+
if (ui.isCancel(input)) {
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
const message = input.trim();
|
|
163
|
+
if (!message)
|
|
164
|
+
continue;
|
|
165
|
+
// Handle slash commands
|
|
166
|
+
if (message.startsWith('/')) {
|
|
167
|
+
await handleCommand(message, ctx, git, mcpManager);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
// Handle skill switching
|
|
171
|
+
if (message.startsWith('@')) {
|
|
172
|
+
const skillName = message.slice(1).trim();
|
|
173
|
+
if (skillName === 'list') {
|
|
174
|
+
ui.note(listSkills().map(s => `@${s.name} - ${s.description}`).join('\n'), 'Available Skills');
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const newSkill = setActiveSkill(skillName);
|
|
178
|
+
if (newSkill) {
|
|
179
|
+
ctx.setSkill(newSkill);
|
|
180
|
+
ui.success(`Switched to @${newSkill.name}`);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
ui.error(`Unknown skill: @${skillName}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
// Add watch context if available
|
|
189
|
+
let fullMessage = message;
|
|
190
|
+
if (watcher) {
|
|
191
|
+
const watchPrompt = watcher.getActionableCommentsPrompt();
|
|
192
|
+
if (watchPrompt) {
|
|
193
|
+
fullMessage = `${watchPrompt}\n\nUser request: ${message}`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Process with agent
|
|
197
|
+
try {
|
|
198
|
+
ctx.addMessage('user', message);
|
|
199
|
+
const history = ctx.getHistory();
|
|
200
|
+
// Summarize if too long
|
|
201
|
+
const summarizedHistory = await summarizeHistory(history, generate, 20);
|
|
202
|
+
const systemPrompt = await ctx.buildSystemPrompt();
|
|
203
|
+
const result = await agent.process(fullMessage, summarizedHistory, systemPrompt);
|
|
204
|
+
// Add response to history
|
|
205
|
+
if (result.response.action && 'message' in result.response.action) {
|
|
206
|
+
ctx.addMessage('assistant', result.response.action.message);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
ui.error(`Error: ${error instanceof Error ? error.message : error}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Cleanup
|
|
214
|
+
watcher?.stop();
|
|
215
|
+
await mcpManager.disconnectAll();
|
|
216
|
+
ui.outro('Goodbye!');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Handle slash commands
|
|
221
|
+
*/
|
|
222
|
+
async function handleCommand(input, ctx, git, mcpManager) {
|
|
223
|
+
const [cmd, ...args] = input.slice(1).split(/\s+/);
|
|
224
|
+
const argsStr = args.join(' ');
|
|
225
|
+
switch (cmd.toLowerCase()) {
|
|
226
|
+
case 'help':
|
|
227
|
+
case 'h':
|
|
228
|
+
ui.note(`
|
|
229
|
+
/add <file> - Add file to context
|
|
230
|
+
/drop [file] - Remove file from context
|
|
231
|
+
/ls - List files in context
|
|
232
|
+
/clear - Clear chat history
|
|
233
|
+
/diff - Show git diff
|
|
234
|
+
/status - Show git status
|
|
235
|
+
/commit [msg] - Commit changes
|
|
236
|
+
/undo - Undo last commit
|
|
237
|
+
/tokens - Show token estimate
|
|
238
|
+
/mcp [cmd] - MCP management
|
|
239
|
+
/skill <name> - Switch skill
|
|
240
|
+
/exit - Exit
|
|
241
|
+
`, 'Commands');
|
|
242
|
+
break;
|
|
243
|
+
case 'add':
|
|
244
|
+
case 'a':
|
|
245
|
+
if (argsStr) {
|
|
246
|
+
const added = ctx.addFile(argsStr);
|
|
247
|
+
if (added) {
|
|
248
|
+
ui.success(`Added ${argsStr}`);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
ui.error(`File not found: ${argsStr}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
ui.error('Usage: /add <file>');
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
case 'drop':
|
|
259
|
+
case 'd':
|
|
260
|
+
if (argsStr) {
|
|
261
|
+
ctx.removeFile(argsStr);
|
|
262
|
+
ui.success(`Dropped ${argsStr}`);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
// Drop all
|
|
266
|
+
const files = ctx.getFiles();
|
|
267
|
+
for (const f of [...files.active, ...files.readOnly]) {
|
|
268
|
+
ctx.removeFile(f);
|
|
269
|
+
}
|
|
270
|
+
ui.success('Dropped all files');
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
case 'ls':
|
|
274
|
+
case 'files':
|
|
275
|
+
const files = ctx.getFiles();
|
|
276
|
+
if (files.active.length === 0 && files.readOnly.length === 0) {
|
|
277
|
+
ui.log('No files in context');
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
ui.showFileStatus([
|
|
281
|
+
...files.active.map(f => ({ path: f, status: 'modified' })),
|
|
282
|
+
...files.readOnly.map(f => ({ path: f, status: 'readonly' })),
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
case 'clear':
|
|
287
|
+
ctx.clearHistory();
|
|
288
|
+
ui.success('Chat history cleared');
|
|
289
|
+
break;
|
|
290
|
+
case 'diff':
|
|
291
|
+
const diff = await git.diff();
|
|
292
|
+
if (diff) {
|
|
293
|
+
ui.showDiff(diff);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
ui.log('No changes');
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
case 'status':
|
|
300
|
+
const status = await git.status();
|
|
301
|
+
ui.log(`Branch: ${status.current}`);
|
|
302
|
+
if (status.modified.length)
|
|
303
|
+
ui.log(`Modified: ${status.modified.join(', ')}`);
|
|
304
|
+
if (status.created.length)
|
|
305
|
+
ui.log(`Created: ${status.created.join(', ')}`);
|
|
306
|
+
if (status.deleted.length)
|
|
307
|
+
ui.log(`Deleted: ${status.deleted.join(', ')}`);
|
|
308
|
+
if (status.not_added.length)
|
|
309
|
+
ui.log(`Untracked: ${status.not_added.join(', ')}`);
|
|
310
|
+
break;
|
|
311
|
+
case 'commit':
|
|
312
|
+
const commitDiff = await git.stagedDiff();
|
|
313
|
+
if (!commitDiff && !(await git.status()).staged.length) {
|
|
314
|
+
await git.addAll();
|
|
315
|
+
}
|
|
316
|
+
const result = await git.commit({ message: argsStr || 'Update' });
|
|
317
|
+
if (result) {
|
|
318
|
+
ui.success(`Committed: ${result.hash} ${result.message}`);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
ui.error('Nothing to commit');
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
case 'undo':
|
|
325
|
+
if (await git.undoLastCommit()) {
|
|
326
|
+
ui.success('Undid last commit');
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
ui.error('Failed to undo');
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
case 'tokens':
|
|
333
|
+
const tokens = await ctx.estimateTokenCount();
|
|
334
|
+
ui.showTokens(tokens);
|
|
335
|
+
break;
|
|
336
|
+
case 'mcp':
|
|
337
|
+
const subCmd = args[0];
|
|
338
|
+
if (subCmd === 'status') {
|
|
339
|
+
const statuses = mcpManager.getAllServerStatuses();
|
|
340
|
+
for (const [name, status] of statuses) {
|
|
341
|
+
ui.log(`${name}: ${status}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else if (subCmd === 'tools') {
|
|
345
|
+
const tools = mcpManager.getAllTools();
|
|
346
|
+
for (const tool of tools) {
|
|
347
|
+
ui.log(`${tool.name} (${tool.serverName}): ${tool.description}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
ui.log('Usage: /mcp [status|tools]');
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
case 'skill':
|
|
355
|
+
if (argsStr) {
|
|
356
|
+
const newSkill = setActiveSkill(argsStr);
|
|
357
|
+
if (newSkill) {
|
|
358
|
+
ctx.setSkill(newSkill);
|
|
359
|
+
ui.success(`Switched to @${newSkill.name}`);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
ui.error(`Unknown skill: ${argsStr}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
ui.note(listSkills().map(s => `@${s.name} - ${s.description}`).join('\n'), 'Available Skills');
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
case 'exit':
|
|
370
|
+
case 'quit':
|
|
371
|
+
case 'q':
|
|
372
|
+
process.exit(0);
|
|
373
|
+
default:
|
|
374
|
+
ui.error(`Unknown command: /${cmd}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import * as ui from '../../lib/ui.js';
|
|
3
|
+
import { getMCPManager, MCPServerStatus } from '../../mcp/manager.js';
|
|
4
|
+
export default class MCPStatus extends Command {
|
|
5
|
+
static description = 'Show MCP server status';
|
|
6
|
+
static examples = ['<%= config.bin %> mcp status'];
|
|
7
|
+
async run() {
|
|
8
|
+
const manager = getMCPManager();
|
|
9
|
+
const statuses = manager.getAllServerStatuses();
|
|
10
|
+
if (statuses.size === 0) {
|
|
11
|
+
ui.log('No MCP servers configured');
|
|
12
|
+
ui.note('Create a mcp.json file to configure MCP servers', 'Hint');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
ui.log('MCP Servers:\n');
|
|
16
|
+
const statusIcon = {
|
|
17
|
+
[MCPServerStatus.CONNECTED]: ui.theme.success('●'),
|
|
18
|
+
[MCPServerStatus.CONNECTING]: ui.theme.warning('○'),
|
|
19
|
+
[MCPServerStatus.DISCONNECTED]: ui.theme.muted('○'),
|
|
20
|
+
[MCPServerStatus.ERROR]: ui.theme.error('●'),
|
|
21
|
+
};
|
|
22
|
+
for (const [name, status] of statuses) {
|
|
23
|
+
ui.log(` ${statusIcon[status]} ${name}: ${status}`);
|
|
24
|
+
}
|
|
25
|
+
// Show tool count
|
|
26
|
+
const tools = manager.getAllTools();
|
|
27
|
+
const resources = manager.getAllResources();
|
|
28
|
+
const prompts = manager.getAllPrompts();
|
|
29
|
+
ui.log(`\nDiscovered: ${tools.length} tools, ${resources.length} resources, ${prompts.length} prompts`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swarm Command - Run multiple agents in parallel
|
|
3
|
+
*/
|
|
4
|
+
import { type SwarmTask, type CoordinatorOptions } from '../swarm/index.js';
|
|
5
|
+
export interface SwarmCliOptions {
|
|
6
|
+
tasksFile?: string;
|
|
7
|
+
task?: string;
|
|
8
|
+
scope?: string;
|
|
9
|
+
concurrency?: number;
|
|
10
|
+
timeout?: number;
|
|
11
|
+
yolo?: boolean;
|
|
12
|
+
branch?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse swarm options from command line arguments
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseSwarmArgs(args: string[]): SwarmCliOptions;
|
|
18
|
+
/**
|
|
19
|
+
* Load tasks from a JSON file
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadTasksFromFile(filePath: string): {
|
|
22
|
+
tasks: SwarmTask[];
|
|
23
|
+
options?: Partial<CoordinatorOptions>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Create tasks from a glob pattern
|
|
27
|
+
*/
|
|
28
|
+
export declare function createTasksFromScope(task: string, scope: string, type?: SwarmTask['type']): Promise<SwarmTask[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Run swarm orchestrator
|
|
31
|
+
*/
|
|
32
|
+
export declare function runSwarm(options: SwarmCliOptions): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Print swarm help
|
|
35
|
+
*/
|
|
36
|
+
export declare function printSwarmHelp(): void;
|