@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,13 @@
1
+ /**
2
+ * Find the .gait directory by traversing up from the current working directory
3
+ * If GAIT_DATA_PATH is set (from CLI), use that instead
4
+ */
5
+ export declare function findGaitDirectory(startDir?: string): Promise<string | null>;
6
+ /**
7
+ * Get the path to a context file, automatically finding the .gait directory
8
+ */
9
+ export declare function getContextFilePath(fileName: string): Promise<string | null>;
10
+ /**
11
+ * Get the .gait config to read storage path settings
12
+ */
13
+ export declare function getGaitConfig(): Promise<any | null>;
@@ -0,0 +1,57 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ /**
4
+ * Find the .gait directory by traversing up from the current working directory
5
+ * If GAIT_DATA_PATH is set (from CLI), use that instead
6
+ */
7
+ export async function findGaitDirectory(startDir) {
8
+ // If running from CLI (gait serve), use the directory where command was run
9
+ if (process.env.GAIT_DATA_PATH) {
10
+ const gaitPath = path.join(process.env.GAIT_DATA_PATH, '.gait');
11
+ try {
12
+ await fs.access(gaitPath);
13
+ return gaitPath;
14
+ }
15
+ catch {
16
+ // Fall through to directory traversal
17
+ }
18
+ }
19
+ // Otherwise traverse up from start directory
20
+ let currentDir = startDir || process.cwd();
21
+ while (currentDir !== path.parse(currentDir).root) {
22
+ const gaitPath = path.join(currentDir, '.gait');
23
+ try {
24
+ await fs.access(gaitPath);
25
+ return gaitPath;
26
+ }
27
+ catch {
28
+ currentDir = path.dirname(currentDir);
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ /**
34
+ * Get the path to a context file, automatically finding the .gait directory
35
+ */
36
+ export async function getContextFilePath(fileName) {
37
+ const gaitDir = await findGaitDirectory();
38
+ if (!gaitDir)
39
+ return null;
40
+ return path.join(gaitDir, 'context', fileName);
41
+ }
42
+ /**
43
+ * Get the .gait config to read storage path settings
44
+ */
45
+ export async function getGaitConfig() {
46
+ const gaitDir = await findGaitDirectory();
47
+ if (!gaitDir)
48
+ return null;
49
+ const configPath = path.join(gaitDir, 'config.json');
50
+ try {
51
+ const config = await fs.readFile(configPath, 'utf-8');
52
+ return JSON.parse(config);
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Gets the consistent project root path for GAIT operations.
3
+ * This ensures that both symlink operations use the same base path.
4
+ */
5
+ export declare function getProjectRoot(): Promise<string>;
6
+ /**
7
+ * Gets the path to the CLAUDE.md symlink in the project root
8
+ */
9
+ export declare function getSymlinkPath(): Promise<string>;
10
+ /**
11
+ * Gets the path to the actual CLAUDE.md file in .gait/rules
12
+ */
13
+ export declare function getTargetPath(): Promise<string>;
@@ -0,0 +1,57 @@
1
+ import path from 'path';
2
+ import { promises as fs } from 'fs';
3
+ /**
4
+ * Gets the consistent project root path for GAIT operations.
5
+ * This ensures that both symlink operations use the same base path.
6
+ */
7
+ export async function getProjectRoot() {
8
+ // First, check if GAIT_DATA_PATH is explicitly set
9
+ if (process.env.GAIT_DATA_PATH) {
10
+ return path.resolve(process.env.GAIT_DATA_PATH);
11
+ }
12
+ // In development, we need to find the actual project root
13
+ // Starting from the current working directory
14
+ let currentPath = process.cwd();
15
+ // If we're in a Next.js app, we might be in packages/web
16
+ // We need to traverse up to find the actual project root
17
+ while (currentPath !== path.dirname(currentPath)) {
18
+ try {
19
+ // Check if we're at the project root by looking for distinctive files
20
+ const possibleRoot = currentPath;
21
+ const hasPackageJson = await fs.access(path.join(possibleRoot, 'package.json')).then(() => true).catch(() => false);
22
+ const hasGaitDir = await fs.access(path.join(possibleRoot, '.gait')).then(() => true).catch(() => false);
23
+ const hasPnpmWorkspace = await fs.access(path.join(possibleRoot, 'pnpm-workspace.yaml')).then(() => true).catch(() => false);
24
+ // If this looks like the project root, use it
25
+ if (hasPackageJson && hasGaitDir && hasPnpmWorkspace) {
26
+ return possibleRoot;
27
+ }
28
+ // Otherwise, check parent directory
29
+ currentPath = path.dirname(currentPath);
30
+ }
31
+ catch {
32
+ currentPath = path.dirname(currentPath);
33
+ }
34
+ }
35
+ // Fallback: if we can't find the project root, use the current working directory
36
+ // But also try a known relative path from the web package
37
+ const webPackagePath = process.cwd();
38
+ if (webPackagePath.includes('packages/web')) {
39
+ // We're in the web package, go up to the root
40
+ return path.resolve(webPackagePath, '../..');
41
+ }
42
+ return process.cwd();
43
+ }
44
+ /**
45
+ * Gets the path to the CLAUDE.md symlink in the project root
46
+ */
47
+ export async function getSymlinkPath() {
48
+ const projectRoot = await getProjectRoot();
49
+ return path.join(projectRoot, 'CLAUDE.md');
50
+ }
51
+ /**
52
+ * Gets the path to the actual CLAUDE.md file in .gait/rules
53
+ */
54
+ export async function getTargetPath() {
55
+ const projectRoot = await getProjectRoot();
56
+ return path.join(projectRoot, '.gait', 'rules', 'CLAUDE.md');
57
+ }
@@ -0,0 +1,28 @@
1
+ import { ChangeProposal, CPStatus } from '@lovelybunch/types';
2
+ export interface CPFilter {
3
+ status?: CPStatus;
4
+ author?: string;
5
+ priority?: string;
6
+ tags?: string[];
7
+ }
8
+ export interface StorageAdapter {
9
+ createCP(cp: ChangeProposal): Promise<void>;
10
+ getCP(id: string): Promise<ChangeProposal | null>;
11
+ updateCP(id: string, cp: Partial<ChangeProposal>): Promise<void>;
12
+ deleteCP(id: string): Promise<void>;
13
+ listCPs(filter?: CPFilter): Promise<ChangeProposal[]>;
14
+ searchCPs(query: string): Promise<ChangeProposal[]>;
15
+ }
16
+ export declare class FileStorageAdapter implements StorageAdapter {
17
+ private basePath;
18
+ constructor(basePath?: string);
19
+ ensureDirectories(): Promise<void>;
20
+ createCP(cp: ChangeProposal): Promise<void>;
21
+ getCP(id: string): Promise<ChangeProposal | null>;
22
+ updateCP(id: string, updates: Partial<ChangeProposal>): Promise<void>;
23
+ deleteCP(id: string): Promise<void>;
24
+ listCPs(filter?: CPFilter): Promise<ChangeProposal[]>;
25
+ searchCPs(query: string): Promise<ChangeProposal[]>;
26
+ private frontmatterToCP;
27
+ private getDefaultContent;
28
+ }
@@ -0,0 +1,224 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import matter from 'gray-matter';
4
+ import Fuse from 'fuse.js';
5
+ export class FileStorageAdapter {
6
+ basePath;
7
+ constructor(basePath) {
8
+ // Use environment variable if available (for web server), otherwise default to .gait
9
+ this.basePath = basePath || (process.env.GAIT_DATA_PATH ?
10
+ path.join(process.env.GAIT_DATA_PATH, '.gait') :
11
+ '.gait');
12
+ }
13
+ async ensureDirectories() {
14
+ const dirs = ['proposals', 'specs', 'flags', 'experiments', 'templates'];
15
+ for (const dir of dirs) {
16
+ await fs.mkdir(path.join(this.basePath, dir), { recursive: true });
17
+ }
18
+ }
19
+ async createCP(cp) {
20
+ await this.ensureDirectories();
21
+ // Extract content from the proposal if it exists
22
+ const { content, ...frontmatter } = cp;
23
+ // Convert the proposal to markdown with YAML frontmatter
24
+ const markdown = matter.stringify(content || this.getDefaultContent(cp), {
25
+ // Required fields
26
+ id: cp.id,
27
+ intent: cp.intent,
28
+ createdAt: cp.metadata.createdAt.toISOString(),
29
+ updatedAt: cp.metadata.updatedAt.toISOString(),
30
+ status: cp.status,
31
+ priority: cp.metadata.priority || 'medium',
32
+ // Author information
33
+ author: {
34
+ id: cp.author.id,
35
+ name: cp.author.name,
36
+ email: cp.author.email || '',
37
+ role: 'engineer', // Default role
38
+ type: cp.author.type
39
+ },
40
+ // Plan
41
+ plan: {
42
+ estimatedDuration: 'TBD',
43
+ dependencies: [],
44
+ steps: cp.planSteps.map(step => ({
45
+ id: step.id,
46
+ description: step.description,
47
+ status: step.status
48
+ }))
49
+ },
50
+ // Metadata
51
+ tags: cp.metadata.tags || [],
52
+ labels: [],
53
+ comments: cp.comments || []
54
+ });
55
+ const filePath = path.join(this.basePath, 'proposals', `${cp.id}.md`);
56
+ await fs.writeFile(filePath, markdown, 'utf-8');
57
+ }
58
+ async getCP(id) {
59
+ try {
60
+ const filePath = path.join(this.basePath, 'proposals', `${id}.md`);
61
+ const fileContent = await fs.readFile(filePath, 'utf-8');
62
+ const { data, content } = matter(fileContent);
63
+ // Convert the frontmatter back to a ChangeProposal
64
+ return this.frontmatterToCP(data, content);
65
+ }
66
+ catch (error) {
67
+ if (error.code === 'ENOENT')
68
+ return null;
69
+ throw error;
70
+ }
71
+ }
72
+ async updateCP(id, updates) {
73
+ const existing = await this.getCP(id);
74
+ if (!existing)
75
+ throw new Error(`CP ${id} not found`);
76
+ // Handle comments separately as they're stored at root level in file format
77
+ let updated = {
78
+ ...existing,
79
+ ...updates,
80
+ metadata: {
81
+ ...existing.metadata,
82
+ ...updates.metadata,
83
+ updatedAt: new Date()
84
+ }
85
+ };
86
+ // If comments are being updated, store them at the root level for the file format
87
+ if (updates.comments) {
88
+ updated.comments = updates.comments;
89
+ }
90
+ await this.createCP(updated);
91
+ }
92
+ async deleteCP(id) {
93
+ const filePath = path.join(this.basePath, 'proposals', `${id}.md`);
94
+ await fs.unlink(filePath);
95
+ }
96
+ async listCPs(filter) {
97
+ const proposalsDir = path.join(this.basePath, 'proposals');
98
+ try {
99
+ const files = await fs.readdir(proposalsDir);
100
+ const proposals = await Promise.all(files
101
+ .filter(f => f.endsWith('.md'))
102
+ .map(async (file) => {
103
+ const content = await fs.readFile(path.join(proposalsDir, file), 'utf-8');
104
+ const { data, content: body } = matter(content);
105
+ return this.frontmatterToCP(data, body);
106
+ }));
107
+ // Apply filters
108
+ return proposals.filter(cp => {
109
+ if (!cp)
110
+ return false;
111
+ if (filter?.status && cp.status !== filter.status)
112
+ return false;
113
+ if (filter?.author && cp.author.id !== filter.author)
114
+ return false;
115
+ if (filter?.priority && cp.metadata.priority !== filter.priority)
116
+ return false;
117
+ if (filter?.tags && filter.tags.length > 0) {
118
+ const cpTags = cp.metadata.tags || [];
119
+ if (!filter.tags.some(tag => cpTags.includes(tag)))
120
+ return false;
121
+ }
122
+ return true;
123
+ }).filter(Boolean);
124
+ }
125
+ catch (error) {
126
+ if (error.code === 'ENOENT')
127
+ return [];
128
+ throw error;
129
+ }
130
+ }
131
+ async searchCPs(query) {
132
+ const allCPs = await this.listCPs();
133
+ const fuse = new Fuse(allCPs, {
134
+ keys: [
135
+ { name: 'intent', weight: 2 },
136
+ { name: 'content', weight: 1 },
137
+ { name: 'author.name', weight: 0.5 },
138
+ { name: 'metadata.tags', weight: 1 }
139
+ ],
140
+ threshold: 0.3,
141
+ includeScore: true
142
+ });
143
+ return fuse.search(query).map(result => result.item);
144
+ }
145
+ frontmatterToCP(data, content) {
146
+ // Handle both old and new format files
147
+ const steps = data.plan?.steps || data.steps || [];
148
+ // Transform comments to match expected format
149
+ const transformedComments = (data.comments || []).map((comment) => ({
150
+ id: comment.id,
151
+ text: comment.content || comment.text || '', // Handle both content and text fields
152
+ author: typeof comment.author === 'string' ? comment.author : comment.author?.name || 'Unknown',
153
+ timestamp: comment.createdAt || comment.timestamp || new Date().toISOString()
154
+ }));
155
+ return {
156
+ id: data.id,
157
+ intent: data.intent,
158
+ author: {
159
+ id: data.author?.id || data.author?.name || 'unknown',
160
+ name: data.author?.name || 'Unknown',
161
+ email: data.author?.email,
162
+ type: data.author?.type || 'human'
163
+ },
164
+ productSpecRef: data.productSpecRef,
165
+ planSteps: steps.map((step) => ({
166
+ id: step.id || `step-${Date.now()}`,
167
+ description: step.description || step.title || 'Unnamed step',
168
+ status: step.status || 'pending',
169
+ command: step.command,
170
+ expectedOutcome: step.expectedOutcome,
171
+ output: step.output,
172
+ error: step.error,
173
+ executedAt: step.executedAt ? new Date(step.executedAt) : undefined
174
+ })),
175
+ evidence: data.evidence || [],
176
+ policies: data.policies || [],
177
+ featureFlags: data.featureFlags || [],
178
+ experiments: data.experiments || [],
179
+ telemetryContracts: data.telemetryContracts || [],
180
+ releasePlan: data.releasePlan || { strategy: 'immediate' },
181
+ status: data.status || 'draft',
182
+ metadata: {
183
+ createdAt: new Date(data.createdAt || Date.now()),
184
+ updatedAt: new Date(data.updatedAt || Date.now()),
185
+ reviewers: data.reviewers || [],
186
+ aiInteractions: data.aiInteractions || [],
187
+ tags: data.tags || [],
188
+ priority: data.priority || 'medium',
189
+ comments: transformedComments
190
+ },
191
+ content // Store the markdown content
192
+ };
193
+ }
194
+ getDefaultContent(cp) {
195
+ return `# ${cp.intent}
196
+
197
+ ## Problem Statement
198
+ Describe the problem this change proposal addresses.
199
+
200
+ ## Proposed Solution
201
+ Describe the solution you're proposing.
202
+
203
+ ### Key Features
204
+ - Feature 1
205
+ - Feature 2
206
+ - Feature 3
207
+
208
+ ## Implementation Details
209
+ Describe the technical implementation approach.
210
+
211
+ ## Testing Strategy
212
+ - [ ] Unit tests
213
+ - [ ] Integration tests
214
+ - [ ] Manual testing
215
+
216
+ ## Rollout Plan
217
+ 1. Development and testing
218
+ 2. Staging deployment
219
+ 3. Production rollout
220
+
221
+ ## Success Metrics
222
+ Define how you'll measure the success of this change.`;
223
+ }
224
+ }
@@ -0,0 +1,66 @@
1
+ import { SymlinkConfig, SymlinkOperationResult } from './types.js';
2
+ /**
3
+ * SymlinkManager handles all symlink operations and persists state
4
+ */
5
+ export declare class SymlinkManager {
6
+ private configPath;
7
+ private projectRoot;
8
+ private state;
9
+ constructor(projectRoot: string);
10
+ /**
11
+ * Initialize the manager and load existing configuration
12
+ */
13
+ initialize(): Promise<void>;
14
+ /**
15
+ * Create default configuration with the CLAUDE.md symlink
16
+ */
17
+ private createDefaultConfig;
18
+ /**
19
+ * Validate symlinks against actual filesystem state
20
+ */
21
+ private validateSymlinks;
22
+ /**
23
+ * Save current state to configuration file
24
+ */
25
+ private saveState;
26
+ /**
27
+ * Get all symlink configurations
28
+ */
29
+ getSymlinks(): Promise<SymlinkConfig[]>;
30
+ /**
31
+ * Get a specific symlink by ID
32
+ */
33
+ getSymlink(id: string): Promise<SymlinkConfig | undefined>;
34
+ /**
35
+ * Add a new symlink configuration
36
+ */
37
+ addSymlink(config: Omit<SymlinkConfig, 'id' | 'createdAt' | 'updatedAt'>): Promise<SymlinkOperationResult>;
38
+ /**
39
+ * Toggle a symlink on/off
40
+ */
41
+ toggleSymlink(id: string): Promise<SymlinkOperationResult>;
42
+ /**
43
+ * Expand tilde in path to actual home directory
44
+ */
45
+ private expandTilde;
46
+ /**
47
+ * Create a symlink on the filesystem
48
+ */
49
+ private createSymlink;
50
+ /**
51
+ * Remove a symlink from the filesystem
52
+ */
53
+ private removeSymlink;
54
+ /**
55
+ * Delete a symlink configuration entirely
56
+ */
57
+ deleteSymlink(id: string): Promise<SymlinkOperationResult>;
58
+ /**
59
+ * Update a symlink configuration
60
+ */
61
+ updateSymlink(id: string, updates: Partial<Omit<SymlinkConfig, 'id' | 'createdAt'>>): Promise<SymlinkOperationResult>;
62
+ }
63
+ /**
64
+ * Get or create the SymlinkManager instance
65
+ */
66
+ export declare function getSymlinkManager(): Promise<SymlinkManager>;