@nolrm/contextkit 0.13.4 → 0.13.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/README.md +65 -46
- package/bin/contextkit.js +4 -2
- package/lib/commands/analyze.js +131 -91
- package/lib/commands/check.js +24 -18
- package/lib/commands/install.js +274 -71
- package/lib/commands/note.js +12 -20
- package/lib/commands/run.js +20 -22
- package/lib/commands/status.js +61 -32
- package/lib/commands/update.js +25 -26
- package/lib/index.js +1 -1
- package/lib/integrations/aider-integration.js +1 -1
- package/lib/integrations/base-integration.js +13 -6
- package/lib/integrations/claude-integration.js +103 -41
- package/lib/integrations/continue-integration.js +5 -5
- package/lib/integrations/copilot-integration.js +1 -1
- package/lib/integrations/cursor-integration.js +70 -28
- package/lib/integrations/gemini-integration.js +13 -11
- package/lib/integrations/index.js +11 -1
- package/lib/utils/banner.js +14 -11
- package/lib/utils/download.js +3 -4
- package/lib/utils/git-hooks.js +3 -4
- package/lib/utils/migrations.js +6 -4
- package/lib/utils/migrations.md +4 -3
- package/lib/utils/notifier.js +1 -1
- package/lib/utils/project-detector.js +11 -5
- package/lib/utils/status-manager.js +6 -7
- package/lib/utils/tool-detector.js +19 -21
- package/package.json +8 -2
package/lib/commands/note.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
|
-
const path = require('path');
|
|
4
3
|
|
|
5
4
|
class NoteCommand {
|
|
6
5
|
constructor() {
|
|
@@ -14,7 +13,7 @@ class NoteCommand {
|
|
|
14
13
|
return;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
if (!await fs.pathExists(this.correctionsPath)) {
|
|
16
|
+
if (!(await fs.pathExists(this.correctionsPath))) {
|
|
18
17
|
console.log(chalk.red('❌ Corrections log not found'));
|
|
19
18
|
console.log(chalk.yellow(' Run: contextkit install'));
|
|
20
19
|
return;
|
|
@@ -23,48 +22,42 @@ class NoteCommand {
|
|
|
23
22
|
try {
|
|
24
23
|
const content = await fs.readFile(this.correctionsPath, 'utf-8');
|
|
25
24
|
const today = new Date().toISOString().split('T')[0];
|
|
26
|
-
|
|
25
|
+
|
|
27
26
|
// Find or create today's session
|
|
28
27
|
const sessionHeader = `### ${today} - ${options.task || 'Development Session'}`;
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
let updatedContent = content;
|
|
31
|
-
|
|
30
|
+
|
|
32
31
|
// Check if today's session exists
|
|
33
32
|
if (content.includes(sessionHeader)) {
|
|
34
33
|
// Add to existing session
|
|
35
34
|
const category = options.category || 'AI Behavior';
|
|
36
35
|
const priority = options.priority || 'MEDIUM';
|
|
37
36
|
const note = `- ${category} | ${message} [${priority}]`;
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
// Find the session and add note to appropriate section
|
|
40
39
|
const sections = ['Rule Updates', 'AI Behavior', 'Preferences', 'Trend Indicators'];
|
|
41
40
|
let sectionFound = false;
|
|
42
|
-
|
|
41
|
+
|
|
43
42
|
for (const section of sections) {
|
|
44
43
|
if (category.toLowerCase().includes(section.toLowerCase().substring(0, 5))) {
|
|
45
44
|
const sectionHeader = `#### ${section}`;
|
|
46
45
|
const sectionRegex = new RegExp(`(${sectionHeader}[\\s\\S]*?)(?=####|###|##|$)`, 'm');
|
|
47
46
|
const match = content.match(sectionRegex);
|
|
48
|
-
|
|
47
|
+
|
|
49
48
|
if (match) {
|
|
50
|
-
updatedContent = content.replace(
|
|
51
|
-
sectionRegex,
|
|
52
|
-
`$1\n${note}`
|
|
53
|
-
);
|
|
49
|
+
updatedContent = content.replace(sectionRegex, `$1\n${note}`);
|
|
54
50
|
sectionFound = true;
|
|
55
51
|
break;
|
|
56
52
|
}
|
|
57
53
|
}
|
|
58
54
|
}
|
|
59
|
-
|
|
55
|
+
|
|
60
56
|
if (!sectionFound) {
|
|
61
57
|
// Add to AI Behavior section as default
|
|
62
58
|
const aiBehaviorRegex = /(#### AI Behavior[\s\S]*?)(?=####|###|##|$)/m;
|
|
63
59
|
if (content.match(aiBehaviorRegex)) {
|
|
64
|
-
updatedContent = content.replace(
|
|
65
|
-
aiBehaviorRegex,
|
|
66
|
-
`$1\n${note}`
|
|
67
|
-
);
|
|
60
|
+
updatedContent = content.replace(aiBehaviorRegex, `$1\n${note}`);
|
|
68
61
|
}
|
|
69
62
|
}
|
|
70
63
|
} else {
|
|
@@ -91,7 +84,7 @@ ${sessionHeader}
|
|
|
91
84
|
- [No trends yet]
|
|
92
85
|
|
|
93
86
|
`;
|
|
94
|
-
|
|
87
|
+
|
|
95
88
|
// Insert after "## Recent Sessions"
|
|
96
89
|
const sessionsRegex = /(## Recent Sessions\n)/;
|
|
97
90
|
if (content.match(sessionsRegex)) {
|
|
@@ -101,7 +94,7 @@ ${sessionHeader}
|
|
|
101
94
|
updatedContent = `## Recent Sessions${newSession}\n${content}`;
|
|
102
95
|
}
|
|
103
96
|
}
|
|
104
|
-
|
|
97
|
+
|
|
105
98
|
await fs.writeFile(this.correctionsPath, updatedContent);
|
|
106
99
|
console.log(chalk.green('✅ Note added to corrections log'));
|
|
107
100
|
console.log(chalk.dim(` ${message}`));
|
|
@@ -117,4 +110,3 @@ async function note(message, options) {
|
|
|
117
110
|
}
|
|
118
111
|
|
|
119
112
|
module.exports = note;
|
|
120
|
-
|
package/lib/commands/run.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const ora = require('ora');
|
|
5
3
|
|
|
6
4
|
class RunCommand {
|
|
7
5
|
constructor() {
|
|
@@ -18,7 +16,7 @@ class RunCommand {
|
|
|
18
16
|
const possiblePaths = [
|
|
19
17
|
`.contextkit/instructions/core/${workflowName}.md`,
|
|
20
18
|
`.contextkit/instructions/${workflowName}.md`,
|
|
21
|
-
`.contextkit/commands/${workflowName}.md
|
|
19
|
+
`.contextkit/commands/${workflowName}.md`,
|
|
22
20
|
];
|
|
23
21
|
|
|
24
22
|
let workflowPath = null;
|
|
@@ -32,7 +30,7 @@ class RunCommand {
|
|
|
32
30
|
if (!workflowPath) {
|
|
33
31
|
console.log(chalk.red(`❌ Workflow not found: ${workflowName}`));
|
|
34
32
|
console.log(chalk.yellow(' Searched in:'));
|
|
35
|
-
possiblePaths.forEach(p => console.log(chalk.dim(` - ${p}`)));
|
|
33
|
+
possiblePaths.forEach((p) => console.log(chalk.dim(` - ${p}`)));
|
|
36
34
|
return;
|
|
37
35
|
}
|
|
38
36
|
|
|
@@ -41,7 +39,7 @@ class RunCommand {
|
|
|
41
39
|
|
|
42
40
|
// Parse workflow
|
|
43
41
|
const parsed = this.parseWorkflow(this.workflowContent);
|
|
44
|
-
|
|
42
|
+
|
|
45
43
|
if (!parsed) {
|
|
46
44
|
console.log(chalk.red('❌ Failed to parse workflow'));
|
|
47
45
|
return;
|
|
@@ -55,15 +53,15 @@ class RunCommand {
|
|
|
55
53
|
// Execute workflow steps
|
|
56
54
|
if (parsed.steps && parsed.steps.length > 0) {
|
|
57
55
|
console.log(chalk.blue(`\n📋 Executing ${parsed.steps.length} step(s)...\n`));
|
|
58
|
-
|
|
56
|
+
|
|
59
57
|
for (let i = 0; i < parsed.steps.length; i++) {
|
|
60
58
|
const step = parsed.steps[i];
|
|
61
59
|
this.currentStep = i + 1;
|
|
62
|
-
|
|
60
|
+
|
|
63
61
|
console.log(chalk.cyan(`\n${'─'.repeat(60)}`));
|
|
64
62
|
console.log(chalk.bold(`Step ${step.number}: ${step.name || 'Unnamed Step'}`));
|
|
65
63
|
console.log(chalk.cyan(`${'─'.repeat(60)}\n`));
|
|
66
|
-
|
|
64
|
+
|
|
67
65
|
await this.executeStep(step, options);
|
|
68
66
|
}
|
|
69
67
|
}
|
|
@@ -80,7 +78,7 @@ class RunCommand {
|
|
|
80
78
|
const result = {
|
|
81
79
|
preFlight: null,
|
|
82
80
|
steps: [],
|
|
83
|
-
postFlight: null
|
|
81
|
+
postFlight: null,
|
|
84
82
|
};
|
|
85
83
|
|
|
86
84
|
// Extract pre-flight
|
|
@@ -93,17 +91,18 @@ class RunCommand {
|
|
|
93
91
|
const processFlowMatch = content.match(/<process_flow>([\s\S]*?)<\/process_flow>/);
|
|
94
92
|
if (processFlowMatch) {
|
|
95
93
|
const processFlow = processFlowMatch[1];
|
|
96
|
-
|
|
94
|
+
|
|
97
95
|
// Extract all steps
|
|
98
|
-
const stepRegex =
|
|
96
|
+
const stepRegex =
|
|
97
|
+
/<step\s+number="(\d+)"(?:\s+subagent="([^"]+)")?(?:\s+name="([^"]+)")?>([\s\S]*?)<\/step>/g;
|
|
99
98
|
let stepMatch;
|
|
100
|
-
|
|
99
|
+
|
|
101
100
|
while ((stepMatch = stepRegex.exec(processFlow)) !== null) {
|
|
102
101
|
result.steps.push({
|
|
103
102
|
number: parseInt(stepMatch[1]),
|
|
104
103
|
subagent: stepMatch[2] || null,
|
|
105
104
|
name: stepMatch[3] || null,
|
|
106
|
-
content: stepMatch[4].trim()
|
|
105
|
+
content: stepMatch[4].trim(),
|
|
107
106
|
});
|
|
108
107
|
}
|
|
109
108
|
}
|
|
@@ -119,12 +118,12 @@ class RunCommand {
|
|
|
119
118
|
|
|
120
119
|
async executePreFlight(preFlightContent) {
|
|
121
120
|
console.log(chalk.blue('🔍 Pre-flight checks...\n'));
|
|
122
|
-
|
|
121
|
+
|
|
123
122
|
// Check for EXECUTE directive
|
|
124
123
|
const executeMatch = preFlightContent.match(/EXECUTE:\s*(.+)/);
|
|
125
124
|
if (executeMatch) {
|
|
126
125
|
const filePath = executeMatch[1].trim().replace('@.contextkit/', '.contextkit/');
|
|
127
|
-
|
|
126
|
+
|
|
128
127
|
if (await fs.pathExists(filePath)) {
|
|
129
128
|
const preFlightFile = await fs.readFile(filePath, 'utf-8');
|
|
130
129
|
console.log(chalk.dim(preFlightFile));
|
|
@@ -151,7 +150,7 @@ class RunCommand {
|
|
|
151
150
|
|
|
152
151
|
// Extract instructions from step content
|
|
153
152
|
const instructions = this.extractInstructions(step.content);
|
|
154
|
-
|
|
153
|
+
|
|
155
154
|
// For now, display instructions and wait for user/AI to execute
|
|
156
155
|
if (instructions.length > 0) {
|
|
157
156
|
console.log(chalk.blue('📝 Instructions to execute:'));
|
|
@@ -167,7 +166,7 @@ class RunCommand {
|
|
|
167
166
|
if (options.interactive) {
|
|
168
167
|
const readline = require('readline').createInterface({
|
|
169
168
|
input: process.stdin,
|
|
170
|
-
output: process.stdout
|
|
169
|
+
output: process.stdout,
|
|
171
170
|
});
|
|
172
171
|
|
|
173
172
|
return new Promise((resolve) => {
|
|
@@ -181,7 +180,7 @@ class RunCommand {
|
|
|
181
180
|
|
|
182
181
|
extractInstructions(content) {
|
|
183
182
|
const instructions = [];
|
|
184
|
-
|
|
183
|
+
|
|
185
184
|
// Extract ACTION directives
|
|
186
185
|
const actionMatches = content.matchAll(/ACTION:\s*(.+)/gi);
|
|
187
186
|
for (const match of actionMatches) {
|
|
@@ -199,17 +198,17 @@ class RunCommand {
|
|
|
199
198
|
|
|
200
199
|
async executePostFlight(postFlightContent) {
|
|
201
200
|
console.log(chalk.blue('\n🔍 Post-flight checks...\n'));
|
|
202
|
-
|
|
201
|
+
|
|
203
202
|
// Check for EXECUTE directive
|
|
204
203
|
const executeMatch = postFlightContent.match(/EXECUTE:\s*(.+)/);
|
|
205
204
|
if (executeMatch) {
|
|
206
205
|
const filePath = executeMatch[1].trim().replace('@.contextkit/', '.contextkit/');
|
|
207
|
-
|
|
206
|
+
|
|
208
207
|
if (await fs.pathExists(filePath)) {
|
|
209
208
|
const postFlightFile = await fs.readFile(filePath, 'utf-8');
|
|
210
209
|
console.log(chalk.dim(postFlightFile));
|
|
211
210
|
console.log('');
|
|
212
|
-
|
|
211
|
+
|
|
213
212
|
// Check for corrections log update instructions
|
|
214
213
|
if (postFlightFile.includes('corrections log')) {
|
|
215
214
|
console.log(chalk.yellow('💡 Remember to update corrections.md if any issues occurred'));
|
|
@@ -229,4 +228,3 @@ async function run(workflowName, options) {
|
|
|
229
228
|
}
|
|
230
229
|
|
|
231
230
|
module.exports = run;
|
|
232
|
-
|
package/lib/commands/status.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
|
-
const ora = require('ora');
|
|
3
2
|
const fs = require('fs-extra');
|
|
4
3
|
const path = require('path');
|
|
5
4
|
const StatusManager = require('../utils/status-manager');
|
|
@@ -11,49 +10,59 @@ class StatusCommand {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
async status() {
|
|
14
|
-
if (!await fs.pathExists(this.configPath)) {
|
|
13
|
+
if (!(await fs.pathExists(this.configPath))) {
|
|
15
14
|
console.log(chalk.red('❌ ContextKit is not installed in this project'));
|
|
16
15
|
console.log(chalk.yellow('💡 Run: contextkit install'));
|
|
17
16
|
return;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
try {
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
20
|
+
const _config = await this.parseConfig();
|
|
21
|
+
const _projectType = await this.detectProjectType();
|
|
22
|
+
const _packageManager = await this.detectPackageManager();
|
|
24
23
|
const status = await this.statusManager.getStatus();
|
|
25
24
|
const analyzeInfo = await this.statusManager.getAnalyzeInfo();
|
|
26
25
|
|
|
27
26
|
console.log(chalk.green('🎵 ContextKit Status'));
|
|
28
27
|
console.log('');
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
console.log(chalk.blue('📦 Installation:'));
|
|
31
30
|
console.log(` Version: ${status.version}`);
|
|
32
31
|
console.log(` Installed: ${new Date(status.installed_at).toLocaleDateString()}`);
|
|
33
32
|
console.log(` Last Updated: ${new Date(status.last_updated).toLocaleDateString()}`);
|
|
34
33
|
console.log('');
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
console.log(chalk.blue('🔍 Analysis:'));
|
|
37
36
|
if (analyzeInfo.isFirstTime) {
|
|
38
37
|
console.log(chalk.yellow(' Status: Not analyzed'));
|
|
39
|
-
console.log(
|
|
38
|
+
console.log(
|
|
39
|
+
chalk.blue(
|
|
40
|
+
' Recommendation: Run @.contextkit/commands/analyze.md to customize standards'
|
|
41
|
+
)
|
|
42
|
+
);
|
|
40
43
|
} else {
|
|
41
|
-
console.log(
|
|
44
|
+
console.log(
|
|
45
|
+
chalk.green(
|
|
46
|
+
` Status: Completed (${new Date(analyzeInfo.lastRun).toLocaleDateString()})`
|
|
47
|
+
)
|
|
48
|
+
);
|
|
42
49
|
console.log(chalk.blue(` Project: ${analyzeInfo.projectType || 'Unknown'}`));
|
|
43
50
|
console.log(chalk.blue(` Package Manager: ${analyzeInfo.packageManager || 'Unknown'}`));
|
|
44
51
|
if (analyzeInfo.customizations.length > 0) {
|
|
45
|
-
console.log(
|
|
52
|
+
console.log(
|
|
53
|
+
chalk.blue(` Customizations: ${analyzeInfo.customizations.length} applied`)
|
|
54
|
+
);
|
|
46
55
|
}
|
|
47
56
|
}
|
|
48
57
|
console.log('');
|
|
49
|
-
|
|
58
|
+
|
|
50
59
|
console.log(chalk.blue('✅ Features:'));
|
|
51
60
|
console.log(` Pre-push hook: ${status.features.pre_push_hook ? '✅' : '❌'}`);
|
|
52
61
|
console.log(` Commit-msg hook: ${status.features.commit_msg_hook ? '✅' : '❌'}`);
|
|
53
62
|
console.log(` Standards: ${status.features.standards ? '✅' : '❌'}`);
|
|
54
63
|
console.log(` Templates: ${status.features.templates ? '✅' : '❌'}`);
|
|
55
64
|
console.log('');
|
|
56
|
-
|
|
65
|
+
|
|
57
66
|
// Check context files
|
|
58
67
|
console.log(chalk.blue('📚 Context Files (Loaded with ck "prompt"):'));
|
|
59
68
|
const contextFiles = await this.checkContextFiles();
|
|
@@ -63,7 +72,7 @@ class StatusCommand {
|
|
|
63
72
|
console.log(` architecture.md: ${contextFiles.architecture ? '✅' : '❌'}`);
|
|
64
73
|
console.log(` ai-guidelines.md: ${contextFiles.guidelines ? '✅' : '❌'}`);
|
|
65
74
|
console.log(` glossary.md: ${contextFiles.glossary ? '✅' : '❌'}`);
|
|
66
|
-
|
|
75
|
+
|
|
67
76
|
if (contextFiles.allPresent) {
|
|
68
77
|
console.log(chalk.green('\n✅ All context files ready for AI prompts!'));
|
|
69
78
|
} else {
|
|
@@ -86,7 +95,6 @@ class StatusCommand {
|
|
|
86
95
|
} else {
|
|
87
96
|
console.log(chalk.green('✅ ContextKit is up to date'));
|
|
88
97
|
}
|
|
89
|
-
|
|
90
98
|
} catch (error) {
|
|
91
99
|
console.log(chalk.red('❌ Error reading ContextKit configuration:'), error.message);
|
|
92
100
|
}
|
|
@@ -95,7 +103,7 @@ class StatusCommand {
|
|
|
95
103
|
async parseConfig() {
|
|
96
104
|
const configContent = await fs.readFile(this.configPath, 'utf8');
|
|
97
105
|
const config = {};
|
|
98
|
-
|
|
106
|
+
|
|
99
107
|
// Simple YAML parsing for our config format
|
|
100
108
|
const lines = configContent.split('\n');
|
|
101
109
|
for (const line of lines) {
|
|
@@ -144,7 +152,10 @@ class StatusCommand {
|
|
|
144
152
|
return 'react';
|
|
145
153
|
} else if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {
|
|
146
154
|
return 'vue';
|
|
147
|
-
} else if (
|
|
155
|
+
} else if (
|
|
156
|
+
packageJson.dependencies?.['@angular/core'] ||
|
|
157
|
+
packageJson.devDependencies?.['@angular/core']
|
|
158
|
+
) {
|
|
148
159
|
return 'angular';
|
|
149
160
|
} else if (packageJson.dependencies?.next || packageJson.devDependencies?.next) {
|
|
150
161
|
return 'nextjs';
|
|
@@ -154,7 +165,10 @@ class StatusCommand {
|
|
|
154
165
|
return 'svelte';
|
|
155
166
|
}
|
|
156
167
|
return 'node';
|
|
157
|
-
} else if (
|
|
168
|
+
} else if (
|
|
169
|
+
(await fs.pathExists('requirements.txt')) ||
|
|
170
|
+
(await fs.pathExists('pyproject.toml'))
|
|
171
|
+
) {
|
|
158
172
|
return 'python';
|
|
159
173
|
} else if (await fs.pathExists('Cargo.toml')) {
|
|
160
174
|
return 'rust';
|
|
@@ -180,24 +194,27 @@ class StatusCommand {
|
|
|
180
194
|
async checkForUpdates(currentVersion) {
|
|
181
195
|
try {
|
|
182
196
|
const axios = require('axios');
|
|
183
|
-
const response = await axios.get(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
197
|
+
const response = await axios.get(
|
|
198
|
+
'https://api.github.com/repos/nolrm/contextkit/releases/latest',
|
|
199
|
+
{
|
|
200
|
+
timeout: 5000,
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
|
|
187
204
|
const latestVersion = response.data.tag_name.replace('v', '');
|
|
188
205
|
const hasUpdate = this.isNewerVersion(latestVersion, currentVersion);
|
|
189
|
-
|
|
206
|
+
|
|
190
207
|
return {
|
|
191
208
|
hasUpdate,
|
|
192
209
|
currentVersion,
|
|
193
|
-
latestVersion
|
|
210
|
+
latestVersion,
|
|
194
211
|
};
|
|
195
212
|
} catch (error) {
|
|
196
213
|
return {
|
|
197
214
|
hasUpdate: false,
|
|
198
215
|
currentVersion,
|
|
199
216
|
latestVersion: 'unknown',
|
|
200
|
-
error: error.message
|
|
217
|
+
error: error.message,
|
|
201
218
|
};
|
|
202
219
|
}
|
|
203
220
|
}
|
|
@@ -205,15 +222,15 @@ class StatusCommand {
|
|
|
205
222
|
isNewerVersion(latest, current) {
|
|
206
223
|
const latestParts = latest.split('.').map(Number);
|
|
207
224
|
const currentParts = current.split('.').map(Number);
|
|
208
|
-
|
|
225
|
+
|
|
209
226
|
for (let i = 0; i < Math.max(latestParts.length, currentParts.length); i++) {
|
|
210
227
|
const latestPart = latestParts[i] || 0;
|
|
211
228
|
const currentPart = currentParts[i] || 0;
|
|
212
|
-
|
|
229
|
+
|
|
213
230
|
if (latestPart > currentPart) return true;
|
|
214
231
|
if (latestPart < currentPart) return false;
|
|
215
232
|
}
|
|
216
|
-
|
|
233
|
+
|
|
217
234
|
return false;
|
|
218
235
|
}
|
|
219
236
|
|
|
@@ -228,11 +245,17 @@ class StatusCommand {
|
|
|
228
245
|
if (allFiles.length === 0) continue;
|
|
229
246
|
|
|
230
247
|
if (result.valid) {
|
|
231
|
-
console.log(
|
|
248
|
+
console.log(
|
|
249
|
+
` ${integration.displayName}: ${chalk.green('✅')} (${result.present.length} files)`
|
|
250
|
+
);
|
|
232
251
|
} else if (result.present.length > 0) {
|
|
233
|
-
console.log(
|
|
252
|
+
console.log(
|
|
253
|
+
` ${integration.displayName}: ${chalk.yellow('⚠️')} (${result.present.length}/${allFiles.length} files)`
|
|
254
|
+
);
|
|
234
255
|
} else {
|
|
235
|
-
console.log(
|
|
256
|
+
console.log(
|
|
257
|
+
` ${integration.displayName}: ${chalk.dim('not installed')} → run: ck ${name}`
|
|
258
|
+
);
|
|
236
259
|
}
|
|
237
260
|
}
|
|
238
261
|
}
|
|
@@ -244,10 +267,16 @@ class StatusCommand {
|
|
|
244
267
|
testing: await fs.pathExists('.contextkit/standards/testing.md'),
|
|
245
268
|
architecture: await fs.pathExists('.contextkit/standards/architecture.md'),
|
|
246
269
|
guidelines: await fs.pathExists('.contextkit/standards/ai-guidelines.md'),
|
|
247
|
-
glossary: await fs.pathExists('.contextkit/standards/glossary.md')
|
|
270
|
+
glossary: await fs.pathExists('.contextkit/standards/glossary.md'),
|
|
248
271
|
};
|
|
249
272
|
|
|
250
|
-
checks.allPresent =
|
|
273
|
+
checks.allPresent =
|
|
274
|
+
checks.context &&
|
|
275
|
+
checks.codeStyle &&
|
|
276
|
+
checks.testing &&
|
|
277
|
+
checks.architecture &&
|
|
278
|
+
checks.guidelines &&
|
|
279
|
+
checks.glossary;
|
|
251
280
|
|
|
252
281
|
return checks;
|
|
253
282
|
}
|
package/lib/commands/update.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const ora = require('ora');
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
|
-
const path = require('path');
|
|
5
4
|
|
|
6
5
|
const DownloadManager = require('../utils/download');
|
|
7
6
|
const ProjectDetector = require('../utils/project-detector');
|
|
@@ -22,7 +21,7 @@ class UpdateCommand {
|
|
|
22
21
|
console.log(chalk.magenta('🔄 Updating ContextKit...'));
|
|
23
22
|
|
|
24
23
|
// Check if ContextKit is installed
|
|
25
|
-
if (!await fs.pathExists('.contextkit/config.yml')) {
|
|
24
|
+
if (!(await fs.pathExists('.contextkit/config.yml'))) {
|
|
26
25
|
console.log(chalk.red('❌ No ContextKit installation found in current directory'));
|
|
27
26
|
console.log(chalk.yellow('💡 Run: contextkit install'));
|
|
28
27
|
return;
|
|
@@ -36,7 +35,9 @@ class UpdateCommand {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
if (updateInfo.hasUpdate) {
|
|
39
|
-
console.log(
|
|
38
|
+
console.log(
|
|
39
|
+
chalk.blue(`📦 Updating from ${updateInfo.currentVersion} to ${updateInfo.latestVersion}`)
|
|
40
|
+
);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// Create backup
|
|
@@ -75,7 +76,7 @@ class UpdateCommand {
|
|
|
75
76
|
// Update Git hooks if they were enabled
|
|
76
77
|
const hookChoices = {
|
|
77
78
|
prePush: config.features?.pre_push_hook || config.features?.git_hooks || false,
|
|
78
|
-
commitMsg: config.features?.commit_msg_hook || config.features?.git_hooks || false
|
|
79
|
+
commitMsg: config.features?.commit_msg_hook || config.features?.git_hooks || false,
|
|
79
80
|
};
|
|
80
81
|
if (hookChoices.prePush || hookChoices.commitMsg) {
|
|
81
82
|
await this.gitHooksManager.installHooks(packageManager, hookChoices);
|
|
@@ -88,7 +89,6 @@ class UpdateCommand {
|
|
|
88
89
|
await this.updateConfigVersion(updateInfo.latestVersion || '1.0.0');
|
|
89
90
|
|
|
90
91
|
console.log(chalk.green('✅ ContextKit updated successfully!'));
|
|
91
|
-
|
|
92
92
|
} catch (error) {
|
|
93
93
|
console.log(chalk.red('❌ Update failed, restoring from backup...'));
|
|
94
94
|
await this.restoreFromBackup(backupPath);
|
|
@@ -104,25 +104,28 @@ class UpdateCommand {
|
|
|
104
104
|
async checkForUpdates() {
|
|
105
105
|
try {
|
|
106
106
|
const axios = require('axios');
|
|
107
|
-
const response = await axios.get(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
const response = await axios.get(
|
|
108
|
+
'https://api.github.com/repos/nolrm/contextkit/releases/latest',
|
|
109
|
+
{
|
|
110
|
+
timeout: 5000,
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
111
114
|
const latestVersion = response.data.tag_name.replace('v', '');
|
|
112
115
|
const currentVersion = await this.getCurrentVersion();
|
|
113
116
|
const hasUpdate = this.isNewerVersion(latestVersion, currentVersion);
|
|
114
|
-
|
|
117
|
+
|
|
115
118
|
return {
|
|
116
119
|
hasUpdate,
|
|
117
120
|
currentVersion,
|
|
118
|
-
latestVersion
|
|
121
|
+
latestVersion,
|
|
119
122
|
};
|
|
120
123
|
} catch (error) {
|
|
121
124
|
return {
|
|
122
125
|
hasUpdate: true, // Assume update available if we can't check
|
|
123
126
|
currentVersion: await this.getCurrentVersion(),
|
|
124
127
|
latestVersion: 'latest',
|
|
125
|
-
error: error.message
|
|
128
|
+
error: error.message,
|
|
126
129
|
};
|
|
127
130
|
}
|
|
128
131
|
}
|
|
@@ -139,22 +142,22 @@ class UpdateCommand {
|
|
|
139
142
|
isNewerVersion(latest, current) {
|
|
140
143
|
const latestParts = latest.split('.').map(Number);
|
|
141
144
|
const currentParts = current.split('.').map(Number);
|
|
142
|
-
|
|
145
|
+
|
|
143
146
|
for (let i = 0; i < Math.max(latestParts.length, currentParts.length); i++) {
|
|
144
147
|
const latestPart = latestParts[i] || 0;
|
|
145
148
|
const currentPart = currentParts[i] || 0;
|
|
146
|
-
|
|
149
|
+
|
|
147
150
|
if (latestPart > currentPart) return true;
|
|
148
151
|
if (latestPart < currentPart) return false;
|
|
149
152
|
}
|
|
150
|
-
|
|
153
|
+
|
|
151
154
|
return false;
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
async createBackup() {
|
|
155
158
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
156
159
|
const backupPath = `.contextkit-backup-${timestamp}`;
|
|
157
|
-
|
|
160
|
+
|
|
158
161
|
await fs.copy('.contextkit', backupPath);
|
|
159
162
|
return backupPath;
|
|
160
163
|
}
|
|
@@ -167,7 +170,7 @@ class UpdateCommand {
|
|
|
167
170
|
async parseConfig() {
|
|
168
171
|
const configContent = await fs.readFile('.contextkit/config.yml', 'utf8');
|
|
169
172
|
const config = {};
|
|
170
|
-
|
|
173
|
+
|
|
171
174
|
// Simple YAML parsing for our config format
|
|
172
175
|
const lines = configContent.split('\n');
|
|
173
176
|
for (const line of lines) {
|
|
@@ -207,7 +210,8 @@ class UpdateCommand {
|
|
|
207
210
|
config.features.commit_msg_hook = trimmed.split('commit_msg_hook:')[1].trim() === 'true';
|
|
208
211
|
} else if (trimmed.startsWith('squad_ci_workflow:')) {
|
|
209
212
|
config.features = config.features || {};
|
|
210
|
-
config.features.squad_ci_workflow =
|
|
213
|
+
config.features.squad_ci_workflow =
|
|
214
|
+
trimmed.split('squad_ci_workflow:')[1].trim() === 'true';
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
217
|
|
|
@@ -223,7 +227,7 @@ class UpdateCommand {
|
|
|
223
227
|
`${this.repoUrl}/standards/README.md`,
|
|
224
228
|
'.contextkit/standards/README.md'
|
|
225
229
|
);
|
|
226
|
-
|
|
230
|
+
|
|
227
231
|
// Keep glossary updated (universal file)
|
|
228
232
|
await this.downloadManager.downloadFile(
|
|
229
233
|
`${this.repoUrl}/standards/glossary.md`,
|
|
@@ -461,9 +465,7 @@ commands:
|
|
|
461
465
|
}
|
|
462
466
|
|
|
463
467
|
async removeLegacyFiles() {
|
|
464
|
-
const legacyFiles = [
|
|
465
|
-
'.contextkit/commands/squad-peer-review.md',
|
|
466
|
-
];
|
|
468
|
+
const legacyFiles = ['.contextkit/commands/squad-peer-review.md'];
|
|
467
469
|
for (const file of legacyFiles) {
|
|
468
470
|
if (await fs.pathExists(file)) {
|
|
469
471
|
await fs.remove(file);
|
|
@@ -473,10 +475,7 @@ commands:
|
|
|
473
475
|
|
|
474
476
|
async updateConfigVersion(version) {
|
|
475
477
|
const configContent = await fs.readFile('.contextkit/config.yml', 'utf8');
|
|
476
|
-
const updatedContent = configContent.replace(
|
|
477
|
-
/version: "[^"]*"/,
|
|
478
|
-
`version: "${version}"`
|
|
479
|
-
);
|
|
478
|
+
const updatedContent = configContent.replace(/version: "[^"]*"/, `version: "${version}"`);
|
|
480
479
|
await fs.writeFile('.contextkit/config.yml', updatedContent);
|
|
481
480
|
}
|
|
482
481
|
}
|
package/lib/index.js
CHANGED
|
@@ -71,7 +71,7 @@ ${this.getStandardsBlock()}
|
|
|
71
71
|
.contextkit/instructions/meta/
|
|
72
72
|
`;
|
|
73
73
|
// Only create if doesn't exist (don't overwrite user customizations)
|
|
74
|
-
if (!await fs.pathExists('.aiderignore')) {
|
|
74
|
+
if (!(await fs.pathExists('.aiderignore'))) {
|
|
75
75
|
await fs.writeFile('.aiderignore', aiderignore);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -2,7 +2,8 @@ const chalk = require('chalk');
|
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
|
-
const MARKER =
|
|
5
|
+
const MARKER =
|
|
6
|
+
'<!-- Generated by ContextKit (@nolrm/contextkit) https://www.npmjs.com/package/@nolrm/contextkit -->';
|
|
6
7
|
const MARKER_END = '<!-- End ContextKit -->';
|
|
7
8
|
const LEGACY_MARKER_V1 = '<!-- Generated by ContextKit -->';
|
|
8
9
|
const LEGACY_MARKER = '<!-- Generated by Vibe Kit -->';
|
|
@@ -64,13 +65,19 @@ class BaseIntegration {
|
|
|
64
65
|
const hasLegacyMarker = existing.includes(LEGACY_MARKER);
|
|
65
66
|
|
|
66
67
|
if (hasCurrentMarker || hasLegacyMarkerV1 || hasLegacyMarker) {
|
|
67
|
-
const activeMarker = hasCurrentMarker
|
|
68
|
-
|
|
68
|
+
const activeMarker = hasCurrentMarker
|
|
69
|
+
? MARKER
|
|
70
|
+
: hasLegacyMarkerV1
|
|
71
|
+
? LEGACY_MARKER_V1
|
|
72
|
+
: LEGACY_MARKER;
|
|
73
|
+
const activeMarkerEnd =
|
|
74
|
+
hasLegacyMarker && !hasCurrentMarker && !hasLegacyMarkerV1
|
|
75
|
+
? LEGACY_MARKER_END
|
|
76
|
+
: MARKER_END;
|
|
69
77
|
const before = existing.substring(0, existing.indexOf(activeMarker));
|
|
70
78
|
const afterMarkerEnd = existing.indexOf(activeMarkerEnd);
|
|
71
|
-
const after =
|
|
72
|
-
? existing.substring(afterMarkerEnd + activeMarkerEnd.length)
|
|
73
|
-
: '';
|
|
79
|
+
const after =
|
|
80
|
+
afterMarkerEnd !== -1 ? existing.substring(afterMarkerEnd + activeMarkerEnd.length) : '';
|
|
74
81
|
await fs.writeFile(filePath, before + markedContent + after);
|
|
75
82
|
} else {
|
|
76
83
|
// Append below existing content
|