@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.
- package/dist/lib/gait-path.d.ts +13 -0
- package/dist/lib/gait-path.js +57 -0
- package/dist/lib/project-paths.d.ts +13 -0
- package/dist/lib/project-paths.js +57 -0
- package/dist/lib/storage/file-storage.d.ts +28 -0
- package/dist/lib/storage/file-storage.js +224 -0
- package/dist/lib/symlinks/symlink-manager.d.ts +66 -0
- package/dist/lib/symlinks/symlink-manager.js +444 -0
- package/dist/lib/symlinks/types.d.ts +23 -0
- package/dist/lib/symlinks/types.js +4 -0
- package/dist/lib/terminal/context-helper.d.ts +11 -0
- package/dist/lib/terminal/context-helper.js +164 -0
- package/dist/lib/terminal/global-manager.d.ts +2 -0
- package/dist/lib/terminal/global-manager.js +15 -0
- package/dist/lib/terminal/shell-utils.d.ts +33 -0
- package/dist/lib/terminal/shell-utils.js +176 -0
- package/dist/lib/terminal/terminal-manager.d.ts +26 -0
- package/dist/lib/terminal/terminal-manager.js +276 -0
- package/dist/lib/user-preferences.d.ts +48 -0
- package/dist/lib/user-preferences.js +87 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/routes/api/symlink-status/route.d.ts +1 -0
- package/dist/routes/api/symlink-status/route.js +37 -0
- package/dist/routes/api/symlinks/[id]/route.d.ts +19 -0
- package/dist/routes/api/symlinks/[id]/route.js +95 -0
- package/dist/routes/api/symlinks/[id]/toggle/route.d.ts +11 -0
- package/dist/routes/api/symlinks/[id]/toggle/route.js +32 -0
- package/dist/routes/api/symlinks/debug/route.d.ts +1 -0
- package/dist/routes/api/symlinks/debug/route.js +35 -0
- package/dist/routes/api/symlinks/route.d.ts +9 -0
- package/dist/routes/api/symlinks/route.js +72 -0
- package/dist/routes/api/toggle-symlink/route.d.ts +2 -0
- package/dist/routes/api/toggle-symlink/route.js +94 -0
- package/dist/routes/api/v1/agents/[id]/index.d.ts +1 -0
- package/dist/routes/api/v1/agents/[id]/index.js +1 -0
- package/dist/routes/api/v1/agents/[id]/route.d.ts +3 -0
- package/dist/routes/api/v1/agents/[id]/route.js +163 -0
- package/dist/routes/api/v1/agents/index.d.ts +1 -0
- package/dist/routes/api/v1/agents/index.js +1 -0
- package/dist/routes/api/v1/agents/route.d.ts +3 -0
- package/dist/routes/api/v1/agents/route.js +133 -0
- package/dist/routes/api/v1/ai/index.d.ts +3 -0
- package/dist/routes/api/v1/ai/index.js +5 -0
- package/dist/routes/api/v1/ai/route.d.ts +8 -0
- package/dist/routes/api/v1/ai/route.js +86 -0
- package/dist/routes/api/v1/chats/[id]/index.d.ts +3 -0
- package/dist/routes/api/v1/chats/[id]/index.js +6 -0
- package/dist/routes/api/v1/chats/[id]/route.d.ts +12 -0
- package/dist/routes/api/v1/chats/[id]/route.js +31 -0
- package/dist/routes/api/v1/chats/index.d.ts +3 -0
- package/dist/routes/api/v1/chats/index.js +6 -0
- package/dist/routes/api/v1/chats/route.d.ts +32 -0
- package/dist/routes/api/v1/chats/route.js +67 -0
- package/dist/routes/api/v1/config/index.d.ts +3 -0
- package/dist/routes/api/v1/config/index.js +5 -0
- package/dist/routes/api/v1/config/route.d.ts +9 -0
- package/dist/routes/api/v1/config/route.js +29 -0
- package/dist/routes/api/v1/context/[...path]/route.d.ts +16 -0
- package/dist/routes/api/v1/context/[...path]/route.js +107 -0
- package/dist/routes/api/v1/context/architecture/route.d.ts +3 -0
- package/dist/routes/api/v1/context/architecture/route.js +198 -0
- package/dist/routes/api/v1/context/index.d.ts +3 -0
- package/dist/routes/api/v1/context/index.js +9 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/index.d.ts +1 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/index.js +1 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/route.d.ts +3 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/route.js +165 -0
- package/dist/routes/api/v1/context/knowledge/index.d.ts +1 -0
- package/dist/routes/api/v1/context/knowledge/index.js +1 -0
- package/dist/routes/api/v1/context/knowledge/route.d.ts +3 -0
- package/dist/routes/api/v1/context/knowledge/route.js +121 -0
- package/dist/routes/api/v1/context/project/route.d.ts +3 -0
- package/dist/routes/api/v1/context/project/route.js +153 -0
- package/dist/routes/api/v1/proposals/[id]/route.d.ts +337 -0
- package/dist/routes/api/v1/proposals/[id]/route.js +99 -0
- package/dist/routes/api/v1/proposals/index.d.ts +3 -0
- package/dist/routes/api/v1/proposals/index.js +10 -0
- package/dist/routes/api/v1/proposals/route.d.ts +315 -0
- package/dist/routes/api/v1/proposals/route.js +103 -0
- package/dist/routes/api/v1/resources/[id]/index.d.ts +3 -0
- package/dist/routes/api/v1/resources/[id]/index.js +7 -0
- package/dist/routes/api/v1/resources/[id]/route.d.ts +46 -0
- package/dist/routes/api/v1/resources/[id]/route.js +143 -0
- package/dist/routes/api/v1/resources/[id]/thumbnail/index.d.ts +3 -0
- package/dist/routes/api/v1/resources/[id]/thumbnail/index.js +5 -0
- package/dist/routes/api/v1/resources/[id]/thumbnail/route.d.ts +2 -0
- package/dist/routes/api/v1/resources/[id]/thumbnail/route.js +50 -0
- package/dist/routes/api/v1/resources/index.d.ts +3 -0
- package/dist/routes/api/v1/resources/index.js +6 -0
- package/dist/routes/api/v1/resources/route.d.ts +51 -0
- package/dist/routes/api/v1/resources/route.js +147 -0
- package/dist/routes/api/v1/search/route.d.ts +3 -0
- package/dist/routes/api/v1/search/route.js +39 -0
- package/dist/routes/api/v1/terminal/[proposalId]/create/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[proposalId]/create/index.js +5 -0
- package/dist/routes/api/v1/terminal/[proposalId]/create/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[proposalId]/create/route.js +27 -0
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.js +5 -0
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.js +21 -0
- package/dist/routes/api/v1/terminal/[proposalId]/resize/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[proposalId]/resize/index.js +5 -0
- package/dist/routes/api/v1/terminal/[proposalId]/resize/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[proposalId]/resize/route.js +21 -0
- package/dist/routes/api/v1/terminal/sessions/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/sessions/index.js +5 -0
- package/dist/routes/api/v1/terminal/sessions/route.d.ts +6 -0
- package/dist/routes/api/v1/terminal/sessions/route.js +29 -0
- package/dist/routes/api/v1/user/index.d.ts +3 -0
- package/dist/routes/api/v1/user/index.js +5 -0
- package/dist/routes/api/v1/user/preferences/route.d.ts +11 -0
- package/dist/routes/api/v1/user/preferences/route.js +31 -0
- package/dist/routes/api/v1/user/profile/route.d.ts +11 -0
- package/dist/routes/api/v1/user/profile/route.js +31 -0
- package/dist/routes/api/v1/user/settings/index.d.ts +1 -0
- package/dist/routes/api/v1/user/settings/index.js +1 -0
- package/dist/routes/api/v1/user/settings/route.d.ts +3 -0
- package/dist/routes/api/v1/user/settings/route.js +51 -0
- package/dist/server-with-static.d.ts +4 -0
- package/dist/server-with-static.js +144 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +91 -0
- package/package.json +42 -0
- package/static/assets/index-BvTnrm0O.js +576 -0
- package/static/assets/index-Cm5dZHTl.css +33 -0
- package/static/assets/index-ORkAkJNi.js +576 -0
- package/static/assets/index-_Keadpms.js +576 -0
- package/static/index.html +17 -0
- package/static/vite.svg +1 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
const app = new Hono();
|
|
6
|
+
function getArchitecturePath() {
|
|
7
|
+
const basePath = process.env.GAIT_DATA_PATH ?
|
|
8
|
+
path.resolve(process.env.GAIT_DATA_PATH, '.gait') :
|
|
9
|
+
path.resolve(process.cwd(), '.gait');
|
|
10
|
+
return path.join(basePath, 'context');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* GET /api/v1/context/architecture
|
|
14
|
+
* Load architecture document
|
|
15
|
+
*/
|
|
16
|
+
app.get('/', async (c) => {
|
|
17
|
+
try {
|
|
18
|
+
const architecturePath = getArchitecturePath();
|
|
19
|
+
// Ensure directory exists
|
|
20
|
+
await fs.mkdir(architecturePath, { recursive: true });
|
|
21
|
+
// Look for architecture.md file
|
|
22
|
+
const filePath = path.join(architecturePath, 'architecture.md');
|
|
23
|
+
try {
|
|
24
|
+
const fileContent = await fs.readFile(filePath, 'utf-8');
|
|
25
|
+
const { data, content } = matter(fileContent);
|
|
26
|
+
// Extract title from first heading or use default
|
|
27
|
+
const title = content.match(/^#\s+(.+)$/m)?.[1] || 'Architecture Overview';
|
|
28
|
+
const document = {
|
|
29
|
+
filename: 'architecture.md',
|
|
30
|
+
metadata: {
|
|
31
|
+
...data,
|
|
32
|
+
updated: data.updated || new Date().toISOString().split('T')[0],
|
|
33
|
+
type: 'architecture',
|
|
34
|
+
category: 'design',
|
|
35
|
+
tags: data.tags || []
|
|
36
|
+
},
|
|
37
|
+
content,
|
|
38
|
+
title
|
|
39
|
+
};
|
|
40
|
+
return c.json({
|
|
41
|
+
success: true,
|
|
42
|
+
document
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (fileError) {
|
|
46
|
+
// If file doesn't exist, return default content
|
|
47
|
+
const defaultContent = `# Architecture Overview
|
|
48
|
+
|
|
49
|
+
This document describes the overall architecture and design patterns used in this project.
|
|
50
|
+
|
|
51
|
+
## System Architecture
|
|
52
|
+
|
|
53
|
+
Describe the high-level system architecture, including major components and their relationships.
|
|
54
|
+
|
|
55
|
+
## Design Patterns
|
|
56
|
+
|
|
57
|
+
Document the key design patterns and architectural principles followed in this project.
|
|
58
|
+
|
|
59
|
+
### Frontend Architecture
|
|
60
|
+
|
|
61
|
+
- Component structure
|
|
62
|
+
- State management
|
|
63
|
+
- Routing approach
|
|
64
|
+
- UI/UX patterns
|
|
65
|
+
|
|
66
|
+
### Backend Architecture
|
|
67
|
+
|
|
68
|
+
- API design
|
|
69
|
+
- Data layer
|
|
70
|
+
- Service architecture
|
|
71
|
+
- Authentication & authorization
|
|
72
|
+
|
|
73
|
+
## Data Flow
|
|
74
|
+
|
|
75
|
+
Explain how data flows through the system, including:
|
|
76
|
+
|
|
77
|
+
- Request/response cycles
|
|
78
|
+
- Data transformations
|
|
79
|
+
- Caching strategies
|
|
80
|
+
- Error handling
|
|
81
|
+
|
|
82
|
+
## Technology Decisions
|
|
83
|
+
|
|
84
|
+
Document key technology choices and the reasoning behind them:
|
|
85
|
+
|
|
86
|
+
- Framework selections
|
|
87
|
+
- Database choices
|
|
88
|
+
- Third-party integrations
|
|
89
|
+
- Development tools
|
|
90
|
+
|
|
91
|
+
## Deployment Architecture
|
|
92
|
+
|
|
93
|
+
Describe the deployment strategy and infrastructure:
|
|
94
|
+
|
|
95
|
+
- Environment setup
|
|
96
|
+
- CI/CD pipeline
|
|
97
|
+
- Monitoring and logging
|
|
98
|
+
- Scaling considerations
|
|
99
|
+
|
|
100
|
+
## Security Considerations
|
|
101
|
+
|
|
102
|
+
Outline security measures and best practices:
|
|
103
|
+
|
|
104
|
+
- Authentication mechanisms
|
|
105
|
+
- Data protection
|
|
106
|
+
- API security
|
|
107
|
+
- Infrastructure security
|
|
108
|
+
|
|
109
|
+
## Performance Considerations
|
|
110
|
+
|
|
111
|
+
Document performance optimization strategies:
|
|
112
|
+
|
|
113
|
+
- Caching layers
|
|
114
|
+
- Database optimization
|
|
115
|
+
- Frontend performance
|
|
116
|
+
- Monitoring and metrics
|
|
117
|
+
`;
|
|
118
|
+
const document = {
|
|
119
|
+
filename: 'architecture.md',
|
|
120
|
+
metadata: {
|
|
121
|
+
version: '1.0',
|
|
122
|
+
updated: new Date().toISOString().split('T')[0],
|
|
123
|
+
type: 'architecture',
|
|
124
|
+
category: 'design',
|
|
125
|
+
tags: []
|
|
126
|
+
},
|
|
127
|
+
content: defaultContent,
|
|
128
|
+
title: 'Architecture Overview'
|
|
129
|
+
};
|
|
130
|
+
return c.json({
|
|
131
|
+
success: true,
|
|
132
|
+
document
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
console.error('Error loading architecture document:', error);
|
|
138
|
+
return c.json({
|
|
139
|
+
success: false,
|
|
140
|
+
error: 'Failed to load architecture document'
|
|
141
|
+
}, 500);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
/**
|
|
145
|
+
* PUT /api/v1/context/architecture
|
|
146
|
+
* Update architecture document
|
|
147
|
+
*/
|
|
148
|
+
app.put('/', async (c) => {
|
|
149
|
+
try {
|
|
150
|
+
const body = await c.req.json();
|
|
151
|
+
if (!body.content) {
|
|
152
|
+
return c.json({ success: false, error: 'Content is required' }, 400);
|
|
153
|
+
}
|
|
154
|
+
const architecturePath = getArchitecturePath();
|
|
155
|
+
await fs.mkdir(architecturePath, { recursive: true });
|
|
156
|
+
const filePath = path.join(architecturePath, 'architecture.md');
|
|
157
|
+
// Read current content if it exists
|
|
158
|
+
let currentData = {};
|
|
159
|
+
try {
|
|
160
|
+
const currentContent = await fs.readFile(filePath, 'utf-8');
|
|
161
|
+
const { data } = matter(currentContent);
|
|
162
|
+
currentData = data;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// File doesn't exist, use defaults
|
|
166
|
+
}
|
|
167
|
+
// Prepare updated metadata
|
|
168
|
+
const updatedMetadata = {
|
|
169
|
+
...currentData,
|
|
170
|
+
...body.metadata,
|
|
171
|
+
updated: new Date().toISOString().split('T')[0],
|
|
172
|
+
type: 'architecture',
|
|
173
|
+
category: 'design',
|
|
174
|
+
version: currentData.version || '1.0'
|
|
175
|
+
};
|
|
176
|
+
// Create the markdown content with frontmatter
|
|
177
|
+
const fileContent = matter.stringify(body.content, updatedMetadata);
|
|
178
|
+
await fs.writeFile(filePath, fileContent, 'utf-8');
|
|
179
|
+
// Extract updated title
|
|
180
|
+
const title = body.title ||
|
|
181
|
+
body.content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
182
|
+
'Architecture Overview';
|
|
183
|
+
return c.json({
|
|
184
|
+
success: true,
|
|
185
|
+
document: {
|
|
186
|
+
filename: 'architecture.md',
|
|
187
|
+
title,
|
|
188
|
+
metadata: updatedMetadata,
|
|
189
|
+
content: body.content
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
console.error('Error updating architecture document:', error);
|
|
195
|
+
return c.json({ success: false, error: 'Failed to update architecture document' }, 500);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
export default app;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import project from './project/route.js';
|
|
3
|
+
import architecture from './architecture/route.js';
|
|
4
|
+
import knowledge from './knowledge/index.js';
|
|
5
|
+
const context = new Hono();
|
|
6
|
+
context.route('/project', project);
|
|
7
|
+
context.route('/architecture', architecture);
|
|
8
|
+
context.route('/knowledge', knowledge);
|
|
9
|
+
export default context;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
const app = new Hono();
|
|
6
|
+
function getKnowledgePath() {
|
|
7
|
+
const basePath = process.env.GAIT_DATA_PATH ?
|
|
8
|
+
path.resolve(process.env.GAIT_DATA_PATH, '.gait') :
|
|
9
|
+
path.resolve(process.cwd(), '.gait');
|
|
10
|
+
return path.join(basePath, 'context', 'knowledge');
|
|
11
|
+
}
|
|
12
|
+
function generateFilename(title) {
|
|
13
|
+
// Convert title to filename-safe format
|
|
14
|
+
return title
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
|
|
17
|
+
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
18
|
+
.replace(/--+/g, '-') // Replace multiple hyphens with single
|
|
19
|
+
.replace(/^-|-$/g, '') // Remove leading/trailing hyphens
|
|
20
|
+
+ '.md';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* GET /api/v1/context/knowledge/:filename
|
|
24
|
+
* Load a specific knowledge document
|
|
25
|
+
*/
|
|
26
|
+
app.get('/:filename', async (c) => {
|
|
27
|
+
try {
|
|
28
|
+
const filename = c.req.param('filename');
|
|
29
|
+
const knowledgePath = getKnowledgePath();
|
|
30
|
+
const actualFilename = filename.endsWith('.md') ? filename : `${filename}.md`;
|
|
31
|
+
const filePath = path.join(knowledgePath, actualFilename);
|
|
32
|
+
const fileContent = await fs.readFile(filePath, 'utf-8');
|
|
33
|
+
const { data, content } = matter(fileContent);
|
|
34
|
+
// Extract title from first heading or use filename
|
|
35
|
+
const title = content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
36
|
+
actualFilename.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
37
|
+
const document = {
|
|
38
|
+
filename: actualFilename,
|
|
39
|
+
metadata: {
|
|
40
|
+
...data,
|
|
41
|
+
updated: data.updated || new Date().toISOString().split('T')[0],
|
|
42
|
+
tags: data.tags || [],
|
|
43
|
+
sources: data.sources || []
|
|
44
|
+
},
|
|
45
|
+
content,
|
|
46
|
+
title
|
|
47
|
+
};
|
|
48
|
+
return c.json({
|
|
49
|
+
success: true,
|
|
50
|
+
document
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code === 'ENOENT') {
|
|
55
|
+
return c.json({ success: false, error: 'Knowledge document not found' }, 404);
|
|
56
|
+
}
|
|
57
|
+
console.error('Error loading knowledge document:', error);
|
|
58
|
+
return c.json({ success: false, error: 'Failed to load knowledge document' }, 500);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* PUT /api/v1/context/knowledge/:filename
|
|
63
|
+
* Update a specific knowledge document
|
|
64
|
+
*/
|
|
65
|
+
app.put('/:filename', async (c) => {
|
|
66
|
+
try {
|
|
67
|
+
const filename = c.req.param('filename');
|
|
68
|
+
const body = await c.req.json();
|
|
69
|
+
const knowledgePath = getKnowledgePath();
|
|
70
|
+
const actualFilename = filename.endsWith('.md') ? filename : `${filename}.md`;
|
|
71
|
+
const filePath = path.join(knowledgePath, actualFilename);
|
|
72
|
+
// Check if file exists
|
|
73
|
+
try {
|
|
74
|
+
await fs.access(filePath);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return c.json({ success: false, error: 'Knowledge document not found' }, 404);
|
|
78
|
+
}
|
|
79
|
+
// Read current content
|
|
80
|
+
const currentContent = await fs.readFile(filePath, 'utf-8');
|
|
81
|
+
const { data: currentData, content: currentMarkdown } = matter(currentContent);
|
|
82
|
+
// Prepare updated content
|
|
83
|
+
const updatedContent = body.content !== undefined ? body.content : currentMarkdown;
|
|
84
|
+
const updatedMetadata = {
|
|
85
|
+
...currentData,
|
|
86
|
+
...body.metadata,
|
|
87
|
+
updated: new Date().toISOString().split('T')[0],
|
|
88
|
+
// Ensure these are arrays
|
|
89
|
+
tags: body.metadata?.tags !== undefined ? body.metadata.tags : (currentData.tags || []),
|
|
90
|
+
sources: body.metadata?.sources !== undefined ? body.metadata.sources : (currentData.sources || [])
|
|
91
|
+
};
|
|
92
|
+
// Handle title change - might need to rename file
|
|
93
|
+
let newFilename = actualFilename;
|
|
94
|
+
let newFilePath = filePath;
|
|
95
|
+
if (body.title) {
|
|
96
|
+
newFilename = generateFilename(body.title);
|
|
97
|
+
newFilePath = path.join(knowledgePath, newFilename);
|
|
98
|
+
// Check if new filename conflicts with existing file (unless it's the same file)
|
|
99
|
+
if (newFilename !== actualFilename) {
|
|
100
|
+
try {
|
|
101
|
+
await fs.access(newFilePath);
|
|
102
|
+
return c.json({ success: false, error: 'A document with this title already exists' }, 409);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// File doesn't exist, which is what we want
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Create the updated markdown content with frontmatter
|
|
110
|
+
const fileContent = matter.stringify(updatedContent, updatedMetadata);
|
|
111
|
+
// Write to new location (or same location if filename unchanged)
|
|
112
|
+
await fs.writeFile(newFilePath, fileContent, 'utf-8');
|
|
113
|
+
// If filename changed, delete old file
|
|
114
|
+
if (newFilename !== actualFilename) {
|
|
115
|
+
await fs.unlink(filePath);
|
|
116
|
+
}
|
|
117
|
+
// Extract updated title
|
|
118
|
+
const title = body.title ||
|
|
119
|
+
updatedContent.match(/^#\s+(.+)$/m)?.[1] ||
|
|
120
|
+
newFilename.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
121
|
+
return c.json({
|
|
122
|
+
success: true,
|
|
123
|
+
document: {
|
|
124
|
+
filename: newFilename,
|
|
125
|
+
title,
|
|
126
|
+
metadata: updatedMetadata,
|
|
127
|
+
content: updatedContent
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.error('Error updating knowledge document:', error);
|
|
133
|
+
return c.json({ success: false, error: 'Failed to update knowledge document' }, 500);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* DELETE /api/v1/context/knowledge/:filename
|
|
138
|
+
* Delete a specific knowledge document
|
|
139
|
+
*/
|
|
140
|
+
app.delete('/:filename', async (c) => {
|
|
141
|
+
try {
|
|
142
|
+
const filename = c.req.param('filename');
|
|
143
|
+
const knowledgePath = getKnowledgePath();
|
|
144
|
+
const actualFilename = filename.endsWith('.md') ? filename : `${filename}.md`;
|
|
145
|
+
const filePath = path.join(knowledgePath, actualFilename);
|
|
146
|
+
// Check if file exists
|
|
147
|
+
try {
|
|
148
|
+
await fs.access(filePath);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return c.json({ success: false, error: 'Knowledge document not found' }, 404);
|
|
152
|
+
}
|
|
153
|
+
// Delete the file
|
|
154
|
+
await fs.unlink(filePath);
|
|
155
|
+
return c.json({
|
|
156
|
+
success: true,
|
|
157
|
+
message: 'Knowledge document deleted successfully'
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
console.error('Error deleting knowledge document:', error);
|
|
162
|
+
return c.json({ success: false, error: 'Failed to delete knowledge document' }, 500);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
export default app;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
import filenameRoute from './[filename]/index.js';
|
|
6
|
+
const app = new Hono();
|
|
7
|
+
function getKnowledgePath() {
|
|
8
|
+
const basePath = process.env.GAIT_DATA_PATH ?
|
|
9
|
+
path.resolve(process.env.GAIT_DATA_PATH, '.gait') :
|
|
10
|
+
path.resolve(process.cwd(), '.gait');
|
|
11
|
+
return path.join(basePath, 'context', 'knowledge');
|
|
12
|
+
}
|
|
13
|
+
function generateFilename(title) {
|
|
14
|
+
// Convert title to filename-safe format
|
|
15
|
+
return title
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
|
|
18
|
+
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
19
|
+
.replace(/--+/g, '-') // Replace multiple hyphens with single
|
|
20
|
+
.replace(/^-|-$/g, '') // Remove leading/trailing hyphens
|
|
21
|
+
+ '.md';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* GET /api/v1/context/knowledge
|
|
25
|
+
* Load all knowledge documents
|
|
26
|
+
*/
|
|
27
|
+
app.get('/', async (c) => {
|
|
28
|
+
try {
|
|
29
|
+
const knowledgePath = getKnowledgePath();
|
|
30
|
+
// Ensure directory exists
|
|
31
|
+
await fs.mkdir(knowledgePath, { recursive: true });
|
|
32
|
+
const files = await fs.readdir(knowledgePath);
|
|
33
|
+
const documents = await Promise.all(files
|
|
34
|
+
.filter(file => file.endsWith('.md'))
|
|
35
|
+
.map(async (file) => {
|
|
36
|
+
const filePath = path.join(knowledgePath, file);
|
|
37
|
+
const fileContent = await fs.readFile(filePath, 'utf-8');
|
|
38
|
+
const { data, content } = matter(fileContent);
|
|
39
|
+
// Extract title from first heading or use filename
|
|
40
|
+
const title = content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
41
|
+
file.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
42
|
+
return {
|
|
43
|
+
filename: file,
|
|
44
|
+
metadata: {
|
|
45
|
+
...data,
|
|
46
|
+
updated: data.updated || new Date().toISOString().split('T')[0],
|
|
47
|
+
tags: data.tags || [],
|
|
48
|
+
sources: data.sources || []
|
|
49
|
+
},
|
|
50
|
+
content,
|
|
51
|
+
title
|
|
52
|
+
};
|
|
53
|
+
}));
|
|
54
|
+
return c.json({
|
|
55
|
+
success: true,
|
|
56
|
+
documents: documents.sort((a, b) => a.filename.localeCompare(b.filename))
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error('Error loading knowledge documents:', error);
|
|
61
|
+
return c.json({
|
|
62
|
+
success: false,
|
|
63
|
+
error: 'Failed to load knowledge documents',
|
|
64
|
+
documents: []
|
|
65
|
+
}, 500);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* POST /api/v1/context/knowledge
|
|
70
|
+
* Create a new knowledge document
|
|
71
|
+
*/
|
|
72
|
+
app.post('/', async (c) => {
|
|
73
|
+
try {
|
|
74
|
+
const body = await c.req.json();
|
|
75
|
+
if (!body.title || !body.content) {
|
|
76
|
+
return c.json({ success: false, error: 'Title and content are required' }, 400);
|
|
77
|
+
}
|
|
78
|
+
const knowledgePath = getKnowledgePath();
|
|
79
|
+
await fs.mkdir(knowledgePath, { recursive: true });
|
|
80
|
+
const filename = generateFilename(body.title);
|
|
81
|
+
const filePath = path.join(knowledgePath, filename);
|
|
82
|
+
// Check if file already exists
|
|
83
|
+
try {
|
|
84
|
+
await fs.access(filePath);
|
|
85
|
+
return c.json({ success: false, error: 'A document with this title already exists' }, 409);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// File doesn't exist, which is what we want
|
|
89
|
+
}
|
|
90
|
+
// Prepare frontmatter
|
|
91
|
+
const now = new Date();
|
|
92
|
+
const frontmatter = {
|
|
93
|
+
version: '1.0',
|
|
94
|
+
updated: now.toISOString().split('T')[0],
|
|
95
|
+
type: 'knowledge',
|
|
96
|
+
category: body.metadata?.category || 'general',
|
|
97
|
+
tags: body.metadata?.tags || [],
|
|
98
|
+
sources: body.metadata?.sources || [],
|
|
99
|
+
...body.metadata
|
|
100
|
+
};
|
|
101
|
+
// Create the markdown content with frontmatter
|
|
102
|
+
const fileContent = matter.stringify(body.content, frontmatter);
|
|
103
|
+
await fs.writeFile(filePath, fileContent, 'utf-8');
|
|
104
|
+
return c.json({
|
|
105
|
+
success: true,
|
|
106
|
+
document: {
|
|
107
|
+
filename,
|
|
108
|
+
title: body.title,
|
|
109
|
+
metadata: frontmatter,
|
|
110
|
+
content: body.content
|
|
111
|
+
}
|
|
112
|
+
}, 201);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error('Error creating knowledge document:', error);
|
|
116
|
+
return c.json({ success: false, error: 'Failed to create knowledge document' }, 500);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Register filename route for individual documents
|
|
120
|
+
app.route('/', filenameRoute);
|
|
121
|
+
export default app;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
const app = new Hono();
|
|
6
|
+
function getProjectPath() {
|
|
7
|
+
const basePath = process.env.GAIT_DATA_PATH ?
|
|
8
|
+
path.resolve(process.env.GAIT_DATA_PATH, '.gait') :
|
|
9
|
+
path.resolve(process.cwd(), '.gait');
|
|
10
|
+
return path.join(basePath, 'context');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* GET /api/v1/context/project
|
|
14
|
+
* Load project overview document
|
|
15
|
+
*/
|
|
16
|
+
app.get('/', async (c) => {
|
|
17
|
+
try {
|
|
18
|
+
const projectPath = getProjectPath();
|
|
19
|
+
// Ensure directory exists
|
|
20
|
+
await fs.mkdir(projectPath, { recursive: true });
|
|
21
|
+
// Look for project.md file
|
|
22
|
+
const filePath = path.join(projectPath, 'project.md');
|
|
23
|
+
try {
|
|
24
|
+
const fileContent = await fs.readFile(filePath, 'utf-8');
|
|
25
|
+
const { data, content } = matter(fileContent);
|
|
26
|
+
// Extract title from first heading or use default
|
|
27
|
+
const title = content.match(/^#\s+(.+)$/m)?.[1] || 'Project Overview';
|
|
28
|
+
const document = {
|
|
29
|
+
filename: 'project.md',
|
|
30
|
+
metadata: {
|
|
31
|
+
...data,
|
|
32
|
+
updated: data.updated || new Date().toISOString().split('T')[0],
|
|
33
|
+
type: 'project',
|
|
34
|
+
category: 'overview',
|
|
35
|
+
tags: data.tags || []
|
|
36
|
+
},
|
|
37
|
+
content,
|
|
38
|
+
title
|
|
39
|
+
};
|
|
40
|
+
return c.json({
|
|
41
|
+
success: true,
|
|
42
|
+
document
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (fileError) {
|
|
46
|
+
// If file doesn't exist, return default content
|
|
47
|
+
const defaultContent = `# Project Overview
|
|
48
|
+
|
|
49
|
+
This document provides an overview of the project, including its purpose, goals, and key information.
|
|
50
|
+
|
|
51
|
+
## Purpose
|
|
52
|
+
|
|
53
|
+
Describe the main purpose and objectives of this project.
|
|
54
|
+
|
|
55
|
+
## Key Features
|
|
56
|
+
|
|
57
|
+
- Feature 1
|
|
58
|
+
- Feature 2
|
|
59
|
+
- Feature 3
|
|
60
|
+
|
|
61
|
+
## Technology Stack
|
|
62
|
+
|
|
63
|
+
List the main technologies, frameworks, and tools used in this project.
|
|
64
|
+
|
|
65
|
+
## Getting Started
|
|
66
|
+
|
|
67
|
+
Provide instructions for setting up and running the project locally.
|
|
68
|
+
|
|
69
|
+
## Documentation
|
|
70
|
+
|
|
71
|
+
Links to additional documentation, guides, and resources.
|
|
72
|
+
`;
|
|
73
|
+
const document = {
|
|
74
|
+
filename: 'project.md',
|
|
75
|
+
metadata: {
|
|
76
|
+
version: '1.0',
|
|
77
|
+
updated: new Date().toISOString().split('T')[0],
|
|
78
|
+
type: 'project',
|
|
79
|
+
category: 'overview',
|
|
80
|
+
tags: []
|
|
81
|
+
},
|
|
82
|
+
content: defaultContent,
|
|
83
|
+
title: 'Project Overview'
|
|
84
|
+
};
|
|
85
|
+
return c.json({
|
|
86
|
+
success: true,
|
|
87
|
+
document
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.error('Error loading project document:', error);
|
|
93
|
+
return c.json({
|
|
94
|
+
success: false,
|
|
95
|
+
error: 'Failed to load project document'
|
|
96
|
+
}, 500);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
/**
|
|
100
|
+
* PUT /api/v1/context/project
|
|
101
|
+
* Update project overview document
|
|
102
|
+
*/
|
|
103
|
+
app.put('/', async (c) => {
|
|
104
|
+
try {
|
|
105
|
+
const body = await c.req.json();
|
|
106
|
+
if (!body.content) {
|
|
107
|
+
return c.json({ success: false, error: 'Content is required' }, 400);
|
|
108
|
+
}
|
|
109
|
+
const projectPath = getProjectPath();
|
|
110
|
+
await fs.mkdir(projectPath, { recursive: true });
|
|
111
|
+
const filePath = path.join(projectPath, 'project.md');
|
|
112
|
+
// Read current content if it exists
|
|
113
|
+
let currentData = {};
|
|
114
|
+
try {
|
|
115
|
+
const currentContent = await fs.readFile(filePath, 'utf-8');
|
|
116
|
+
const { data } = matter(currentContent);
|
|
117
|
+
currentData = data;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// File doesn't exist, use defaults
|
|
121
|
+
}
|
|
122
|
+
// Prepare updated metadata
|
|
123
|
+
const updatedMetadata = {
|
|
124
|
+
...currentData,
|
|
125
|
+
...body.metadata,
|
|
126
|
+
updated: new Date().toISOString().split('T')[0],
|
|
127
|
+
type: 'project',
|
|
128
|
+
category: 'overview',
|
|
129
|
+
version: currentData.version || '1.0'
|
|
130
|
+
};
|
|
131
|
+
// Create the markdown content with frontmatter
|
|
132
|
+
const fileContent = matter.stringify(body.content, updatedMetadata);
|
|
133
|
+
await fs.writeFile(filePath, fileContent, 'utf-8');
|
|
134
|
+
// Extract updated title
|
|
135
|
+
const title = body.title ||
|
|
136
|
+
body.content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
137
|
+
'Project Overview';
|
|
138
|
+
return c.json({
|
|
139
|
+
success: true,
|
|
140
|
+
document: {
|
|
141
|
+
filename: 'project.md',
|
|
142
|
+
title,
|
|
143
|
+
metadata: updatedMetadata,
|
|
144
|
+
content: body.content
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('Error updating project document:', error);
|
|
150
|
+
return c.json({ success: false, error: 'Failed to update project document' }, 500);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
export default app;
|