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,76 @@
1
+ /**
2
+ * AIPTY - Cross-platform PTY wrapper for Claude Code and Gemini CLI
3
+ * Ported from claude-pty-wrapper/index.js
4
+ */
5
+ import { EventEmitter } from 'events';
6
+ import type { CliProvider } from '../domains/agent/types.js';
7
+ export interface AIPTYOptions {
8
+ provider: CliProvider;
9
+ model?: string;
10
+ tools?: string[];
11
+ cwd?: string;
12
+ debug?: boolean;
13
+ }
14
+ export declare class AIPTY extends EventEmitter {
15
+ private process;
16
+ private output;
17
+ private outputBuffer;
18
+ private isReady;
19
+ private provider;
20
+ private config;
21
+ private model;
22
+ private tools;
23
+ private cwd;
24
+ private debug;
25
+ constructor(options: AIPTYOptions);
26
+ /**
27
+ * Start AI CLI session
28
+ */
29
+ start(): Promise<void>;
30
+ /**
31
+ * Send text (raw write)
32
+ */
33
+ write(text: string): void;
34
+ /**
35
+ * Send a prompt and wait for response
36
+ */
37
+ send(prompt: string, options?: {
38
+ timeout?: number;
39
+ }): Promise<string>;
40
+ /**
41
+ * Extract the last answer from output
42
+ */
43
+ extractLastAnswer(): string;
44
+ /**
45
+ * Extract answer from Claude output
46
+ */
47
+ private extractAnswerClaude;
48
+ /**
49
+ * Extract answer from Gemini output
50
+ */
51
+ private extractAnswerGemini;
52
+ /**
53
+ * Get raw output
54
+ */
55
+ getOutput(lines?: number): string;
56
+ /**
57
+ * Get full output
58
+ */
59
+ getFullOutput(): string;
60
+ /**
61
+ * Check if session is running
62
+ */
63
+ isRunning(): boolean;
64
+ /**
65
+ * Check if session is ready
66
+ */
67
+ getIsReady(): boolean;
68
+ /**
69
+ * Get provider
70
+ */
71
+ getProvider(): CliProvider;
72
+ /**
73
+ * Stop the session
74
+ */
75
+ stop(): Promise<void>;
76
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * AIPTY - Cross-platform PTY wrapper for Claude Code and Gemini CLI
3
+ * Ported from claude-pty-wrapper/index.js
4
+ */
5
+ import * as pty from 'node-pty';
6
+ import { EventEmitter } from 'events';
7
+ import { PTY_PROVIDERS, buildPtyArgs } from '../domains/agent/pty-providers.js';
8
+ export class AIPTY extends EventEmitter {
9
+ process = null;
10
+ output = '';
11
+ outputBuffer = [];
12
+ isReady = false;
13
+ provider;
14
+ config;
15
+ model;
16
+ tools;
17
+ cwd;
18
+ debug;
19
+ constructor(options) {
20
+ super();
21
+ this.provider = options.provider;
22
+ this.config = PTY_PROVIDERS[options.provider];
23
+ if (!this.config) {
24
+ throw new Error(`Unknown provider: ${options.provider}. Use 'claude' or 'gemini'`);
25
+ }
26
+ this.model = options.model || '';
27
+ this.tools = options.tools || [];
28
+ this.cwd = options.cwd || process.cwd();
29
+ this.debug = options.debug || false;
30
+ }
31
+ /**
32
+ * Start AI CLI session
33
+ */
34
+ start() {
35
+ return new Promise((resolve, reject) => {
36
+ if (this.process) {
37
+ reject(new Error('Session already running'));
38
+ return;
39
+ }
40
+ const command = this.config.command;
41
+ const args = buildPtyArgs(this.provider, this.model, this.tools);
42
+ if (this.debug) {
43
+ console.log(`[DEBUG] Spawning: ${command} ${args.join(' ')}`);
44
+ }
45
+ try {
46
+ this.process = pty.spawn(command, args, {
47
+ name: 'xterm-256color',
48
+ cols: 120,
49
+ rows: 40,
50
+ cwd: this.cwd,
51
+ env: { ...process.env, TERM: 'xterm-256color' }
52
+ });
53
+ }
54
+ catch (err) {
55
+ reject(new Error(`Failed to spawn ${command}: ${err.message}`));
56
+ return;
57
+ }
58
+ this.output = '';
59
+ this.outputBuffer = [];
60
+ this.process.onData((data) => {
61
+ this.output += data;
62
+ this.outputBuffer.push({ time: Date.now(), data });
63
+ if (this.debug) {
64
+ process.stdout.write(data);
65
+ }
66
+ this.emit('data', data);
67
+ // Check if ready (prompt appeared)
68
+ if (!this.isReady && this.output.includes(this.config.readyIndicator)) {
69
+ this.isReady = true;
70
+ this.emit('ready');
71
+ }
72
+ });
73
+ this.process.onExit(({ exitCode }) => {
74
+ this.isReady = false;
75
+ this.process = null;
76
+ this.emit('exit', exitCode);
77
+ });
78
+ // Wait for ready or timeout
79
+ const timeout = setTimeout(() => {
80
+ if (!this.isReady) {
81
+ // Check if trust prompt appeared (Claude specific)
82
+ if (this.output.includes('Yes, I trust')) {
83
+ this.write('1'); // Auto-trust
84
+ setTimeout(() => {
85
+ if (this.isReady)
86
+ resolve();
87
+ else
88
+ reject(new Error(`Timeout waiting for ${this.config.name} to be ready`));
89
+ }, 5000);
90
+ }
91
+ else {
92
+ reject(new Error(`Timeout waiting for ${this.config.name} to start`));
93
+ }
94
+ }
95
+ }, 60000); // 60s timeout for npx downloads
96
+ this.once('ready', () => {
97
+ clearTimeout(timeout);
98
+ resolve();
99
+ });
100
+ });
101
+ }
102
+ /**
103
+ * Send text (raw write)
104
+ */
105
+ write(text) {
106
+ if (!this.process) {
107
+ throw new Error('No active session');
108
+ }
109
+ this.process.write(text);
110
+ }
111
+ /**
112
+ * Send a prompt and wait for response
113
+ */
114
+ send(prompt, options = {}) {
115
+ return new Promise((resolve, reject) => {
116
+ if (!this.process || !this.isReady) {
117
+ reject(new Error('No active session or not ready'));
118
+ return;
119
+ }
120
+ const timeout = options.timeout || 120000;
121
+ const responseStart = this.output.length;
122
+ this.write(prompt);
123
+ setTimeout(() => {
124
+ this.write('\r');
125
+ if (this.debug) {
126
+ console.log('\n[DEBUG] Prompt sent, waiting for response...');
127
+ }
128
+ }, 500);
129
+ const checkInterval = setInterval(() => {
130
+ const newOutput = this.output.slice(responseStart);
131
+ // Check completion based on provider
132
+ let isComplete = false;
133
+ if (this.provider === 'claude') {
134
+ const hasResponse = newOutput.includes('●');
135
+ const promptAfterResponse = newOutput.lastIndexOf('❯') > newOutput.lastIndexOf('●');
136
+ isComplete = hasResponse && promptAfterResponse;
137
+ }
138
+ else if (this.provider === 'gemini') {
139
+ // Gemini: look for response block then new prompt
140
+ const hasResponse = newOutput.includes('✦') && newOutput.length > 100;
141
+ isComplete = hasResponse;
142
+ }
143
+ if (isComplete) {
144
+ clearInterval(checkInterval);
145
+ clearTimeout(timeoutId);
146
+ const answer = this.extractLastAnswer();
147
+ resolve(answer);
148
+ }
149
+ }, 500);
150
+ const timeoutId = setTimeout(() => {
151
+ clearInterval(checkInterval);
152
+ reject(new Error('Timeout waiting for response'));
153
+ }, timeout);
154
+ });
155
+ }
156
+ /**
157
+ * Extract the last answer from output
158
+ */
159
+ extractLastAnswer() {
160
+ const cleanOutput = this.output
161
+ .replace(/\x1b\[1C/g, ' ')
162
+ .replace(/\x1b\[[0-9;]*[A-Za-z]/g, '')
163
+ .replace(/\x1b\][^\x07]*\x07/g, '')
164
+ .replace(/\r/g, '');
165
+ if (this.provider === 'claude') {
166
+ return this.extractAnswerClaude(cleanOutput);
167
+ }
168
+ else if (this.provider === 'gemini') {
169
+ return this.extractAnswerGemini(cleanOutput);
170
+ }
171
+ return '';
172
+ }
173
+ /**
174
+ * Extract answer from Claude output
175
+ */
176
+ extractAnswerClaude(cleanOutput) {
177
+ const matches = cleanOutput.match(/●\s*([\s\S]*?)(?=❯|$)/g);
178
+ if (matches && matches.length > 0) {
179
+ let lastMatch = matches[matches.length - 1];
180
+ lastMatch = lastMatch
181
+ .replace(/^●\s*/, '')
182
+ .replace(/[─╭╰│┌┐└┘├┤┬┴┼]+/g, '')
183
+ .replace(/\? for shortcuts/g, '')
184
+ .replace(/esc to interrupt/g, '')
185
+ .replace(/Churning…/g, '')
186
+ .replace(/\(thinking\)/g, '')
187
+ .trim();
188
+ const lines = lastMatch.split('\n')
189
+ .map(l => l.trim())
190
+ .filter(l => {
191
+ if (l.length === 0)
192
+ return false;
193
+ if (l.match(/^[\s─╭╰│┌┐└┘├┤┬┴┼✢✶✻✽·*]+$/))
194
+ return false;
195
+ if (l.match(/Cascading|Churning|thinking|thought for/i))
196
+ return false;
197
+ if (l.match(/^\d+s\)$/))
198
+ return false;
199
+ return true;
200
+ });
201
+ return lines.join('\n').trim();
202
+ }
203
+ return '';
204
+ }
205
+ /**
206
+ * Extract answer from Gemini output
207
+ */
208
+ extractAnswerGemini(cleanOutput) {
209
+ const lines = cleanOutput.split('\n');
210
+ let inResponse = false;
211
+ const response = [];
212
+ for (const line of lines) {
213
+ const trimmed = line.trim();
214
+ // Skip UI elements
215
+ if (trimmed.match(/^[┃┏┓┗┛━─╭╮╯╰│]+$/) ||
216
+ trimmed.match(/^Gemini/i) ||
217
+ trimmed.match(/^Type .* to/i)) {
218
+ continue;
219
+ }
220
+ if (trimmed.length > 0 && !trimmed.match(/^>/)) {
221
+ response.push(trimmed);
222
+ }
223
+ }
224
+ return response.join('\n').trim();
225
+ }
226
+ /**
227
+ * Get raw output
228
+ */
229
+ getOutput(lines = 100) {
230
+ const allLines = this.output.split('\n');
231
+ return allLines.slice(-lines).join('\n');
232
+ }
233
+ /**
234
+ * Get full output
235
+ */
236
+ getFullOutput() {
237
+ return this.output;
238
+ }
239
+ /**
240
+ * Check if session is running
241
+ */
242
+ isRunning() {
243
+ return this.process !== null && this.isReady;
244
+ }
245
+ /**
246
+ * Check if session is ready
247
+ */
248
+ getIsReady() {
249
+ return this.isReady;
250
+ }
251
+ /**
252
+ * Get provider
253
+ */
254
+ getProvider() {
255
+ return this.provider;
256
+ }
257
+ /**
258
+ * Stop the session
259
+ */
260
+ stop() {
261
+ return new Promise((resolve) => {
262
+ if (!this.process) {
263
+ resolve();
264
+ return;
265
+ }
266
+ this.once('exit', () => resolve());
267
+ this.write(this.config.exitCommand);
268
+ setTimeout(() => {
269
+ if (this.process) {
270
+ this.process.kill();
271
+ }
272
+ resolve();
273
+ }, 3000);
274
+ });
275
+ }
276
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Archive extraction service for .tar.gz files
3
+ */
4
+ export interface ExtractOptions {
5
+ source: string;
6
+ destination: string;
7
+ strip?: number;
8
+ filter?: (path: string) => boolean;
9
+ }
10
+ export interface ExtractResult {
11
+ success: boolean;
12
+ extractedFiles: string[];
13
+ error?: string;
14
+ }
15
+ /**
16
+ * Extract a .tar.gz archive
17
+ */
18
+ export declare function extractTarGz(options: ExtractOptions): Promise<ExtractResult>;
19
+ /**
20
+ * Simple file copy for non-archive files
21
+ */
22
+ export declare function copyFile(source: string, destination: string): Promise<void>;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Archive extraction service for .tar.gz files
3
+ */
4
+ import { createReadStream, createWriteStream, existsSync, mkdirSync } from 'fs';
5
+ import { pipeline } from 'stream/promises';
6
+ import { createGunzip } from 'zlib';
7
+ import { dirname } from 'path';
8
+ import * as tar from 'tar';
9
+ /**
10
+ * Extract a .tar.gz archive
11
+ */
12
+ export async function extractTarGz(options) {
13
+ const { source, destination, strip = 1, filter } = options;
14
+ const extractedFiles = [];
15
+ if (!existsSync(source)) {
16
+ return { success: false, extractedFiles: [], error: `Source file not found: ${source}` };
17
+ }
18
+ // Ensure destination exists
19
+ if (!existsSync(destination)) {
20
+ mkdirSync(destination, { recursive: true });
21
+ }
22
+ try {
23
+ await pipeline(createReadStream(source), createGunzip(), tar.x({
24
+ cwd: destination,
25
+ strip,
26
+ filter: (path) => {
27
+ if (filter && !filter(path)) {
28
+ return false;
29
+ }
30
+ extractedFiles.push(path);
31
+ return true;
32
+ },
33
+ }));
34
+ return { success: true, extractedFiles };
35
+ }
36
+ catch (error) {
37
+ return {
38
+ success: false,
39
+ extractedFiles,
40
+ error: error instanceof Error ? error.message : String(error),
41
+ };
42
+ }
43
+ }
44
+ /**
45
+ * Simple file copy for non-archive files
46
+ */
47
+ export async function copyFile(source, destination) {
48
+ const destDir = dirname(destination);
49
+ if (!existsSync(destDir)) {
50
+ mkdirSync(destDir, { recursive: true });
51
+ }
52
+ await pipeline(createReadStream(source), createWriteStream(destination));
53
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Auto-update checker service
3
+ *
4
+ * Checks for updates on CLI startup based on config settings.
5
+ * Uses a timestamp file to track last check time.
6
+ */
7
+ /**
8
+ * Run auto-update check on startup
9
+ * This runs in the background and only shows notifications
10
+ */
11
+ export declare function runAutoUpdateCheck(): Promise<void>;
12
+ /**
13
+ * Force check for updates (ignores interval)
14
+ */
15
+ export declare function forceUpdateCheck(): Promise<{
16
+ cli: {
17
+ current: string;
18
+ latest: string;
19
+ available: boolean;
20
+ } | null;
21
+ kits: {
22
+ current: string;
23
+ latest: string;
24
+ available: boolean;
25
+ } | null;
26
+ }>;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Auto-update checker service
3
+ *
4
+ * Checks for updates on CLI startup based on config settings.
5
+ * Uses a timestamp file to track last check time.
6
+ */
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
8
+ import { join, dirname } from 'path';
9
+ import { homedir } from 'os';
10
+ import { loadConfig } from '../domains/config/manager.js';
11
+ import { checkForUpdates } from '../commands/update/index.js';
12
+ import { brand } from '../utils/colors.js';
13
+ // File to track last update check
14
+ const UPDATE_CHECK_FILE = join(homedir(), '.gemkit', 'last-update-check.json');
15
+ /**
16
+ * Get last update check state
17
+ */
18
+ function getLastCheckState() {
19
+ try {
20
+ if (!existsSync(UPDATE_CHECK_FILE)) {
21
+ return null;
22
+ }
23
+ return JSON.parse(readFileSync(UPDATE_CHECK_FILE, 'utf-8'));
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ /**
30
+ * Save update check state
31
+ */
32
+ function saveCheckState(state) {
33
+ try {
34
+ const dir = dirname(UPDATE_CHECK_FILE);
35
+ if (!existsSync(dir)) {
36
+ mkdirSync(dir, { recursive: true });
37
+ }
38
+ writeFileSync(UPDATE_CHECK_FILE, JSON.stringify(state, null, 2));
39
+ }
40
+ catch {
41
+ // Ignore errors
42
+ }
43
+ }
44
+ /**
45
+ * Check if enough time has passed since last check
46
+ */
47
+ function shouldCheck(intervalHours) {
48
+ const state = getLastCheckState();
49
+ if (!state) {
50
+ return true;
51
+ }
52
+ const lastCheck = new Date(state.lastCheck);
53
+ const now = new Date();
54
+ const hoursSinceCheck = (now.getTime() - lastCheck.getTime()) / (1000 * 60 * 60);
55
+ return hoursSinceCheck >= intervalHours;
56
+ }
57
+ /**
58
+ * Run auto-update check on startup
59
+ * This runs in the background and only shows notifications
60
+ */
61
+ export async function runAutoUpdateCheck() {
62
+ const config = loadConfig();
63
+ const updateConfig = config.update;
64
+ // Skip if auto-check is disabled
65
+ if (!updateConfig?.autoCheck) {
66
+ return;
67
+ }
68
+ // Skip if not enough time has passed
69
+ if (!shouldCheck(updateConfig.checkInterval)) {
70
+ return;
71
+ }
72
+ try {
73
+ const updates = await checkForUpdates();
74
+ const state = getLastCheckState() || { lastCheck: '' };
75
+ const notifications = [];
76
+ // Check CLI updates
77
+ if (updates.cli?.available && state.notifiedCli !== updates.cli.latest) {
78
+ notifications.push(`${brand.primary('CLI')} update available: v${updates.cli.current} → v${updates.cli.latest}`);
79
+ state.notifiedCli = updates.cli.latest;
80
+ }
81
+ // Check Kits updates
82
+ if (updates.kits?.available && state.notifiedKits !== updates.kits.latest) {
83
+ notifications.push(`${brand.primary('Kits')} update available: v${updates.kits.current} → v${updates.kits.latest}`);
84
+ state.notifiedKits = updates.kits.latest;
85
+ }
86
+ // Show notifications
87
+ if (notifications.length > 0) {
88
+ console.log();
89
+ console.log(brand.dim('─'.repeat(50)));
90
+ console.log(`${brand.warn('⬆')} Updates available:`);
91
+ notifications.forEach((n) => console.log(` ${n}`));
92
+ console.log(` Run ${brand.primary('gk update')} to update.`);
93
+ console.log(brand.dim('─'.repeat(50)));
94
+ console.log();
95
+ }
96
+ // Save state
97
+ state.lastCheck = new Date().toISOString();
98
+ saveCheckState(state);
99
+ }
100
+ catch {
101
+ // Silently fail - don't interrupt user workflow
102
+ }
103
+ }
104
+ /**
105
+ * Force check for updates (ignores interval)
106
+ */
107
+ export async function forceUpdateCheck() {
108
+ const updates = await checkForUpdates();
109
+ // Update state
110
+ const state = {
111
+ lastCheck: new Date().toISOString(),
112
+ notifiedCli: updates.cli?.latest,
113
+ notifiedKits: updates.kits?.latest,
114
+ };
115
+ saveCheckState(state);
116
+ return updates;
117
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Hashing utilities for project identification
3
+ */
4
+ /**
5
+ * Generate SHA256 hash of a string
6
+ */
7
+ export declare function sha256(input: string): string;
8
+ /**
9
+ * Generate short hash (first 8 characters)
10
+ */
11
+ export declare function shortHash(input: string): string;
12
+ /**
13
+ * Generate project hash from directory path
14
+ */
15
+ export declare function generateProjectHash(projectDir: string): string;
16
+ /**
17
+ * Generate file hash for change detection
18
+ */
19
+ export declare function hashFile(filePath: string): string | null;
20
+ /**
21
+ * Generate hash from multiple inputs
22
+ */
23
+ export declare function combineHash(...inputs: string[]): string;
24
+ /**
25
+ * Generate unique ID with timestamp
26
+ */
27
+ export declare function generateUniqueId(prefix?: string): string;
28
+ /**
29
+ * Generate gkSessionId - consistent format for ALL clients
30
+ * Matches gk-session-manager.cjs generateGkSessionId()
31
+ *
32
+ * @param appName - Application identifier (e.g., 'gemini-main', 'vscode')
33
+ * @param pid - Process ID to embed in the session ID
34
+ * @returns Format: "{appName}-{PID}-{timestamp36}-{random4}"
35
+ */
36
+ export declare function generateGkSessionId(appName: string, pid: number): string;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Hashing utilities for project identification
3
+ */
4
+ import { createHash } from 'crypto';
5
+ import { existsSync, readFileSync } from 'fs';
6
+ /**
7
+ * Generate SHA256 hash of a string
8
+ */
9
+ export function sha256(input) {
10
+ return createHash('sha256').update(input).digest('hex');
11
+ }
12
+ /**
13
+ * Generate short hash (first 8 characters)
14
+ */
15
+ export function shortHash(input) {
16
+ return sha256(input).substring(0, 8);
17
+ }
18
+ /**
19
+ * Generate project hash from directory path
20
+ */
21
+ export function generateProjectHash(projectDir) {
22
+ // Normalize path for consistent hashing
23
+ const normalized = projectDir.replace(/\\/g, '/').toLowerCase();
24
+ return shortHash(normalized);
25
+ }
26
+ /**
27
+ * Generate file hash for change detection
28
+ */
29
+ export function hashFile(filePath) {
30
+ if (!existsSync(filePath)) {
31
+ return null;
32
+ }
33
+ const content = readFileSync(filePath);
34
+ return createHash('sha256').update(content).digest('hex');
35
+ }
36
+ /**
37
+ * Generate hash from multiple inputs
38
+ */
39
+ export function combineHash(...inputs) {
40
+ const combined = inputs.join(':');
41
+ return sha256(combined);
42
+ }
43
+ /**
44
+ * Generate unique ID with timestamp
45
+ */
46
+ export function generateUniqueId(prefix = '') {
47
+ const timestamp = Date.now().toString(36);
48
+ const random = Math.random().toString(36).substring(2, 8);
49
+ return prefix ? `${prefix}-${timestamp}-${random}` : `${timestamp}-${random}`;
50
+ }
51
+ /**
52
+ * Generate gkSessionId - consistent format for ALL clients
53
+ * Matches gk-session-manager.cjs generateGkSessionId()
54
+ *
55
+ * @param appName - Application identifier (e.g., 'gemini-main', 'vscode')
56
+ * @param pid - Process ID to embed in the session ID
57
+ * @returns Format: "{appName}-{PID}-{timestamp36}-{random4}"
58
+ */
59
+ export function generateGkSessionId(appName, pid) {
60
+ const timestamp = Date.now().toString(36);
61
+ const random = Math.random().toString(36).substring(2, 6);
62
+ return `${appName}-${pid}-${timestamp}-${random}`;
63
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Logging service for GemKit CLI
3
+ */
4
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
5
+ interface LoggerConfig {
6
+ level: LogLevel;
7
+ verbose: boolean;
8
+ json: boolean;
9
+ }
10
+ export declare function configureLogger(options: Partial<LoggerConfig>): void;
11
+ export declare function debug(message: string, data?: unknown): void;
12
+ export declare function info(message: string, data?: unknown): void;
13
+ export declare function success(message: string, data?: unknown): void;
14
+ export declare function warn(message: string, data?: unknown): void;
15
+ export declare function error(message: string, data?: unknown): void;
16
+ export declare function table(data: Record<string, unknown>[]): void;
17
+ export declare function json(data: unknown): void;
18
+ export declare const logger: {
19
+ debug: typeof debug;
20
+ info: typeof info;
21
+ success: typeof success;
22
+ warn: typeof warn;
23
+ error: typeof error;
24
+ table: typeof table;
25
+ json: typeof json;
26
+ configure: typeof configureLogger;
27
+ };
28
+ export {};