gemkit-cli 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +152 -5
  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 +86 -57
  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,68 @@
1
+ /**
2
+ * GitHub releases API - Public repos only, no auth
3
+ */
4
+ import { loadConfig } from '../config/manager.js';
5
+ import { getCache, setCache } from '../cache/manager.js';
6
+ import { GitHubError } from '../../utils/errors.js';
7
+ const CACHE_KEY = 'github-releases';
8
+ const CACHE_TTL = 300; // 5 minutes
9
+ /**
10
+ * Fetch releases from GitHub
11
+ */
12
+ export async function fetchReleases(limit = 10) {
13
+ // Check cache first
14
+ const cached = getCache(CACHE_KEY);
15
+ if (cached) {
16
+ return cached.slice(0, limit);
17
+ }
18
+ const config = loadConfig();
19
+ const { repo, apiUrl } = config.github;
20
+ const url = `${apiUrl}/repos/${repo}/releases?per_page=${limit}`;
21
+ try {
22
+ const response = await fetch(url, {
23
+ headers: {
24
+ 'Accept': 'application/vnd.github.v3+json',
25
+ 'User-Agent': 'gemkit-cli',
26
+ },
27
+ });
28
+ if (!response.ok) {
29
+ throw new GitHubError(`Failed to fetch releases: ${response.status}`);
30
+ }
31
+ const data = await response.json();
32
+ const releases = data.map(r => ({
33
+ version: r.tag_name.replace(/^v/, ''),
34
+ tag: r.tag_name,
35
+ publishedAt: r.published_at,
36
+ prerelease: r.prerelease,
37
+ assets: r.assets.map(a => ({
38
+ name: a.name,
39
+ url: a.url,
40
+ size: a.size,
41
+ downloadUrl: a.browser_download_url,
42
+ })),
43
+ }));
44
+ // Cache results
45
+ setCache(CACHE_KEY, releases, CACHE_TTL);
46
+ return releases;
47
+ }
48
+ catch (error) {
49
+ if (error instanceof GitHubError) {
50
+ throw error;
51
+ }
52
+ throw new GitHubError(`Failed to fetch releases: ${error}`);
53
+ }
54
+ }
55
+ /**
56
+ * Get latest release
57
+ */
58
+ export async function getLatestRelease() {
59
+ const releases = await fetchReleases(1);
60
+ return releases.length > 0 ? releases[0] : null;
61
+ }
62
+ /**
63
+ * Get release by version
64
+ */
65
+ export async function getReleaseByVersion(version) {
66
+ const releases = await fetchReleases(50);
67
+ return releases.find(r => r.version === version || r.tag === version) || null;
68
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Conflict detection for file updates
3
+ */
4
+ export interface ConflictInfo {
5
+ path: string;
6
+ type: 'modified' | 'deleted' | 'new';
7
+ localHash?: string;
8
+ originalHash?: string;
9
+ }
10
+ /**
11
+ * Detect conflicts between local changes and update
12
+ */
13
+ export declare function detectConflicts(projectDir?: string): ConflictInfo[];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Conflict detection for file updates
3
+ */
4
+ import { existsSync } from 'fs';
5
+ import { hashFile } from '../../services/hash.js';
6
+ import { loadMetadata } from './metadata.js';
7
+ /**
8
+ * Detect conflicts between local changes and update
9
+ */
10
+ export function detectConflicts(projectDir) {
11
+ const conflicts = [];
12
+ const metadata = loadMetadata(projectDir);
13
+ if (!metadata) {
14
+ return conflicts;
15
+ }
16
+ // Check customized files
17
+ for (const custom of metadata.customizedFiles) {
18
+ if (!existsSync(custom.path)) {
19
+ conflicts.push({
20
+ path: custom.path,
21
+ type: 'deleted',
22
+ originalHash: custom.hash,
23
+ });
24
+ }
25
+ else {
26
+ const currentHash = hashFile(custom.path);
27
+ if (currentHash !== custom.hash) {
28
+ conflicts.push({
29
+ path: custom.path,
30
+ type: 'modified',
31
+ localHash: currentHash || undefined,
32
+ originalHash: custom.hash,
33
+ });
34
+ }
35
+ }
36
+ }
37
+ return conflicts;
38
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * File synchronization for installation/update
3
+ */
4
+ export interface SyncResult {
5
+ added: string[];
6
+ updated: string[];
7
+ skipped: string[];
8
+ errors: string[];
9
+ }
10
+ /**
11
+ * Sync files from source to destination
12
+ */
13
+ export declare function syncFiles(sourceDir: string, destDir: string, options?: {
14
+ force?: boolean;
15
+ excludePatterns?: string[];
16
+ }): SyncResult;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * File synchronization for installation/update
3
+ */
4
+ import { existsSync, mkdirSync, readdirSync, copyFileSync } from 'fs';
5
+ import { join, dirname, relative } from 'path';
6
+ import { hashFile } from '../../services/hash.js';
7
+ /**
8
+ * Sync files from source to destination
9
+ */
10
+ export function syncFiles(sourceDir, destDir, options = {}) {
11
+ const result = {
12
+ added: [],
13
+ updated: [],
14
+ skipped: [],
15
+ errors: [],
16
+ };
17
+ if (!existsSync(sourceDir)) {
18
+ result.errors.push(`Source directory not found: ${sourceDir}`);
19
+ return result;
20
+ }
21
+ const files = getAllFiles(sourceDir);
22
+ for (const file of files) {
23
+ const relativePath = relative(sourceDir, file);
24
+ const destPath = join(destDir, relativePath);
25
+ // Check exclude patterns
26
+ if (options.excludePatterns?.some(p => relativePath.includes(p))) {
27
+ result.skipped.push(relativePath);
28
+ continue;
29
+ }
30
+ try {
31
+ // Ensure destination directory exists
32
+ const destDirPath = dirname(destPath);
33
+ if (!existsSync(destDirPath)) {
34
+ mkdirSync(destDirPath, { recursive: true });
35
+ }
36
+ if (!existsSync(destPath)) {
37
+ // New file
38
+ copyFileSync(file, destPath);
39
+ result.added.push(relativePath);
40
+ }
41
+ else if (options.force) {
42
+ // Force update
43
+ copyFileSync(file, destPath);
44
+ result.updated.push(relativePath);
45
+ }
46
+ else {
47
+ // Check if file changed
48
+ const sourceHash = hashFile(file);
49
+ const destHash = hashFile(destPath);
50
+ if (sourceHash !== destHash) {
51
+ result.skipped.push(relativePath);
52
+ }
53
+ }
54
+ }
55
+ catch (error) {
56
+ result.errors.push(`Failed to sync ${relativePath}: ${error}`);
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+ /**
62
+ * Get all files in directory recursively
63
+ */
64
+ function getAllFiles(dir) {
65
+ const files = [];
66
+ const entries = readdirSync(dir, { withFileTypes: true });
67
+ for (const entry of entries) {
68
+ const fullPath = join(dir, entry.name);
69
+ if (entry.isDirectory()) {
70
+ files.push(...getAllFiles(fullPath));
71
+ }
72
+ else {
73
+ files.push(fullPath);
74
+ }
75
+ }
76
+ return files;
77
+ }
@@ -0,0 +1,3 @@
1
+ export * from './metadata.js';
2
+ export * from './file-sync.js';
3
+ export * from './conflict.js';
@@ -0,0 +1,3 @@
1
+ export * from './metadata.js';
2
+ export * from './file-sync.js';
3
+ export * from './conflict.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Installation metadata management
3
+ */
4
+ import type { GemKitMetadata } from '../../types/index.js';
5
+ /**
6
+ * Load installation metadata
7
+ */
8
+ export declare function loadMetadata(projectDir?: string): GemKitMetadata | null;
9
+ /**
10
+ * Save installation metadata
11
+ */
12
+ export declare function saveMetadata(metadata: GemKitMetadata, projectDir?: string): void;
13
+ /**
14
+ * Create initial metadata
15
+ */
16
+ export declare function createMetadata(version: string, scope: 'local' | 'global', installedFiles: string[]): GemKitMetadata;
17
+ /**
18
+ * Check if GemKit is installed
19
+ */
20
+ export declare function isInstalled(projectDir?: string): boolean;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Installation metadata management
3
+ */
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
5
+ import { dirname } from 'path';
6
+ import { getLocalMetadataPath } from '../../utils/paths.js';
7
+ /**
8
+ * Load installation metadata
9
+ */
10
+ export function loadMetadata(projectDir) {
11
+ const metadataPath = getLocalMetadataPath(projectDir);
12
+ if (!existsSync(metadataPath)) {
13
+ return null;
14
+ }
15
+ try {
16
+ const content = readFileSync(metadataPath, 'utf-8');
17
+ return JSON.parse(content);
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ /**
24
+ * Save installation metadata
25
+ */
26
+ export function saveMetadata(metadata, projectDir) {
27
+ const metadataPath = getLocalMetadataPath(projectDir);
28
+ const dir = dirname(metadataPath);
29
+ if (!existsSync(dir)) {
30
+ mkdirSync(dir, { recursive: true });
31
+ }
32
+ writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
33
+ }
34
+ /**
35
+ * Create initial metadata
36
+ */
37
+ export function createMetadata(version, scope, installedFiles) {
38
+ return {
39
+ name: 'gemkit',
40
+ version,
41
+ installedAt: new Date().toISOString(),
42
+ scope,
43
+ installedFiles,
44
+ customizedFiles: [],
45
+ };
46
+ }
47
+ /**
48
+ * Check if GemKit is installed
49
+ */
50
+ export function isInstalled(projectDir) {
51
+ return loadMetadata(projectDir) !== null;
52
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types.js';
2
+ export * from './resolver.js';
@@ -0,0 +1,2 @@
1
+ export * from './types.js';
2
+ export * from './resolver.js';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Plan resolver utilities
3
+ * Replaces: gk-set-active-plan.cjs (setActivePlan function)
4
+ */
5
+ import { Plan } from './types.js';
6
+ /**
7
+ * List all plans in project
8
+ */
9
+ export declare function listPlans(projectDir?: string): Plan[];
10
+ /**
11
+ * Create a new plan
12
+ */
13
+ export declare function createPlan(name: string, projectDir?: string): Plan;
14
+ /**
15
+ * Set active plan in session file AND .env
16
+ * Matches gk-set-active-plan.cjs behavior:
17
+ * 1. Updates session.activePlan in ~/.gemkit/projects/{projectDir}/gk-session-{gkSessionId}.json
18
+ * 2. Updates ACTIVE_PLAN in .gemini/.env
19
+ */
20
+ export declare function setActivePlan(name: string, projectDir?: string): boolean;
21
+ /**
22
+ * Get plan info
23
+ */
24
+ export declare function getPlanInfo(name: string, projectDir?: string): Plan | null;
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Plan resolver utilities
3
+ * Replaces: gk-set-active-plan.cjs (setActivePlan function)
4
+ */
5
+ import { existsSync, readdirSync, mkdirSync, writeFileSync, statSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { getPlansDir, getLocalEnvPath } from '../../utils/paths.js';
8
+ import { getActivePlan, getActiveGkSessionId, getProjectDir, readEnv } from '../session/env.js';
9
+ import { getSession } from '../session/manager.js';
10
+ import { setActivePlan as sessionSetActivePlan } from '../session/writer.js';
11
+ /**
12
+ * List all plans in project
13
+ */
14
+ export function listPlans(projectDir) {
15
+ const plansDir = getPlansDir(projectDir);
16
+ if (!existsSync(plansDir)) {
17
+ return [];
18
+ }
19
+ const activePlan = getActivePlan();
20
+ const dirs = readdirSync(plansDir, { withFileTypes: true })
21
+ .filter(d => d.isDirectory())
22
+ .map(d => d.name);
23
+ return dirs.map(name => {
24
+ const path = join(plansDir, name);
25
+ const planFile = join(path, 'plan.md');
26
+ let createdAt = '';
27
+ // Try to get creation date from plan.md or folder name
28
+ if (existsSync(planFile)) {
29
+ const stat = statSync(planFile);
30
+ createdAt = stat.birthtime.toISOString();
31
+ }
32
+ return {
33
+ name,
34
+ path,
35
+ createdAt,
36
+ isActive: name === activePlan,
37
+ };
38
+ });
39
+ }
40
+ /**
41
+ * Create a new plan
42
+ */
43
+ export function createPlan(name, projectDir) {
44
+ const plansDir = getPlansDir(projectDir);
45
+ const planPath = join(plansDir, name);
46
+ if (!existsSync(plansDir)) {
47
+ mkdirSync(plansDir, { recursive: true });
48
+ }
49
+ if (existsSync(planPath)) {
50
+ throw new Error(`Plan already exists: ${name}`);
51
+ }
52
+ // Create plan structure
53
+ mkdirSync(planPath, { recursive: true });
54
+ mkdirSync(join(planPath, 'research'), { recursive: true });
55
+ mkdirSync(join(planPath, 'artifacts'), { recursive: true });
56
+ // Create plan.md template
57
+ const template = `# ${name}
58
+
59
+ ## Overview
60
+
61
+ [Describe the plan objectives here]
62
+
63
+ ## Tasks
64
+
65
+ - [ ] Task 1
66
+ - [ ] Task 2
67
+ - [ ] Task 3
68
+
69
+ ## Notes
70
+
71
+ `;
72
+ writeFileSync(join(planPath, 'plan.md'), template);
73
+ return {
74
+ name,
75
+ path: planPath,
76
+ createdAt: new Date().toISOString(),
77
+ isActive: false,
78
+ };
79
+ }
80
+ /**
81
+ * Set active plan in session file AND .env
82
+ * Matches gk-set-active-plan.cjs behavior:
83
+ * 1. Updates session.activePlan in ~/.gemkit/projects/{projectDir}/gk-session-{gkSessionId}.json
84
+ * 2. Updates ACTIVE_PLAN in .gemini/.env
85
+ */
86
+ export function setActivePlan(name, projectDir) {
87
+ // Get active session ID and project dir from .env
88
+ const gkSessionId = getActiveGkSessionId();
89
+ if (!gkSessionId) {
90
+ // No active session - just update env directly
91
+ const envPath = getLocalEnvPath(projectDir);
92
+ const env = readEnv();
93
+ // Build new env content with ACTIVE_PLAN
94
+ const content = [
95
+ '# Auto-generated by gemkit-cli',
96
+ `# Updated at: ${new Date().toISOString()}`,
97
+ '',
98
+ '# GEMKIT IDs',
99
+ `ACTIVE_GK_SESSION_ID=${env.ACTIVE_GK_SESSION_ID || ''}`,
100
+ `GK_PROJECT_HASH=${env.GK_PROJECT_HASH || ''}`,
101
+ `PROJECT_DIR=${env.PROJECT_DIR || ''}`,
102
+ '',
103
+ '# GEMINI IDs (mapped)',
104
+ `ACTIVE_GEMINI_SESSION_ID=${env.ACTIVE_GEMINI_SESSION_ID || ''}`,
105
+ `GEMINI_PROJECT_HASH=${env.GEMINI_PROJECT_HASH || ''}`,
106
+ '',
107
+ '# PLAN INFO',
108
+ `ACTIVE_PLAN=${name}`,
109
+ `SUGGESTED_PLAN=${env.SUGGESTED_PLAN || ''}`,
110
+ `PLAN_DATE_FORMAT=${env.PLAN_DATE_FORMAT || ''}`,
111
+ ''
112
+ ].join('\n');
113
+ writeFileSync(envPath, content);
114
+ return true;
115
+ }
116
+ const projDir = getProjectDir(projectDir);
117
+ // Verify session exists
118
+ const session = getSession(projDir, gkSessionId);
119
+ if (!session) {
120
+ // Session not found - just update env
121
+ const envPath = getLocalEnvPath(projectDir);
122
+ const env = readEnv();
123
+ const content = [
124
+ '# Auto-generated by gemkit-cli',
125
+ `# Updated at: ${new Date().toISOString()}`,
126
+ '',
127
+ '# GEMKIT IDs',
128
+ `ACTIVE_GK_SESSION_ID=${env.ACTIVE_GK_SESSION_ID || ''}`,
129
+ `GK_PROJECT_HASH=${env.GK_PROJECT_HASH || ''}`,
130
+ `PROJECT_DIR=${env.PROJECT_DIR || ''}`,
131
+ '',
132
+ '# GEMINI IDs (mapped)',
133
+ `ACTIVE_GEMINI_SESSION_ID=${env.ACTIVE_GEMINI_SESSION_ID || ''}`,
134
+ `GEMINI_PROJECT_HASH=${env.GEMINI_PROJECT_HASH || ''}`,
135
+ '',
136
+ '# PLAN INFO',
137
+ `ACTIVE_PLAN=${name}`,
138
+ `SUGGESTED_PLAN=${env.SUGGESTED_PLAN || ''}`,
139
+ `PLAN_DATE_FORMAT=${env.PLAN_DATE_FORMAT || ''}`,
140
+ ''
141
+ ].join('\n');
142
+ writeFileSync(envPath, content);
143
+ return true;
144
+ }
145
+ // Update session file AND env file using session writer
146
+ return sessionSetActivePlan(projDir, gkSessionId, name);
147
+ }
148
+ /**
149
+ * Get plan info
150
+ */
151
+ export function getPlanInfo(name, projectDir) {
152
+ const plansDir = getPlansDir(projectDir);
153
+ const planPath = join(plansDir, name);
154
+ if (!existsSync(planPath)) {
155
+ return null;
156
+ }
157
+ const activePlan = getActivePlan();
158
+ return {
159
+ name,
160
+ path: planPath,
161
+ createdAt: '',
162
+ isActive: name === activePlan,
163
+ };
164
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Plan type definitions
3
+ */
4
+ export interface Plan {
5
+ name: string;
6
+ path: string;
7
+ createdAt: string;
8
+ isActive: boolean;
9
+ }
10
+ export interface PlanTemplate {
11
+ name: string;
12
+ content: string;
13
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Plan type definitions
3
+ */
4
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Environment variable utilities for session management
3
+ * Aligned with gk-session-manager.cjs readEnv()
4
+ */
5
+ /**
6
+ * Environment data structure matching gk-session-manager.cjs
7
+ */
8
+ export interface GkEnvData {
9
+ ACTIVE_GK_SESSION_ID: string;
10
+ GK_PROJECT_HASH: string;
11
+ PROJECT_DIR: string;
12
+ ACTIVE_GEMINI_SESSION_ID: string;
13
+ GEMINI_PROJECT_HASH: string;
14
+ GEMINI_PARENT_ID: string;
15
+ ACTIVE_PLAN: string;
16
+ SUGGESTED_PLAN: string;
17
+ PLAN_DATE_FORMAT: string;
18
+ }
19
+ /**
20
+ * Read environment variables from .gemini/.env
21
+ * Matches gk-session-manager.cjs readEnv()
22
+ */
23
+ export declare function readEnv(projectPath?: string): GkEnvData;
24
+ /**
25
+ * Get active GK session ID from .gemini/.env
26
+ */
27
+ export declare function getActiveGkSessionId(projectPath?: string): string | undefined;
28
+ /**
29
+ * Get active Gemini session ID from .gemini/.env
30
+ */
31
+ export declare function getActiveGeminiSessionId(projectPath?: string): string | undefined;
32
+ /**
33
+ * Get project directory from .gemini/.env or derive from cwd
34
+ */
35
+ export declare function getProjectDir(projectPath?: string): string;
36
+ /**
37
+ * Get GK project hash from .gemini/.env
38
+ */
39
+ export declare function getGkProjectHash(projectPath?: string): string | undefined;
40
+ /**
41
+ * Get Gemini project hash from .gemini/.env
42
+ */
43
+ export declare function getGeminiProjectHash(projectPath?: string): string | undefined;
44
+ /**
45
+ * Get active plan from .gemini/.env
46
+ */
47
+ export declare function getActivePlan(projectPath?: string): string | undefined;
48
+ /**
49
+ * Get suggested plan from .gemini/.env
50
+ */
51
+ export declare function getSuggestedPlan(projectPath?: string): string | undefined;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Environment variable utilities for session management
3
+ * Aligned with gk-session-manager.cjs readEnv()
4
+ */
5
+ import { existsSync, readFileSync } from 'fs';
6
+ import { getLocalEnvPath, sanitizeProjectPath } from '../../utils/paths.js';
7
+ /**
8
+ * Read environment variables from .gemini/.env
9
+ * Matches gk-session-manager.cjs readEnv()
10
+ */
11
+ export function readEnv(projectPath = process.cwd()) {
12
+ const result = {
13
+ // GemKit IDs
14
+ ACTIVE_GK_SESSION_ID: '',
15
+ GK_PROJECT_HASH: '',
16
+ PROJECT_DIR: '',
17
+ // Gemini IDs (mapped)
18
+ ACTIVE_GEMINI_SESSION_ID: '',
19
+ GEMINI_PROJECT_HASH: '',
20
+ GEMINI_PARENT_ID: '',
21
+ // Plan info
22
+ ACTIVE_PLAN: '',
23
+ SUGGESTED_PLAN: '',
24
+ PLAN_DATE_FORMAT: ''
25
+ };
26
+ const envPath = getLocalEnvPath(projectPath);
27
+ if (!existsSync(envPath)) {
28
+ return result;
29
+ }
30
+ try {
31
+ const content = readFileSync(envPath, 'utf-8');
32
+ // GemKit IDs
33
+ const gkSessionMatch = content.match(/^ACTIVE_GK_SESSION_ID=(.*)$/m);
34
+ if (gkSessionMatch)
35
+ result.ACTIVE_GK_SESSION_ID = gkSessionMatch[1].trim();
36
+ const gkHashMatch = content.match(/^GK_PROJECT_HASH=(.*)$/m);
37
+ if (gkHashMatch)
38
+ result.GK_PROJECT_HASH = gkHashMatch[1].trim();
39
+ const projectDirMatch = content.match(/^PROJECT_DIR=(.*)$/m);
40
+ if (projectDirMatch)
41
+ result.PROJECT_DIR = projectDirMatch[1].trim();
42
+ // Gemini IDs
43
+ const geminiSessionMatch = content.match(/^ACTIVE_GEMINI_SESSION_ID=(.*)$/m);
44
+ if (geminiSessionMatch)
45
+ result.ACTIVE_GEMINI_SESSION_ID = geminiSessionMatch[1].trim();
46
+ const geminiHashMatch = content.match(/^GEMINI_PROJECT_HASH=(.*)$/m);
47
+ if (geminiHashMatch)
48
+ result.GEMINI_PROJECT_HASH = geminiHashMatch[1].trim();
49
+ const geminiParentMatch = content.match(/^GEMINI_PARENT_ID=(.*)$/m);
50
+ if (geminiParentMatch)
51
+ result.GEMINI_PARENT_ID = geminiParentMatch[1].trim();
52
+ // Plan info
53
+ const activePlanMatch = content.match(/^ACTIVE_PLAN=(.*)$/m);
54
+ if (activePlanMatch)
55
+ result.ACTIVE_PLAN = activePlanMatch[1].trim();
56
+ const suggestedPlanMatch = content.match(/^SUGGESTED_PLAN=(.*)$/m);
57
+ if (suggestedPlanMatch)
58
+ result.SUGGESTED_PLAN = suggestedPlanMatch[1].trim();
59
+ const dateFormatMatch = content.match(/^PLAN_DATE_FORMAT=(.*)$/m);
60
+ if (dateFormatMatch)
61
+ result.PLAN_DATE_FORMAT = dateFormatMatch[1].trim();
62
+ }
63
+ catch (e) {
64
+ // Return empty values on error
65
+ }
66
+ return result;
67
+ }
68
+ /**
69
+ * Get active GK session ID from .gemini/.env
70
+ */
71
+ export function getActiveGkSessionId(projectPath = process.cwd()) {
72
+ const env = readEnv(projectPath);
73
+ return env.ACTIVE_GK_SESSION_ID || undefined;
74
+ }
75
+ /**
76
+ * Get active Gemini session ID from .gemini/.env
77
+ */
78
+ export function getActiveGeminiSessionId(projectPath = process.cwd()) {
79
+ const env = readEnv(projectPath);
80
+ return env.ACTIVE_GEMINI_SESSION_ID || undefined;
81
+ }
82
+ /**
83
+ * Get project directory from .gemini/.env or derive from cwd
84
+ */
85
+ export function getProjectDir(projectPath = process.cwd()) {
86
+ const env = readEnv(projectPath);
87
+ if (env.PROJECT_DIR)
88
+ return env.PROJECT_DIR;
89
+ return sanitizeProjectPath(projectPath);
90
+ }
91
+ /**
92
+ * Get GK project hash from .gemini/.env
93
+ */
94
+ export function getGkProjectHash(projectPath = process.cwd()) {
95
+ const env = readEnv(projectPath);
96
+ return env.GK_PROJECT_HASH || undefined;
97
+ }
98
+ /**
99
+ * Get Gemini project hash from .gemini/.env
100
+ */
101
+ export function getGeminiProjectHash(projectPath = process.cwd()) {
102
+ const env = readEnv(projectPath);
103
+ return env.GEMINI_PROJECT_HASH || undefined;
104
+ }
105
+ /**
106
+ * Get active plan from .gemini/.env
107
+ */
108
+ export function getActivePlan(projectPath = process.cwd()) {
109
+ const env = readEnv(projectPath);
110
+ return env.ACTIVE_PLAN || undefined;
111
+ }
112
+ /**
113
+ * Get suggested plan from .gemini/.env
114
+ */
115
+ export function getSuggestedPlan(projectPath = process.cwd()) {
116
+ const env = readEnv(projectPath);
117
+ return env.SUGGESTED_PLAN || undefined;
118
+ }