gemkit-cli 0.2.3 → 0.3.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.
Files changed (160) hide show
  1. package/README.md +141 -7
  2. package/dist/commands/agent/index.d.ts +9 -0
  3. package/dist/commands/agent/index.js +1329 -0
  4. package/dist/commands/cache/index.d.ts +5 -0
  5. package/dist/commands/cache/index.js +43 -0
  6. package/dist/commands/catalog/index.d.ts +2 -0
  7. package/dist/commands/catalog/index.js +57 -0
  8. package/dist/commands/config/index.d.ts +7 -0
  9. package/dist/commands/config/index.js +122 -0
  10. package/dist/commands/convert/index.d.ts +8 -0
  11. package/dist/commands/convert/index.js +391 -0
  12. package/dist/commands/doctor/index.d.ts +2 -0
  13. package/dist/commands/doctor/index.js +243 -0
  14. package/dist/commands/extension/index.d.ts +5 -0
  15. package/dist/commands/extension/index.js +52 -0
  16. package/dist/commands/index.d.ts +5 -0
  17. package/dist/commands/index.js +37 -0
  18. package/dist/commands/init/index.d.ts +6 -0
  19. package/dist/commands/init/index.js +345 -0
  20. package/dist/commands/new/index.d.ts +5 -0
  21. package/dist/commands/new/index.js +49 -0
  22. package/dist/commands/office/index.d.ts +5 -0
  23. package/dist/commands/office/index.js +283 -0
  24. package/dist/commands/paste/index.d.ts +10 -0
  25. package/dist/commands/paste/index.js +533 -0
  26. package/dist/commands/plan/index.d.ts +8 -0
  27. package/dist/commands/plan/index.js +247 -0
  28. package/dist/commands/session/index.d.ts +8 -0
  29. package/dist/commands/session/index.js +289 -0
  30. package/dist/commands/tokens/index.d.ts +6 -0
  31. package/dist/commands/tokens/index.js +148 -0
  32. package/dist/commands/update/index.d.ts +26 -0
  33. package/dist/commands/update/index.js +199 -0
  34. package/dist/commands/versions/index.d.ts +5 -0
  35. package/dist/commands/versions/index.js +39 -0
  36. package/dist/domains/agent/index.d.ts +8 -0
  37. package/dist/domains/agent/index.js +8 -0
  38. package/dist/domains/agent/mappings.d.ts +32 -0
  39. package/dist/domains/agent/mappings.js +164 -0
  40. package/dist/domains/agent/profile.d.ts +26 -0
  41. package/dist/domains/agent/profile.js +225 -0
  42. package/dist/domains/agent/pty-context.d.ts +11 -0
  43. package/dist/domains/agent/pty-context.js +83 -0
  44. package/dist/domains/agent/pty-providers.d.ts +18 -0
  45. package/dist/domains/agent/pty-providers.js +66 -0
  46. package/dist/domains/agent/pty-session.d.ts +33 -0
  47. package/dist/domains/agent/pty-session.js +82 -0
  48. package/dist/domains/agent/pty-types.d.ts +127 -0
  49. package/dist/domains/agent/pty-types.js +4 -0
  50. package/dist/domains/agent/search.d.ts +45 -0
  51. package/dist/domains/agent/search.js +614 -0
  52. package/dist/domains/agent/types.d.ts +78 -0
  53. package/dist/domains/agent/types.js +5 -0
  54. package/dist/domains/agent-office/documents-scanner.d.ts +9 -0
  55. package/dist/domains/agent-office/documents-scanner.js +143 -0
  56. package/dist/domains/agent-office/event-emitter.d.ts +43 -0
  57. package/dist/domains/agent-office/event-emitter.js +86 -0
  58. package/dist/domains/agent-office/file-watcher.d.ts +40 -0
  59. package/dist/domains/agent-office/file-watcher.js +173 -0
  60. package/dist/domains/agent-office/icons.d.ts +11 -0
  61. package/dist/domains/agent-office/icons.js +36 -0
  62. package/dist/domains/agent-office/index.d.ts +12 -0
  63. package/dist/domains/agent-office/index.js +20 -0
  64. package/dist/domains/agent-office/renderer/web/assets.d.ts +11 -0
  65. package/dist/domains/agent-office/renderer/web/assets.js +3419 -0
  66. package/dist/domains/agent-office/renderer/web/server.d.ts +42 -0
  67. package/dist/domains/agent-office/renderer/web/server.js +228 -0
  68. package/dist/domains/agent-office/renderer/web.d.ts +30 -0
  69. package/dist/domains/agent-office/renderer/web.js +111 -0
  70. package/dist/domains/agent-office/session-bridge.d.ts +23 -0
  71. package/dist/domains/agent-office/session-bridge.js +171 -0
  72. package/dist/domains/agent-office/state-machine.d.ts +5 -0
  73. package/dist/domains/agent-office/state-machine.js +82 -0
  74. package/dist/domains/agent-office/types.d.ts +91 -0
  75. package/dist/domains/agent-office/types.js +4 -0
  76. package/dist/domains/cache/index.d.ts +1 -0
  77. package/dist/domains/cache/index.js +1 -0
  78. package/dist/domains/cache/manager.d.ts +22 -0
  79. package/dist/domains/cache/manager.js +84 -0
  80. package/dist/domains/config/index.d.ts +5 -0
  81. package/dist/domains/config/index.js +5 -0
  82. package/dist/domains/config/manager.d.ts +24 -0
  83. package/dist/domains/config/manager.js +85 -0
  84. package/dist/domains/config/schema.d.ts +17 -0
  85. package/dist/domains/config/schema.js +96 -0
  86. package/dist/domains/convert/converter.d.ts +78 -0
  87. package/dist/domains/convert/converter.js +471 -0
  88. package/dist/domains/convert/index.d.ts +5 -0
  89. package/dist/domains/convert/index.js +5 -0
  90. package/dist/domains/convert/types.d.ts +88 -0
  91. package/dist/domains/convert/types.js +18 -0
  92. package/dist/domains/github/download.d.ts +12 -0
  93. package/dist/domains/github/download.js +51 -0
  94. package/dist/domains/github/index.d.ts +2 -0
  95. package/dist/domains/github/index.js +2 -0
  96. package/dist/domains/github/releases.d.ts +16 -0
  97. package/dist/domains/github/releases.js +68 -0
  98. package/dist/domains/installation/conflict.d.ts +13 -0
  99. package/dist/domains/installation/conflict.js +38 -0
  100. package/dist/domains/installation/file-sync.d.ts +16 -0
  101. package/dist/domains/installation/file-sync.js +77 -0
  102. package/dist/domains/installation/index.d.ts +3 -0
  103. package/dist/domains/installation/index.js +3 -0
  104. package/dist/domains/installation/metadata.d.ts +20 -0
  105. package/dist/domains/installation/metadata.js +52 -0
  106. package/dist/domains/plan/index.d.ts +2 -0
  107. package/dist/domains/plan/index.js +2 -0
  108. package/dist/domains/plan/resolver.d.ts +24 -0
  109. package/dist/domains/plan/resolver.js +164 -0
  110. package/dist/domains/plan/types.d.ts +13 -0
  111. package/dist/domains/plan/types.js +4 -0
  112. package/dist/domains/session/env.d.ts +51 -0
  113. package/dist/domains/session/env.js +118 -0
  114. package/dist/domains/session/index.d.ts +8 -0
  115. package/dist/domains/session/index.js +8 -0
  116. package/dist/domains/session/manager.d.ts +56 -0
  117. package/dist/domains/session/manager.js +205 -0
  118. package/dist/domains/session/paths.d.ts +6 -0
  119. package/dist/domains/session/paths.js +6 -0
  120. package/dist/domains/session/types.d.ts +121 -0
  121. package/dist/domains/session/types.js +5 -0
  122. package/dist/domains/session/writer.d.ts +82 -0
  123. package/dist/domains/session/writer.js +431 -0
  124. package/dist/domains/tokens/index.d.ts +5 -0
  125. package/dist/domains/tokens/index.js +5 -0
  126. package/dist/domains/tokens/pricing.d.ts +38 -0
  127. package/dist/domains/tokens/pricing.js +129 -0
  128. package/dist/domains/tokens/scanner.d.ts +42 -0
  129. package/dist/domains/tokens/scanner.js +168 -0
  130. package/dist/index.d.ts +5 -0
  131. package/dist/index.js +90 -59
  132. package/dist/services/aipty.d.ts +76 -0
  133. package/dist/services/aipty.js +276 -0
  134. package/dist/services/archive.d.ts +22 -0
  135. package/dist/services/archive.js +53 -0
  136. package/dist/services/auto-update.d.ts +26 -0
  137. package/dist/services/auto-update.js +117 -0
  138. package/dist/services/hash.d.ts +36 -0
  139. package/dist/services/hash.js +63 -0
  140. package/dist/services/logger.d.ts +28 -0
  141. package/dist/services/logger.js +102 -0
  142. package/dist/services/music.d.ts +67 -0
  143. package/dist/services/music.js +290 -0
  144. package/dist/services/npm.d.ts +22 -0
  145. package/dist/services/npm.js +65 -0
  146. package/dist/services/pty-client.d.ts +66 -0
  147. package/dist/services/pty-client.js +154 -0
  148. package/dist/services/pty-server.d.ts +102 -0
  149. package/dist/services/pty-server.js +613 -0
  150. package/dist/types/index.d.ts +155 -0
  151. package/dist/types/index.js +4 -0
  152. package/dist/utils/colors.d.ts +43 -0
  153. package/dist/utils/colors.js +98 -0
  154. package/dist/utils/errors.d.ts +24 -0
  155. package/dist/utils/errors.js +56 -0
  156. package/dist/utils/paths.d.ts +46 -0
  157. package/dist/utils/paths.js +89 -0
  158. package/dist/utils/platform.d.ts +11 -0
  159. package/dist/utils/platform.js +31 -0
  160. 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,5 @@
1
+ /**
2
+ * Versions command - List available versions
3
+ */
4
+ import type { CAC } from 'cac';
5
+ export declare function registerVersionsCommand(cli: CAC): void;
@@ -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,8 @@
1
+ /**
2
+ * Agent domain exports
3
+ * NO spawner, NO routing, NO activity exports
4
+ */
5
+ export * from './types.js';
6
+ export * from './profile.js';
7
+ export * from './search.js';
8
+ export * from './mappings.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Agent domain exports
3
+ * NO spawner, NO routing, NO activity exports
4
+ */
5
+ export * from './types.js';
6
+ export * from './profile.js';
7
+ export * from './search.js';
8
+ export * from './mappings.js';
@@ -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[];