@paulduvall/claude-dev-toolkit 0.0.1-alpha.1 → 0.0.1-alpha.11

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.
Files changed (141) hide show
  1. package/README.md +74 -23
  2. package/bin/claude-commands +263 -64
  3. package/commands/active/xarchitecture.md +393 -0
  4. package/commands/active/xconfig.md +127 -0
  5. package/commands/active/xdebug.md +130 -0
  6. package/commands/active/xdocs.md +178 -0
  7. package/commands/active/xgit.md +149 -0
  8. package/commands/active/xpipeline.md +152 -0
  9. package/commands/active/xquality.md +96 -0
  10. package/commands/active/xrefactor.md +198 -0
  11. package/commands/active/xrelease.md +142 -0
  12. package/commands/active/xsecurity.md +92 -0
  13. package/commands/active/xspec.md +174 -0
  14. package/commands/active/xtdd.md +151 -0
  15. package/commands/active/xtest.md +89 -0
  16. package/commands/experiments/xact.md +742 -0
  17. package/commands/experiments/xanalytics.md +113 -0
  18. package/commands/experiments/xanalyze.md +70 -0
  19. package/commands/experiments/xapi.md +161 -0
  20. package/commands/experiments/xatomic.md +112 -0
  21. package/commands/experiments/xaws.md +85 -0
  22. package/commands/experiments/xcicd.md +337 -0
  23. package/commands/experiments/xcommit.md +122 -0
  24. package/commands/experiments/xcompliance.md +182 -0
  25. package/commands/experiments/xconstraints.md +89 -0
  26. package/commands/experiments/xcoverage.md +90 -0
  27. package/commands/experiments/xdb.md +102 -0
  28. package/commands/experiments/xdesign.md +121 -0
  29. package/commands/experiments/xevaluate.md +111 -0
  30. package/commands/experiments/xfootnote.md +12 -0
  31. package/commands/experiments/xgenerate.md +117 -0
  32. package/commands/experiments/xgovernance.md +149 -0
  33. package/commands/experiments/xgreen.md +66 -0
  34. package/commands/experiments/xiac.md +118 -0
  35. package/commands/experiments/xincident.md +137 -0
  36. package/commands/experiments/xinfra.md +115 -0
  37. package/commands/experiments/xknowledge.md +115 -0
  38. package/commands/experiments/xmaturity.md +120 -0
  39. package/commands/experiments/xmetrics.md +118 -0
  40. package/commands/experiments/xmonitoring.md +128 -0
  41. package/commands/experiments/xnew.md +898 -0
  42. package/commands/experiments/xobservable.md +114 -0
  43. package/commands/experiments/xoidc.md +165 -0
  44. package/commands/experiments/xoptimize.md +115 -0
  45. package/commands/experiments/xperformance.md +112 -0
  46. package/commands/experiments/xplanning.md +131 -0
  47. package/commands/experiments/xpolicy.md +115 -0
  48. package/commands/experiments/xproduct.md +98 -0
  49. package/commands/experiments/xreadiness.md +75 -0
  50. package/commands/experiments/xred.md +55 -0
  51. package/commands/experiments/xrisk.md +128 -0
  52. package/commands/experiments/xrules.md +124 -0
  53. package/commands/experiments/xsandbox.md +120 -0
  54. package/commands/experiments/xscan.md +102 -0
  55. package/commands/experiments/xsetup.md +123 -0
  56. package/commands/experiments/xtemplate.md +116 -0
  57. package/commands/experiments/xtrace.md +212 -0
  58. package/commands/experiments/xux.md +171 -0
  59. package/commands/experiments/xvalidate.md +104 -0
  60. package/commands/experiments/xworkflow.md +113 -0
  61. package/hooks/README.md +231 -0
  62. package/hooks/file-logger.sh +98 -0
  63. package/hooks/lib/argument-parser.sh +422 -0
  64. package/hooks/lib/config-constants.sh +230 -0
  65. package/hooks/lib/context-manager.sh +549 -0
  66. package/hooks/lib/error-handler.sh +412 -0
  67. package/hooks/lib/execution-engine.sh +627 -0
  68. package/hooks/lib/file-utils.sh +375 -0
  69. package/hooks/lib/subagent-discovery.sh +465 -0
  70. package/hooks/lib/subagent-validator.sh +597 -0
  71. package/hooks/on-error-debug.sh +221 -0
  72. package/hooks/pre-commit-quality.sh +204 -0
  73. package/hooks/pre-write-security.sh +107 -0
  74. package/hooks/prevent-credential-exposure.sh +265 -0
  75. package/hooks/subagent-trigger-simple.sh +193 -0
  76. package/hooks/subagent-trigger.sh +253 -0
  77. package/lib/backup-restore-command.js +140 -0
  78. package/lib/base/base-command.js +252 -0
  79. package/lib/base/command-result.js +184 -0
  80. package/lib/config/constants.js +255 -0
  81. package/lib/config.js +228 -3
  82. package/lib/configure-command.js +428 -0
  83. package/lib/dependency-validator.js +64 -5
  84. package/lib/hook-installer-core.js +2 -2
  85. package/lib/installation-instruction-generator-backup.js +579 -0
  86. package/lib/installation-instruction-generator.js +213 -495
  87. package/lib/installer.js +134 -56
  88. package/lib/oidc-command.js +363 -0
  89. package/lib/result.js +138 -0
  90. package/lib/services/backup-list-service.js +226 -0
  91. package/lib/services/backup-service.js +230 -0
  92. package/lib/services/command-installer-service.js +217 -0
  93. package/lib/services/logger-service.js +201 -0
  94. package/lib/services/package-manager-service.js +319 -0
  95. package/lib/services/platform-instruction-service.js +294 -0
  96. package/lib/services/recovery-instruction-service.js +348 -0
  97. package/lib/services/restore-service.js +221 -0
  98. package/lib/setup-command.js +309 -0
  99. package/lib/subagent-formatter.js +278 -0
  100. package/lib/subagents-core.js +237 -0
  101. package/lib/subagents.js +508 -0
  102. package/lib/types.d.ts +183 -0
  103. package/lib/utils/claude-path-config.js +184 -0
  104. package/lib/utils/file-system-utils.js +152 -0
  105. package/lib/utils.js +8 -4
  106. package/lib/verify-command.js +430 -0
  107. package/package.json +17 -4
  108. package/scripts/postinstall.js +28 -10
  109. package/subagents/api-guardian.md +29 -0
  110. package/subagents/audit-trail-verifier.md +24 -0
  111. package/subagents/change-scoper.md +23 -0
  112. package/subagents/ci-pipeline-curator.md +24 -0
  113. package/subagents/code-review-assistant.md +258 -0
  114. package/subagents/continuous-release-orchestrator.md +29 -0
  115. package/subagents/contract-tester.md +24 -0
  116. package/subagents/data-steward.md +29 -0
  117. package/subagents/debug-context.md +197 -0
  118. package/subagents/debug-specialist.md +138 -0
  119. package/subagents/dependency-steward.md +24 -0
  120. package/subagents/deployment-strategist.md +29 -0
  121. package/subagents/documentation-curator.md +29 -0
  122. package/subagents/environment-guardian.md +29 -0
  123. package/subagents/license-compliance-guardian.md +29 -0
  124. package/subagents/observability-engineer.md +25 -0
  125. package/subagents/performance-guardian.md +29 -0
  126. package/subagents/product-owner-proxy.md +28 -0
  127. package/subagents/requirements-reviewer.md +26 -0
  128. package/subagents/rollback-first-responder.md +24 -0
  129. package/subagents/sbom-provenance.md +25 -0
  130. package/subagents/security-auditor.md +29 -0
  131. package/subagents/style-enforcer.md +23 -0
  132. package/subagents/test-writer.md +24 -0
  133. package/subagents/trunk-guardian.md +29 -0
  134. package/subagents/workflow-coordinator.md +26 -0
  135. package/templates/README.md +100 -0
  136. package/templates/basic-settings.json +30 -0
  137. package/templates/comprehensive-settings.json +206 -0
  138. package/templates/hybrid-hook-config.yaml +133 -0
  139. package/templates/security-focused-settings.json +62 -0
  140. package/templates/subagent-hooks.yaml +188 -0
  141. package/tsconfig.json +37 -0
