@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,88 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const BaseIntegration = require('./base-integration');
|
|
3
|
+
|
|
4
|
+
class WindsurfIntegration extends BaseIntegration {
|
|
5
|
+
constructor() {
|
|
6
|
+
super();
|
|
7
|
+
this.name = 'windsurf';
|
|
8
|
+
this.displayName = 'Windsurf';
|
|
9
|
+
this.bridgeFiles = ['.windsurfrules'];
|
|
10
|
+
this.generatedFiles = ['.windsurf/rules/contextkit-standards.md'];
|
|
11
|
+
this.platformDir = '.windsurf/rules';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async install() {
|
|
15
|
+
await super.install();
|
|
16
|
+
// Remove old vibe-kit-named files
|
|
17
|
+
const fs = require('fs-extra');
|
|
18
|
+
const legacyFile = '.windsurf/rules/vibe-kit-standards.md';
|
|
19
|
+
if (await fs.pathExists(legacyFile)) {
|
|
20
|
+
await fs.remove(legacyFile);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async generateFiles() {
|
|
25
|
+
// Bridge file: .windsurfrules (auto-loaded by Windsurf)
|
|
26
|
+
const bridgeContent = `# Project Standards (ContextKit)
|
|
27
|
+
|
|
28
|
+
This project uses [ContextKit](https://github.com/nolrm/contextkit) for AI development standards.
|
|
29
|
+
|
|
30
|
+
${this.getStandardsBlock()}
|
|
31
|
+
|
|
32
|
+
## Key Rules
|
|
33
|
+
|
|
34
|
+
- Follow coding conventions in \`.contextkit/standards/code-style.md\`
|
|
35
|
+
- Use numbered test cases as defined in \`.contextkit/standards/testing.md\`
|
|
36
|
+
- Check \`.contextkit/standards/glossary.md\` for project-specific terminology
|
|
37
|
+
- Reference \`.contextkit/templates/\` for code generation patterns`;
|
|
38
|
+
|
|
39
|
+
await this.writeBridgeFile('.windsurfrules', bridgeContent);
|
|
40
|
+
|
|
41
|
+
// Modular rules file
|
|
42
|
+
const standardsRule = `---
|
|
43
|
+
description: ContextKit project standards
|
|
44
|
+
alwaysApply: true
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
# ContextKit Standards
|
|
48
|
+
|
|
49
|
+
This project uses ContextKit for structured development standards.
|
|
50
|
+
|
|
51
|
+
## References
|
|
52
|
+
|
|
53
|
+
- \`.contextkit/standards/code-style.md\` — Coding conventions
|
|
54
|
+
- \`.contextkit/standards/testing.md\` — Testing patterns (numbered test cases required)
|
|
55
|
+
- \`.contextkit/standards/architecture.md\` — Architecture patterns
|
|
56
|
+
- \`.contextkit/standards/ai-guidelines.md\` — AI behavior rules
|
|
57
|
+
- \`.contextkit/standards/glossary.md\` — Project terminology
|
|
58
|
+
- \`.contextkit/product/mission-lite.md\` — Product context
|
|
59
|
+
|
|
60
|
+
## Templates
|
|
61
|
+
|
|
62
|
+
- \`.contextkit/templates/component.tsx\` — Component template
|
|
63
|
+
- \`.contextkit/templates/test.tsx\` — Test template
|
|
64
|
+
- \`.contextkit/templates/hook.ts\` — Custom hook template
|
|
65
|
+
- \`.contextkit/templates/api.ts\` — API service template
|
|
66
|
+
|
|
67
|
+
## Commands
|
|
68
|
+
|
|
69
|
+
- \`.contextkit/commands/analyze.md\` — Analyze and customize standards
|
|
70
|
+
- \`.contextkit/commands/create-component.md\` — Create components
|
|
71
|
+
- \`.contextkit/commands/create-feature.md\` — Create features
|
|
72
|
+
`;
|
|
73
|
+
await this.writeGeneratedFile('.windsurf/rules/contextkit-standards.md', standardsRule);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
showUsage() {
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(chalk.bold(' Windsurf Usage:'));
|
|
79
|
+
console.log(' .windsurfrules is auto-loaded by Windsurf');
|
|
80
|
+
console.log(' .windsurf/rules/ provides additional context');
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(chalk.dim(' Files created:'));
|
|
83
|
+
console.log(chalk.dim(' .windsurfrules (bridge - auto-loaded)'));
|
|
84
|
+
console.log(chalk.dim(' .windsurf/rules/contextkit-standards.md (rules)'));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = WindsurfIntegration;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
class DownloadManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.timeout = 30000; // 30 seconds
|
|
8
|
+
this.retries = 3;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async downloadFile(url, outputPath, retryCount = 0) {
|
|
12
|
+
try {
|
|
13
|
+
// Ensure directory exists
|
|
14
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
15
|
+
|
|
16
|
+
const response = await axios({
|
|
17
|
+
method: 'GET',
|
|
18
|
+
url: url,
|
|
19
|
+
responseType: 'stream',
|
|
20
|
+
timeout: this.timeout,
|
|
21
|
+
headers: {
|
|
22
|
+
'User-Agent': 'contextkit-cli/1.0.0'
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const writer = fs.createWriteStream(outputPath);
|
|
27
|
+
response.data.pipe(writer);
|
|
28
|
+
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
writer.on('finish', resolve);
|
|
31
|
+
writer.on('error', reject);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (retryCount < this.retries) {
|
|
36
|
+
console.log(`Retrying download (${retryCount + 1}/${this.retries})...`);
|
|
37
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * (retryCount + 1)));
|
|
38
|
+
return this.downloadFile(url, outputPath, retryCount + 1);
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Failed to download ${url}: ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async downloadMultiple(files) {
|
|
45
|
+
const promises = files.map(({ url, path }) => this.downloadFile(url, path));
|
|
46
|
+
return Promise.all(promises);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = DownloadManager;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
class GitHooksManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.hooksDir = '.husky';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async installHooks(packageManager) {
|
|
11
|
+
console.log(chalk.yellow('🪝 Setting up Git hooks...'));
|
|
12
|
+
|
|
13
|
+
// Check if Node.js is available
|
|
14
|
+
if (!this.checkCommandExists('node')) {
|
|
15
|
+
console.log(chalk.yellow('⚠️ Node.js not found, skipping Git hooks setup'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Install Husky based on package manager
|
|
21
|
+
await this.installHusky(packageManager);
|
|
22
|
+
|
|
23
|
+
// Initialize Husky
|
|
24
|
+
await this.initializeHusky(packageManager);
|
|
25
|
+
|
|
26
|
+
// Add hooks
|
|
27
|
+
await this.addHooks(packageManager);
|
|
28
|
+
|
|
29
|
+
console.log(chalk.green('✅ Git hooks setup complete'));
|
|
30
|
+
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.log(chalk.red('❌ Git hooks setup failed:'), error.message);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async installHusky(packageManager) {
|
|
38
|
+
const huskyInstalled = await this.checkHuskyInstalled(packageManager);
|
|
39
|
+
|
|
40
|
+
if (!huskyInstalled) {
|
|
41
|
+
console.log(chalk.blue(`📦 Installing Husky with ${packageManager}...`));
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
switch (packageManager) {
|
|
45
|
+
case 'yarn':
|
|
46
|
+
execSync('yarn add --dev husky', { stdio: 'inherit' });
|
|
47
|
+
break;
|
|
48
|
+
case 'pnpm':
|
|
49
|
+
execSync('pnpm add --save-dev husky', { stdio: 'inherit' });
|
|
50
|
+
break;
|
|
51
|
+
case 'npm':
|
|
52
|
+
default:
|
|
53
|
+
execSync('npm install --save-dev husky', { stdio: 'inherit' });
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
console.log(chalk.green(`✅ Husky installed with ${packageManager}`));
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.log(chalk.yellow(`⚠️ Failed to install Husky with ${packageManager}, trying fallback...`));
|
|
59
|
+
// Fallback to npm if the detected package manager fails
|
|
60
|
+
if (packageManager !== 'npm') {
|
|
61
|
+
try {
|
|
62
|
+
execSync('npm install --save-dev husky', { stdio: 'inherit' });
|
|
63
|
+
console.log(chalk.green('✅ Husky installed with npm (fallback)'));
|
|
64
|
+
} catch (fallbackError) {
|
|
65
|
+
console.log(chalk.red('❌ Failed to install Husky with any package manager'));
|
|
66
|
+
throw new Error(`Failed to install Husky: ${fallbackError.message}`);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
console.log(chalk.green('✅ Husky already installed'));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async checkHuskyInstalled(packageManager) {
|
|
78
|
+
try {
|
|
79
|
+
// Check if husky is in package.json dependencies
|
|
80
|
+
const packageJson = await fs.readJson('package.json').catch(() => ({}));
|
|
81
|
+
const allDeps = {
|
|
82
|
+
...packageJson.dependencies,
|
|
83
|
+
...packageJson.devDependencies,
|
|
84
|
+
...packageJson.peerDependencies
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (allDeps.husky) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Fallback: try package manager specific commands
|
|
92
|
+
switch (packageManager) {
|
|
93
|
+
case 'yarn':
|
|
94
|
+
execSync('yarn list --pattern husky', { stdio: 'pipe' });
|
|
95
|
+
return true;
|
|
96
|
+
case 'pnpm':
|
|
97
|
+
execSync('pnpm list husky', { stdio: 'pipe' });
|
|
98
|
+
return true;
|
|
99
|
+
case 'npm':
|
|
100
|
+
default:
|
|
101
|
+
execSync('npm list husky', { stdio: 'pipe' });
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async initializeHusky(packageManager) {
|
|
110
|
+
// Modern Husky doesn't need explicit initialization
|
|
111
|
+
// Just ensure .husky directory exists
|
|
112
|
+
await fs.ensureDir(this.hooksDir);
|
|
113
|
+
|
|
114
|
+
// Create husky.sh if it doesn't exist (for compatibility)
|
|
115
|
+
const huskyShPath = `${this.hooksDir}/_/husky.sh`;
|
|
116
|
+
if (!await fs.pathExists(huskyShPath)) {
|
|
117
|
+
await fs.ensureDir(`${this.hooksDir}/_`);
|
|
118
|
+
const huskyShContent = `#!/usr/bin/env sh
|
|
119
|
+
if [ -z "$husky_skip_init" ]; then
|
|
120
|
+
debug () {
|
|
121
|
+
if [ "$HUSKY_DEBUG" = "1" ]; then
|
|
122
|
+
echo "husky (debug) - $1"
|
|
123
|
+
fi
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
readonly hook_name="$(basename -- "$0")"
|
|
127
|
+
debug "starting $hook_name..."
|
|
128
|
+
|
|
129
|
+
if [ "$HUSKY" = "0" ]; then
|
|
130
|
+
debug "HUSKY env variable is set to 0, skipping hook"
|
|
131
|
+
exit 0
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
if [ -f ~/.huskyrc ]; then
|
|
135
|
+
debug "sourcing ~/.huskyrc"
|
|
136
|
+
. ~/.huskyrc
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
readonly husky_skip_init=1
|
|
140
|
+
export husky_skip_init
|
|
141
|
+
sh -e "$0" "$@"
|
|
142
|
+
fi
|
|
143
|
+
`;
|
|
144
|
+
await fs.writeFile(huskyShPath, huskyShContent);
|
|
145
|
+
await fs.chmod(huskyShPath, '755');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(chalk.green('✅ Husky initialized'));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async addHooks(packageManager) {
|
|
152
|
+
// Backup existing hooks
|
|
153
|
+
await this.backupExistingHooks();
|
|
154
|
+
|
|
155
|
+
// Add new hooks
|
|
156
|
+
const hooks = [
|
|
157
|
+
{ name: 'pre-commit', script: '.contextkit/hooks/pre-commit.sh' },
|
|
158
|
+
{ name: 'pre-push', script: '.contextkit/hooks/pre-push.sh' },
|
|
159
|
+
{ name: 'commit-msg', script: '.contextkit/hooks/commit-msg.sh' }
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
for (const hook of hooks) {
|
|
163
|
+
await this.addHook(packageManager, hook.name, hook.script);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async addHook(packageManager, hookName, scriptPath) {
|
|
168
|
+
const hookPath = `${this.hooksDir}/${hookName}`;
|
|
169
|
+
|
|
170
|
+
// Ensure .husky directory exists
|
|
171
|
+
await fs.ensureDir(this.hooksDir);
|
|
172
|
+
|
|
173
|
+
// Create hook file directly (modern Husky approach)
|
|
174
|
+
const hookContent = `#!/usr/bin/env sh
|
|
175
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
176
|
+
|
|
177
|
+
${scriptPath}
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
await fs.writeFile(hookPath, hookContent);
|
|
181
|
+
await fs.chmod(hookPath, '755');
|
|
182
|
+
|
|
183
|
+
console.log(chalk.green(`✅ Created hook: ${hookName}`));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async backupExistingHooks() {
|
|
187
|
+
const hooks = ['pre-commit', 'pre-push', 'commit-msg'];
|
|
188
|
+
|
|
189
|
+
for (const hook of hooks) {
|
|
190
|
+
const hookPath = `${this.hooksDir}/${hook}`;
|
|
191
|
+
if (fs.existsSync(hookPath)) {
|
|
192
|
+
const backupPath = `${hookPath}.backup`;
|
|
193
|
+
await fs.copy(hookPath, backupPath);
|
|
194
|
+
console.log(chalk.yellow(`📦 Backed up existing hook: ${hook}`));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
checkCommandExists(command) {
|
|
200
|
+
try {
|
|
201
|
+
execSync(`which ${command}`, { stdio: 'pipe' });
|
|
202
|
+
return true;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async uninstallHooks() {
|
|
209
|
+
console.log(chalk.yellow('🪝 Removing Git hooks...'));
|
|
210
|
+
|
|
211
|
+
const hooks = ['pre-commit', 'pre-push', 'commit-msg'];
|
|
212
|
+
|
|
213
|
+
for (const hook of hooks) {
|
|
214
|
+
const hookPath = `${this.hooksDir}/${hook}`;
|
|
215
|
+
const backupPath = `${hookPath}.backup`;
|
|
216
|
+
|
|
217
|
+
if (fs.existsSync(backupPath)) {
|
|
218
|
+
await fs.move(backupPath, hookPath);
|
|
219
|
+
console.log(chalk.green(`✅ Restored original hook: ${hook}`));
|
|
220
|
+
} else if (fs.existsSync(hookPath)) {
|
|
221
|
+
await fs.remove(hookPath);
|
|
222
|
+
console.log(chalk.green(`✅ Removed hook: ${hook}`));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = GitHooksManager;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class ProjectDetector {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.projectRoot = process.cwd();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
detectProjectType() {
|
|
10
|
+
if (fs.existsSync('package.json')) {
|
|
11
|
+
const packageJson = this.readPackageJson();
|
|
12
|
+
|
|
13
|
+
// Detect framework
|
|
14
|
+
if (this.hasDependency(packageJson, 'react') || this.hasDependency(packageJson, '@types/react')) {
|
|
15
|
+
return this.detectReactVariant(packageJson);
|
|
16
|
+
} else if (this.hasDependency(packageJson, 'vue') || this.hasDependency(packageJson, '@vue')) {
|
|
17
|
+
return this.detectVueVariant(packageJson);
|
|
18
|
+
} else if (this.hasDependency(packageJson, '@angular/core')) {
|
|
19
|
+
return 'angular';
|
|
20
|
+
} else if (this.hasDependency(packageJson, 'next')) {
|
|
21
|
+
return 'nextjs';
|
|
22
|
+
} else if (this.hasDependency(packageJson, 'nuxt')) {
|
|
23
|
+
return 'nuxt';
|
|
24
|
+
} else if (this.hasDependency(packageJson, 'svelte')) {
|
|
25
|
+
return 'svelte';
|
|
26
|
+
} else {
|
|
27
|
+
return 'node';
|
|
28
|
+
}
|
|
29
|
+
} else if (fs.existsSync('requirements.txt') || fs.existsSync('pyproject.toml')) {
|
|
30
|
+
return 'python';
|
|
31
|
+
} else if (fs.existsSync('Cargo.toml')) {
|
|
32
|
+
return 'rust';
|
|
33
|
+
} else if (fs.existsSync('go.mod')) {
|
|
34
|
+
return 'go';
|
|
35
|
+
} else if (fs.existsSync('composer.json')) {
|
|
36
|
+
return 'php';
|
|
37
|
+
} else if (fs.existsSync('Gemfile')) {
|
|
38
|
+
return 'ruby';
|
|
39
|
+
} else if (fs.existsSync('pom.xml') || fs.existsSync('build.gradle')) {
|
|
40
|
+
return 'java';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return 'generic';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
detectReactVariant(packageJson) {
|
|
47
|
+
if (this.hasDependency(packageJson, 'vite')) {
|
|
48
|
+
return 'react-vite';
|
|
49
|
+
} else if (this.hasDependency(packageJson, 'webpack')) {
|
|
50
|
+
return 'react-webpack';
|
|
51
|
+
} else if (this.hasDependency(packageJson, 'rollup')) {
|
|
52
|
+
return 'react-rollup';
|
|
53
|
+
} else if (this.hasDependency(packageJson, 'next')) {
|
|
54
|
+
return 'nextjs';
|
|
55
|
+
}
|
|
56
|
+
return 'react';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
detectVueVariant(packageJson) {
|
|
60
|
+
if (this.hasDependency(packageJson, 'vite')) {
|
|
61
|
+
return 'vue-vite';
|
|
62
|
+
} else if (this.hasDependency(packageJson, 'webpack')) {
|
|
63
|
+
return 'vue-webpack';
|
|
64
|
+
} else if (this.hasDependency(packageJson, 'nuxt')) {
|
|
65
|
+
return 'nuxt';
|
|
66
|
+
}
|
|
67
|
+
return 'vue';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
detectPackageManager() {
|
|
71
|
+
if (fs.existsSync('yarn.lock')) {
|
|
72
|
+
return 'yarn';
|
|
73
|
+
} else if (fs.existsSync('pnpm-lock.yaml')) {
|
|
74
|
+
return 'pnpm';
|
|
75
|
+
} else if (fs.existsSync('package-lock.json')) {
|
|
76
|
+
return 'npm';
|
|
77
|
+
} else if (fs.existsSync('package.json')) {
|
|
78
|
+
return 'npm'; // Default to npm
|
|
79
|
+
}
|
|
80
|
+
return 'none';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
hasDependency(packageJson, dependency) {
|
|
84
|
+
const deps = packageJson.dependencies || {};
|
|
85
|
+
const devDeps = packageJson.devDependencies || {};
|
|
86
|
+
return deps[dependency] || devDeps[dependency];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
readPackageJson() {
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
isGitRepository() {
|
|
98
|
+
return fs.existsSync('.git');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getProjectName() {
|
|
102
|
+
if (fs.existsSync('package.json')) {
|
|
103
|
+
const packageJson = this.readPackageJson();
|
|
104
|
+
return packageJson.name || path.basename(this.projectRoot);
|
|
105
|
+
}
|
|
106
|
+
return path.basename(this.projectRoot);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = ProjectDetector;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class StatusManager {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.statusPath = '.contextkit/status.json';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async getStatus() {
|
|
10
|
+
try {
|
|
11
|
+
if (await fs.pathExists(this.statusPath)) {
|
|
12
|
+
return await fs.readJson(this.statusPath);
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.warn('Warning: Could not read status file:', error.message);
|
|
16
|
+
}
|
|
17
|
+
return this.getDefaultStatus();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getDefaultStatus() {
|
|
21
|
+
const packageJson = require('../../package.json');
|
|
22
|
+
return {
|
|
23
|
+
version: packageJson.version,
|
|
24
|
+
installed_at: new Date().toISOString(),
|
|
25
|
+
last_updated: new Date().toISOString(),
|
|
26
|
+
analyze: {
|
|
27
|
+
run: false,
|
|
28
|
+
last_run: null,
|
|
29
|
+
customizations: [],
|
|
30
|
+
project_type: null,
|
|
31
|
+
package_manager: null
|
|
32
|
+
},
|
|
33
|
+
features: {
|
|
34
|
+
git_hooks: false,
|
|
35
|
+
standards: true,
|
|
36
|
+
templates: true
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async saveStatus(status) {
|
|
42
|
+
try {
|
|
43
|
+
await fs.ensureDir('.contextkit');
|
|
44
|
+
await fs.writeJson(this.statusPath, status, { spaces: 2 });
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw new Error(`Failed to save status: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async updateAnalyzeStatus(analyzeData) {
|
|
51
|
+
const status = await this.getStatus();
|
|
52
|
+
status.analyze = {
|
|
53
|
+
run: true,
|
|
54
|
+
last_run: new Date().toISOString(),
|
|
55
|
+
customizations: analyzeData.customizations || [],
|
|
56
|
+
project_type: analyzeData.project_type,
|
|
57
|
+
package_manager: analyzeData.package_manager
|
|
58
|
+
};
|
|
59
|
+
status.last_updated = new Date().toISOString();
|
|
60
|
+
await this.saveStatus(status);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async updateFeatureStatus(feature, enabled) {
|
|
64
|
+
const status = await this.getStatus();
|
|
65
|
+
status.features[feature] = enabled;
|
|
66
|
+
status.last_updated = new Date().toISOString();
|
|
67
|
+
await this.saveStatus(status);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async isFirstTimeAnalyze() {
|
|
71
|
+
const status = await this.getStatus();
|
|
72
|
+
return !status.analyze.run;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async getAnalyzeInfo() {
|
|
76
|
+
const status = await this.getStatus();
|
|
77
|
+
return {
|
|
78
|
+
isFirstTime: !status.analyze.run,
|
|
79
|
+
lastRun: status.analyze.last_run,
|
|
80
|
+
projectType: status.analyze.project_type,
|
|
81
|
+
packageManager: status.analyze.package_manager,
|
|
82
|
+
customizations: status.analyze.customizations
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async updateVersion(version) {
|
|
87
|
+
const status = await this.getStatus();
|
|
88
|
+
status.version = version;
|
|
89
|
+
status.last_updated = new Date().toISOString();
|
|
90
|
+
await this.saveStatus(status);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async resetAnalyzeStatus() {
|
|
94
|
+
const status = await this.getStatus();
|
|
95
|
+
status.analyze = {
|
|
96
|
+
run: false,
|
|
97
|
+
last_run: null,
|
|
98
|
+
customizations: [],
|
|
99
|
+
project_type: null,
|
|
100
|
+
package_manager: null
|
|
101
|
+
};
|
|
102
|
+
status.last_updated = new Date().toISOString();
|
|
103
|
+
await this.saveStatus(status);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = StatusManager;
|