gemkit-cli 0.2.3 → 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 +141 -7
- 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 +87 -58
- 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,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tokens command - Session token usage analysis
|
|
3
|
+
* Matches token_stats.py --latest output format
|
|
4
|
+
*/
|
|
5
|
+
import { getCurrentSessionTokens } from '../../domains/tokens/scanner.js';
|
|
6
|
+
import { calculateCost, formatCost, formatTokens } from '../../domains/tokens/pricing.js';
|
|
7
|
+
import { logger } from '../../services/logger.js';
|
|
8
|
+
import { brand, pc } from '../../utils/colors.js';
|
|
9
|
+
// Box drawing characters
|
|
10
|
+
const BOX = {
|
|
11
|
+
topLeft: '╭',
|
|
12
|
+
topRight: '╮',
|
|
13
|
+
bottomLeft: '╰',
|
|
14
|
+
bottomRight: '╯',
|
|
15
|
+
horizontal: '─',
|
|
16
|
+
vertical: '│',
|
|
17
|
+
middleLeft: '├',
|
|
18
|
+
middleRight: '┤',
|
|
19
|
+
};
|
|
20
|
+
function boxTop(width = 75) {
|
|
21
|
+
return brand.dim(`${BOX.topLeft}${BOX.horizontal.repeat(width - 2)}${BOX.topRight}`);
|
|
22
|
+
}
|
|
23
|
+
function boxBottom(width = 75) {
|
|
24
|
+
return brand.dim(`${BOX.bottomLeft}${BOX.horizontal.repeat(width - 2)}${BOX.bottomRight}`);
|
|
25
|
+
}
|
|
26
|
+
function boxLine(text, width = 75) {
|
|
27
|
+
// Strip ANSI codes for length calculation
|
|
28
|
+
const visibleLen = text.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
29
|
+
const padding = Math.max(0, width - 4 - visibleLen);
|
|
30
|
+
return `${brand.dim(BOX.vertical)} ${text}${' '.repeat(padding)} ${brand.dim(BOX.vertical)}`;
|
|
31
|
+
}
|
|
32
|
+
function sectionHeader(text) {
|
|
33
|
+
return `\n${pc.bold(brand.geminiPurple(`## ${text}`))}`;
|
|
34
|
+
}
|
|
35
|
+
function padLeft(text, width) {
|
|
36
|
+
const visibleLen = text.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
37
|
+
const padding = Math.max(0, width - visibleLen);
|
|
38
|
+
return ' '.repeat(padding) + text;
|
|
39
|
+
}
|
|
40
|
+
function padRight(text, width) {
|
|
41
|
+
const visibleLen = text.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
42
|
+
const padding = Math.max(0, width - visibleLen);
|
|
43
|
+
return text + ' '.repeat(padding);
|
|
44
|
+
}
|
|
45
|
+
function gradientText(text) {
|
|
46
|
+
// Gradient from Gemini blue (#1a73e8) to purple (#8e24aa)
|
|
47
|
+
const result = [];
|
|
48
|
+
const length = text.length;
|
|
49
|
+
for (let i = 0; i < length; i++) {
|
|
50
|
+
const ratio = i / Math.max(length - 1, 1);
|
|
51
|
+
const r = Math.round(26 + (142 - 26) * ratio);
|
|
52
|
+
const g = Math.round(115 + (36 - 115) * ratio);
|
|
53
|
+
const b = Math.round(232 + (170 - 232) * ratio);
|
|
54
|
+
result.push(`\x1b[38;2;${r};${g};${b}m${text[i]}`);
|
|
55
|
+
}
|
|
56
|
+
return result.join('') + '\x1b[0m';
|
|
57
|
+
}
|
|
58
|
+
function displaySessionAnalysis(analysis) {
|
|
59
|
+
const cost = calculateCost(analysis.tokens, analysis.model);
|
|
60
|
+
// Header box
|
|
61
|
+
console.log();
|
|
62
|
+
console.log(boxTop(75));
|
|
63
|
+
console.log(boxLine(`${gradientText('SESSION ANALYSIS')} ${brand.dim('Cost:')} ${brand.warn(formatCost(cost.total))}`, 75));
|
|
64
|
+
console.log(boxBottom(75));
|
|
65
|
+
// Session info
|
|
66
|
+
console.log(`\n ${brand.dim('Session ID:')} ${brand.accent(analysis.sessionId)}`);
|
|
67
|
+
console.log(` ${brand.dim('Start Time:')} ${analysis.startTime || 'N/A'}`);
|
|
68
|
+
console.log(` ${brand.dim('Duration:')} ${brand.accent(analysis.duration?.formatted || 'N/A')}`);
|
|
69
|
+
console.log(` ${brand.dim('Model:')} ${brand.geminiBlue(analysis.modelsUsed.join(', '))}`);
|
|
70
|
+
console.log(` ${brand.dim('Messages:')} ${brand.accent(String(analysis.messageCount))}`);
|
|
71
|
+
// Token Breakdown
|
|
72
|
+
console.log(sectionHeader('Token Breakdown'));
|
|
73
|
+
console.log(`\n ${padRight('Category', 10)} ${padLeft('Count', 14)} ${padLeft('Formatted', 10)}`);
|
|
74
|
+
console.log(` ${brand.dim('─'.repeat(10))} ${brand.dim('─'.repeat(14))} ${brand.dim('─'.repeat(10))}`);
|
|
75
|
+
const tokenKeys = ['input', 'output', 'cached', 'thoughts', 'tool'];
|
|
76
|
+
for (const key of tokenKeys) {
|
|
77
|
+
const value = analysis.tokens[key];
|
|
78
|
+
const countCol = padLeft(brand.accent(value.toLocaleString()), 14);
|
|
79
|
+
const fmtCol = padLeft(brand.accent(formatTokens(value)), 10);
|
|
80
|
+
console.log(` ${padRight(key, 10)} ${countCol} ${fmtCol}`);
|
|
81
|
+
}
|
|
82
|
+
console.log(` ${brand.dim('─'.repeat(10))} ${brand.dim('─'.repeat(14))} ${brand.dim('─'.repeat(10))}`);
|
|
83
|
+
const totalCount = padLeft(brand.success(analysis.tokens.total.toLocaleString()), 14);
|
|
84
|
+
const totalFmt = padLeft(brand.success(formatTokens(analysis.tokens.total)), 10);
|
|
85
|
+
console.log(` ${padRight(pc.bold('total'), 10)} ${totalCount} ${totalFmt}`);
|
|
86
|
+
// Estimated Cost
|
|
87
|
+
console.log(sectionHeader('Estimated Cost'));
|
|
88
|
+
console.log(`\n ${padRight('Input', 12)} ${padLeft(brand.warn(formatCost(cost.input)), 12)}`);
|
|
89
|
+
console.log(` ${padRight('Output', 12)} ${padLeft(brand.warn(formatCost(cost.output)), 12)}`);
|
|
90
|
+
console.log(` ${padRight('Cached', 12)} ${padLeft(brand.warn(formatCost(cost.cached)), 12)}`);
|
|
91
|
+
console.log(` ${padRight('Thoughts', 12)} ${padLeft(brand.warn(formatCost(cost.thoughts)), 12)}`);
|
|
92
|
+
console.log(` ${brand.dim('─'.repeat(24))}`);
|
|
93
|
+
console.log(` ${padRight(pc.bold('TOTAL'), 12)} ${padLeft(pc.bold(brand.warn(formatCost(cost.total))), 12)}`);
|
|
94
|
+
// Averages
|
|
95
|
+
if (analysis.messageCount > 0) {
|
|
96
|
+
console.log(sectionHeader('Averages'));
|
|
97
|
+
const tokensPerMsg = Math.round(analysis.tokens.total / analysis.messageCount);
|
|
98
|
+
const costPerMsg = cost.total / analysis.messageCount;
|
|
99
|
+
console.log(`\n ${brand.dim('Tokens/msg:')} ${brand.accent(tokensPerMsg.toLocaleString())}`);
|
|
100
|
+
console.log(` ${brand.dim('Cost/msg:')} ${brand.warn(formatCost(costPerMsg))}`);
|
|
101
|
+
}
|
|
102
|
+
console.log();
|
|
103
|
+
}
|
|
104
|
+
function displayJsonOutput(analysis) {
|
|
105
|
+
const cost = calculateCost(analysis.tokens, analysis.model);
|
|
106
|
+
console.log(JSON.stringify({
|
|
107
|
+
sessionId: analysis.sessionId,
|
|
108
|
+
startTime: analysis.startTime,
|
|
109
|
+
duration: analysis.duration,
|
|
110
|
+
model: analysis.model,
|
|
111
|
+
modelsUsed: analysis.modelsUsed,
|
|
112
|
+
messageCount: analysis.messageCount,
|
|
113
|
+
tokens: analysis.tokens,
|
|
114
|
+
averages: analysis.averages,
|
|
115
|
+
cost: {
|
|
116
|
+
input: cost.input,
|
|
117
|
+
output: cost.output,
|
|
118
|
+
cached: cost.cached,
|
|
119
|
+
thoughts: cost.thoughts,
|
|
120
|
+
total: cost.total
|
|
121
|
+
}
|
|
122
|
+
}, null, 2));
|
|
123
|
+
}
|
|
124
|
+
export function registerTokensCommand(cli) {
|
|
125
|
+
cli
|
|
126
|
+
.command('tokens', 'Show token usage for current session')
|
|
127
|
+
.alias('t')
|
|
128
|
+
.option('--json', 'Output as JSON')
|
|
129
|
+
.action(async (options) => {
|
|
130
|
+
const analysis = await getCurrentSessionTokens();
|
|
131
|
+
if (!analysis) {
|
|
132
|
+
if (options.json) {
|
|
133
|
+
console.log(JSON.stringify({ found: false }, null, 2));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.log();
|
|
137
|
+
logger.info('No token usage found for current session.');
|
|
138
|
+
console.log();
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (options.json) {
|
|
143
|
+
displayJsonOutput(analysis);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
displaySessionAnalysis(analysis);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command - Update GemKit CLI and/or Kits to latest version
|
|
3
|
+
*
|
|
4
|
+
* Two update sources:
|
|
5
|
+
* 1. CLI (npm): gemkit-cli package from npm registry
|
|
6
|
+
* 2. Kits (GitHub): .gemini folder from gemkit-kits-starter repo
|
|
7
|
+
*/
|
|
8
|
+
import type { CAC } from 'cac';
|
|
9
|
+
declare const CLI_VERSION: string;
|
|
10
|
+
export declare function registerUpdateCommand(cli: CAC): void;
|
|
11
|
+
/**
|
|
12
|
+
* Check for updates (used by auto-update checker)
|
|
13
|
+
*/
|
|
14
|
+
export declare function checkForUpdates(): Promise<{
|
|
15
|
+
cli: {
|
|
16
|
+
current: string;
|
|
17
|
+
latest: string;
|
|
18
|
+
available: boolean;
|
|
19
|
+
} | null;
|
|
20
|
+
kits: {
|
|
21
|
+
current: string;
|
|
22
|
+
latest: string;
|
|
23
|
+
available: boolean;
|
|
24
|
+
} | null;
|
|
25
|
+
}>;
|
|
26
|
+
export { CLI_VERSION };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command - Update GemKit CLI and/or Kits to latest version
|
|
3
|
+
*
|
|
4
|
+
* Two update sources:
|
|
5
|
+
* 1. CLI (npm): gemkit-cli package from npm registry
|
|
6
|
+
* 2. Kits (GitHub): .gemini folder from gemkit-kits-starter repo
|
|
7
|
+
*/
|
|
8
|
+
import { spawnSync } from 'child_process';
|
|
9
|
+
import { getLatestRelease } from '../../domains/github/releases.js';
|
|
10
|
+
import { downloadRelease } from '../../domains/github/download.js';
|
|
11
|
+
import { extractTarGz } from '../../services/archive.js';
|
|
12
|
+
import { loadMetadata, saveMetadata } from '../../domains/installation/metadata.js';
|
|
13
|
+
import { getLocalGeminiDir } from '../../utils/paths.js';
|
|
14
|
+
import { logger } from '../../services/logger.js';
|
|
15
|
+
import { brand } from '../../utils/colors.js';
|
|
16
|
+
import { getLatestNpmVersion, isUpdateAvailable } from '../../services/npm.js';
|
|
17
|
+
import { readFileSync } from 'fs';
|
|
18
|
+
import { join, dirname } from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
// Package name for CLI
|
|
21
|
+
const CLI_PACKAGE_NAME = 'gemkit-cli';
|
|
22
|
+
// Get current CLI version from package.json dynamically
|
|
23
|
+
function getCliVersion() {
|
|
24
|
+
try {
|
|
25
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
+
const __dirname = dirname(__filename);
|
|
27
|
+
// Try multiple possible locations for package.json
|
|
28
|
+
// After esbuild bundling: dist/index.js -> ../package.json
|
|
29
|
+
// During development: src/commands/update/index.ts -> ../../../package.json
|
|
30
|
+
const paths = [
|
|
31
|
+
join(__dirname, '..', 'package.json'), // from dist/ (bundled)
|
|
32
|
+
join(__dirname, '..', '..', '..', 'package.json'), // from src/commands/update/ (dev)
|
|
33
|
+
];
|
|
34
|
+
for (const pkgPath of paths) {
|
|
35
|
+
try {
|
|
36
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
37
|
+
if (pkg.name === CLI_PACKAGE_NAME && pkg.version) {
|
|
38
|
+
return pkg.version;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Try next path
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Ignore errors
|
|
48
|
+
}
|
|
49
|
+
return '0.0.0'; // Fallback
|
|
50
|
+
}
|
|
51
|
+
const CLI_VERSION = getCliVersion();
|
|
52
|
+
export function registerUpdateCommand(cli) {
|
|
53
|
+
cli
|
|
54
|
+
.command('update', 'Update GemKit CLI and/or Kits to latest version')
|
|
55
|
+
.option('-f, --force', 'Force update even if already up to date')
|
|
56
|
+
.option('--no-backup', 'Disable backup before kits update')
|
|
57
|
+
.option('--cli', 'Update CLI only (npm package)')
|
|
58
|
+
.option('--kits', 'Update Kits only (.gemini folder)')
|
|
59
|
+
.action(async (options) => {
|
|
60
|
+
// Default: update both if no specific flag
|
|
61
|
+
const updateCli = options.cli === true || (!options.cli && !options.kits);
|
|
62
|
+
const updateKits = options.kits === true || (!options.cli && !options.kits);
|
|
63
|
+
let cliUpdated = false;
|
|
64
|
+
let kitsUpdated = false;
|
|
65
|
+
// Update CLI if requested
|
|
66
|
+
if (updateCli) {
|
|
67
|
+
cliUpdated = await updateCliPackage(options.force);
|
|
68
|
+
}
|
|
69
|
+
// Update Kits if requested
|
|
70
|
+
if (updateKits) {
|
|
71
|
+
kitsUpdated = await updateKitsFolder(options.force);
|
|
72
|
+
}
|
|
73
|
+
// Summary
|
|
74
|
+
console.log();
|
|
75
|
+
if (cliUpdated || kitsUpdated) {
|
|
76
|
+
logger.success('Update complete!');
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
logger.info('Everything is up to date.');
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Update CLI via npm
|
|
85
|
+
*/
|
|
86
|
+
async function updateCliPackage(force) {
|
|
87
|
+
console.log();
|
|
88
|
+
logger.info(`${brand.primary('CLI')} Checking for updates...`);
|
|
89
|
+
const latest = await getLatestNpmVersion(CLI_PACKAGE_NAME);
|
|
90
|
+
if (!latest) {
|
|
91
|
+
logger.warn('Failed to check npm registry for CLI updates.');
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const currentVersion = CLI_VERSION;
|
|
95
|
+
const latestVersion = latest.version;
|
|
96
|
+
if (!isUpdateAvailable(currentVersion, latestVersion) && !force) {
|
|
97
|
+
logger.success(`CLI is up to date (v${currentVersion})`);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
logger.info(`Updating CLI: v${currentVersion} → v${latestVersion}`);
|
|
101
|
+
// Run npm update
|
|
102
|
+
const result = spawnSync('npm', ['install', '-g', `${CLI_PACKAGE_NAME}@latest`], {
|
|
103
|
+
encoding: 'utf-8',
|
|
104
|
+
stdio: 'inherit',
|
|
105
|
+
shell: true,
|
|
106
|
+
});
|
|
107
|
+
if (result.status !== 0) {
|
|
108
|
+
logger.error('CLI update failed. Try running manually:');
|
|
109
|
+
logger.info(` npm install -g ${CLI_PACKAGE_NAME}@latest`);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
logger.success(`CLI updated to v${latestVersion}`);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Update Kits from GitHub
|
|
117
|
+
*/
|
|
118
|
+
async function updateKitsFolder(force) {
|
|
119
|
+
console.log();
|
|
120
|
+
logger.info(`${brand.primary('Kits')} Checking for updates...`);
|
|
121
|
+
const metadata = loadMetadata();
|
|
122
|
+
if (!metadata) {
|
|
123
|
+
logger.warn('GemKit not initialized. Run "gk init" first to install kits.');
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
const latest = await getLatestRelease();
|
|
127
|
+
if (!latest) {
|
|
128
|
+
logger.warn('Failed to check GitHub for kits updates.');
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (latest.version === metadata.version && !force) {
|
|
132
|
+
logger.success(`Kits are up to date (v${metadata.version})`);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
logger.info(`Updating Kits: v${metadata.version} → v${latest.version}`);
|
|
136
|
+
// Download and extract to temp
|
|
137
|
+
const tarPath = await downloadRelease(latest);
|
|
138
|
+
const extractDir = getLocalGeminiDir();
|
|
139
|
+
const result = await extractTarGz({
|
|
140
|
+
source: tarPath,
|
|
141
|
+
destination: extractDir,
|
|
142
|
+
strip: 1,
|
|
143
|
+
});
|
|
144
|
+
if (!result.success) {
|
|
145
|
+
logger.error(`Kits update failed: ${result.error}`);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
// Update metadata
|
|
149
|
+
const newMetadata = {
|
|
150
|
+
...metadata,
|
|
151
|
+
version: latest.version,
|
|
152
|
+
installedAt: new Date().toISOString(),
|
|
153
|
+
installedFiles: result.extractedFiles,
|
|
154
|
+
};
|
|
155
|
+
saveMetadata(newMetadata);
|
|
156
|
+
logger.success(`Kits updated to v${latest.version}`);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Check for updates (used by auto-update checker)
|
|
161
|
+
*/
|
|
162
|
+
export async function checkForUpdates() {
|
|
163
|
+
const result = {
|
|
164
|
+
cli: null,
|
|
165
|
+
kits: null,
|
|
166
|
+
};
|
|
167
|
+
// Check CLI
|
|
168
|
+
try {
|
|
169
|
+
const latestCli = await getLatestNpmVersion(CLI_PACKAGE_NAME);
|
|
170
|
+
if (latestCli) {
|
|
171
|
+
result.cli = {
|
|
172
|
+
current: CLI_VERSION,
|
|
173
|
+
latest: latestCli.version,
|
|
174
|
+
available: isUpdateAvailable(CLI_VERSION, latestCli.version),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Ignore errors
|
|
180
|
+
}
|
|
181
|
+
// Check Kits
|
|
182
|
+
try {
|
|
183
|
+
const metadata = loadMetadata();
|
|
184
|
+
const latestKits = await getLatestRelease();
|
|
185
|
+
if (metadata && latestKits) {
|
|
186
|
+
result.kits = {
|
|
187
|
+
current: metadata.version,
|
|
188
|
+
latest: latestKits.version,
|
|
189
|
+
available: isUpdateAvailable(metadata.version, latestKits.version),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// Ignore errors
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
// Export CLI version for use elsewhere
|
|
199
|
+
export { CLI_VERSION };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Versions command - List available versions
|
|
3
|
+
*/
|
|
4
|
+
import { fetchReleases } from '../../domains/github/releases.js';
|
|
5
|
+
import { loadMetadata } from '../../domains/installation/metadata.js';
|
|
6
|
+
import { brand, pc } from '../../utils/colors.js';
|
|
7
|
+
export function registerVersionsCommand(cli) {
|
|
8
|
+
cli
|
|
9
|
+
.command('versions', 'List available GemKit versions')
|
|
10
|
+
.alias('v')
|
|
11
|
+
.option('--json', 'Output as JSON')
|
|
12
|
+
.action(async (options) => {
|
|
13
|
+
const metadata = loadMetadata();
|
|
14
|
+
const releases = await fetchReleases();
|
|
15
|
+
if (options.json) {
|
|
16
|
+
console.log(JSON.stringify({
|
|
17
|
+
current: metadata?.version || null,
|
|
18
|
+
available: releases
|
|
19
|
+
}, null, 2));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(pc.bold(brand.geminiPurple('GemKit Versions')));
|
|
24
|
+
console.log();
|
|
25
|
+
if (metadata) {
|
|
26
|
+
console.log(` ${brand.dim('Current:')} ${brand.success(metadata.version)}`);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.log(` ${brand.dim('Current:')} ${brand.dim('Not installed')}`);
|
|
30
|
+
}
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(` ${pc.bold('Available Releases:')}`);
|
|
33
|
+
for (const release of releases) {
|
|
34
|
+
const marker = release.version === metadata?.version ? brand.success(' (current)') : '';
|
|
35
|
+
console.log(` - ${brand.primary(release.version)}${marker}`);
|
|
36
|
+
}
|
|
37
|
+
console.log();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model and tool mappings between Gemini and Claude CLIs
|
|
3
|
+
*/
|
|
4
|
+
import type { CliProvider } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Map model to target CLI provider
|
|
7
|
+
*/
|
|
8
|
+
export declare function mapModel(model: string, targetProvider: CliProvider): string;
|
|
9
|
+
/**
|
|
10
|
+
* Map a single tool to target CLI provider
|
|
11
|
+
*/
|
|
12
|
+
export declare function mapTool(tool: string, targetProvider: CliProvider): string;
|
|
13
|
+
/**
|
|
14
|
+
* Map array of tools to target CLI provider (deduplicated)
|
|
15
|
+
*/
|
|
16
|
+
export declare function mapTools(tools: string[], targetProvider: CliProvider): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Get agent directory paths with fallback
|
|
19
|
+
* Claude: .claude/agents/ -> .gemini/agents/
|
|
20
|
+
* Gemini: .gemini/agents/ -> .claude/agents/
|
|
21
|
+
*/
|
|
22
|
+
export declare function getAgentPaths(targetProvider: CliProvider): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Get skills directory paths with fallback
|
|
25
|
+
* Claude: .claude/skills/ -> .gemini/extensions/
|
|
26
|
+
* Gemini: .gemini/extensions/ -> .claude/skills/
|
|
27
|
+
*/
|
|
28
|
+
export declare function getSkillPaths(targetProvider: CliProvider): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Get default model for CLI provider
|
|
31
|
+
*/
|
|
32
|
+
export declare function getDefaultModel(provider: CliProvider): string;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model and tool mappings between Gemini and Claude CLIs
|
|
3
|
+
*/
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// MODEL MAPPINGS
|
|
6
|
+
// ============================================================================
|
|
7
|
+
const GEMINI_TO_CLAUDE_MODEL = {
|
|
8
|
+
'gemini-3-pro-preview': 'opus',
|
|
9
|
+
'gemini-2.5-pro': 'opus',
|
|
10
|
+
'gemini-3-flash-preview': 'sonnet',
|
|
11
|
+
'gemini-2.5-flash': 'sonnet',
|
|
12
|
+
'gemini-2.5-flash-lite': 'haiku',
|
|
13
|
+
};
|
|
14
|
+
const CLAUDE_TO_GEMINI_MODEL = {
|
|
15
|
+
'opus': 'gemini-2.5-pro',
|
|
16
|
+
'sonnet': 'gemini-2.5-flash',
|
|
17
|
+
'haiku': 'gemini-2.5-flash-lite',
|
|
18
|
+
};
|
|
19
|
+
const CLAUDE_MODELS = ['haiku', 'sonnet', 'opus'];
|
|
20
|
+
/**
|
|
21
|
+
* Map model to target CLI provider
|
|
22
|
+
*/
|
|
23
|
+
export function mapModel(model, targetProvider) {
|
|
24
|
+
if (targetProvider === 'claude') {
|
|
25
|
+
// Check if already a Claude model
|
|
26
|
+
if (CLAUDE_MODELS.includes(model)) {
|
|
27
|
+
return model;
|
|
28
|
+
}
|
|
29
|
+
return GEMINI_TO_CLAUDE_MODEL[model] || 'sonnet'; // default: sonnet
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Check if already a Gemini model
|
|
33
|
+
if (model.startsWith('gemini-')) {
|
|
34
|
+
return model;
|
|
35
|
+
}
|
|
36
|
+
return CLAUDE_TO_GEMINI_MODEL[model] || 'gemini-2.5-flash'; // default: gemini-2.5-flash
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// TOOL MAPPINGS
|
|
41
|
+
// ============================================================================
|
|
42
|
+
const GEMINI_TO_CLAUDE_TOOL = {
|
|
43
|
+
'list_directory': 'Bash',
|
|
44
|
+
'read_file': 'Read',
|
|
45
|
+
'write_file': 'Write',
|
|
46
|
+
'glob': 'Glob',
|
|
47
|
+
'search_file_content': 'Grep',
|
|
48
|
+
'replace': 'Edit',
|
|
49
|
+
'run_shell_command': 'Bash',
|
|
50
|
+
'web_fetch': 'WebFetch',
|
|
51
|
+
'google_web_search': 'WebSearch',
|
|
52
|
+
'save_memory': 'TodoWrite',
|
|
53
|
+
'write_todos': 'TodoWrite',
|
|
54
|
+
};
|
|
55
|
+
const CLAUDE_TO_GEMINI_TOOL = {
|
|
56
|
+
'Read': 'read_file',
|
|
57
|
+
'Write': 'write_file',
|
|
58
|
+
'Edit': 'replace',
|
|
59
|
+
'Bash': 'run_shell_command',
|
|
60
|
+
'Glob': 'glob',
|
|
61
|
+
'Grep': 'search_file_content',
|
|
62
|
+
'WebFetch': 'web_fetch',
|
|
63
|
+
'WebSearch': 'google_web_search',
|
|
64
|
+
'TodoWrite': 'write_todos',
|
|
65
|
+
'Task': '', // No direct equivalent
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Check if a tool name is a Claude tool (PascalCase)
|
|
69
|
+
*/
|
|
70
|
+
function isClaudeTool(tool) {
|
|
71
|
+
return /^[A-Z]/.test(tool);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if a tool name is a Gemini tool (snake_case or starts with gemini)
|
|
75
|
+
*/
|
|
76
|
+
function isGeminiTool(tool) {
|
|
77
|
+
return tool.includes('_') || tool.startsWith('gemini');
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Map a single tool to target CLI provider
|
|
81
|
+
*/
|
|
82
|
+
export function mapTool(tool, targetProvider) {
|
|
83
|
+
// Extract base tool and permission suffix
|
|
84
|
+
// Gemini: run_shell_command(*) -> base: run_shell_command, suffix: (*)
|
|
85
|
+
// Claude: Bash(git:*) -> base: Bash, suffix: (git:*)
|
|
86
|
+
const match = tool.match(/^([^(]+)(\(.*\))?$/);
|
|
87
|
+
const baseTool = match?.[1] || tool;
|
|
88
|
+
const suffix = match?.[2] || '';
|
|
89
|
+
if (targetProvider === 'claude') {
|
|
90
|
+
// Already a Claude tool
|
|
91
|
+
if (isClaudeTool(baseTool)) {
|
|
92
|
+
return tool;
|
|
93
|
+
}
|
|
94
|
+
const mapped = GEMINI_TO_CLAUDE_TOOL[baseTool];
|
|
95
|
+
if (!mapped)
|
|
96
|
+
return tool; // Keep original if no mapping
|
|
97
|
+
// For Gemini's run_shell_command(*), map to just Bash (Claude handles permissions differently)
|
|
98
|
+
if (baseTool === 'run_shell_command' && suffix === '(*)') {
|
|
99
|
+
return mapped; // Just 'Bash', no suffix
|
|
100
|
+
}
|
|
101
|
+
return mapped + suffix;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Already a Gemini tool
|
|
105
|
+
if (isGeminiTool(baseTool)) {
|
|
106
|
+
return tool;
|
|
107
|
+
}
|
|
108
|
+
const mapped = CLAUDE_TO_GEMINI_TOOL[baseTool];
|
|
109
|
+
if (!mapped)
|
|
110
|
+
return tool; // Keep original if no mapping
|
|
111
|
+
// For Claude's Bash, map to run_shell_command(*)
|
|
112
|
+
if (baseTool === 'Bash' && !suffix) {
|
|
113
|
+
return 'run_shell_command(*)';
|
|
114
|
+
}
|
|
115
|
+
return mapped + suffix;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Map array of tools to target CLI provider (deduplicated)
|
|
120
|
+
*/
|
|
121
|
+
export function mapTools(tools, targetProvider) {
|
|
122
|
+
const mapped = tools
|
|
123
|
+
.map(t => mapTool(t, targetProvider))
|
|
124
|
+
.filter(Boolean);
|
|
125
|
+
return [...new Set(mapped)]; // Deduplicate
|
|
126
|
+
}
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// FOLDER PATHS
|
|
129
|
+
// ============================================================================
|
|
130
|
+
/**
|
|
131
|
+
* Get agent directory paths with fallback
|
|
132
|
+
* Claude: .claude/agents/ -> .gemini/agents/
|
|
133
|
+
* Gemini: .gemini/agents/ -> .claude/agents/
|
|
134
|
+
*/
|
|
135
|
+
export function getAgentPaths(targetProvider) {
|
|
136
|
+
if (targetProvider === 'claude') {
|
|
137
|
+
return ['.claude/agents', '.gemini/agents'];
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
return ['.gemini/agents', '.claude/agents'];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get skills directory paths with fallback
|
|
145
|
+
* Claude: .claude/skills/ -> .gemini/extensions/
|
|
146
|
+
* Gemini: .gemini/extensions/ -> .claude/skills/
|
|
147
|
+
*/
|
|
148
|
+
export function getSkillPaths(targetProvider) {
|
|
149
|
+
if (targetProvider === 'claude') {
|
|
150
|
+
return ['.claude/skills', '.gemini/extensions'];
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
return ['.gemini/extensions', '.claude/skills'];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// DEFAULT MODELS
|
|
158
|
+
// ============================================================================
|
|
159
|
+
/**
|
|
160
|
+
* Get default model for CLI provider
|
|
161
|
+
*/
|
|
162
|
+
export function getDefaultModel(provider) {
|
|
163
|
+
return provider === 'claude' ? 'sonnet' : 'gemini-2.5-flash';
|
|
164
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent profile loading
|
|
3
|
+
*/
|
|
4
|
+
import { AgentProfile, CliProvider } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Load a single agent profile by name
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadAgentProfile(name: string, projectDir?: string): AgentProfile | null;
|
|
9
|
+
/**
|
|
10
|
+
* List all available agent profiles
|
|
11
|
+
*/
|
|
12
|
+
export declare function listAgentProfiles(projectDir?: string): AgentProfile[];
|
|
13
|
+
/**
|
|
14
|
+
* Get agent profile details as formatted string
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatAgentProfile(profile: AgentProfile): string;
|
|
17
|
+
/**
|
|
18
|
+
* Load agent profile with fallback between providers
|
|
19
|
+
* Tries primary provider's folder first, then falls back to the other provider's folder
|
|
20
|
+
* Maps model and tools to target provider when using fallback
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadAgentProfileWithFallback(name: string, targetProvider: CliProvider, projectDir?: string): AgentProfile | null;
|
|
23
|
+
/**
|
|
24
|
+
* List all available agent profiles with fallback between providers
|
|
25
|
+
*/
|
|
26
|
+
export declare function listAgentProfilesWithFallback(targetProvider: CliProvider, projectDir?: string): AgentProfile[];
|