package/lib/result.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Result/Either Pattern Implementation
3
+ * Provides functional error handling without throwing exceptions
4
+ */
5
+
6
+ /**
7
+ * Success result containing a value
8
+ */
9
+ class Ok {
10
+ constructor(value) {
11
+ this.value = value;
12
+ this.isOk = true;
13
+ this.isError = false;
14
+ }
15
+
16
+ map(fn) {
17
+ try {
18
+ return new Ok(fn(this.value));
19
+ } catch (error) {
20
+ return new Err(error);
21
+ }
22
+ }
23
+
24
+ flatMap(fn) {
25
+ try {
26
+ return fn(this.value);
27
+ } catch (error) {
28
+ return new Err(error);
29
+ }
30
+ }
31
+
32
+ mapError() {
33
+ return this;
34
+ }
35
+
36
+ unwrap() {
37
+ return this.value;
38
+ }
39
+
40
+ unwrapOr() {
41
+ return this.value;
42
+ }
43
+
44
+ match(okFn, errFn) {
45
+ return okFn(this.value);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Error result containing an error
51
+ */
52
+ class Err {
53
+ constructor(error) {
54
+ this.error = error;
55
+ this.isOk = false;
56
+ this.isError = true;
57
+ }
58
+
59
+ map() {
60
+ return this;
61
+ }
62
+
63
+ flatMap() {
64
+ return this;
65
+ }
66
+
67
+ mapError(fn) {
68
+ try {
69
+ return new Err(fn(this.error));
70
+ } catch (error) {
71
+ return new Err(error);
72
+ }
73
+ }
74
+
75
+ unwrap() {
76
+ throw new Error(`Called unwrap on an Err: ${this.error}`);
77
+ }
78
+
79
+ unwrapOr(defaultValue) {
80
+ return defaultValue;
81
+ }
82
+
83
+ match(okFn, errFn) {
84
+ return errFn(this.error);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Static factory methods for creating Results
90
+ */
91
+ class Result {
92
+ static ok(value) {
93
+ return new Ok(value);
94
+ }
95
+
96
+ static err(error) {
97
+ return new Err(error);
98
+ }
99
+
100
+ /**
101
+ * Wraps a function that might throw in a Result
102
+ */
103
+ static try(fn) {
104
+ try {
105
+ return Result.ok(fn());
106
+ } catch (error) {
107
+ return Result.err(error);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Wraps an async function that might throw in a Result
113
+ */
114
+ static async tryAsync(fn) {
115
+ try {
116
+ const result = await fn();
117
+ return Result.ok(result);
118
+ } catch (error) {
119
+ return Result.err(error);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Combines multiple Results - returns Ok if all are Ok, Err if any are Err
125
+ */
126
+ static all(results) {
127
+ const values = [];
128
+ for (const result of results) {
129
+ if (result.isError) {
130
+ return result;
131
+ }
132
+ values.push(result.value);
133
+ }
134
+ return Result.ok(values);
135
+ }
136
+ }
137
+
138
+ module.exports = { Result, Ok, Err };
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Backup List Service
3
+ * Service for listing and managing backup inventory
4
+ */
5
+
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const FileSystemUtils = require('../utils/file-system-utils');
9
+ const ClaudePathConfig = require('../utils/claude-path-config');
10
+
11
+ class BackupListService {
12
+ constructor(config = null) {
13
+ this.config = config || new ClaudePathConfig();
14
+ }
15
+
16
+ /**
17
+ * List all available backups
18
+ */
19
+ async list() {
20
+ if (!fs.existsSync(this.config.backupsDir)) {
21
+ return [];
22
+ }
23
+
24
+ const backups = [];
25
+
26
+ try {
27
+ const entries = fs.readdirSync(this.config.backupsDir);
28
+
29
+ for (const entry of entries) {
30
+ const fullPath = path.join(this.config.backupsDir, entry);
31
+ const stats = FileSystemUtils.getStats(fullPath);
32
+
33
+ if (!stats) continue;
34
+
35
+ // Compressed backups
36
+ if (entry.endsWith('.tar.gz')) {
37
+ const name = entry.replace('.tar.gz', '');
38
+ backups.push({
39
+ name,
40
+ type: 'compressed',
41
+ size: stats.size,
42
+ modified: stats.mtime,
43
+ path: fullPath
44
+ });
45
+ }
46
+ // Directory backups
47
+ else if (stats.isDirectory() && !entry.startsWith('.')) {
48
+ const metadata = await this.readBackupMetadata(fullPath);
49
+
50
+ backups.push({
51
+ name: entry,
52
+ type: 'directory',
53
+ size: FileSystemUtils.getDirectorySize(fullPath),
54
+ modified: stats.mtime,
55
+ path: fullPath,
56
+ metadata
57
+ });
58
+ }
59
+ }
60
+
61
+ // Sort by modification time (newest first)
62
+ backups.sort((a, b) => b.modified - a.modified);
63
+
64
+ return backups;
65
+ } catch (error) {
66
+ throw new Error(`Failed to list backups: ${error.message}`);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Display backups in user-friendly format
72
+ */
73
+ async display() {
74
+ console.log('📦 Available Backups:\n');
75
+
76
+ const backups = await this.list();
77
+
78
+ if (backups.length === 0) {
79
+ console.log('No backups found');
80
+ return [];
81
+ }
82
+
83
+ backups.forEach(backup => {
84
+ const date = backup.modified.toLocaleString();
85
+ const size = FileSystemUtils.formatSize(backup.size);
86
+ const type = backup.type === 'compressed' ? '📦' : '📁';
87
+
88
+ console.log(`${type} ${backup.name}`);
89
+ console.log(` Date: ${date}`);
90
+ console.log(` Size: ${size}`);
91
+
92
+ if (backup.metadata) {
93
+ console.log(` Files: ${backup.metadata.totalFiles}`);
94
+ if (backup.metadata.components) {
95
+ const components = Object.keys(backup.metadata.components).filter(k => backup.metadata.components[k]);
96
+ if (components.length > 0) {
97
+ console.log(` Contains: ${components.join(', ')}`);
98
+ }
99
+ }
100
+ }
101
+ console.log('');
102
+ });
103
+
104
+ console.log(`Total: ${backups.length} backup(s)`);
105
+ console.log('\n💡 To restore a backup, run:');
106
+ console.log(' claude-commands restore <backup-name>');
107
+
108
+ return backups;
109
+ }
110
+
111
+ /**
112
+ * Find a specific backup by name
113
+ */
114
+ async findBackup(name) {
115
+ const backups = await this.list();
116
+ return backups.find(backup => backup.name === name);
117
+ }
118
+
119
+ /**
120
+ * Get backup details
121
+ */
122
+ async getBackupDetails(name) {
123
+ const backup = await this.findBackup(name);
124
+
125
+ if (!backup) {
126
+ throw new Error(`Backup '${name}' not found`);
127
+ }
128
+
129
+ const details = {
130
+ ...backup,
131
+ exists: fs.existsSync(backup.path),
132
+ readable: FileSystemUtils.isReadable(backup.path)
133
+ };
134
+
135
+ // Add component details if metadata available
136
+ if (backup.metadata && backup.metadata.components) {
137
+ details.components = backup.metadata.components;
138
+ }
139
+
140
+ return details;
141
+ }
142
+
143
+ /**
144
+ * Read backup metadata if available
145
+ */
146
+ async readBackupMetadata(backupPath) {
147
+ const metadataPath = path.join(backupPath, 'backup-metadata.json');
148
+
149
+ if (!FileSystemUtils.isReadable(metadataPath)) {
150
+ return null;
151
+ }
152
+
153
+ try {
154
+ const content = FileSystemUtils.readFile(metadataPath);
155
+ return JSON.parse(content);
156
+ } catch (error) {
157
+ // Return null for invalid metadata
158
+ return null;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Clean up old backups (keep only N most recent)
164
+ */
165
+ async cleanup(keepCount = 10) {
166
+ const backups = await this.list();
167
+
168
+ if (backups.length <= keepCount) {
169
+ return { cleaned: 0, kept: backups.length };
170
+ }
171
+
172
+ const toRemove = backups.slice(keepCount);
173
+ let cleanedCount = 0;
174
+
175
+ for (const backup of toRemove) {
176
+ try {
177
+ if (FileSystemUtils.remove(backup.path)) {
178
+ cleanedCount++;
179
+ }
180
+ } catch (error) {
181
+ console.warn(`⚠️ Warning: Could not remove backup ${backup.name} - ${error.message}`);
182
+ }
183
+ }
184
+
185
+ return {
186
+ cleaned: cleanedCount,
187
+ kept: backups.length - cleanedCount
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Get backup statistics
193
+ */
194
+ async getStats() {
195
+ const backups = await this.list();
196
+
197
+ let totalSize = 0;
198
+ let oldestDate = null;
199
+ let newestDate = null;
200
+ const types = { compressed: 0, directory: 0 };
201
+
202
+ backups.forEach(backup => {
203
+ totalSize += backup.size;
204
+ types[backup.type]++;
205
+
206
+ if (!oldestDate || backup.modified < oldestDate) {
207
+ oldestDate = backup.modified;
208
+ }
209
+ if (!newestDate || backup.modified > newestDate) {
210
+ newestDate = backup.modified;
211
+ }
212
+ });
213
+
214
+ return {
215
+ count: backups.length,
216
+ totalSize,
217
+ formattedSize: FileSystemUtils.formatSize(totalSize),
218
+ types,
219
+ oldestDate,
220
+ newestDate,
221
+ averageSize: backups.length > 0 ? totalSize / backups.length : 0
222
+ };
223
+ }
224
+ }
225
+
226
+ module.exports = BackupListService;
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Backup Service
3
+ * Focused service for creating backups of Claude Code configuration
4
+ */
5
+
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const FileSystemUtils = require('../utils/file-system-utils');
9
+ const ClaudePathConfig = require('../utils/claude-path-config');
10
+
11
+ class BackupService {
12
+ constructor(config = null) {
13
+ this.config = config || new ClaudePathConfig();
14
+ this.backedUpCount = 0;
15
+ }
16
+
17
+ /**
18
+ * Create a complete backup
19
+ */
20
+ async create(name = null) {
21
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('.')[0];
22
+ const backupName = name || `backup-${timestamp}`;
23
+ const backupPath = path.join(this.config.backupsDir, backupName);
24
+
25
+ // Ensure backup directory exists
26
+ FileSystemUtils.ensureDirectory(this.config.backupsDir);
27
+
28
+ // Check if backup already exists
29
+ if (fs.existsSync(backupPath)) {
30
+ throw new Error(`Backup '${backupName}' already exists`);
31
+ }
32
+
33
+ // Create backup directory
34
+ FileSystemUtils.ensureDirectory(backupPath);
35
+
36
+ // Reset counter
37
+ this.backedUpCount = 0;
38
+
39
+ // Backup components
40
+ const components = {};
41
+ let totalFiles = 0;
42
+ let totalSize = 0;
43
+
44
+ // Backup settings
45
+ if (FileSystemUtils.isReadable(this.config.settingsPath)) {
46
+ await this.backupSettings(backupPath);
47
+ components.settings = true;
48
+ totalFiles++;
49
+ const stats = FileSystemUtils.getStats(this.config.settingsPath);
50
+ if (stats) totalSize += stats.size;
51
+ }
52
+
53
+ // Backup commands
54
+ const commandsResult = await this.backupCommands(backupPath);
55
+ if (commandsResult.count > 0) {
56
+ components.commands = true;
57
+ totalFiles += commandsResult.count;
58
+ totalSize += commandsResult.size;
59
+ }
60
+
61
+ // Backup hooks
62
+ const hooksResult = await this.backupHooks(backupPath);
63
+ if (hooksResult.count > 0) {
64
+ components.hooks = true;
65
+ totalFiles += hooksResult.count;
66
+ totalSize += hooksResult.size;
67
+ }
68
+
69
+ // Create metadata
70
+ const metadata = this.createMetadata(backupName, components, totalFiles, totalSize);
71
+ await this.saveMetadata(backupPath, metadata);
72
+
73
+ return {
74
+ name: backupName,
75
+ path: backupPath,
76
+ components,
77
+ totalFiles,
78
+ totalSize,
79
+ metadata
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Backup settings.json file
85
+ */
86
+ async backupSettings(backupPath) {
87
+ if (!FileSystemUtils.isReadable(this.config.settingsPath)) {
88
+ return false;
89
+ }
90
+
91
+ const destPath = path.join(backupPath, 'settings.json');
92
+ const success = FileSystemUtils.copyFile(this.config.settingsPath, destPath);
93
+
94
+ if (success) {
95
+ this.backedUpCount++;
96
+ console.log('✅ Backed up settings.json');
97
+ }
98
+
99
+ return success;
100
+ }
101
+
102
+ /**
103
+ * Backup commands directory
104
+ */
105
+ async backupCommands(backupPath) {
106
+ if (!fs.existsSync(this.config.commandsDir)) {
107
+ return { count: 0, size: 0 };
108
+ }
109
+
110
+ const commandsBackupDir = path.join(backupPath, 'commands');
111
+ FileSystemUtils.ensureDirectory(commandsBackupDir);
112
+
113
+ let count = 0;
114
+ let size = 0;
115
+
116
+ try {
117
+ const files = fs.readdirSync(this.config.commandsDir);
118
+
119
+ for (const file of files) {
120
+ if (file.endsWith('.md')) {
121
+ const sourcePath = path.join(this.config.commandsDir, file);
122
+ const destPath = path.join(commandsBackupDir, file);
123
+
124
+ if (FileSystemUtils.copyFile(sourcePath, destPath)) {
125
+ count++;
126
+ const stats = FileSystemUtils.getStats(sourcePath);
127
+ if (stats) size += stats.size;
128
+ this.backedUpCount++;
129
+ }
130
+ }
131
+ }
132
+
133
+ if (count > 0) {
134
+ console.log(`✅ Backed up ${count} commands`);
135
+ }
136
+ } catch (error) {
137
+ console.warn(`⚠️ Warning: Could not backup commands - ${error.message}`);
138
+ }
139
+
140
+ return { count, size };
141
+ }
142
+
143
+ /**
144
+ * Backup hooks directory
145
+ */
146
+ async backupHooks(backupPath) {
147
+ const hooksDir = this.config.hooksDir;
148
+
149
+ if (!fs.existsSync(hooksDir)) {
150
+ return { count: 0, size: 0 };
151
+ }
152
+
153
+ const hooksBackupDir = path.join(backupPath, 'hooks');
154
+ FileSystemUtils.ensureDirectory(hooksBackupDir);
155
+
156
+ let count = 0;
157
+ let size = 0;
158
+
159
+ try {
160
+ const files = fs.readdirSync(hooksDir);
161
+
162
+ for (const file of files) {
163
+ const sourcePath = path.join(hooksDir, file);
164
+ const stats = FileSystemUtils.getStats(sourcePath);
165
+
166
+ if (stats && stats.isFile()) {
167
+ const destPath = path.join(hooksBackupDir, file);
168
+
169
+ if (FileSystemUtils.copyFile(sourcePath, destPath)) {
170
+ count++;
171
+ size += stats.size;
172
+ this.backedUpCount++;
173
+ }
174
+ }
175
+ }
176
+
177
+ if (count > 0) {
178
+ console.log(`✅ Backed up ${count} hooks`);
179
+ }
180
+ } catch (error) {
181
+ console.warn(`⚠️ Warning: Could not backup hooks - ${error.message}`);
182
+ }
183
+
184
+ return { count, size };
185
+ }
186
+
187
+ /**
188
+ * Create backup metadata
189
+ */
190
+ createMetadata(name, components, totalFiles, totalSize) {
191
+ return {
192
+ name,
193
+ timestamp: new Date().toISOString(),
194
+ components,
195
+ totalFiles,
196
+ totalSize,
197
+ claudeVersion: this.getClaudeVersion(),
198
+ system: {
199
+ platform: require('os').platform(),
200
+ arch: require('os').arch(),
201
+ nodeVersion: process.version
202
+ }
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Save metadata to backup directory
208
+ */
209
+ async saveMetadata(backupPath, metadata) {
210
+ const metadataPath = path.join(backupPath, 'backup-metadata.json');
211
+ const content = JSON.stringify(metadata, null, 2);
212
+
213
+ return FileSystemUtils.writeFile(metadataPath, content);
214
+ }
215
+
216
+ /**
217
+ * Get Claude Code version
218
+ */
219
+ getClaudeVersion() {
220
+ try {
221
+ const packagePath = path.join(__dirname, '..', '..', 'package.json');
222
+ const packageData = JSON.parse(FileSystemUtils.readFile(packagePath) || '{}');
223
+ return packageData.version || 'unknown';
224
+ } catch (error) {
225
+ return 'unknown';
226
+ }
227
+ }
228
+ }
229
+
230
+ module.exports = BackupService;