@paulduvall/claude-dev-toolkit 0.0.1-alpha.1
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 +254 -0
- package/bin/claude-commands +132 -0
- package/lib/claude-code-compatibility.js +545 -0
- package/lib/command-selector.js +245 -0
- package/lib/config.js +182 -0
- package/lib/context-utils.js +80 -0
- package/lib/dependency-validator.js +354 -0
- package/lib/error-factory.js +394 -0
- package/lib/error-handler-utils.js +432 -0
- package/lib/error-recovery-system.js +563 -0
- package/lib/failure-recovery-installer.js +370 -0
- package/lib/hook-installer-core.js +330 -0
- package/lib/hook-installer.js +187 -0
- package/lib/hook-metadata-service.js +352 -0
- package/lib/hook-validator.js +358 -0
- package/lib/installation-configuration.js +380 -0
- package/lib/installation-instruction-generator.js +564 -0
- package/lib/installer.js +68 -0
- package/lib/package-manager-service.js +270 -0
- package/lib/permission-error-handler.js +543 -0
- package/lib/platform-utils.js +491 -0
- package/lib/setup-wizard-ui.js +245 -0
- package/lib/setup-wizard.js +355 -0
- package/lib/system-requirements-checker.js +558 -0
- package/lib/utils.js +15 -0
- package/lib/validation-utils.js +320 -0
- package/lib/version-validator-service.js +326 -0
- package/package.json +73 -0
- package/scripts/postinstall.js +182 -0
- package/scripts/validate.js +94 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Manages command selection and categorization
|
|
5
|
+
* Extracted from InteractiveSetupWizard for better separation of concerns
|
|
6
|
+
*/
|
|
7
|
+
class CommandSelector {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.commandCategories = {
|
|
10
|
+
'planning': ['xplanning', 'xspec', 'xarchitecture'],
|
|
11
|
+
'development': ['xgit', 'xtest', 'xquality', 'xrefactor', 'xtdd'],
|
|
12
|
+
'security': ['xsecurity', 'xpolicy', 'xcompliance'],
|
|
13
|
+
'deployment': ['xrelease', 'xpipeline', 'xinfra'],
|
|
14
|
+
'documentation': ['xdocs']
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
this.presets = {
|
|
18
|
+
'developer': {
|
|
19
|
+
installationType: 'standard',
|
|
20
|
+
commandSets: ['development', 'planning'],
|
|
21
|
+
securityHooks: true,
|
|
22
|
+
template: 'basic'
|
|
23
|
+
},
|
|
24
|
+
'security-focused': {
|
|
25
|
+
installationType: 'full',
|
|
26
|
+
commandSets: ['security', 'development'],
|
|
27
|
+
securityHooks: true,
|
|
28
|
+
template: 'security-focused'
|
|
29
|
+
},
|
|
30
|
+
'minimal': {
|
|
31
|
+
installationType: 'minimal',
|
|
32
|
+
commandSets: [],
|
|
33
|
+
securityHooks: false,
|
|
34
|
+
template: 'basic'
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get available command categories
|
|
41
|
+
* @returns {Object} Command categories with commands
|
|
42
|
+
*/
|
|
43
|
+
getCommandCategories() {
|
|
44
|
+
return this.commandCategories;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get category names
|
|
49
|
+
* @returns {Array<string>} Category names
|
|
50
|
+
*/
|
|
51
|
+
getCategoryNames() {
|
|
52
|
+
return Object.keys(this.commandCategories);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get commands for a specific category
|
|
57
|
+
* @param {string} category - Category name
|
|
58
|
+
* @returns {Array<string>} Commands in category
|
|
59
|
+
*/
|
|
60
|
+
getCommandsForCategory(category) {
|
|
61
|
+
return this.commandCategories[category] || [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Select command sets based on categories
|
|
66
|
+
* @param {Array<string>} categories - Selected categories
|
|
67
|
+
* @returns {Object} Selection result with commands
|
|
68
|
+
*/
|
|
69
|
+
selectCommandSets(categories) {
|
|
70
|
+
const validCategories = categories.filter(cat =>
|
|
71
|
+
this.commandCategories.hasOwnProperty(cat)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
selected: validCategories,
|
|
76
|
+
commands: validCategories.flatMap(cat => this.commandCategories[cat] || []),
|
|
77
|
+
invalid: categories.filter(cat => !this.commandCategories.hasOwnProperty(cat))
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get all available commands
|
|
83
|
+
* @returns {Array<string>} All commands across categories
|
|
84
|
+
*/
|
|
85
|
+
getAllCommands() {
|
|
86
|
+
return Object.values(this.commandCategories).flat();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get command count for each category
|
|
91
|
+
* @returns {Object} Category names mapped to command counts
|
|
92
|
+
*/
|
|
93
|
+
getCategoryCommandCounts() {
|
|
94
|
+
const counts = {};
|
|
95
|
+
Object.entries(this.commandCategories).forEach(([category, commands]) => {
|
|
96
|
+
counts[category] = commands.length;
|
|
97
|
+
});
|
|
98
|
+
return counts;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Find category for a specific command
|
|
103
|
+
* @param {string} command - Command name
|
|
104
|
+
* @returns {string|null} Category name or null if not found
|
|
105
|
+
*/
|
|
106
|
+
findCategoryForCommand(command) {
|
|
107
|
+
for (const [category, commands] of Object.entries(this.commandCategories)) {
|
|
108
|
+
if (commands.includes(command)) {
|
|
109
|
+
return category;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get available presets
|
|
117
|
+
* @returns {Object} Available presets
|
|
118
|
+
*/
|
|
119
|
+
getPresets() {
|
|
120
|
+
return this.presets;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get preset names
|
|
125
|
+
* @returns {Array<string>} Preset names
|
|
126
|
+
*/
|
|
127
|
+
getPresetNames() {
|
|
128
|
+
return Object.keys(this.presets);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Apply a preset configuration
|
|
133
|
+
* @param {string} presetName - Name of preset to apply
|
|
134
|
+
* @returns {Object|null} Preset configuration or null if not found
|
|
135
|
+
*/
|
|
136
|
+
applyPreset(presetName) {
|
|
137
|
+
return this.presets[presetName] || null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Validate command selection
|
|
142
|
+
* @param {Array<string>} commands - Commands to validate
|
|
143
|
+
* @returns {Object} Validation result
|
|
144
|
+
*/
|
|
145
|
+
validateCommandSelection(commands) {
|
|
146
|
+
const allCommands = this.getAllCommands();
|
|
147
|
+
const valid = commands.filter(cmd => allCommands.includes(cmd));
|
|
148
|
+
const invalid = commands.filter(cmd => !allCommands.includes(cmd));
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
valid: invalid.length === 0,
|
|
152
|
+
validCommands: valid,
|
|
153
|
+
invalidCommands: invalid,
|
|
154
|
+
errors: invalid.length > 0 ? [`Invalid commands: ${invalid.join(', ')}`] : []
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get command statistics
|
|
160
|
+
* @returns {Object} Statistics about commands and categories
|
|
161
|
+
*/
|
|
162
|
+
getCommandStats() {
|
|
163
|
+
const totalCommands = this.getAllCommands().length;
|
|
164
|
+
const categoryCount = Object.keys(this.commandCategories).length;
|
|
165
|
+
const presetCount = Object.keys(this.presets).length;
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
totalCommands,
|
|
169
|
+
categoryCount,
|
|
170
|
+
presetCount,
|
|
171
|
+
averageCommandsPerCategory: Math.round(totalCommands / categoryCount),
|
|
172
|
+
categoriesWithCounts: this.getCategoryCommandCounts()
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Add new command category
|
|
178
|
+
* @param {string} category - Category name
|
|
179
|
+
* @param {Array<string>} commands - Commands in category
|
|
180
|
+
* @returns {boolean} Success status
|
|
181
|
+
*/
|
|
182
|
+
addCommandCategory(category, commands) {
|
|
183
|
+
if (this.commandCategories.hasOwnProperty(category)) {
|
|
184
|
+
return false; // Category already exists
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.commandCategories[category] = [...commands];
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Remove command category
|
|
193
|
+
* @param {string} category - Category name to remove
|
|
194
|
+
* @returns {boolean} Success status
|
|
195
|
+
*/
|
|
196
|
+
removeCommandCategory(category) {
|
|
197
|
+
if (!this.commandCategories.hasOwnProperty(category)) {
|
|
198
|
+
return false; // Category doesn't exist
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
delete this.commandCategories[category];
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Add command to existing category
|
|
207
|
+
* @param {string} category - Category name
|
|
208
|
+
* @param {string} command - Command to add
|
|
209
|
+
* @returns {boolean} Success status
|
|
210
|
+
*/
|
|
211
|
+
addCommandToCategory(category, command) {
|
|
212
|
+
if (!this.commandCategories.hasOwnProperty(category)) {
|
|
213
|
+
return false; // Category doesn't exist
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (this.commandCategories[category].includes(command)) {
|
|
217
|
+
return false; // Command already exists in category
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.commandCategories[category].push(command);
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Remove command from category
|
|
226
|
+
* @param {string} category - Category name
|
|
227
|
+
* @param {string} command - Command to remove
|
|
228
|
+
* @returns {boolean} Success status
|
|
229
|
+
*/
|
|
230
|
+
removeCommandFromCategory(category, command) {
|
|
231
|
+
if (!this.commandCategories.hasOwnProperty(category)) {
|
|
232
|
+
return false; // Category doesn't exist
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const index = this.commandCategories[category].indexOf(command);
|
|
236
|
+
if (index === -1) {
|
|
237
|
+
return false; // Command not in category
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.commandCategories[category].splice(index, 1);
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = CommandSelector;
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Configuration management for Claude Dev Toolkit
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parse JSONC (JSON with Comments) format
|
|
8
|
+
* Handles comment key-value pairs and block comments
|
|
9
|
+
* @param {string} content - Raw JSONC content
|
|
10
|
+
* @returns {Object} - Parsed JSON object
|
|
11
|
+
* @throws {Error} - If JSON parsing fails
|
|
12
|
+
*/
|
|
13
|
+
function parseJSONC(content) {
|
|
14
|
+
const lines = content.split('\n');
|
|
15
|
+
const cleanedLines = [];
|
|
16
|
+
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
// Skip lines that are comment key-value pairs like '"// comment": "value",'
|
|
19
|
+
if (line.trim().match(/^"\/\/[^"]*":\s*"[^"]*",?\s*$/)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
// Skip pure comment lines
|
|
23
|
+
if (line.trim().startsWith('//')) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
cleanedLines.push(line);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const cleanedContent = cleanedLines.join('\n')
|
|
30
|
+
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove /* */ comments
|
|
31
|
+
.replace(/,(\s*[}\]])/g, '$1'); // Remove trailing commas
|
|
32
|
+
|
|
33
|
+
return JSON.parse(cleanedContent);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Deep merge two objects, with second object taking precedence
|
|
38
|
+
* @param {Object} target - Target object to merge into
|
|
39
|
+
* @param {Object} source - Source object to merge from
|
|
40
|
+
* @returns {Object} - Merged object
|
|
41
|
+
*/
|
|
42
|
+
function deepMerge(target, source) {
|
|
43
|
+
const result = { ...target };
|
|
44
|
+
|
|
45
|
+
for (const [key, value] of Object.entries(source)) {
|
|
46
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
47
|
+
result[key] = deepMerge(result[key] || {}, value);
|
|
48
|
+
} else {
|
|
49
|
+
result[key] = value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Apply a configuration template to Claude Code settings
|
|
58
|
+
* Implements REQ-009: Configuration Template Application
|
|
59
|
+
*
|
|
60
|
+
* @param {string} templatePath - Path to the template file
|
|
61
|
+
* @param {string} settingsPath - Path to the settings file to create/update
|
|
62
|
+
* @returns {boolean} - True if successful, false otherwise
|
|
63
|
+
*/
|
|
64
|
+
function applyConfigurationTemplate(templatePath, settingsPath) {
|
|
65
|
+
try {
|
|
66
|
+
// Validate inputs
|
|
67
|
+
if (!templatePath || !settingsPath) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check if template exists
|
|
72
|
+
if (!fs.existsSync(templatePath)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Read and parse template (handle JSONC with comments)
|
|
77
|
+
const templateContent = fs.readFileSync(templatePath, 'utf8');
|
|
78
|
+
let templateData;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
templateData = parseJSONC(templateContent);
|
|
82
|
+
} catch (parseError) {
|
|
83
|
+
// Invalid JSON/JSONC format
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Validate template data
|
|
88
|
+
if (!templateData || typeof templateData !== 'object') {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Read existing settings if they exist
|
|
93
|
+
let existingSettings = {};
|
|
94
|
+
if (fs.existsSync(settingsPath)) {
|
|
95
|
+
try {
|
|
96
|
+
const existingContent = fs.readFileSync(settingsPath, 'utf8');
|
|
97
|
+
existingSettings = JSON.parse(existingContent);
|
|
98
|
+
} catch (parseError) {
|
|
99
|
+
// If existing settings are invalid, start fresh but log the issue
|
|
100
|
+
existingSettings = {};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Deep merge template with existing settings (template takes precedence)
|
|
105
|
+
const mergedSettings = deepMerge(existingSettings, templateData);
|
|
106
|
+
|
|
107
|
+
// Ensure target directory exists with correct permissions
|
|
108
|
+
const settingsDir = path.dirname(settingsPath);
|
|
109
|
+
fs.mkdirSync(settingsDir, { recursive: true, mode: 0o755 });
|
|
110
|
+
|
|
111
|
+
// Write merged settings with formatted output
|
|
112
|
+
const settingsJson = JSON.stringify(mergedSettings, null, 2);
|
|
113
|
+
fs.writeFileSync(settingsPath, settingsJson, { mode: 0o644 });
|
|
114
|
+
|
|
115
|
+
// Verify file was created successfully
|
|
116
|
+
return fs.existsSync(settingsPath);
|
|
117
|
+
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Log error in development but don't expose details
|
|
120
|
+
if (process.env.NODE_ENV === 'development') {
|
|
121
|
+
console.error('Configuration template application error:', error);
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get available configuration templates
|
|
129
|
+
* @param {string} templatesDir - Directory containing templates
|
|
130
|
+
* @returns {Array} - List of available templates with metadata
|
|
131
|
+
*/
|
|
132
|
+
function getAvailableTemplates(templatesDir) {
|
|
133
|
+
try {
|
|
134
|
+
const templates = [];
|
|
135
|
+
const files = fs.readdirSync(templatesDir);
|
|
136
|
+
|
|
137
|
+
for (const file of files) {
|
|
138
|
+
if (file.endsWith('.json')) {
|
|
139
|
+
const templatePath = path.join(templatesDir, file);
|
|
140
|
+
try {
|
|
141
|
+
const content = fs.readFileSync(templatePath, 'utf8');
|
|
142
|
+
const data = parseJSONC(content);
|
|
143
|
+
|
|
144
|
+
templates.push({
|
|
145
|
+
id: path.basename(file, '.json'),
|
|
146
|
+
name: file,
|
|
147
|
+
path: templatePath,
|
|
148
|
+
description: data['// Description'] || `${file} template`,
|
|
149
|
+
features: Object.keys(data).filter(key => !key.startsWith('//')).length
|
|
150
|
+
});
|
|
151
|
+
} catch (error) {
|
|
152
|
+
// Skip invalid templates
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return templates;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
getConfigPath: () => {
|
|
166
|
+
return path.join(os.homedir(), '.claude', 'commands');
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
defaultConfig: {
|
|
170
|
+
commandsPath: './commands',
|
|
171
|
+
hooksEnabled: true,
|
|
172
|
+
colorOutput: true
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// REQ-009 Implementation
|
|
176
|
+
applyConfigurationTemplate,
|
|
177
|
+
getAvailableTemplates,
|
|
178
|
+
|
|
179
|
+
// Utility functions (exposed for testing)
|
|
180
|
+
parseJSONC,
|
|
181
|
+
deepMerge
|
|
182
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared context creation patterns to eliminate duplication and provide
|
|
5
|
+
* consistent context objects across the application.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Standardized context object creation
|
|
9
|
+
* - Context validation and enhancement
|
|
10
|
+
* - Operation context tracking
|
|
11
|
+
* - Context-aware processing patterns
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
class ContextUtils {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.config = {
|
|
17
|
+
contextTypes: {
|
|
18
|
+
VALIDATION: 'validation',
|
|
19
|
+
OPERATION: 'operation',
|
|
20
|
+
ERROR_HANDLING: 'error_handling',
|
|
21
|
+
INSTALLATION: 'installation',
|
|
22
|
+
SYSTEM: 'system'
|
|
23
|
+
},
|
|
24
|
+
operationTypes: {
|
|
25
|
+
COMMAND_INSTALLATION: 'command_installation',
|
|
26
|
+
DEPENDENCY_CHECK: 'dependency_check',
|
|
27
|
+
PERMISSION_CHECK: 'permission_check',
|
|
28
|
+
SYSTEM_VALIDATION: 'system_validation',
|
|
29
|
+
CONFIGURATION: 'configuration'
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create base context object with common properties
|
|
36
|
+
* @param {string} type - Context type
|
|
37
|
+
* @param {Object} data - Context data
|
|
38
|
+
* @param {Object} options - Additional options
|
|
39
|
+
* @returns {Object} Base context object
|
|
40
|
+
*/
|
|
41
|
+
createBaseContext(type, data = {}, options = {}) {
|
|
42
|
+
return {
|
|
43
|
+
type,
|
|
44
|
+
timestamp: new Date().toISOString(),
|
|
45
|
+
id: this._generateContextId(),
|
|
46
|
+
data,
|
|
47
|
+
metadata: {
|
|
48
|
+
version: '1.0.0',
|
|
49
|
+
source: 'context-utils',
|
|
50
|
+
...options.metadata
|
|
51
|
+
},
|
|
52
|
+
...options
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create operation context for tracking operations
|
|
58
|
+
* @param {string} operation - Operation name
|
|
59
|
+
* @param {Object} parameters - Operation parameters
|
|
60
|
+
* @param {Object} environment - Environment information
|
|
61
|
+
* @returns {Object} Operation context
|
|
62
|
+
*/
|
|
63
|
+
createOperationContext(operation, parameters = {}, environment = {}) {
|
|
64
|
+
return this.createBaseContext(this.config.contextTypes.OPERATION, {
|
|
65
|
+
operation,
|
|
66
|
+
parameters,
|
|
67
|
+
environment: {
|
|
68
|
+
platform: process.platform,
|
|
69
|
+
nodeVersion: process.version,
|
|
70
|
+
workingDirectory: process.cwd(),
|
|
71
|
+
...environment
|
|
72
|
+
},
|
|
73
|
+
status: 'initialized',
|
|
74
|
+
startTime: new Date().toISOString(),
|
|
75
|
+
progress: {
|
|
76
|
+
current: 0,
|
|
77
|
+
total: 1,
|
|
78
|
+
steps: []
|
|
79
|
+
}
|
|
80
|
+
});\n }\n \n /**\n * Create validation context for input validation\n * @param {*} input - Input to validate\n * @param {Object} rules - Validation rules\n * @param {Object} options - Validation options\n * @returns {Object} Validation context\n */\n createValidationContext(input, rules = {}, options = {}) {\n return this.createBaseContext(this.config.contextTypes.VALIDATION, {\n input,\n rules,\n options: {\n strict: false,\n allowEmpty: false,\n transformInput: false,\n ...options\n },\n results: {\n valid: null,\n errors: [],\n warnings: [],\n transformed: null\n }\n });\n }\n \n /**\n * Create error handling context\n * @param {Error} error - Error to handle\n * @param {Object} operation - Operation context where error occurred\n * @param {Object} recovery - Recovery options\n * @returns {Object} Error handling context\n */\n createErrorHandlingContext(error, operation = {}, recovery = {}) {\n return this.createBaseContext(this.config.contextTypes.ERROR_HANDLING, {\n error: {\n original: error,\n code: error?.code,\n message: error?.message,\n type: error?.constructor?.name,\n stack: error?.stack\n },\n operation,\n recovery: {\n attempted: false,\n strategies: [],\n successful: false,\n ...recovery\n },\n handling: {\n strategy: null,\n handled: false,\n escalated: false\n }\n });\n }\n \n /**\n * Create installation context for tracking installations\n * @param {string} target - Installation target (command, dependency, etc.)\n * @param {Object} configuration - Installation configuration\n * @param {Object} environment - Environment details\n * @returns {Object} Installation context\n */\n createInstallationContext(target, configuration = {}, environment = {}) {\n return this.createBaseContext(this.config.contextTypes.INSTALLATION, {\n target,\n configuration,\n environment: {\n platform: process.platform,\n arch: process.arch,\n userHome: require('os').homedir(),\n ...environment\n },\n installation: {\n phase: 'preparation',\n steps: [],\n backup: null,\n rollback: null\n },\n validation: {\n preInstall: null,\n postInstall: null\n }\n });\n }\n \n /**\n * Create system context for system operations\n * @param {Object} systemInfo - System information\n * @param {Object} requirements - System requirements\n * @returns {Object} System context\n */\n createSystemContext(systemInfo = {}, requirements = {}) {\n const os = require('os');\n \n return this.createBaseContext(this.config.contextTypes.SYSTEM, {\n system: {\n platform: process.platform,\n arch: process.arch,\n nodeVersion: process.version,\n totalMemory: os.totalmem(),\n freeMemory: os.freemem(),\n homeDir: os.homedir(),\n tempDir: os.tmpdir(),\n ...systemInfo\n },\n requirements,\n compatibility: {\n checked: false,\n compatible: null,\n issues: []\n }\n });\n }\n \n /**\n * Enhance existing context with additional data\n * @param {Object} context - Existing context to enhance\n * @param {Object} enhancement - Enhancement data\n * @returns {Object} Enhanced context\n */\n enhanceContext(context, enhancement) {\n if (!context || typeof context !== 'object') {\n throw new Error('Context must be a valid object');\n }\n \n return {\n ...context,\n ...enhancement,\n metadata: {\n ...context.metadata,\n ...enhancement.metadata,\n enhanced: true,\n enhancedAt: new Date().toISOString()\n }\n };\n }\n \n /**\n * Update operation context progress\n * @param {Object} context - Operation context to update\n * @param {number} current - Current step\n * @param {number} total - Total steps (optional)\n * @param {string} step - Current step description\n * @returns {Object} Updated context\n */\n updateOperationProgress(context, current, total = null, step = null) {\n if (!this.isOperationContext(context)) {\n throw new Error('Context must be an operation context');\n }\n \n const updatedContext = { ...context };\n updatedContext.data.progress.current = current;\n \n if (total !== null) {\n updatedContext.data.progress.total = total;\n }\n \n if (step !== null) {\n updatedContext.data.progress.steps.push({\n step,\n timestamp: new Date().toISOString(),\n index: current\n });\n }\n \n // Update status based on progress\n if (current >= updatedContext.data.progress.total) {\n updatedContext.data.status = 'completed';\n updatedContext.data.endTime = new Date().toISOString();\n } else {\n updatedContext.data.status = 'in_progress';\n }\n \n return updatedContext;\n }\n \n /**\n * Mark operation context as failed\n * @param {Object} context - Operation context to mark as failed\n * @param {Error} error - Error that caused failure\n * @returns {Object} Updated context\n */\n markOperationFailed(context, error) {\n if (!this.isOperationContext(context)) {\n throw new Error('Context must be an operation context');\n }\n \n return {\n ...context,\n data: {\n ...context.data,\n status: 'failed',\n endTime: new Date().toISOString(),\n error: {\n code: error?.code,\n message: error?.message,\n type: error?.constructor?.name\n }\n }\n };\n }\n \n /**\n * Check if context is of specific type\n * @param {Object} context - Context to check\n * @param {string} type - Expected type\n * @returns {boolean} True if context is of expected type\n */\n isContextType(context, type) {\n return context && context.type === type;\n }\n \n /**\n * Check if context is an operation context\n * @param {Object} context - Context to check\n * @returns {boolean} True if operation context\n */\n isOperationContext(context) {\n return this.isContextType(context, this.config.contextTypes.OPERATION);\n }\n \n /**\n * Check if context is a validation context\n * @param {Object} context - Context to check\n * @returns {boolean} True if validation context\n */\n isValidationContext(context) {\n return this.isContextType(context, this.config.contextTypes.VALIDATION);\n }\n \n /**\n * Extract summary information from context\n * @param {Object} context - Context to summarize\n * @returns {Object} Context summary\n */\n getContextSummary(context) {\n if (!context || typeof context !== 'object') {\n return { valid: false, error: 'Invalid context' };\n }\n \n const summary = {\n type: context.type,\n id: context.id,\n timestamp: context.timestamp,\n valid: true\n };\n \n // Add type-specific summary information\n if (this.isOperationContext(context)) {\n summary.operation = {\n name: context.data?.operation,\n status: context.data?.status,\n progress: `${context.data?.progress?.current || 0}/${context.data?.progress?.total || 1}`\n };\n } else if (this.isValidationContext(context)) {\n summary.validation = {\n valid: context.data?.results?.valid,\n errors: context.data?.results?.errors?.length || 0,\n warnings: context.data?.results?.warnings?.length || 0\n };\n }\n \n return summary;\n }\n \n /**\n * Generate unique context ID\n * @returns {string} Unique context ID\n * @private\n */\n _generateContextId() {\n return `ctx_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n }\n}\n\nmodule.exports = ContextUtils;
|