@lovelybunch/api 1.0.7

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 (131) hide show
  1. package/dist/lib/gait-path.d.ts +13 -0
  2. package/dist/lib/gait-path.js +57 -0
  3. package/dist/lib/project-paths.d.ts +13 -0
  4. package/dist/lib/project-paths.js +57 -0
  5. package/dist/lib/storage/file-storage.d.ts +28 -0
  6. package/dist/lib/storage/file-storage.js +224 -0
  7. package/dist/lib/symlinks/symlink-manager.d.ts +66 -0
  8. package/dist/lib/symlinks/symlink-manager.js +444 -0
  9. package/dist/lib/symlinks/types.d.ts +23 -0
  10. package/dist/lib/symlinks/types.js +4 -0
  11. package/dist/lib/terminal/context-helper.d.ts +11 -0
  12. package/dist/lib/terminal/context-helper.js +164 -0
  13. package/dist/lib/terminal/global-manager.d.ts +2 -0
  14. package/dist/lib/terminal/global-manager.js +15 -0
  15. package/dist/lib/terminal/shell-utils.d.ts +33 -0
  16. package/dist/lib/terminal/shell-utils.js +176 -0
  17. package/dist/lib/terminal/terminal-manager.d.ts +26 -0
  18. package/dist/lib/terminal/terminal-manager.js +276 -0
  19. package/dist/lib/user-preferences.d.ts +48 -0
  20. package/dist/lib/user-preferences.js +87 -0
  21. package/dist/lib/utils.d.ts +2 -0
  22. package/dist/lib/utils.js +5 -0
  23. package/dist/routes/api/symlink-status/route.d.ts +1 -0
  24. package/dist/routes/api/symlink-status/route.js +37 -0
  25. package/dist/routes/api/symlinks/[id]/route.d.ts +19 -0
  26. package/dist/routes/api/symlinks/[id]/route.js +95 -0
  27. package/dist/routes/api/symlinks/[id]/toggle/route.d.ts +11 -0
  28. package/dist/routes/api/symlinks/[id]/toggle/route.js +32 -0
  29. package/dist/routes/api/symlinks/debug/route.d.ts +1 -0
  30. package/dist/routes/api/symlinks/debug/route.js +35 -0
  31. package/dist/routes/api/symlinks/route.d.ts +9 -0
  32. package/dist/routes/api/symlinks/route.js +72 -0
  33. package/dist/routes/api/toggle-symlink/route.d.ts +2 -0
  34. package/dist/routes/api/toggle-symlink/route.js +94 -0
  35. package/dist/routes/api/v1/agents/[id]/index.d.ts +1 -0
  36. package/dist/routes/api/v1/agents/[id]/index.js +1 -0
  37. package/dist/routes/api/v1/agents/[id]/route.d.ts +3 -0
  38. package/dist/routes/api/v1/agents/[id]/route.js +163 -0
  39. package/dist/routes/api/v1/agents/index.d.ts +1 -0
  40. package/dist/routes/api/v1/agents/index.js +1 -0
  41. package/dist/routes/api/v1/agents/route.d.ts +3 -0
  42. package/dist/routes/api/v1/agents/route.js +133 -0
  43. package/dist/routes/api/v1/ai/index.d.ts +3 -0
  44. package/dist/routes/api/v1/ai/index.js +5 -0
  45. package/dist/routes/api/v1/ai/route.d.ts +8 -0
  46. package/dist/routes/api/v1/ai/route.js +86 -0
  47. package/dist/routes/api/v1/chats/[id]/index.d.ts +3 -0
  48. package/dist/routes/api/v1/chats/[id]/index.js +6 -0
  49. package/dist/routes/api/v1/chats/[id]/route.d.ts +12 -0
  50. package/dist/routes/api/v1/chats/[id]/route.js +31 -0
  51. package/dist/routes/api/v1/chats/index.d.ts +3 -0
  52. package/dist/routes/api/v1/chats/index.js +6 -0
  53. package/dist/routes/api/v1/chats/route.d.ts +32 -0
  54. package/dist/routes/api/v1/chats/route.js +67 -0
  55. package/dist/routes/api/v1/config/index.d.ts +3 -0
  56. package/dist/routes/api/v1/config/index.js +5 -0
  57. package/dist/routes/api/v1/config/route.d.ts +9 -0
  58. package/dist/routes/api/v1/config/route.js +29 -0
  59. package/dist/routes/api/v1/context/[...path]/route.d.ts +16 -0
  60. package/dist/routes/api/v1/context/[...path]/route.js +107 -0
  61. package/dist/routes/api/v1/context/architecture/route.d.ts +3 -0
  62. package/dist/routes/api/v1/context/architecture/route.js +198 -0
  63. package/dist/routes/api/v1/context/index.d.ts +3 -0
  64. package/dist/routes/api/v1/context/index.js +9 -0
  65. package/dist/routes/api/v1/context/knowledge/[filename]/index.d.ts +1 -0
  66. package/dist/routes/api/v1/context/knowledge/[filename]/index.js +1 -0
  67. package/dist/routes/api/v1/context/knowledge/[filename]/route.d.ts +3 -0
  68. package/dist/routes/api/v1/context/knowledge/[filename]/route.js +165 -0
  69. package/dist/routes/api/v1/context/knowledge/index.d.ts +1 -0
  70. package/dist/routes/api/v1/context/knowledge/index.js +1 -0
  71. package/dist/routes/api/v1/context/knowledge/route.d.ts +3 -0
  72. package/dist/routes/api/v1/context/knowledge/route.js +121 -0
  73. package/dist/routes/api/v1/context/project/route.d.ts +3 -0
  74. package/dist/routes/api/v1/context/project/route.js +153 -0
  75. package/dist/routes/api/v1/proposals/[id]/route.d.ts +337 -0
  76. package/dist/routes/api/v1/proposals/[id]/route.js +99 -0
  77. package/dist/routes/api/v1/proposals/index.d.ts +3 -0
  78. package/dist/routes/api/v1/proposals/index.js +10 -0
  79. package/dist/routes/api/v1/proposals/route.d.ts +315 -0
  80. package/dist/routes/api/v1/proposals/route.js +103 -0
  81. package/dist/routes/api/v1/resources/[id]/index.d.ts +3 -0
  82. package/dist/routes/api/v1/resources/[id]/index.js +7 -0
  83. package/dist/routes/api/v1/resources/[id]/route.d.ts +46 -0
  84. package/dist/routes/api/v1/resources/[id]/route.js +143 -0
  85. package/dist/routes/api/v1/resources/[id]/thumbnail/index.d.ts +3 -0
  86. package/dist/routes/api/v1/resources/[id]/thumbnail/index.js +5 -0
  87. package/dist/routes/api/v1/resources/[id]/thumbnail/route.d.ts +2 -0
  88. package/dist/routes/api/v1/resources/[id]/thumbnail/route.js +50 -0
  89. package/dist/routes/api/v1/resources/index.d.ts +3 -0
  90. package/dist/routes/api/v1/resources/index.js +6 -0
  91. package/dist/routes/api/v1/resources/route.d.ts +51 -0
  92. package/dist/routes/api/v1/resources/route.js +147 -0
  93. package/dist/routes/api/v1/search/route.d.ts +3 -0
  94. package/dist/routes/api/v1/search/route.js +39 -0
  95. package/dist/routes/api/v1/terminal/[proposalId]/create/index.d.ts +3 -0
  96. package/dist/routes/api/v1/terminal/[proposalId]/create/index.js +5 -0
  97. package/dist/routes/api/v1/terminal/[proposalId]/create/route.d.ts +10 -0
  98. package/dist/routes/api/v1/terminal/[proposalId]/create/route.js +27 -0
  99. package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.d.ts +3 -0
  100. package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.js +5 -0
  101. package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.d.ts +10 -0
  102. package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.js +21 -0
  103. package/dist/routes/api/v1/terminal/[proposalId]/resize/index.d.ts +3 -0
  104. package/dist/routes/api/v1/terminal/[proposalId]/resize/index.js +5 -0
  105. package/dist/routes/api/v1/terminal/[proposalId]/resize/route.d.ts +10 -0
  106. package/dist/routes/api/v1/terminal/[proposalId]/resize/route.js +21 -0
  107. package/dist/routes/api/v1/terminal/sessions/index.d.ts +3 -0
  108. package/dist/routes/api/v1/terminal/sessions/index.js +5 -0
  109. package/dist/routes/api/v1/terminal/sessions/route.d.ts +6 -0
  110. package/dist/routes/api/v1/terminal/sessions/route.js +29 -0
  111. package/dist/routes/api/v1/user/index.d.ts +3 -0
  112. package/dist/routes/api/v1/user/index.js +5 -0
  113. package/dist/routes/api/v1/user/preferences/route.d.ts +11 -0
  114. package/dist/routes/api/v1/user/preferences/route.js +31 -0
  115. package/dist/routes/api/v1/user/profile/route.d.ts +11 -0
  116. package/dist/routes/api/v1/user/profile/route.js +31 -0
  117. package/dist/routes/api/v1/user/settings/index.d.ts +1 -0
  118. package/dist/routes/api/v1/user/settings/index.js +1 -0
  119. package/dist/routes/api/v1/user/settings/route.d.ts +3 -0
  120. package/dist/routes/api/v1/user/settings/route.js +51 -0
  121. package/dist/server-with-static.d.ts +4 -0
  122. package/dist/server-with-static.js +144 -0
  123. package/dist/server.d.ts +1 -0
  124. package/dist/server.js +91 -0
  125. package/package.json +42 -0
  126. package/static/assets/index-BvTnrm0O.js +576 -0
  127. package/static/assets/index-Cm5dZHTl.css +33 -0
  128. package/static/assets/index-ORkAkJNi.js +576 -0
  129. package/static/assets/index-_Keadpms.js +576 -0
  130. package/static/index.html +17 -0
  131. package/static/vite.svg +1 -0
