@nolrm/contextkit 0.7.3
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/LICENSE +21 -0
- package/README.md +216 -0
- package/bin/contextkit.js +324 -0
- package/bin/vibe-kit.js +3 -0
- package/install-fallback.sh +59 -0
- package/lib/commands/ai.js +147 -0
- package/lib/commands/analyze.js +544 -0
- package/lib/commands/check.js +290 -0
- package/lib/commands/dashboard.js +383 -0
- package/lib/commands/install.js +1454 -0
- package/lib/commands/note.js +120 -0
- package/lib/commands/publish.js +184 -0
- package/lib/commands/pull.js +191 -0
- package/lib/commands/run.js +232 -0
- package/lib/commands/status.js +253 -0
- package/lib/commands/update.js +376 -0
- package/lib/index.js +9 -0
- package/lib/integrations/aider-integration.js +93 -0
- package/lib/integrations/base-integration.js +123 -0
- package/lib/integrations/claude-integration.js +141 -0
- package/lib/integrations/codex-integration.js +45 -0
- package/lib/integrations/continue-integration.js +99 -0
- package/lib/integrations/copilot-integration.js +73 -0
- package/lib/integrations/cursor-integration.js +162 -0
- package/lib/integrations/gemini-integration.js +62 -0
- package/lib/integrations/index.js +33 -0
- package/lib/integrations/windsurf-integration.js +88 -0
- package/lib/utils/download.js +50 -0
- package/lib/utils/git-hooks.js +228 -0
- package/lib/utils/project-detector.js +110 -0
- package/lib/utils/status-manager.js +107 -0
- package/lib/utils/tool-detector.js +137 -0
- package/package.json +85 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const ora = require('ora');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const DownloadManager = require('../utils/download');
|
|
7
|
+
const ProjectDetector = require('../utils/project-detector');
|
|
8
|
+
const GitHooksManager = require('../utils/git-hooks');
|
|
9
|
+
|
|
10
|
+
class UpdateCommand {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.downloadManager = new DownloadManager();
|
|
13
|
+
this.projectDetector = new ProjectDetector();
|
|
14
|
+
this.gitHooksManager = new GitHooksManager();
|
|
15
|
+
this.repoUrl = 'https://raw.githubusercontent.com/nolrm/contextkit/main';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async update(options = {}) {
|
|
19
|
+
console.log(chalk.magenta('🔄 Updating ContextKit...'));
|
|
20
|
+
|
|
21
|
+
// Check if ContextKit is installed
|
|
22
|
+
if (!await fs.pathExists('.contextkit/config.yml')) {
|
|
23
|
+
console.log(chalk.red('❌ No ContextKit installation found in current directory'));
|
|
24
|
+
console.log(chalk.yellow('💡 Run: contextkit install'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check for updates
|
|
29
|
+
const updateInfo = await this.checkForUpdates();
|
|
30
|
+
if (!updateInfo.hasUpdate && !options.force) {
|
|
31
|
+
console.log(chalk.green('✅ ContextKit is already up to date!'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (updateInfo.hasUpdate) {
|
|
36
|
+
console.log(chalk.blue(`📦 Updating from ${updateInfo.currentVersion} to ${updateInfo.latestVersion}`));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create backup
|
|
40
|
+
const backupPath = await this.createBackup();
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Detect current configuration
|
|
44
|
+
const config = await this.parseConfig();
|
|
45
|
+
const projectType = this.projectDetector.detectProjectType();
|
|
46
|
+
const packageManager = this.projectDetector.detectPackageManager();
|
|
47
|
+
|
|
48
|
+
// Download latest files
|
|
49
|
+
await this.downloadFiles(projectType);
|
|
50
|
+
|
|
51
|
+
// Restore user configuration
|
|
52
|
+
await this.restoreUserConfig(config);
|
|
53
|
+
|
|
54
|
+
// Update Git hooks if they were enabled
|
|
55
|
+
if (config.features?.git_hooks) {
|
|
56
|
+
await this.gitHooksManager.installHooks(packageManager);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Refresh installed platform integrations
|
|
60
|
+
await this.refreshIntegrations();
|
|
61
|
+
|
|
62
|
+
// Update version in config
|
|
63
|
+
await this.updateConfigVersion(updateInfo.latestVersion || '1.0.0');
|
|
64
|
+
|
|
65
|
+
console.log(chalk.green('✅ ContextKit updated successfully!'));
|
|
66
|
+
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.log(chalk.red('❌ Update failed, restoring from backup...'));
|
|
69
|
+
await this.restoreFromBackup(backupPath);
|
|
70
|
+
throw error;
|
|
71
|
+
} finally {
|
|
72
|
+
// Clean up backup
|
|
73
|
+
if (await fs.pathExists(backupPath)) {
|
|
74
|
+
await fs.remove(backupPath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async checkForUpdates() {
|
|
80
|
+
try {
|
|
81
|
+
const axios = require('axios');
|
|
82
|
+
const response = await axios.get('https://api.github.com/repos/nolrm/contextkit/releases/latest', {
|
|
83
|
+
timeout: 5000
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const latestVersion = response.data.tag_name.replace('v', '');
|
|
87
|
+
const currentVersion = await this.getCurrentVersion();
|
|
88
|
+
const hasUpdate = this.isNewerVersion(latestVersion, currentVersion);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
hasUpdate,
|
|
92
|
+
currentVersion,
|
|
93
|
+
latestVersion
|
|
94
|
+
};
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return {
|
|
97
|
+
hasUpdate: true, // Assume update available if we can't check
|
|
98
|
+
currentVersion: await this.getCurrentVersion(),
|
|
99
|
+
latestVersion: 'latest',
|
|
100
|
+
error: error.message
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async getCurrentVersion() {
|
|
106
|
+
try {
|
|
107
|
+
const config = await this.parseConfig();
|
|
108
|
+
return config.version || '1.0.0';
|
|
109
|
+
} catch {
|
|
110
|
+
return '1.0.0';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
isNewerVersion(latest, current) {
|
|
115
|
+
const latestParts = latest.split('.').map(Number);
|
|
116
|
+
const currentParts = current.split('.').map(Number);
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < Math.max(latestParts.length, currentParts.length); i++) {
|
|
119
|
+
const latestPart = latestParts[i] || 0;
|
|
120
|
+
const currentPart = currentParts[i] || 0;
|
|
121
|
+
|
|
122
|
+
if (latestPart > currentPart) return true;
|
|
123
|
+
if (latestPart < currentPart) return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async createBackup() {
|
|
130
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
131
|
+
const backupPath = `.contextkit-backup-${timestamp}`;
|
|
132
|
+
|
|
133
|
+
await fs.copy('.contextkit', backupPath);
|
|
134
|
+
return backupPath;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async restoreFromBackup(backupPath) {
|
|
138
|
+
await fs.remove('.contextkit');
|
|
139
|
+
await fs.copy(backupPath, '.contextkit');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async parseConfig() {
|
|
143
|
+
const configContent = await fs.readFile('.contextkit/config.yml', 'utf8');
|
|
144
|
+
const config = {};
|
|
145
|
+
|
|
146
|
+
// Simple YAML parsing for our config format
|
|
147
|
+
const lines = configContent.split('\n');
|
|
148
|
+
for (const line of lines) {
|
|
149
|
+
const trimmed = line.trim();
|
|
150
|
+
if (trimmed.startsWith('version:')) {
|
|
151
|
+
config.version = trimmed.split('version:')[1].trim().replace(/"/g, '');
|
|
152
|
+
} else if (trimmed.startsWith('project_name:')) {
|
|
153
|
+
config.project_name = trimmed.split('project_name:')[1].trim().replace(/"/g, '');
|
|
154
|
+
} else if (trimmed.startsWith('project_type:')) {
|
|
155
|
+
config.project_type = trimmed.split('project_type:')[1].trim().replace(/"/g, '');
|
|
156
|
+
} else if (trimmed.startsWith('testing:')) {
|
|
157
|
+
config.features = config.features || {};
|
|
158
|
+
config.features.testing = trimmed.split('testing:')[1].trim() === 'true';
|
|
159
|
+
} else if (trimmed.startsWith('documentation:')) {
|
|
160
|
+
config.features = config.features || {};
|
|
161
|
+
config.features.documentation = trimmed.split('documentation:')[1].trim() === 'true';
|
|
162
|
+
} else if (trimmed.startsWith('code_review:')) {
|
|
163
|
+
config.features = config.features || {};
|
|
164
|
+
config.features.code_review = trimmed.split('code_review:')[1].trim() === 'true';
|
|
165
|
+
} else if (trimmed.startsWith('linting:')) {
|
|
166
|
+
config.features = config.features || {};
|
|
167
|
+
config.features.linting = trimmed.split('linting:')[1].trim() === 'true';
|
|
168
|
+
} else if (trimmed.startsWith('type_safety:')) {
|
|
169
|
+
config.features = config.features || {};
|
|
170
|
+
config.features.type_safety = trimmed.split('type_safety:')[1].trim() === 'true';
|
|
171
|
+
} else if (trimmed.startsWith('git_hooks:')) {
|
|
172
|
+
config.features = config.features || {};
|
|
173
|
+
config.features.git_hooks = trimmed.split('git_hooks:')[1].trim() === 'true';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return config;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async downloadFiles(projectType) {
|
|
181
|
+
const spinner = ora('Downloading latest files...').start();
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
// Download only the real files (not skeleton files - those come from install)
|
|
185
|
+
await this.downloadManager.downloadFile(
|
|
186
|
+
`${this.repoUrl}/standards/README.md`,
|
|
187
|
+
'.contextkit/standards/README.md'
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Keep glossary updated (universal file)
|
|
191
|
+
await this.downloadManager.downloadFile(
|
|
192
|
+
`${this.repoUrl}/standards/glossary.md`,
|
|
193
|
+
'.contextkit/standards/glossary.md'
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Download commands
|
|
197
|
+
await this.downloadManager.downloadFile(
|
|
198
|
+
`${this.repoUrl}/commands/create-feature.md`,
|
|
199
|
+
'.contextkit/commands/create-feature.md'
|
|
200
|
+
);
|
|
201
|
+
await this.downloadManager.downloadFile(
|
|
202
|
+
`${this.repoUrl}/commands/create-component.md`,
|
|
203
|
+
'.contextkit/commands/create-component.md'
|
|
204
|
+
);
|
|
205
|
+
await this.downloadManager.downloadFile(
|
|
206
|
+
`${this.repoUrl}/commands/run-tests.md`,
|
|
207
|
+
'.contextkit/commands/run-tests.md'
|
|
208
|
+
);
|
|
209
|
+
await this.downloadManager.downloadFile(
|
|
210
|
+
`${this.repoUrl}/commands/add-documentation.md`,
|
|
211
|
+
'.contextkit/commands/add-documentation.md'
|
|
212
|
+
);
|
|
213
|
+
await this.downloadManager.downloadFile(
|
|
214
|
+
`${this.repoUrl}/commands/quality-check.md`,
|
|
215
|
+
'.contextkit/commands/quality-check.md'
|
|
216
|
+
);
|
|
217
|
+
await this.downloadManager.downloadFile(
|
|
218
|
+
`${this.repoUrl}/commands/analyze.md`,
|
|
219
|
+
'.contextkit/commands/analyze.md'
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// Download hooks
|
|
223
|
+
await this.downloadManager.downloadFile(
|
|
224
|
+
`${this.repoUrl}/hooks/pre-commit.sh`,
|
|
225
|
+
'.contextkit/hooks/pre-commit.sh'
|
|
226
|
+
);
|
|
227
|
+
await this.downloadManager.downloadFile(
|
|
228
|
+
`${this.repoUrl}/hooks/pre-push.sh`,
|
|
229
|
+
'.contextkit/hooks/pre-push.sh'
|
|
230
|
+
);
|
|
231
|
+
await this.downloadManager.downloadFile(
|
|
232
|
+
`${this.repoUrl}/hooks/commit-msg.sh`,
|
|
233
|
+
'.contextkit/hooks/commit-msg.sh'
|
|
234
|
+
);
|
|
235
|
+
await this.downloadManager.downloadFile(
|
|
236
|
+
`${this.repoUrl}/hooks/setup-hooks.sh`,
|
|
237
|
+
'.contextkit/hooks/setup-hooks.sh'
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Download types
|
|
241
|
+
await this.downloadManager.downloadFile(
|
|
242
|
+
`${this.repoUrl}/types/strict.tsconfig.json`,
|
|
243
|
+
'.contextkit/types/strict.tsconfig.json'
|
|
244
|
+
);
|
|
245
|
+
await this.downloadManager.downloadFile(
|
|
246
|
+
`${this.repoUrl}/types/global.d.ts`,
|
|
247
|
+
'.contextkit/types/global.d.ts'
|
|
248
|
+
);
|
|
249
|
+
await this.downloadManager.downloadFile(
|
|
250
|
+
`${this.repoUrl}/types/type-check.sh`,
|
|
251
|
+
'.contextkit/types/type-check.sh'
|
|
252
|
+
);
|
|
253
|
+
await this.downloadManager.downloadFile(
|
|
254
|
+
`${this.repoUrl}/types/typescript-strict.json`,
|
|
255
|
+
'.contextkit/types/typescript-strict.json'
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// Download templates
|
|
259
|
+
await this.downloadManager.downloadFile(
|
|
260
|
+
`${this.repoUrl}/templates/component.tsx`,
|
|
261
|
+
'.contextkit/templates/component.tsx'
|
|
262
|
+
);
|
|
263
|
+
await this.downloadManager.downloadFile(
|
|
264
|
+
`${this.repoUrl}/templates/test.tsx`,
|
|
265
|
+
'.contextkit/templates/test.tsx'
|
|
266
|
+
);
|
|
267
|
+
await this.downloadManager.downloadFile(
|
|
268
|
+
`${this.repoUrl}/templates/story.tsx`,
|
|
269
|
+
'.contextkit/templates/story.tsx'
|
|
270
|
+
);
|
|
271
|
+
await this.downloadManager.downloadFile(
|
|
272
|
+
`${this.repoUrl}/templates/hook.ts`,
|
|
273
|
+
'.contextkit/templates/hook.ts'
|
|
274
|
+
);
|
|
275
|
+
await this.downloadManager.downloadFile(
|
|
276
|
+
`${this.repoUrl}/templates/api.ts`,
|
|
277
|
+
'.contextkit/templates/api.ts'
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// Download scripts
|
|
281
|
+
await this.downloadManager.downloadFile(
|
|
282
|
+
`${this.repoUrl}/legacy/update.sh`,
|
|
283
|
+
'.contextkit/scripts/update.sh'
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// Make scripts executable
|
|
287
|
+
await fs.chmod('.contextkit/hooks/pre-commit.sh', '755');
|
|
288
|
+
await fs.chmod('.contextkit/hooks/pre-push.sh', '755');
|
|
289
|
+
await fs.chmod('.contextkit/hooks/commit-msg.sh', '755');
|
|
290
|
+
await fs.chmod('.contextkit/hooks/setup-hooks.sh', '755');
|
|
291
|
+
await fs.chmod('.contextkit/types/type-check.sh', '755');
|
|
292
|
+
await fs.chmod('.contextkit/scripts/update.sh', '755');
|
|
293
|
+
|
|
294
|
+
spinner.succeed('Files updated successfully');
|
|
295
|
+
} catch (error) {
|
|
296
|
+
spinner.fail('Failed to download files');
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async restoreUserConfig(config) {
|
|
302
|
+
// Restore user's project-specific configuration
|
|
303
|
+
const configContent = `# ContextKit Configuration
|
|
304
|
+
version: "${config.version}"
|
|
305
|
+
project_name: "${config.project_name}"
|
|
306
|
+
project_type: "${config.project_type}"
|
|
307
|
+
|
|
308
|
+
# Features
|
|
309
|
+
features:
|
|
310
|
+
testing: ${config.features?.testing || true}
|
|
311
|
+
documentation: ${config.features?.documentation || true}
|
|
312
|
+
code_review: ${config.features?.code_review || true}
|
|
313
|
+
linting: ${config.features?.linting || true}
|
|
314
|
+
type_safety: ${config.features?.type_safety || true}
|
|
315
|
+
git_hooks: ${config.features?.git_hooks || false}
|
|
316
|
+
|
|
317
|
+
# Paths (customize for your project)
|
|
318
|
+
paths:
|
|
319
|
+
components: "${config.paths?.components || 'src/components'}"
|
|
320
|
+
tests: "${config.paths?.tests || 'src/__tests__'}"
|
|
321
|
+
stories: "${config.paths?.stories || 'src/stories'}"
|
|
322
|
+
docs: "${config.paths?.docs || 'docs'}"
|
|
323
|
+
|
|
324
|
+
# Commands
|
|
325
|
+
commands:
|
|
326
|
+
create_component: "${config.commands?.create_component || '@.contextkit/commands/create-component.md'}"
|
|
327
|
+
create_feature: "${config.commands?.create_feature || '@.contextkit/commands/create-feature.md'}"
|
|
328
|
+
run_tests: "${config.commands?.run_tests || '@.contextkit/commands/run-tests.md'}"
|
|
329
|
+
add_docs: "${config.commands?.add_docs || '@.contextkit/commands/add-documentation.md'}"
|
|
330
|
+
quality_check: "${config.commands?.quality_check || '@.contextkit/commands/quality-check.md'}"
|
|
331
|
+
analyze: "${config.commands?.analyze || '@.contextkit/commands/analyze.md'}"
|
|
332
|
+
`;
|
|
333
|
+
|
|
334
|
+
await fs.writeFile('.contextkit/config.yml', configContent);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async refreshIntegrations() {
|
|
338
|
+
const { getAllIntegrationNames, getIntegration } = require('../integrations');
|
|
339
|
+
|
|
340
|
+
let refreshed = 0;
|
|
341
|
+
for (const name of getAllIntegrationNames()) {
|
|
342
|
+
const integration = getIntegration(name);
|
|
343
|
+
const result = await integration.validate();
|
|
344
|
+
|
|
345
|
+
// Only refresh integrations that already have files installed
|
|
346
|
+
if (result.present.length > 0) {
|
|
347
|
+
try {
|
|
348
|
+
await integration.install();
|
|
349
|
+
refreshed++;
|
|
350
|
+
} catch {
|
|
351
|
+
// Skip failed refreshes silently
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (refreshed > 0) {
|
|
357
|
+
console.log(chalk.green(` ✅ Refreshed ${refreshed} platform integration(s)`));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async updateConfigVersion(version) {
|
|
362
|
+
const configContent = await fs.readFile('.contextkit/config.yml', 'utf8');
|
|
363
|
+
const updatedContent = configContent.replace(
|
|
364
|
+
/version: "[^"]*"/,
|
|
365
|
+
`version: "${version}"`
|
|
366
|
+
);
|
|
367
|
+
await fs.writeFile('.contextkit/config.yml', updatedContent);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function update(options) {
|
|
372
|
+
const updater = new UpdateCommand();
|
|
373
|
+
await updater.update(options);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
module.exports = update;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const BaseIntegration = require('./base-integration');
|
|
5
|
+
|
|
6
|
+
class AiderIntegration extends BaseIntegration {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
this.name = 'aider';
|
|
10
|
+
this.displayName = 'Aider';
|
|
11
|
+
this.bridgeFiles = ['CONVENTIONS.md'];
|
|
12
|
+
this.generatedFiles = ['.aider/rules.md', '.aiderignore'];
|
|
13
|
+
this.platformDir = '.aider';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async generateFiles() {
|
|
17
|
+
// Bridge file: CONVENTIONS.md (auto-loaded by Aider)
|
|
18
|
+
const bridgeContent = `# Project Conventions (ContextKit)
|
|
19
|
+
|
|
20
|
+
This project uses [ContextKit](https://github.com/nolrm/contextkit) for AI development standards.
|
|
21
|
+
|
|
22
|
+
${this.getStandardsBlock()}
|
|
23
|
+
|
|
24
|
+
## Key Rules
|
|
25
|
+
|
|
26
|
+
- Follow coding conventions in \`.contextkit/standards/code-style.md\`
|
|
27
|
+
- Use numbered test cases as defined in \`.contextkit/standards/testing.md\`
|
|
28
|
+
- Check \`.contextkit/standards/glossary.md\` for project-specific terminology
|
|
29
|
+
- Reference \`.contextkit/templates/\` for code generation patterns`;
|
|
30
|
+
|
|
31
|
+
await this.writeBridgeFile('CONVENTIONS.md', bridgeContent);
|
|
32
|
+
|
|
33
|
+
// .aider/rules.md — backward compat rules file
|
|
34
|
+
const rulesPath = path.join(__dirname, '../../integrations/aider.rules.md');
|
|
35
|
+
if (await fs.pathExists(rulesPath)) {
|
|
36
|
+
await fs.copy(rulesPath, '.aider/rules.md');
|
|
37
|
+
} else {
|
|
38
|
+
const rules = `# ContextKit Rules
|
|
39
|
+
|
|
40
|
+
## Standards Reference
|
|
41
|
+
|
|
42
|
+
- @.contextkit/standards/code-style.md — Coding conventions
|
|
43
|
+
- @.contextkit/standards/testing.md — Testing patterns (numbered test cases required)
|
|
44
|
+
- @.contextkit/standards/architecture.md — Architecture patterns
|
|
45
|
+
- @.contextkit/standards/ai-guidelines.md — AI behavior rules
|
|
46
|
+
- @.contextkit/standards/glossary.md — Project terminology
|
|
47
|
+
|
|
48
|
+
## Templates
|
|
49
|
+
|
|
50
|
+
- @.contextkit/templates/component.tsx — Component template
|
|
51
|
+
- @.contextkit/templates/test.tsx — Test template
|
|
52
|
+
- @.contextkit/templates/hook.ts — Custom hook template
|
|
53
|
+
- @.contextkit/templates/api.ts — API service template
|
|
54
|
+
|
|
55
|
+
## Always Include
|
|
56
|
+
|
|
57
|
+
1. Follow project coding standards
|
|
58
|
+
2. Use numbered test cases
|
|
59
|
+
3. Include TypeScript types
|
|
60
|
+
4. Follow established patterns
|
|
61
|
+
5. Check glossary for domain terms
|
|
62
|
+
`;
|
|
63
|
+
await fs.writeFile('.aider/rules.md', rules);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// .aiderignore — exclude contextkit internals from edits
|
|
67
|
+
const aiderignore = `.contextkit/hooks/
|
|
68
|
+
.contextkit/types/
|
|
69
|
+
.contextkit/scripts/
|
|
70
|
+
.contextkit/policies/
|
|
71
|
+
.contextkit/instructions/meta/
|
|
72
|
+
`;
|
|
73
|
+
// Only create if doesn't exist (don't overwrite user customizations)
|
|
74
|
+
if (!await fs.pathExists('.aiderignore')) {
|
|
75
|
+
await fs.writeFile('.aiderignore', aiderignore);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
showUsage() {
|
|
80
|
+
console.log('');
|
|
81
|
+
console.log(chalk.bold(' Aider Usage:'));
|
|
82
|
+
console.log(' CONVENTIONS.md is auto-loaded by Aider');
|
|
83
|
+
console.log(' .aider/rules.md provides additional context');
|
|
84
|
+
console.log(' Just run: aider "create a button component"');
|
|
85
|
+
console.log('');
|
|
86
|
+
console.log(chalk.dim(' Files created:'));
|
|
87
|
+
console.log(chalk.dim(' CONVENTIONS.md (bridge - auto-loaded)'));
|
|
88
|
+
console.log(chalk.dim(' .aider/rules.md (rules)'));
|
|
89
|
+
console.log(chalk.dim(' .aiderignore (exclude internals)'));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = AiderIntegration;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const MARKER = '<!-- Generated by ContextKit -->';
|
|
6
|
+
const MARKER_END = '<!-- End ContextKit -->';
|
|
7
|
+
const LEGACY_MARKER = '<!-- Generated by Vibe Kit -->';
|
|
8
|
+
const LEGACY_MARKER_END = '<!-- End Vibe Kit -->';
|
|
9
|
+
|
|
10
|
+
class BaseIntegration {
|
|
11
|
+
constructor() {
|
|
12
|
+
if (new.target === BaseIntegration) {
|
|
13
|
+
throw new Error('BaseIntegration is abstract and cannot be instantiated directly');
|
|
14
|
+
}
|
|
15
|
+
this.name = '';
|
|
16
|
+
this.displayName = '';
|
|
17
|
+
this.generatedFiles = [];
|
|
18
|
+
this.bridgeFiles = [];
|
|
19
|
+
this.platformDir = '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async install() {
|
|
23
|
+
if (this.platformDir) {
|
|
24
|
+
await fs.ensureDir(this.platformDir);
|
|
25
|
+
}
|
|
26
|
+
await this.generateFiles();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async generateFiles() {
|
|
30
|
+
throw new Error('generateFiles() must be implemented by subclass');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async validate() {
|
|
34
|
+
const results = { valid: true, missing: [], present: [] };
|
|
35
|
+
|
|
36
|
+
const allFiles = [...this.generatedFiles, ...this.bridgeFiles];
|
|
37
|
+
for (const file of allFiles) {
|
|
38
|
+
if (await fs.pathExists(file)) {
|
|
39
|
+
results.present.push(file);
|
|
40
|
+
} else {
|
|
41
|
+
results.missing.push(file);
|
|
42
|
+
results.valid = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async writeBridgeFile(filePath, content) {
|
|
50
|
+
const dir = path.dirname(filePath);
|
|
51
|
+
if (dir !== '.') {
|
|
52
|
+
await fs.ensureDir(dir);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const markedContent = `${MARKER}\n${content}\n${MARKER_END}`;
|
|
56
|
+
|
|
57
|
+
if (await fs.pathExists(filePath)) {
|
|
58
|
+
const existing = await fs.readFile(filePath, 'utf-8');
|
|
59
|
+
|
|
60
|
+
// Check for current or legacy markers
|
|
61
|
+
const hasCurrentMarker = existing.includes(MARKER);
|
|
62
|
+
const hasLegacyMarker = existing.includes(LEGACY_MARKER);
|
|
63
|
+
|
|
64
|
+
if (hasCurrentMarker || hasLegacyMarker) {
|
|
65
|
+
const activeMarker = hasCurrentMarker ? MARKER : LEGACY_MARKER;
|
|
66
|
+
const activeMarkerEnd = hasCurrentMarker ? MARKER_END : LEGACY_MARKER_END;
|
|
67
|
+
const before = existing.substring(0, existing.indexOf(activeMarker));
|
|
68
|
+
const afterMarkerEnd = existing.indexOf(activeMarkerEnd);
|
|
69
|
+
const after = afterMarkerEnd !== -1
|
|
70
|
+
? existing.substring(afterMarkerEnd + activeMarkerEnd.length)
|
|
71
|
+
: '';
|
|
72
|
+
await fs.writeFile(filePath, before + markedContent + after);
|
|
73
|
+
} else {
|
|
74
|
+
// Append below existing content
|
|
75
|
+
await fs.writeFile(filePath, existing.trimEnd() + '\n\n' + markedContent + '\n');
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
await fs.writeFile(filePath, markedContent + '\n');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async writeGeneratedFile(filePath, content) {
|
|
83
|
+
const dir = path.dirname(filePath);
|
|
84
|
+
if (dir !== '.') {
|
|
85
|
+
await fs.ensureDir(dir);
|
|
86
|
+
}
|
|
87
|
+
await fs.writeFile(filePath, content);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getStandardsBlock() {
|
|
91
|
+
return `## Project Standards
|
|
92
|
+
|
|
93
|
+
The following standards files define this project's conventions:
|
|
94
|
+
|
|
95
|
+
- \`.contextkit/standards/code-style.md\` — Coding conventions and style rules
|
|
96
|
+
- \`.contextkit/standards/testing.md\` — Testing patterns and requirements
|
|
97
|
+
- \`.contextkit/standards/architecture.md\` — Architecture decisions and patterns
|
|
98
|
+
- \`.contextkit/standards/ai-guidelines.md\` — AI behavior and usage guidelines
|
|
99
|
+
- \`.contextkit/standards/workflows.md\` — Development workflows and processes
|
|
100
|
+
- \`.contextkit/standards/glossary.md\` — Project terminology and shortcuts
|
|
101
|
+
|
|
102
|
+
## Product Context
|
|
103
|
+
|
|
104
|
+
- \`.contextkit/product/mission-lite.md\` — Product mission (condensed)
|
|
105
|
+
- \`.contextkit/product/decisions.md\` — Architecture Decision Records
|
|
106
|
+
- \`.contextkit/product/roadmap.md\` — Development roadmap
|
|
107
|
+
|
|
108
|
+
## Commands
|
|
109
|
+
|
|
110
|
+
- \`.contextkit/commands/analyze.md\` — Analyze and customize standards
|
|
111
|
+
- \`.contextkit/commands/create-component.md\` — Create components
|
|
112
|
+
- \`.contextkit/commands/create-feature.md\` — Create features
|
|
113
|
+
- \`.contextkit/commands/run-tests.md\` — Run tests
|
|
114
|
+
- \`.contextkit/commands/quality-check.md\` — Quality checks`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
showUsage() {
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log(chalk.dim(` See .contextkit/standards/ for project standards`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = BaseIntegration;
|