gemkit-cli 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -5
- package/dist/commands/agent/index.d.ts +9 -0
- package/dist/commands/agent/index.js +1329 -0
- package/dist/commands/cache/index.d.ts +5 -0
- package/dist/commands/cache/index.js +43 -0
- package/dist/commands/catalog/index.d.ts +2 -0
- package/dist/commands/catalog/index.js +57 -0
- package/dist/commands/config/index.d.ts +7 -0
- package/dist/commands/config/index.js +122 -0
- package/dist/commands/convert/index.d.ts +8 -0
- package/dist/commands/convert/index.js +391 -0
- package/dist/commands/doctor/index.d.ts +2 -0
- package/dist/commands/doctor/index.js +243 -0
- package/dist/commands/extension/index.d.ts +5 -0
- package/dist/commands/extension/index.js +52 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.js +37 -0
- package/dist/commands/init/index.d.ts +6 -0
- package/dist/commands/init/index.js +345 -0
- package/dist/commands/new/index.d.ts +5 -0
- package/dist/commands/new/index.js +49 -0
- package/dist/commands/office/index.d.ts +5 -0
- package/dist/commands/office/index.js +283 -0
- package/dist/commands/paste/index.d.ts +10 -0
- package/dist/commands/paste/index.js +533 -0
- package/dist/commands/plan/index.d.ts +8 -0
- package/dist/commands/plan/index.js +247 -0
- package/dist/commands/session/index.d.ts +8 -0
- package/dist/commands/session/index.js +289 -0
- package/dist/commands/tokens/index.d.ts +6 -0
- package/dist/commands/tokens/index.js +148 -0
- package/dist/commands/update/index.d.ts +26 -0
- package/dist/commands/update/index.js +199 -0
- package/dist/commands/versions/index.d.ts +5 -0
- package/dist/commands/versions/index.js +39 -0
- package/dist/domains/agent/index.d.ts +8 -0
- package/dist/domains/agent/index.js +8 -0
- package/dist/domains/agent/mappings.d.ts +32 -0
- package/dist/domains/agent/mappings.js +164 -0
- package/dist/domains/agent/profile.d.ts +26 -0
- package/dist/domains/agent/profile.js +225 -0
- package/dist/domains/agent/pty-context.d.ts +11 -0
- package/dist/domains/agent/pty-context.js +83 -0
- package/dist/domains/agent/pty-providers.d.ts +18 -0
- package/dist/domains/agent/pty-providers.js +66 -0
- package/dist/domains/agent/pty-session.d.ts +33 -0
- package/dist/domains/agent/pty-session.js +82 -0
- package/dist/domains/agent/pty-types.d.ts +127 -0
- package/dist/domains/agent/pty-types.js +4 -0
- package/dist/domains/agent/search.d.ts +45 -0
- package/dist/domains/agent/search.js +614 -0
- package/dist/domains/agent/types.d.ts +78 -0
- package/dist/domains/agent/types.js +5 -0
- package/dist/domains/agent-office/documents-scanner.d.ts +9 -0
- package/dist/domains/agent-office/documents-scanner.js +143 -0
- package/dist/domains/agent-office/event-emitter.d.ts +43 -0
- package/dist/domains/agent-office/event-emitter.js +86 -0
- package/dist/domains/agent-office/file-watcher.d.ts +40 -0
- package/dist/domains/agent-office/file-watcher.js +173 -0
- package/dist/domains/agent-office/icons.d.ts +11 -0
- package/dist/domains/agent-office/icons.js +36 -0
- package/dist/domains/agent-office/index.d.ts +12 -0
- package/dist/domains/agent-office/index.js +20 -0
- package/dist/domains/agent-office/renderer/web/assets.d.ts +11 -0
- package/dist/domains/agent-office/renderer/web/assets.js +3419 -0
- package/dist/domains/agent-office/renderer/web/server.d.ts +42 -0
- package/dist/domains/agent-office/renderer/web/server.js +228 -0
- package/dist/domains/agent-office/renderer/web.d.ts +30 -0
- package/dist/domains/agent-office/renderer/web.js +111 -0
- package/dist/domains/agent-office/session-bridge.d.ts +23 -0
- package/dist/domains/agent-office/session-bridge.js +171 -0
- package/dist/domains/agent-office/state-machine.d.ts +5 -0
- package/dist/domains/agent-office/state-machine.js +82 -0
- package/dist/domains/agent-office/types.d.ts +91 -0
- package/dist/domains/agent-office/types.js +4 -0
- package/dist/domains/cache/index.d.ts +1 -0
- package/dist/domains/cache/index.js +1 -0
- package/dist/domains/cache/manager.d.ts +22 -0
- package/dist/domains/cache/manager.js +84 -0
- package/dist/domains/config/index.d.ts +5 -0
- package/dist/domains/config/index.js +5 -0
- package/dist/domains/config/manager.d.ts +24 -0
- package/dist/domains/config/manager.js +85 -0
- package/dist/domains/config/schema.d.ts +17 -0
- package/dist/domains/config/schema.js +96 -0
- package/dist/domains/convert/converter.d.ts +78 -0
- package/dist/domains/convert/converter.js +471 -0
- package/dist/domains/convert/index.d.ts +5 -0
- package/dist/domains/convert/index.js +5 -0
- package/dist/domains/convert/types.d.ts +88 -0
- package/dist/domains/convert/types.js +18 -0
- package/dist/domains/github/download.d.ts +12 -0
- package/dist/domains/github/download.js +51 -0
- package/dist/domains/github/index.d.ts +2 -0
- package/dist/domains/github/index.js +2 -0
- package/dist/domains/github/releases.d.ts +16 -0
- package/dist/domains/github/releases.js +68 -0
- package/dist/domains/installation/conflict.d.ts +13 -0
- package/dist/domains/installation/conflict.js +38 -0
- package/dist/domains/installation/file-sync.d.ts +16 -0
- package/dist/domains/installation/file-sync.js +77 -0
- package/dist/domains/installation/index.d.ts +3 -0
- package/dist/domains/installation/index.js +3 -0
- package/dist/domains/installation/metadata.d.ts +20 -0
- package/dist/domains/installation/metadata.js +52 -0
- package/dist/domains/plan/index.d.ts +2 -0
- package/dist/domains/plan/index.js +2 -0
- package/dist/domains/plan/resolver.d.ts +24 -0
- package/dist/domains/plan/resolver.js +164 -0
- package/dist/domains/plan/types.d.ts +13 -0
- package/dist/domains/plan/types.js +4 -0
- package/dist/domains/session/env.d.ts +51 -0
- package/dist/domains/session/env.js +118 -0
- package/dist/domains/session/index.d.ts +8 -0
- package/dist/domains/session/index.js +8 -0
- package/dist/domains/session/manager.d.ts +56 -0
- package/dist/domains/session/manager.js +205 -0
- package/dist/domains/session/paths.d.ts +6 -0
- package/dist/domains/session/paths.js +6 -0
- package/dist/domains/session/types.d.ts +121 -0
- package/dist/domains/session/types.js +5 -0
- package/dist/domains/session/writer.d.ts +82 -0
- package/dist/domains/session/writer.js +431 -0
- package/dist/domains/tokens/index.d.ts +5 -0
- package/dist/domains/tokens/index.js +5 -0
- package/dist/domains/tokens/pricing.d.ts +38 -0
- package/dist/domains/tokens/pricing.js +129 -0
- package/dist/domains/tokens/scanner.d.ts +42 -0
- package/dist/domains/tokens/scanner.js +168 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +86 -57
- package/dist/services/aipty.d.ts +76 -0
- package/dist/services/aipty.js +276 -0
- package/dist/services/archive.d.ts +22 -0
- package/dist/services/archive.js +53 -0
- package/dist/services/auto-update.d.ts +26 -0
- package/dist/services/auto-update.js +117 -0
- package/dist/services/hash.d.ts +36 -0
- package/dist/services/hash.js +63 -0
- package/dist/services/logger.d.ts +28 -0
- package/dist/services/logger.js +102 -0
- package/dist/services/music.d.ts +67 -0
- package/dist/services/music.js +290 -0
- package/dist/services/npm.d.ts +22 -0
- package/dist/services/npm.js +65 -0
- package/dist/services/pty-client.d.ts +66 -0
- package/dist/services/pty-client.js +154 -0
- package/dist/services/pty-server.d.ts +102 -0
- package/dist/services/pty-server.js +613 -0
- package/dist/types/index.d.ts +155 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/colors.d.ts +43 -0
- package/dist/utils/colors.js +98 -0
- package/dist/utils/errors.d.ts +24 -0
- package/dist/utils/errors.js +56 -0
- package/dist/utils/paths.d.ts +46 -0
- package/dist/utils/paths.js +89 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +31 -0
- package/package.json +55 -54
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { spawn, spawnSync } from 'child_process';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { getLatestRelease, getReleaseByVersion } from '../../domains/github/releases.js';
|
|
6
|
+
import { downloadRelease } from '../../domains/github/download.js';
|
|
7
|
+
import { extractTarGz } from '../../services/archive.js';
|
|
8
|
+
import { createMetadata, saveMetadata, isInstalled } from '../../domains/installation/metadata.js';
|
|
9
|
+
import { logger } from '../../services/logger.js';
|
|
10
|
+
import { brand, pc } from '../../utils/colors.js';
|
|
11
|
+
import { CLI_VERSION } from '../update/index.js';
|
|
12
|
+
import { getLatestNpmVersion, isUpdateAvailable } from '../../services/npm.js';
|
|
13
|
+
// Package name for CLI
|
|
14
|
+
const CLI_PACKAGE_NAME = 'gemkit-cli';
|
|
15
|
+
/**
|
|
16
|
+
* Update CLI via npm
|
|
17
|
+
*/
|
|
18
|
+
function updateCli(version) {
|
|
19
|
+
const result = spawnSync('npm', ['install', '-g', `${CLI_PACKAGE_NAME}@${version}`], {
|
|
20
|
+
encoding: 'utf-8',
|
|
21
|
+
stdio: 'pipe',
|
|
22
|
+
shell: true,
|
|
23
|
+
});
|
|
24
|
+
return result.status === 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get exclusion patterns based on installation mode
|
|
28
|
+
*/
|
|
29
|
+
function getExcludePatterns(mode) {
|
|
30
|
+
switch (mode) {
|
|
31
|
+
case 'gemini':
|
|
32
|
+
// Exclude Claude and generic agent folders (Gemini uses .gemini)
|
|
33
|
+
return ['.claude', '.claude/**', '.agent', '.agent/**'];
|
|
34
|
+
case 'claude':
|
|
35
|
+
// Exclude generic agent folder (Claude Code uses .claude)
|
|
36
|
+
return ['.agent', '.agent/**'];
|
|
37
|
+
case 'antigravity':
|
|
38
|
+
// Exclude Claude folder (Antigravity uses .agent)
|
|
39
|
+
return ['.claude', '.claude/**'];
|
|
40
|
+
case 'full':
|
|
41
|
+
default:
|
|
42
|
+
// No exclusions - install everything
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get mode description for display
|
|
48
|
+
*/
|
|
49
|
+
function getModeDescription(mode) {
|
|
50
|
+
switch (mode) {
|
|
51
|
+
case 'gemini':
|
|
52
|
+
return 'Gemini CLI (excludes .claude, .agent)';
|
|
53
|
+
case 'claude':
|
|
54
|
+
return 'Claude Code (excludes .agent)';
|
|
55
|
+
case 'antigravity':
|
|
56
|
+
return 'Antigravity (excludes .claude)';
|
|
57
|
+
case 'full':
|
|
58
|
+
default:
|
|
59
|
+
return 'Full (all files)';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if a path matches any of the exclude patterns
|
|
64
|
+
* Supports simple directory patterns like '.claude', '.claude/**'
|
|
65
|
+
*/
|
|
66
|
+
function shouldExclude(filePath, excludePatterns) {
|
|
67
|
+
// Normalize path separators
|
|
68
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
69
|
+
for (const pattern of excludePatterns) {
|
|
70
|
+
// Simple directory match: '.claude' or '.agent'
|
|
71
|
+
if (!pattern.includes('*')) {
|
|
72
|
+
if (normalizedPath === pattern || normalizedPath.startsWith(pattern + '/')) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Glob pattern: '.claude/**' means anything under .claude
|
|
77
|
+
else if (pattern.endsWith('/**')) {
|
|
78
|
+
const baseDir = pattern.slice(0, -3); // Remove '/**'
|
|
79
|
+
if (normalizedPath.startsWith(baseDir + '/')) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Create a filter function for extraction based on exclude patterns
|
|
88
|
+
*/
|
|
89
|
+
function createExcludeFilter(excludePatterns) {
|
|
90
|
+
if (excludePatterns.length === 0) {
|
|
91
|
+
return undefined; // No filtering needed
|
|
92
|
+
}
|
|
93
|
+
return (path) => {
|
|
94
|
+
// Return true to INCLUDE the file, false to EXCLUDE
|
|
95
|
+
return !shouldExclude(path, excludePatterns);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Install spawn-agent extension using gemini CLI (silent - for use with spinner)
|
|
100
|
+
* Runs: gemini extensions install .gemini/extensions/spawn-agent
|
|
101
|
+
*/
|
|
102
|
+
async function installSpawnAgentExtensionSilent() {
|
|
103
|
+
const extensionPath = join('.gemini', 'extensions', 'spawn-agent');
|
|
104
|
+
// Check if extension directory exists
|
|
105
|
+
if (!existsSync(extensionPath)) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: `Extension directory not found: ${extensionPath}`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
const child = spawn('gemini', ['extensions', 'install', extensionPath], {
|
|
113
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
114
|
+
shell: process.platform === 'win32',
|
|
115
|
+
cwd: process.cwd()
|
|
116
|
+
});
|
|
117
|
+
let stdout = '';
|
|
118
|
+
let stderr = '';
|
|
119
|
+
let resolved = false;
|
|
120
|
+
// Timeout after 30 seconds
|
|
121
|
+
const timeout = setTimeout(() => {
|
|
122
|
+
if (!resolved) {
|
|
123
|
+
resolved = true;
|
|
124
|
+
child.kill();
|
|
125
|
+
resolve({
|
|
126
|
+
success: false,
|
|
127
|
+
error: 'Extension installation timed out'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}, 30000);
|
|
131
|
+
child.stdout?.on('data', (data) => {
|
|
132
|
+
stdout += data.toString();
|
|
133
|
+
});
|
|
134
|
+
child.stderr?.on('data', (data) => {
|
|
135
|
+
stderr += data.toString();
|
|
136
|
+
});
|
|
137
|
+
child.on('close', (code) => {
|
|
138
|
+
if (!resolved) {
|
|
139
|
+
resolved = true;
|
|
140
|
+
clearTimeout(timeout);
|
|
141
|
+
if (code === 0) {
|
|
142
|
+
resolve({ success: true });
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
resolve({
|
|
146
|
+
success: false,
|
|
147
|
+
error: stderr || stdout || `Exit code: ${code}`
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
child.on('error', (err) => {
|
|
153
|
+
if (!resolved) {
|
|
154
|
+
resolved = true;
|
|
155
|
+
clearTimeout(timeout);
|
|
156
|
+
resolve({
|
|
157
|
+
success: false,
|
|
158
|
+
error: `Failed to run gemini CLI: ${err.message}`
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if spawn-agent extension is installed in Gemini
|
|
166
|
+
*/
|
|
167
|
+
export async function isSpawnAgentInstalled() {
|
|
168
|
+
return new Promise((resolve) => {
|
|
169
|
+
const child = spawn('gemini', ['extensions', 'list'], {
|
|
170
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
171
|
+
shell: process.platform === 'win32'
|
|
172
|
+
});
|
|
173
|
+
let stdout = '';
|
|
174
|
+
let resolved = false;
|
|
175
|
+
// Timeout after 20 seconds
|
|
176
|
+
const timeout = setTimeout(() => {
|
|
177
|
+
if (!resolved) {
|
|
178
|
+
resolved = true;
|
|
179
|
+
child.kill();
|
|
180
|
+
resolve(false);
|
|
181
|
+
}
|
|
182
|
+
}, 20000);
|
|
183
|
+
child.stdout?.on('data', (data) => {
|
|
184
|
+
stdout += data.toString();
|
|
185
|
+
});
|
|
186
|
+
child.on('close', (code) => {
|
|
187
|
+
if (!resolved) {
|
|
188
|
+
resolved = true;
|
|
189
|
+
clearTimeout(timeout);
|
|
190
|
+
if (code === 0) {
|
|
191
|
+
// Check if spawn-agent appears in the output
|
|
192
|
+
const isInstalled = stdout.toLowerCase().includes('spawn-agent');
|
|
193
|
+
resolve(isInstalled);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
resolve(false);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
child.on('error', () => {
|
|
201
|
+
if (!resolved) {
|
|
202
|
+
resolved = true;
|
|
203
|
+
clearTimeout(timeout);
|
|
204
|
+
resolve(false);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
export function registerInitCommand(cli) {
|
|
210
|
+
cli
|
|
211
|
+
.command('init', 'Initialize GemKit in project')
|
|
212
|
+
.option('--version <ver>', 'Specific version to install')
|
|
213
|
+
.option('-f, --force', 'Overwrite existing installation')
|
|
214
|
+
.option('--exclude <patterns...>', 'File patterns to exclude')
|
|
215
|
+
.option('--skip-extension', 'Skip spawn-agent extension installation')
|
|
216
|
+
.option('--full', 'Install all files (default)')
|
|
217
|
+
.option('--gemini', 'Gemini CLI mode (excludes .claude, .agent folders)')
|
|
218
|
+
.option('--claude', 'Claude Code mode (excludes .agent folder)')
|
|
219
|
+
.option('--antigravity', 'Antigravity mode (excludes .claude folder)')
|
|
220
|
+
.action(async (options) => {
|
|
221
|
+
// Determine installation mode (priority: gemini > claude > antigravity > full)
|
|
222
|
+
let mode = 'full';
|
|
223
|
+
if (options.gemini)
|
|
224
|
+
mode = 'gemini';
|
|
225
|
+
else if (options.claude)
|
|
226
|
+
mode = 'claude';
|
|
227
|
+
else if (options.antigravity)
|
|
228
|
+
mode = 'antigravity';
|
|
229
|
+
else if (options.full)
|
|
230
|
+
mode = 'full';
|
|
231
|
+
// Check if already installed
|
|
232
|
+
if (isInstalled() && !options.force) {
|
|
233
|
+
console.log();
|
|
234
|
+
logger.warn('GemKit is already installed. Use --force to reinstall.');
|
|
235
|
+
console.log();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
console.log();
|
|
239
|
+
console.log(pc.bold(brand.geminiPurple('Initializing GemKit')));
|
|
240
|
+
console.log();
|
|
241
|
+
console.log(` ${brand.dim('Mode')} ${brand.accent(getModeDescription(mode))}`);
|
|
242
|
+
console.log();
|
|
243
|
+
// Check CLI version and auto-update if available
|
|
244
|
+
const cliSpinner = ora({
|
|
245
|
+
text: 'Checking CLI version...',
|
|
246
|
+
color: 'magenta'
|
|
247
|
+
}).start();
|
|
248
|
+
try {
|
|
249
|
+
const latestCli = await getLatestNpmVersion(CLI_PACKAGE_NAME);
|
|
250
|
+
if (latestCli && isUpdateAvailable(CLI_VERSION, latestCli.version)) {
|
|
251
|
+
cliSpinner.text = `Updating CLI v${CLI_VERSION} → v${latestCli.version}...`;
|
|
252
|
+
const updated = updateCli(latestCli.version);
|
|
253
|
+
if (updated) {
|
|
254
|
+
cliSpinner.succeed(`CLI updated v${brand.primary(CLI_VERSION)} → v${brand.primary(latestCli.version)}`);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
cliSpinner.warn(`CLI v${brand.primary(CLI_VERSION)} (update to v${latestCli.version} failed)`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
cliSpinner.succeed(`CLI v${brand.primary(CLI_VERSION)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
cliSpinner.succeed(`CLI v${brand.primary(CLI_VERSION)}`);
|
|
266
|
+
}
|
|
267
|
+
// Step 1: Fetch Kits release info
|
|
268
|
+
const fetchSpinner = ora({
|
|
269
|
+
text: 'Fetching latest Kits release...',
|
|
270
|
+
color: 'magenta'
|
|
271
|
+
}).start();
|
|
272
|
+
const release = options.version
|
|
273
|
+
? await getReleaseByVersion(options.version)
|
|
274
|
+
: await getLatestRelease();
|
|
275
|
+
if (!release) {
|
|
276
|
+
fetchSpinner.fail('No Kits release found');
|
|
277
|
+
console.log();
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
fetchSpinner.succeed(`Found Kits v${brand.primary(release.version)}`);
|
|
281
|
+
// Step 2: Download Kits
|
|
282
|
+
const downloadSpinner = ora({
|
|
283
|
+
text: 'Downloading Kits...',
|
|
284
|
+
color: 'magenta'
|
|
285
|
+
}).start();
|
|
286
|
+
let tarPath;
|
|
287
|
+
try {
|
|
288
|
+
tarPath = await downloadRelease(release);
|
|
289
|
+
downloadSpinner.succeed('Kits downloaded');
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
downloadSpinner.fail(`Kits download failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
293
|
+
console.log();
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
// Step 3: Extract Kits
|
|
297
|
+
const excludePatterns = getExcludePatterns(mode);
|
|
298
|
+
const extractSpinner = ora({
|
|
299
|
+
text: mode === 'full' ? 'Extracting Kits...' : `Extracting Kits (${mode} mode)...`,
|
|
300
|
+
color: 'magenta'
|
|
301
|
+
}).start();
|
|
302
|
+
const extractDir = process.cwd();
|
|
303
|
+
const result = await extractTarGz({
|
|
304
|
+
source: tarPath,
|
|
305
|
+
destination: extractDir,
|
|
306
|
+
strip: 1,
|
|
307
|
+
filter: createExcludeFilter(excludePatterns),
|
|
308
|
+
});
|
|
309
|
+
if (!result.success) {
|
|
310
|
+
extractSpinner.fail(`Kits extraction failed: ${result.error}`);
|
|
311
|
+
console.log();
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
extractSpinner.succeed(`Extracted ${result.extractedFiles.length} files`);
|
|
315
|
+
// Save metadata
|
|
316
|
+
const metadata = createMetadata(release.version, 'local', result.extractedFiles);
|
|
317
|
+
saveMetadata(metadata);
|
|
318
|
+
console.log();
|
|
319
|
+
logger.success(`Kits v${brand.primary(release.version)} installed successfully!`);
|
|
320
|
+
// Step 4: Install spawn-agent extension (unless skipped or using non-Gemini mode)
|
|
321
|
+
const shouldInstallExtension = !options.skipExtension && (mode === 'full' || mode === 'gemini');
|
|
322
|
+
if (shouldInstallExtension) {
|
|
323
|
+
console.log();
|
|
324
|
+
const extensionSpinner = ora({
|
|
325
|
+
text: 'Installing spawn-agent extension...',
|
|
326
|
+
color: 'magenta'
|
|
327
|
+
}).start();
|
|
328
|
+
const extensionResult = await installSpawnAgentExtensionSilent();
|
|
329
|
+
if (extensionResult.success) {
|
|
330
|
+
extensionSpinner.succeed('spawn-agent extension installed in Gemini CLI');
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
extensionSpinner.warn(`Could not install spawn-agent extension: ${extensionResult.error}`);
|
|
334
|
+
logger.info(`You can manually install it with: ${brand.primary('gemini extensions install .gemini/extensions/spawn-agent')}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
else if (mode === 'claude' || mode === 'antigravity') {
|
|
338
|
+
console.log();
|
|
339
|
+
logger.info(`Skipped Gemini extension (${mode} mode).`);
|
|
340
|
+
}
|
|
341
|
+
console.log();
|
|
342
|
+
logger.info(`Run ${brand.primary('gk doctor')} to verify installation.`);
|
|
343
|
+
console.log();
|
|
344
|
+
});
|
|
345
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* New command - Create a new GemKit project
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { getLatestRelease } from '../../domains/github/index.js';
|
|
7
|
+
import { downloadRelease } from '../../domains/github/download.js';
|
|
8
|
+
import { extractTarGz } from '../../services/archive.js';
|
|
9
|
+
import { logger } from '../../services/logger.js';
|
|
10
|
+
import { brand, pc } from '../../utils/colors.js';
|
|
11
|
+
export function registerNewCommand(cli) {
|
|
12
|
+
cli
|
|
13
|
+
.command('new <name>', 'Create a new project from starter kit')
|
|
14
|
+
.action(async (name) => {
|
|
15
|
+
const destDir = join(process.cwd(), name);
|
|
16
|
+
if (existsSync(destDir)) {
|
|
17
|
+
console.log();
|
|
18
|
+
logger.error(`Directory already exists: ${name}`);
|
|
19
|
+
console.log();
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(pc.bold(brand.geminiPurple('Creating New Project')));
|
|
24
|
+
console.log();
|
|
25
|
+
logger.info(`Name: ${brand.primary(name)}`);
|
|
26
|
+
const latest = await getLatestRelease();
|
|
27
|
+
if (!latest) {
|
|
28
|
+
logger.error('Failed to fetch latest starter kit.');
|
|
29
|
+
console.log();
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
logger.info(`Fetching latest starter kit (v${latest.version})...`);
|
|
33
|
+
const tarPath = await downloadRelease(latest);
|
|
34
|
+
const result = await extractTarGz({
|
|
35
|
+
source: tarPath,
|
|
36
|
+
destination: destDir,
|
|
37
|
+
strip: 1,
|
|
38
|
+
});
|
|
39
|
+
if (result.success) {
|
|
40
|
+
console.log();
|
|
41
|
+
logger.success(`Project ${brand.primary(name)} created successfully!`);
|
|
42
|
+
console.log(`\nNext steps:\n cd ${brand.primary(name)}\n gk init\n`);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
logger.error(`Failed to create project: ${result.error}`);
|
|
46
|
+
console.log();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Office command - Gamified visualization for multi-agent workflows
|
|
3
|
+
*/
|
|
4
|
+
import { startWebDashboard, OfficeEventEmitter, createInitialState, SessionFileWatcher, sessionToOfficeState, } from '../../domains/agent-office/index.js';
|
|
5
|
+
import { logger } from '../../services/logger.js';
|
|
6
|
+
import { brand, ui, pc } from '../../utils/colors.js';
|
|
7
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
8
|
+
// HELP FUNCTIONS
|
|
9
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
10
|
+
function showMainHelp() {
|
|
11
|
+
console.log();
|
|
12
|
+
console.log(pc.bold(brand.geminiPurple('Agent Office')));
|
|
13
|
+
console.log();
|
|
14
|
+
console.log('Usage:');
|
|
15
|
+
console.log(` ${brand.primary('gk office')} <subcommand> [options]`);
|
|
16
|
+
console.log();
|
|
17
|
+
console.log('Subcommands:');
|
|
18
|
+
console.log(` ${brand.primary('start')} Start the visualization`);
|
|
19
|
+
console.log(` ${brand.primary('status')} Show current office state`);
|
|
20
|
+
console.log(` ${brand.primary('watch')} Watch office state changes`);
|
|
21
|
+
console.log();
|
|
22
|
+
console.log('Options:');
|
|
23
|
+
console.log(` ${brand.dim('-p, --port <n>')} [start] Web server port (default: 3847)`);
|
|
24
|
+
console.log(` ${brand.dim('--no-open')} [start] Don't auto-open browser`);
|
|
25
|
+
console.log(` ${brand.dim('--json')} [status/watch] Output as JSON`);
|
|
26
|
+
console.log();
|
|
27
|
+
console.log('Examples:');
|
|
28
|
+
console.log(` ${brand.dim('gk office start')}`);
|
|
29
|
+
console.log(` ${brand.dim('gk office start --port 4000')}`);
|
|
30
|
+
console.log(` ${brand.dim('gk office status --json')}`);
|
|
31
|
+
console.log(` ${brand.dim('gk office watch')}`);
|
|
32
|
+
console.log();
|
|
33
|
+
}
|
|
34
|
+
function showStartHelp() {
|
|
35
|
+
console.log();
|
|
36
|
+
console.log(pc.bold(brand.geminiPurple('gk office start')));
|
|
37
|
+
console.log(brand.dim('Start the Agent Office web dashboard'));
|
|
38
|
+
console.log();
|
|
39
|
+
console.log('Usage:');
|
|
40
|
+
console.log(` ${brand.primary('gk office start')} [options]`);
|
|
41
|
+
console.log();
|
|
42
|
+
console.log('Options:');
|
|
43
|
+
console.log(` ${brand.dim('-p, --port <n>')} Web server port (default: 3847)`);
|
|
44
|
+
console.log(` ${brand.dim('--no-open')} Don't auto-open browser`);
|
|
45
|
+
console.log();
|
|
46
|
+
console.log('Examples:');
|
|
47
|
+
console.log(` ${brand.dim('gk office start')}`);
|
|
48
|
+
console.log(` ${brand.dim('gk office start --port 4000')}`);
|
|
49
|
+
console.log(` ${brand.dim('gk office start --port 4000 --no-open')}`);
|
|
50
|
+
console.log();
|
|
51
|
+
}
|
|
52
|
+
function showStatusHelp() {
|
|
53
|
+
console.log();
|
|
54
|
+
console.log(pc.bold(brand.geminiPurple('gk office status')));
|
|
55
|
+
console.log(brand.dim('Show current Agent Office state'));
|
|
56
|
+
console.log();
|
|
57
|
+
console.log('Usage:');
|
|
58
|
+
console.log(` ${brand.primary('gk office status')} [options]`);
|
|
59
|
+
console.log();
|
|
60
|
+
console.log('Options:');
|
|
61
|
+
console.log(` ${brand.dim('--json')} Output as JSON`);
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
function showWatchHelp() {
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(pc.bold(brand.geminiPurple('gk office watch')));
|
|
67
|
+
console.log(brand.dim('Watch Agent Office state changes in real-time'));
|
|
68
|
+
console.log();
|
|
69
|
+
console.log('Usage:');
|
|
70
|
+
console.log(` ${brand.primary('gk office watch')} [options]`);
|
|
71
|
+
console.log();
|
|
72
|
+
console.log('Options:');
|
|
73
|
+
console.log(` ${brand.dim('--json')} Output as JSON`);
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
77
|
+
// HANDLERS
|
|
78
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
79
|
+
/**
|
|
80
|
+
* Handle start subcommand
|
|
81
|
+
*/
|
|
82
|
+
async function handleStart(options) {
|
|
83
|
+
console.log();
|
|
84
|
+
logger.info('Starting Agent Office...');
|
|
85
|
+
console.log();
|
|
86
|
+
try {
|
|
87
|
+
const dashboard = await startWebDashboard({
|
|
88
|
+
port: options.port || 3847,
|
|
89
|
+
autoOpen: options.open !== false,
|
|
90
|
+
onReady: (port) => {
|
|
91
|
+
logger.success(`Dashboard running at ${brand.primary(`http://localhost:${port}`)}`);
|
|
92
|
+
console.log(brand.dim(' Press Ctrl+C to stop'));
|
|
93
|
+
console.log();
|
|
94
|
+
},
|
|
95
|
+
onError: (error) => {
|
|
96
|
+
logger.error(`Error: ${error.message}`);
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
process.on('SIGINT', () => {
|
|
100
|
+
dashboard.stop();
|
|
101
|
+
console.log();
|
|
102
|
+
logger.info('Agent Office stopped.');
|
|
103
|
+
console.log();
|
|
104
|
+
process.exit(0);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof Error) {
|
|
109
|
+
if (error.message === 'No active session found') {
|
|
110
|
+
console.log();
|
|
111
|
+
logger.warn('No active session found.');
|
|
112
|
+
console.log(brand.dim(' Start a GemKit session first with: gk agent'));
|
|
113
|
+
console.log();
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
logger.error(`Failed to start: ${error.message}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Handle status subcommand
|
|
124
|
+
*/
|
|
125
|
+
function handleStatus(options) {
|
|
126
|
+
const emitter = new OfficeEventEmitter(createInitialState());
|
|
127
|
+
const watcher = new SessionFileWatcher({
|
|
128
|
+
onSessionChange: (session) => {
|
|
129
|
+
const state = sessionToOfficeState(session);
|
|
130
|
+
if (options.json) {
|
|
131
|
+
console.log(JSON.stringify({
|
|
132
|
+
...state,
|
|
133
|
+
agents: Object.fromEntries(state.agents),
|
|
134
|
+
}, null, 2));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.log();
|
|
138
|
+
console.log(pc.bold(brand.geminiPurple('Agent Office Status')));
|
|
139
|
+
console.log(ui.line());
|
|
140
|
+
console.log();
|
|
141
|
+
console.log(` ${brand.dim('Active Plan:')} ${state.activePlan ? brand.primary(state.activePlan) : brand.dim('(none)')}`);
|
|
142
|
+
console.log(` ${brand.dim('Agents:')} ${state.agents.size}`);
|
|
143
|
+
console.log(` ${brand.dim('Inbox Items:')} ${state.inbox.length}`);
|
|
144
|
+
console.log(` ${brand.dim('Documents:')} ${state.documents.length}`);
|
|
145
|
+
if (state.agents.size > 0) {
|
|
146
|
+
console.log();
|
|
147
|
+
console.log(brand.dim(' Agents:'));
|
|
148
|
+
for (const [id, agent] of state.agents) {
|
|
149
|
+
const statusColor = agent.state === 'working' ? brand.warn :
|
|
150
|
+
agent.state === 'idle' ? brand.success : brand.dim;
|
|
151
|
+
console.log(` ${agent.icon} ${brand.primary(agent.role)} ${statusColor(`[${agent.state}]`)}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
console.log();
|
|
155
|
+
}
|
|
156
|
+
watcher.stop();
|
|
157
|
+
emitter.dispose();
|
|
158
|
+
},
|
|
159
|
+
onEvent: () => { }, // No-op for status
|
|
160
|
+
onError: (error) => {
|
|
161
|
+
console.log();
|
|
162
|
+
logger.error(`Error: ${error.message}`);
|
|
163
|
+
console.log();
|
|
164
|
+
process.exit(1);
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
const started = watcher.start();
|
|
168
|
+
if (!started) {
|
|
169
|
+
console.log();
|
|
170
|
+
logger.warn('No active session found.');
|
|
171
|
+
console.log(brand.dim(' Start a GemKit session first with: gk agent'));
|
|
172
|
+
console.log();
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Handle watch subcommand
|
|
178
|
+
*/
|
|
179
|
+
function handleWatch(options) {
|
|
180
|
+
console.log();
|
|
181
|
+
logger.info('Watching Agent Office state...');
|
|
182
|
+
console.log(brand.dim(' Press Ctrl+C to stop'));
|
|
183
|
+
console.log();
|
|
184
|
+
const emitter = new OfficeEventEmitter(createInitialState());
|
|
185
|
+
const watcher = new SessionFileWatcher({
|
|
186
|
+
onSessionChange: (session) => {
|
|
187
|
+
const state = sessionToOfficeState(session);
|
|
188
|
+
if (options.json) {
|
|
189
|
+
console.log(JSON.stringify({
|
|
190
|
+
timestamp: new Date().toISOString(),
|
|
191
|
+
...state,
|
|
192
|
+
agents: Object.fromEntries(state.agents),
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const time = new Date().toLocaleTimeString();
|
|
197
|
+
console.log(`${brand.dim(time)} ${brand.info('State:')} ${state.agents.size} agents, ${state.inbox.length} inbox items`);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
onEvent: (event) => {
|
|
201
|
+
if (options.json) {
|
|
202
|
+
console.log(JSON.stringify({ type: 'event', event }));
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const time = new Date().toLocaleTimeString();
|
|
206
|
+
console.log(`${brand.dim(time)} ${brand.secondary('Event:')} ${event.type}`);
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
onError: (error) => {
|
|
210
|
+
logger.error(`Error: ${error.message}`);
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
const started = watcher.start();
|
|
214
|
+
if (!started) {
|
|
215
|
+
console.log();
|
|
216
|
+
logger.warn('No active session found.');
|
|
217
|
+
console.log(brand.dim(' Start a GemKit session first with: gk agent'));
|
|
218
|
+
console.log();
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
process.on('SIGINT', () => {
|
|
222
|
+
watcher.stop();
|
|
223
|
+
emitter.dispose();
|
|
224
|
+
console.log();
|
|
225
|
+
logger.info('Stopped watching.');
|
|
226
|
+
console.log();
|
|
227
|
+
process.exit(0);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
231
|
+
// COMMAND REGISTRATION
|
|
232
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
233
|
+
export function registerOfficeCommand(cli) {
|
|
234
|
+
cli
|
|
235
|
+
.command('office [subcommand]', 'Agent Office visualization (start, status, watch)')
|
|
236
|
+
.option('-p, --port <port>', '[start] Web server port', { default: 3847 })
|
|
237
|
+
.option('--no-open', "[start] Don't auto-open browser")
|
|
238
|
+
.option('--json', '[status/watch] Output as JSON')
|
|
239
|
+
.action(async (subcommand, options) => {
|
|
240
|
+
// Parse port as number
|
|
241
|
+
if (options.port) {
|
|
242
|
+
options.port = parseInt(String(options.port), 10);
|
|
243
|
+
}
|
|
244
|
+
// Handle help for subcommands
|
|
245
|
+
if (options.help || options.h) {
|
|
246
|
+
switch (subcommand) {
|
|
247
|
+
case 'start':
|
|
248
|
+
showStartHelp();
|
|
249
|
+
return;
|
|
250
|
+
case 'status':
|
|
251
|
+
showStatusHelp();
|
|
252
|
+
return;
|
|
253
|
+
case 'watch':
|
|
254
|
+
showWatchHelp();
|
|
255
|
+
return;
|
|
256
|
+
default:
|
|
257
|
+
showMainHelp();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Route to handlers
|
|
262
|
+
switch (subcommand) {
|
|
263
|
+
case 'start':
|
|
264
|
+
await handleStart(options);
|
|
265
|
+
break;
|
|
266
|
+
case 'status':
|
|
267
|
+
handleStatus(options);
|
|
268
|
+
break;
|
|
269
|
+
case 'watch':
|
|
270
|
+
handleWatch(options);
|
|
271
|
+
break;
|
|
272
|
+
case undefined:
|
|
273
|
+
case 'help':
|
|
274
|
+
showMainHelp();
|
|
275
|
+
break;
|
|
276
|
+
default:
|
|
277
|
+
console.log();
|
|
278
|
+
logger.error(`Unknown subcommand: ${subcommand}`);
|
|
279
|
+
showMainHelp();
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|