@@ -0,0 +1,444 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ /**
5
+ * SymlinkManager handles all symlink operations and persists state
6
+ */
7
+ export class SymlinkManager {
8
+ configPath;
9
+ projectRoot;
10
+ state;
11
+ constructor(projectRoot) {
12
+ this.projectRoot = projectRoot;
13
+ this.configPath = path.join(projectRoot, '.gait', 'symlinks.json');
14
+ this.state = {
15
+ version: '1.0.0',
16
+ symlinks: []
17
+ };
18
+ }
19
+ /**
20
+ * Initialize the manager and load existing configuration
21
+ */
22
+ async initialize() {
23
+ try {
24
+ console.log(`[SymlinkManager] Initializing with project root: ${this.projectRoot}`);
25
+ console.log(`[SymlinkManager] Config path: ${this.configPath}`);
26
+ // Ensure .gait directory exists
27
+ const gaitDir = path.join(this.projectRoot, '.gait');
28
+ await fs.mkdir(gaitDir, { recursive: true });
29
+ // Try to load existing configuration
30
+ try {
31
+ const configData = await fs.readFile(this.configPath, 'utf-8');
32
+ this.state = JSON.parse(configData);
33
+ console.log(`[SymlinkManager] Loaded ${this.state.symlinks.length} symlinks from config`);
34
+ // Validate state against actual filesystem
35
+ await this.validateSymlinks();
36
+ }
37
+ catch (error) {
38
+ if (error.code === 'ENOENT') {
39
+ console.log('[SymlinkManager] No existing config found, creating default');
40
+ // Config doesn't exist, create default
41
+ await this.createDefaultConfig();
42
+ }
43
+ else {
44
+ console.error('[SymlinkManager] Error loading config:', error);
45
+ throw error;
46
+ }
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error('[SymlinkManager] Failed to initialize:', error);
51
+ throw error;
52
+ }
53
+ }
54
+ /**
55
+ * Create default configuration with the CLAUDE.md symlink
56
+ */
57
+ async createDefaultConfig() {
58
+ // Check if the legacy CLAUDE.md symlink exists
59
+ const claudeSymlinkPath = path.join(this.projectRoot, 'CLAUDE.md');
60
+ let isActive = false;
61
+ try {
62
+ const stats = await fs.lstat(claudeSymlinkPath);
63
+ isActive = stats.isSymbolicLink();
64
+ }
65
+ catch {
66
+ // Symlink doesn't exist
67
+ }
68
+ const defaultSymlink = {
69
+ id: 'claude-rules',
70
+ name: 'Claude Rules',
71
+ description: 'Project-specific rules and guidelines for Claude AI assistant',
72
+ linkPath: 'CLAUDE.md',
73
+ targetPath: '.gait/rules/CLAUDE.md',
74
+ isActive,
75
+ createdAt: new Date().toISOString(),
76
+ updatedAt: new Date().toISOString()
77
+ };
78
+ this.state.symlinks = [defaultSymlink];
79
+ await this.saveState();
80
+ }
81
+ /**
82
+ * Validate symlinks against actual filesystem state
83
+ */
84
+ async validateSymlinks() {
85
+ for (const symlink of this.state.symlinks) {
86
+ // Expand tilde and resolve path
87
+ const expandedLinkPath = this.expandTilde(symlink.linkPath);
88
+ const fullLinkPath = path.isAbsolute(expandedLinkPath)
89
+ ? expandedLinkPath
90
+ : path.join(this.projectRoot, expandedLinkPath);
91
+ try {
92
+ const stats = await fs.lstat(fullLinkPath);
93
+ const actuallyActive = stats.isSymbolicLink();
94
+ if (actuallyActive !== symlink.isActive) {
95
+ // State mismatch - filesystem is source of truth
96
+ symlink.isActive = actuallyActive;
97
+ symlink.updatedAt = new Date().toISOString();
98
+ }
99
+ }
100
+ catch (error) {
101
+ if (error.code === 'ENOENT') {
102
+ // File doesn't exist
103
+ if (symlink.isActive) {
104
+ symlink.isActive = false;
105
+ symlink.updatedAt = new Date().toISOString();
106
+ }
107
+ }
108
+ }
109
+ }
110
+ await this.saveState();
111
+ }
112
+ /**
113
+ * Save current state to configuration file
114
+ */
115
+ async saveState() {
116
+ try {
117
+ // Ensure directory exists
118
+ await fs.mkdir(path.dirname(this.configPath), { recursive: true });
119
+ // Write to temp file first
120
+ const tempPath = `${this.configPath}.tmp`;
121
+ await fs.writeFile(tempPath, JSON.stringify(this.state, null, 2), 'utf-8');
122
+ // Atomic rename
123
+ await fs.rename(tempPath, this.configPath);
124
+ console.log(`[SymlinkManager] Saved state with ${this.state.symlinks.length} symlinks to ${this.configPath}`);
125
+ }
126
+ catch (error) {
127
+ console.error('[SymlinkManager] Failed to save state:', error);
128
+ throw error;
129
+ }
130
+ }
131
+ /**
132
+ * Get all symlink configurations
133
+ */
134
+ async getSymlinks() {
135
+ await this.validateSymlinks();
136
+ return this.state.symlinks;
137
+ }
138
+ /**
139
+ * Get a specific symlink by ID
140
+ */
141
+ async getSymlink(id) {
142
+ await this.validateSymlinks();
143
+ return this.state.symlinks.find(s => s.id === id);
144
+ }
145
+ /**
146
+ * Add a new symlink configuration
147
+ */
148
+ async addSymlink(config) {
149
+ try {
150
+ // Check if link path already exists
151
+ if (this.state.symlinks.some(s => s.linkPath === config.linkPath)) {
152
+ return {
153
+ success: false,
154
+ message: `A symlink at ${config.linkPath} already exists`
155
+ };
156
+ }
157
+ const newSymlink = {
158
+ ...config,
159
+ id: `symlink-${Date.now()}`,
160
+ createdAt: new Date().toISOString(),
161
+ updatedAt: new Date().toISOString()
162
+ };
163
+ this.state.symlinks.push(newSymlink);
164
+ if (config.isActive) {
165
+ const result = await this.createSymlink(newSymlink.id);
166
+ if (!result.success) {
167
+ // Roll back
168
+ this.state.symlinks = this.state.symlinks.filter(s => s.id !== newSymlink.id);
169
+ return result;
170
+ }
171
+ }
172
+ await this.saveState();
173
+ return {
174
+ success: true,
175
+ message: 'Symlink configuration added successfully',
176
+ symlink: newSymlink
177
+ };
178
+ }
179
+ catch (error) {
180
+ return {
181
+ success: false,
182
+ message: 'Failed to add symlink',
183
+ error: error.message
184
+ };
185
+ }
186
+ }
187
+ /**
188
+ * Toggle a symlink on/off
189
+ */
190
+ async toggleSymlink(id) {
191
+ const symlink = this.state.symlinks.find(s => s.id === id);
192
+ if (!symlink) {
193
+ return {
194
+ success: false,
195
+ message: `Symlink with ID ${id} not found`
196
+ };
197
+ }
198
+ if (symlink.isActive) {
199
+ return await this.removeSymlink(id);
200
+ }
201
+ else {
202
+ return await this.createSymlink(id);
203
+ }
204
+ }
205
+ /**
206
+ * Expand tilde in path to actual home directory
207
+ */
208
+ expandTilde(filepath) {
209
+ if (filepath.startsWith('~/')) {
210
+ return path.join(os.homedir(), filepath.slice(2));
211
+ }
212
+ return filepath;
213
+ }
214
+ /**
215
+ * Create a symlink on the filesystem
216
+ */
217
+ async createSymlink(id) {
218
+ const symlink = this.state.symlinks.find(s => s.id === id);
219
+ if (!symlink) {
220
+ return {
221
+ success: false,
222
+ message: `Symlink with ID ${id} not found`
223
+ };
224
+ }
225
+ try {
226
+ // Expand tilde in paths and resolve them
227
+ const expandedLinkPath = this.expandTilde(symlink.linkPath);
228
+ const expandedTargetPath = this.expandTilde(symlink.targetPath);
229
+ // If paths are relative (not starting with / or ~), make them relative to project root
230
+ const fullLinkPath = path.isAbsolute(expandedLinkPath)
231
+ ? expandedLinkPath
232
+ : path.join(this.projectRoot, expandedLinkPath);
233
+ const fullTargetPath = path.isAbsolute(expandedTargetPath)
234
+ ? expandedTargetPath
235
+ : path.join(this.projectRoot, expandedTargetPath);
236
+ // Ensure target exists
237
+ try {
238
+ await fs.access(fullTargetPath);
239
+ }
240
+ catch {
241
+ // Create target directory and file if needed
242
+ await fs.mkdir(path.dirname(fullTargetPath), { recursive: true });
243
+ // Create a default file if it doesn't exist
244
+ await fs.writeFile(fullTargetPath, `# ${symlink.name}
245
+
246
+ ${symlink.description || 'Configuration file'}
247
+
248
+ Created: ${new Date().toISOString()}
249
+ `);
250
+ }
251
+ // Remove existing file/symlink if it exists
252
+ try {
253
+ await fs.unlink(fullLinkPath);
254
+ }
255
+ catch (error) {
256
+ if (error.code !== 'ENOENT')
257
+ throw error;
258
+ }
259
+ // Create the symlink (use relative path for portability)
260
+ const relativePath = path.relative(path.dirname(fullLinkPath), fullTargetPath);
261
+ await fs.symlink(relativePath, fullLinkPath);
262
+ symlink.isActive = true;
263
+ symlink.updatedAt = new Date().toISOString();
264
+ await this.saveState();
265
+ return {
266
+ success: true,
267
+ message: `Symlink ${symlink.name} created successfully`,
268
+ symlink
269
+ };
270
+ }
271
+ catch (error) {
272
+ return {
273
+ success: false,
274
+ message: `Failed to create symlink`,
275
+ error: error.message
276
+ };
277
+ }
278
+ }
279
+ /**
280
+ * Remove a symlink from the filesystem
281
+ */
282
+ async removeSymlink(id) {
283
+ const symlink = this.state.symlinks.find(s => s.id === id);
284
+ if (!symlink) {
285
+ return {
286
+ success: false,
287
+ message: `Symlink with ID ${id} not found`
288
+ };
289
+ }
290
+ try {
291
+ // Expand tilde and resolve path
292
+ const expandedLinkPath = this.expandTilde(symlink.linkPath);
293
+ const fullLinkPath = path.isAbsolute(expandedLinkPath)
294
+ ? expandedLinkPath
295
+ : path.join(this.projectRoot, expandedLinkPath);
296
+ try {
297
+ const stats = await fs.lstat(fullLinkPath);
298
+ if (stats.isSymbolicLink()) {
299
+ await fs.unlink(fullLinkPath);
300
+ }
301
+ }
302
+ catch (error) {
303
+ if (error.code !== 'ENOENT')
304
+ throw error;
305
+ }
306
+ symlink.isActive = false;
307
+ symlink.updatedAt = new Date().toISOString();
308
+ await this.saveState();
309
+ return {
310
+ success: true,
311
+ message: `Symlink ${symlink.name} removed successfully`,
312
+ symlink
313
+ };
314
+ }
315
+ catch (error) {
316
+ return {
317
+ success: false,
318
+ message: `Failed to remove symlink`,
319
+ error: error.message
320
+ };
321
+ }
322
+ }
323
+ /**
324
+ * Delete a symlink configuration entirely
325
+ */
326
+ async deleteSymlink(id) {
327
+ const symlink = this.state.symlinks.find(s => s.id === id);
328
+ if (!symlink) {
329
+ return {
330
+ success: false,
331
+ message: `Symlink with ID ${id} not found`
332
+ };
333
+ }
334
+ // Remove from filesystem first if active
335
+ if (symlink.isActive) {
336
+ await this.removeSymlink(id);
337
+ }
338
+ // Remove from configuration
339
+ this.state.symlinks = this.state.symlinks.filter(s => s.id !== id);
340
+ await this.saveState();
341
+ return {
342
+ success: true,
343
+ message: `Symlink configuration deleted successfully`
344
+ };
345
+ }
346
+ /**
347
+ * Update a symlink configuration
348
+ */
349
+ async updateSymlink(id, updates) {
350
+ const symlink = this.state.symlinks.find(s => s.id === id);
351
+ if (!symlink) {
352
+ return {
353
+ success: false,
354
+ message: `Symlink with ID ${id} not found`
355
+ };
356
+ }
357
+ try {
358
+ // If changing paths and symlink is active, need to recreate
359
+ if (symlink.isActive && (updates.linkPath || updates.targetPath)) {
360
+ await this.removeSymlink(id);
361
+ }
362
+ // Apply updates
363
+ Object.assign(symlink, updates, {
364
+ updatedAt: new Date().toISOString()
365
+ });
366
+ // Recreate if was active and paths changed
367
+ if (symlink.isActive && (updates.linkPath || updates.targetPath)) {
368
+ const result = await this.createSymlink(id);
369
+ if (!result.success) {
370
+ return result;
371
+ }
372
+ }
373
+ await this.saveState();
374
+ return {
375
+ success: true,
376
+ message: 'Symlink updated successfully',
377
+ symlink
378
+ };
379
+ }
380
+ catch (error) {
381
+ return {
382
+ success: false,
383
+ message: 'Failed to update symlink',
384
+ error: error.message
385
+ };
386
+ }
387
+ }
388
+ }
389
+ // Singleton instance and initialization promise
390
+ let managerInstance = null;
391
+ let initializationPromise = null;
392
+ /**
393
+ * Get or create the SymlinkManager instance
394
+ */
395
+ export async function getSymlinkManager() {
396
+ // If already initializing, wait for that to complete
397
+ if (initializationPromise) {
398
+ return initializationPromise;
399
+ }
400
+ // If already initialized, return the instance
401
+ if (managerInstance) {
402
+ return managerInstance;
403
+ }
404
+ // Start initialization
405
+ initializationPromise = (async () => {
406
+ try {
407
+ // Determine project root more reliably
408
+ let projectRoot = process.env.GAIT_PROJECT_ROOT;
409
+ if (!projectRoot) {
410
+ // Try to find the project root by looking for distinctive files
411
+ const cwd = process.cwd();
412
+ // Check if we're in packages/web
413
+ if (cwd.includes('packages/web')) {
414
+ projectRoot = path.resolve(cwd, '../..');
415
+ }
416
+ else if (cwd.includes('packages')) {
417
+ projectRoot = path.resolve(cwd, '..');
418
+ }
419
+ else {
420
+ projectRoot = cwd;
421
+ }
422
+ // Verify this is the right directory by checking for .gait
423
+ try {
424
+ await fs.access(path.join(projectRoot, '.gait'));
425
+ }
426
+ catch {
427
+ console.warn(`Could not find .gait directory at ${projectRoot}, using cwd`);
428
+ projectRoot = cwd;
429
+ }
430
+ }
431
+ console.log(`[SymlinkManager] Using project root: ${projectRoot}`);
432
+ managerInstance = new SymlinkManager(projectRoot);
433
+ await managerInstance.initialize();
434
+ return managerInstance;
435
+ }
436
+ catch (error) {
437
+ // Reset on error so we can retry
438
+ initializationPromise = null;
439
+ managerInstance = null;
440
+ throw error;
441
+ }
442
+ })();
443
+ return initializationPromise;
444
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Symlink configuration types
3
+ */
4
+ export interface SymlinkConfig {
5
+ id: string;
6
+ name: string;
7
+ description?: string;
8
+ linkPath: string;
9
+ targetPath: string;
10
+ isActive: boolean;
11
+ createdAt: string;
12
+ updatedAt: string;
13
+ }
14
+ export interface SymlinksState {
15
+ version: string;
16
+ symlinks: SymlinkConfig[];
17
+ }
18
+ export interface SymlinkOperationResult {
19
+ success: boolean;
20
+ message: string;
21
+ error?: string;
22
+ symlink?: SymlinkConfig;
23
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Symlink configuration types
3
+ */
4
+ export {};
@@ -0,0 +1,11 @@
1
+ export interface ContextInfo {
2
+ proposalId: string;
3
+ proposalPath: string;
4
+ contextPath: string;
5
+ projectRoot: string;
6
+ }
7
+ export declare function generateWelcomeMessage(context: ContextInfo): string;
8
+ export declare function generateClaudeSetupCommands(context: ContextInfo): string[];
9
+ export declare function generateContextCommands(_context: ContextInfo): string[];
10
+ export declare function generateBashAliases(_context: ContextInfo): string[];
11
+ export declare function createInitScript(context: ContextInfo): string;
@@ -0,0 +1,164 @@
1
+ // Context helper functions for terminal sessions
2
+ // These can be used to generate helpful commands and information
3
+ export function generateWelcomeMessage(context) {
4
+ return `
5
+ \x1b[32m╭─────────────────────────────────────────────────────────────╮\x1b[0m
6
+ \x1b[32m│ GAIT Terminal Session │\x1b[0m
7
+ \x1b[32m╰─────────────────────────────────────────────────────────────╯\x1b[0m
8
+
9
+ \x1b[36mProposal:\x1b[0m ${context.proposalId}
10
+ \x1b[36mProject Root:\x1b[0m ${context.projectRoot}
11
+
12
+ \x1b[33mAvailable Environment Variables:\x1b[0m
13
+ GAIT_PROPOSAL_ID="${context.proposalId}"
14
+ GAIT_CONTEXT_PATH="${context.contextPath}"
15
+ GAIT_PROPOSAL_PATH="${context.proposalPath}"
16
+
17
+ \x1b[33mQuick Commands:\x1b[0m
18
+ \x1b[32mgait-help\x1b[0m - Show this help message
19
+ \x1b[32mgait-proposal\x1b[0m - View current proposal
20
+ \x1b[32mgait-files\x1b[0m - List context files
21
+ \x1b[32mgait-claude\x1b[0m - Setup Claude Code CLI
22
+
23
+ \x1b[90mTip: Use 'gait-claude' to configure Claude Code for this proposal\x1b[0m
24
+
25
+ `;
26
+ }
27
+ export function generateClaudeSetupCommands(context) {
28
+ return [
29
+ '# Claude Code CLI Setup Commands',
30
+ '# Run these commands to configure Claude Code for this proposal:',
31
+ '',
32
+ '# 1. Set the working directory context',
33
+ `export CLAUDE_CONTEXT_PATH="${context.contextPath}"`,
34
+ `export CLAUDE_PROPOSAL_PATH="${context.proposalPath}"`,
35
+ '',
36
+ '# 2. Create a Claude Code project configuration',
37
+ 'cat > .claude-project.json << EOF',
38
+ '{',
39
+ ` "name": "GAIT Proposal ${context.proposalId}",`,
40
+ ` "description": "Working on proposal ${context.proposalId}",`,
41
+ ' "context": [',
42
+ ` "${context.proposalPath}",`,
43
+ ` "${context.contextPath}/project.md",`,
44
+ ` "${context.contextPath}/architecture.md",`,
45
+ ' ],',
46
+ ' "rules": [',
47
+ ' "Follow the project architecture patterns",',
48
+ ' "Reference the proposal requirements",',
49
+ ' "Maintain consistency with existing code"',
50
+ ' ]',
51
+ '}',
52
+ 'EOF',
53
+ '',
54
+ '# 3. Start Claude Code with context',
55
+ 'claude-code --project .claude-project.json',
56
+ '',
57
+ '# Alternative: Direct command with context files',
58
+ `claude-code --context "${context.proposalPath}" --context "${context.contextPath}/project.md"`,
59
+ ];
60
+ }
61
+ export function generateContextCommands(_context) {
62
+ return [
63
+ '# Context File Commands',
64
+ '# Use these commands to explore the project context:',
65
+ '',
66
+ '# View the current proposal',
67
+ `cat "$GAIT_PROPOSAL_PATH"`,
68
+ '',
69
+ '# View project overview',
70
+ `cat "$GAIT_CONTEXT_PATH/project.md"`,
71
+ '',
72
+ '# View architecture documentation',
73
+ `cat "$GAIT_CONTEXT_PATH/architecture.md"`,
74
+ '',
75
+ '',
76
+ '# List all context files',
77
+ `find "$GAIT_CONTEXT_PATH" -name "*.md" -type f`,
78
+ '',
79
+ '# Search for specific terms in context',
80
+ `grep -r "search_term" "$GAIT_CONTEXT_PATH"`,
81
+ ];
82
+ }
83
+ export function generateBashAliases(_context) {
84
+ return [];
85
+ }
86
+ export function createInitScript(context) {
87
+ return `#!/bin/bash
88
+ # GAIT Terminal Initialization Script
89
+ # This script is automatically sourced when a terminal session starts
90
+
91
+ # Function to show context help
92
+ gait-context-help() {
93
+ cat << 'GAIT_HELP_EOF'
94
+ ${generateWelcomeMessage(context)}
95
+ GAIT_HELP_EOF
96
+ }
97
+
98
+ # Function to show proposal content
99
+ gait-proposal() {
100
+ if [ -f "$GAIT_PROPOSAL_PATH" ]; then
101
+ echo "\\x1b[36m=== Proposal: $GAIT_PROPOSAL_ID ===\\x1b[0m"
102
+ cat "$GAIT_PROPOSAL_PATH"
103
+ else
104
+ echo "\\x1b[31mProposal file not found: $GAIT_PROPOSAL_PATH\\x1b[0m"
105
+ fi
106
+ }
107
+
108
+ # Function to list context files
109
+ gait-files() {
110
+ echo "\\x1b[36m=== Context Files ===\\x1b[0m"
111
+ if [ -d "$GAIT_CONTEXT_PATH" ]; then
112
+ find "$GAIT_CONTEXT_PATH" -name "*.md" -type f | sort
113
+ else
114
+ echo "\\x1b[31mContext directory not found: $GAIT_CONTEXT_PATH\\x1b[0m"
115
+ fi
116
+ }
117
+
118
+ # Function to setup Claude Code
119
+ gait-claude() {
120
+ echo "\\x1b[36m=== Claude Code Setup ===\\x1b[0m"
121
+
122
+ # Check if Claude Code is available
123
+ if ! command -v claude-code &> /dev/null; then
124
+ echo "\\x1b[33mClaude Code CLI not found. Please install it first:\\x1b[0m"
125
+ echo "npm install -g @anthropic-ai/claude-code"
126
+ return 1
127
+ fi
128
+
129
+ # Create project configuration
130
+ cat > .claude-project.json << 'EOF'
131
+ {
132
+ "name": "GAIT Proposal ${context.proposalId}",
133
+ "description": "Working on proposal ${context.proposalId}",
134
+ "context": [
135
+ "${context.proposalPath}",
136
+ "${context.contextPath}/project.md",
137
+ "${context.contextPath}/architecture.md",
138
+ ],
139
+ "rules": [
140
+ "Follow the project architecture patterns",
141
+ "Reference the proposal requirements",
142
+ "Maintain consistency with existing code"
143
+ ]
144
+ }
145
+ EOF
146
+
147
+ echo "\\x1b[32mClaude Code project configuration created!\\x1b[0m"
148
+ echo "\\x1b[33mRun: claude-code --project .claude-project.json\\x1b[0m"
149
+ }
150
+
151
+ # Create helpful alias for help command
152
+ alias gait-help='gait-context-help'
153
+
154
+ # Clear screen first to avoid any leftover content
155
+ clear
156
+
157
+ # Show welcome message
158
+ gait-context-help
159
+
160
+ # Set a custom prompt to show the proposal ID
161
+ # This will be replaced with shell-specific syntax in prepareShellInit
162
+ export PS1="[${context.proposalId}] \\w $ "
163
+ `;
164
+ }
@@ -0,0 +1,2 @@
1
+ import { TerminalManager } from './terminal-manager.js';
2
+ export declare function getGlobalTerminalManager(): TerminalManager;
@@ -0,0 +1,15 @@
1
+ import { TerminalManager } from './terminal-manager.js';
2
+ // Use a type assertion to avoid TypeScript conflicts
3
+ const globalAny = global;
4
+ // Initialize the global singleton if it doesn't exist
5
+ const getOrCreateManager = () => {
6
+ if (!globalAny.gaitTerminalManager) {
7
+ globalAny.gaitTerminalManager = new TerminalManager();
8
+ }
9
+ return globalAny.gaitTerminalManager;
10
+ };
11
+ // Initialize on module load
12
+ getOrCreateManager();
13
+ export function getGlobalTerminalManager() {
14
+ return getOrCreateManager();
15
+ }