claude-autopm 1.18.0 → 1.20.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 +159 -0
- package/autopm/.claude/agents/README.md +1 -1
- package/autopm/.claude/agents/core/mcp-manager.md +1 -1
- package/autopm/.claude/agents/decision-matrices/python-backend-selection.md +25 -25
- package/autopm/.claude/agents/decision-matrices/ui-framework-selection.md +43 -43
- package/autopm/.claude/agents/devops/github-operations-specialist.md +1 -1
- package/autopm/.claude/agents/frameworks/README.md +5 -5
- package/autopm/.claude/agents/frameworks/e2e-test-engineer.md +1 -1
- package/autopm/.claude/agents/frameworks/nats-messaging-expert.md +1 -1
- package/autopm/.claude/agents/frameworks/react-frontend-engineer.md +1 -1
- package/autopm/.claude/agents/frameworks/react-ui-expert.md +3 -3
- package/autopm/.claude/agents/frameworks/tailwindcss-expert.md +3 -3
- package/autopm/.claude/agents/frameworks/ux-design-expert.md +3 -3
- package/autopm/.claude/commands/infrastructure/traefik-setup.md +1 -1
- package/autopm/.claude/commands/playwright/test-scaffold.md +1 -1
- package/autopm/.claude/commands/pm/context.md +11 -0
- package/autopm/.claude/commands/pm/epic-decompose.md +25 -2
- package/autopm/.claude/commands/pm/epic-oneshot.md +13 -0
- package/autopm/.claude/commands/pm/epic-start.md +19 -0
- package/autopm/.claude/commands/pm/epic-sync-modular.md +10 -10
- package/autopm/.claude/commands/pm/epic-sync.md +14 -14
- package/autopm/.claude/commands/pm/issue-start.md +50 -5
- package/autopm/.claude/commands/pm/issue-sync.md +15 -15
- package/autopm/.claude/commands/pm/what-next.md +11 -0
- package/autopm/.claude/commands/ui/bootstrap-scaffold.md +6 -5
- package/autopm/.claude/commands/ui/tailwind-system.md +1 -1
- package/autopm/.claude/examples/mcp/playwright-mcp.md +2 -2
- package/autopm/.claude/examples/mcp-servers.example.json +2 -2
- package/autopm/.claude/hooks/docker-first-enforcement.sh +1 -1
- package/autopm/.claude/mcp/MCP-REGISTRY.md +1 -1
- package/autopm/.claude/mcp/playwright-mcp.md +2 -2
- package/autopm/.claude/rules/agent-coordination.md +26 -24
- package/autopm/.claude/rules/docker-first-development.md +1 -1
- package/autopm/.claude/rules/infrastructure-pipeline.md +1 -1
- package/autopm/.claude/rules/ui-development-standards.md +1 -1
- package/autopm/.claude/rules/visual-testing.md +3 -3
- package/autopm/.claude/scripts/azure/active-work.js +2 -2
- package/autopm/.claude/scripts/azure/blocked.js +13 -13
- package/autopm/.claude/scripts/azure/daily.js +1 -1
- package/autopm/.claude/scripts/azure/dashboard.js +1 -1
- package/autopm/.claude/scripts/azure/feature-list.js +2 -2
- package/autopm/.claude/scripts/azure/feature-status.js +1 -1
- package/autopm/.claude/scripts/azure/next-task.js +1 -1
- package/autopm/.claude/scripts/azure/search.js +1 -1
- package/autopm/.claude/scripts/azure/setup.js +15 -15
- package/autopm/.claude/scripts/azure/sprint-report.js +2 -2
- package/autopm/.claude/scripts/azure/sync.js +1 -1
- package/autopm/.claude/scripts/azure/us-list.js +1 -1
- package/autopm/.claude/scripts/azure/us-status.js +1 -1
- package/autopm/.claude/scripts/azure/validate.js +13 -13
- package/autopm/.claude/scripts/lib/frontmatter-utils.sh +42 -7
- package/autopm/.claude/scripts/lib/logging-utils.sh +20 -16
- package/autopm/.claude/scripts/lib/validation-utils.sh +1 -1
- package/autopm/.claude/scripts/pm/context.js +338 -0
- package/autopm/.claude/scripts/pm/issue-sync/format-comment.sh +3 -3
- package/autopm/.claude/scripts/pm/lib/README.md +85 -0
- package/autopm/.claude/scripts/pm/lib/logger.js +78 -0
- package/autopm/.claude/scripts/pm/next.js +25 -1
- package/autopm/.claude/scripts/pm/what-next.js +660 -0
- package/autopm/.claude/teams.json +3 -5
- package/autopm/.claude/templates/claude-templates/addons/devops-agents.md +2 -2
- package/autopm/.claude/templates/claude-templates/addons/docker-agents.md +4 -4
- package/autopm/.claude/templates/claude-templates/addons/minimal-agents.md +1 -1
- package/autopm/.claude/templates/issue-decomposition/api.yaml +2 -2
- package/autopm/.claude/templates/issue-decomposition/auth.yaml +4 -4
- package/autopm/.claude/templates/issue-decomposition/crud.yaml +3 -3
- package/autopm/.claude/templates/issue-decomposition/default.yaml +1 -1
- package/autopm/.claude/templates/issue-decomposition/ui-feature.yaml +2 -2
- package/bin/autopm.js +25 -0
- package/package.json +1 -2
- package/lib/agentExecutor.js.deprecated +0 -101
- package/lib/azure/cache.js +0 -80
- package/lib/azure/client.js +0 -77
- package/lib/azure/formatter.js +0 -177
- package/lib/commandHelpers.js +0 -177
- package/lib/context/manager.js +0 -290
- package/lib/documentation/manager.js +0 -528
- package/lib/github/workflow-manager.js +0 -546
- package/lib/helpers/azure-batch-api.js +0 -133
- package/lib/helpers/azure-cache-manager.js +0 -287
- package/lib/helpers/azure-parallel-processor.js +0 -158
- package/lib/helpers/azure-work-item-create.js +0 -278
- package/lib/helpers/gh-issue-create.js +0 -250
- package/lib/helpers/interactive-prompt.js +0 -336
- package/lib/helpers/output-manager.js +0 -335
- package/lib/helpers/progress-indicator.js +0 -258
- package/lib/performance/benchmarker.js +0 -429
- package/lib/pm/epic-decomposer.js +0 -273
- package/lib/pm/epic-syncer.js +0 -221
- package/lib/prdMetadata.js +0 -270
- package/lib/providers/azure/index.js +0 -234
- package/lib/providers/factory.js +0 -87
- package/lib/providers/github/index.js +0 -204
- package/lib/providers/interface.js +0 -73
- package/lib/python/scaffold-manager.js +0 -576
- package/lib/react/scaffold-manager.js +0 -745
- package/lib/regression/analyzer.js +0 -578
- package/lib/release/manager.js +0 -324
- package/lib/tailwind/manager.js +0 -486
- package/lib/traefik/manager.js +0 -484
- package/lib/utils/colors.js +0 -126
- package/lib/utils/config.js +0 -317
- package/lib/utils/filesystem.js +0 -316
- package/lib/utils/logger.js +0 -135
- package/lib/utils/prompts.js +0 -294
- package/lib/utils/shell.js +0 -237
- package/lib/validators/email-validator.js +0 -337
- package/lib/workflow/manager.js +0 -449
package/lib/utils/config.js
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration management utility
|
|
3
|
-
* Handles loading and saving configuration files
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs-extra');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const yaml = require('js-yaml');
|
|
9
|
-
const dotenv = require('dotenv');
|
|
10
|
-
|
|
11
|
-
class Config {
|
|
12
|
-
constructor(logger) {
|
|
13
|
-
this.logger = logger;
|
|
14
|
-
this.configDir = '.claude';
|
|
15
|
-
this.configFile = 'config.json';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Get config file path
|
|
20
|
-
*/
|
|
21
|
-
getConfigPath(projectPath = process.cwd()) {
|
|
22
|
-
return path.join(projectPath, this.configDir, this.configFile);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Load configuration
|
|
27
|
-
*/
|
|
28
|
-
async load(projectPath = process.cwd()) {
|
|
29
|
-
const configPath = this.getConfigPath(projectPath);
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
if (await fs.pathExists(configPath)) {
|
|
33
|
-
const config = await fs.readJson(configPath);
|
|
34
|
-
this.logger.debug(`Loaded configuration from: ${configPath}`);
|
|
35
|
-
return config;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
this.logger.debug('No configuration file found, using defaults');
|
|
39
|
-
return this.getDefaultConfig();
|
|
40
|
-
} catch (error) {
|
|
41
|
-
this.logger.error('Failed to load configuration', error);
|
|
42
|
-
return this.getDefaultConfig();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Save configuration
|
|
48
|
-
*/
|
|
49
|
-
async save(config, projectPath = process.cwd()) {
|
|
50
|
-
const configPath = this.getConfigPath(projectPath);
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
await fs.ensureDir(path.dirname(configPath));
|
|
54
|
-
await fs.writeJson(configPath, config, { spaces: 2 });
|
|
55
|
-
this.logger.success(`Configuration saved to: ${configPath}`);
|
|
56
|
-
return true;
|
|
57
|
-
} catch (error) {
|
|
58
|
-
this.logger.error('Failed to save configuration', error);
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Get default configuration
|
|
65
|
-
*/
|
|
66
|
-
getDefaultConfig() {
|
|
67
|
-
return {
|
|
68
|
-
version: '1.0.0',
|
|
69
|
-
provider: 'github',
|
|
70
|
-
executionStrategy: 'adaptive',
|
|
71
|
-
features: {
|
|
72
|
-
docker_first_development: false,
|
|
73
|
-
kubernetes_devops_testing: false,
|
|
74
|
-
github_actions_k8s: false,
|
|
75
|
-
enforce_docker_tests: false,
|
|
76
|
-
integration_tests: true
|
|
77
|
-
},
|
|
78
|
-
paths: {
|
|
79
|
-
epics: '.claude/epics',
|
|
80
|
-
prds: '.claude/prds',
|
|
81
|
-
context: '.claude/context'
|
|
82
|
-
},
|
|
83
|
-
settings: {
|
|
84
|
-
verbose: false,
|
|
85
|
-
autoSync: false,
|
|
86
|
-
parallelExecution: true
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Merge configurations
|
|
93
|
-
*/
|
|
94
|
-
mergeConfig(base, updates) {
|
|
95
|
-
return {
|
|
96
|
-
...base,
|
|
97
|
-
...updates,
|
|
98
|
-
features: {
|
|
99
|
-
...base.features,
|
|
100
|
-
...(updates.features || {})
|
|
101
|
-
},
|
|
102
|
-
paths: {
|
|
103
|
-
...base.paths,
|
|
104
|
-
...(updates.paths || {})
|
|
105
|
-
},
|
|
106
|
-
settings: {
|
|
107
|
-
...base.settings,
|
|
108
|
-
...(updates.settings || {})
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Load environment variables
|
|
115
|
-
*/
|
|
116
|
-
async loadEnv(projectPath = process.cwd()) {
|
|
117
|
-
const envPath = path.join(projectPath, this.configDir, '.env');
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
if (await fs.pathExists(envPath)) {
|
|
121
|
-
dotenv.config({ path: envPath });
|
|
122
|
-
this.logger.debug(`Loaded environment variables from: ${envPath}`);
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
this.logger.debug('No .env file found');
|
|
127
|
-
return false;
|
|
128
|
-
} catch (error) {
|
|
129
|
-
this.logger.error('Failed to load .env file', error);
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Save environment variables
|
|
136
|
-
*/
|
|
137
|
-
async saveEnv(variables, projectPath = process.cwd()) {
|
|
138
|
-
const envPath = path.join(projectPath, this.configDir, '.env');
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
await fs.ensureDir(path.dirname(envPath));
|
|
142
|
-
|
|
143
|
-
const envContent = Object.entries(variables)
|
|
144
|
-
.map(([key, value]) => `${key}="${value}"`)
|
|
145
|
-
.join('\n');
|
|
146
|
-
|
|
147
|
-
await fs.writeFile(envPath, envContent);
|
|
148
|
-
await fs.chmod(envPath, 0o600); // Secure permissions
|
|
149
|
-
|
|
150
|
-
this.logger.success(`Environment variables saved to: ${envPath}`);
|
|
151
|
-
return true;
|
|
152
|
-
} catch (error) {
|
|
153
|
-
this.logger.error('Failed to save .env file', error);
|
|
154
|
-
throw error;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Get configuration presets
|
|
160
|
-
*/
|
|
161
|
-
getPresets() {
|
|
162
|
-
return {
|
|
163
|
-
minimal: {
|
|
164
|
-
executionStrategy: 'sequential',
|
|
165
|
-
features: {
|
|
166
|
-
docker_first_development: false,
|
|
167
|
-
kubernetes_devops_testing: false,
|
|
168
|
-
github_actions_k8s: false,
|
|
169
|
-
enforce_docker_tests: false,
|
|
170
|
-
integration_tests: true
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
docker: {
|
|
174
|
-
executionStrategy: 'adaptive',
|
|
175
|
-
features: {
|
|
176
|
-
docker_first_development: true,
|
|
177
|
-
kubernetes_devops_testing: false,
|
|
178
|
-
github_actions_k8s: false,
|
|
179
|
-
enforce_docker_tests: true,
|
|
180
|
-
integration_tests: true
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
devops: {
|
|
184
|
-
executionStrategy: 'adaptive',
|
|
185
|
-
features: {
|
|
186
|
-
docker_first_development: true,
|
|
187
|
-
kubernetes_devops_testing: true,
|
|
188
|
-
github_actions_k8s: true,
|
|
189
|
-
enforce_docker_tests: true,
|
|
190
|
-
integration_tests: true
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
performance: {
|
|
194
|
-
executionStrategy: 'hybrid-parallel',
|
|
195
|
-
features: {
|
|
196
|
-
docker_first_development: false,
|
|
197
|
-
kubernetes_devops_testing: false,
|
|
198
|
-
github_actions_k8s: false,
|
|
199
|
-
enforce_docker_tests: false,
|
|
200
|
-
integration_tests: true
|
|
201
|
-
},
|
|
202
|
-
settings: {
|
|
203
|
-
parallelExecution: true
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Apply preset configuration
|
|
211
|
-
*/
|
|
212
|
-
applyPreset(presetName) {
|
|
213
|
-
const presets = this.getPresets();
|
|
214
|
-
const preset = presets[presetName];
|
|
215
|
-
|
|
216
|
-
if (!preset) {
|
|
217
|
-
throw new Error(`Unknown preset: ${presetName}`);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const defaultConfig = this.getDefaultConfig();
|
|
221
|
-
return this.mergeConfig(defaultConfig, preset);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Validate configuration
|
|
226
|
-
*/
|
|
227
|
-
validateConfig(config) {
|
|
228
|
-
const errors = [];
|
|
229
|
-
|
|
230
|
-
// Check required fields
|
|
231
|
-
if (!config.version) {
|
|
232
|
-
errors.push('Missing version field');
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (!config.provider) {
|
|
236
|
-
errors.push('Missing provider field');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (!['github', 'azure'].includes(config.provider)) {
|
|
240
|
-
errors.push(`Invalid provider: ${config.provider}`);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (!config.executionStrategy) {
|
|
244
|
-
errors.push('Missing executionStrategy field');
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const validStrategies = ['sequential', 'adaptive', 'hybrid-parallel'];
|
|
248
|
-
if (!validStrategies.includes(config.executionStrategy)) {
|
|
249
|
-
errors.push(`Invalid executionStrategy: ${config.executionStrategy}`);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return {
|
|
253
|
-
valid: errors.length === 0,
|
|
254
|
-
errors
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Load YAML configuration
|
|
260
|
-
*/
|
|
261
|
-
async loadYaml(filepath) {
|
|
262
|
-
try {
|
|
263
|
-
const content = await fs.readFile(filepath, 'utf8');
|
|
264
|
-
return yaml.load(content);
|
|
265
|
-
} catch (error) {
|
|
266
|
-
this.logger.error(`Failed to load YAML file: ${filepath}`, error);
|
|
267
|
-
throw error;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Save YAML configuration
|
|
273
|
-
*/
|
|
274
|
-
async saveYaml(filepath, data) {
|
|
275
|
-
try {
|
|
276
|
-
const content = yaml.dump(data, { indent: 2 });
|
|
277
|
-
await fs.writeFile(filepath, content);
|
|
278
|
-
this.logger.debug(`Saved YAML file: ${filepath}`);
|
|
279
|
-
return true;
|
|
280
|
-
} catch (error) {
|
|
281
|
-
this.logger.error(`Failed to save YAML file: ${filepath}`, error);
|
|
282
|
-
throw error;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Check if project is configured
|
|
288
|
-
*/
|
|
289
|
-
async isConfigured(projectPath = process.cwd()) {
|
|
290
|
-
const configPath = this.getConfigPath(projectPath);
|
|
291
|
-
return fs.pathExists(configPath);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Get provider-specific settings
|
|
296
|
-
*/
|
|
297
|
-
getProviderSettings(config) {
|
|
298
|
-
switch (config.provider) {
|
|
299
|
-
case 'github':
|
|
300
|
-
return {
|
|
301
|
-
useSubIssues: true,
|
|
302
|
-
labelsPrefix: 'pm:',
|
|
303
|
-
...config.github
|
|
304
|
-
};
|
|
305
|
-
case 'azure':
|
|
306
|
-
return {
|
|
307
|
-
organization: process.env.AZURE_DEVOPS_ORG,
|
|
308
|
-
project: process.env.AZURE_DEVOPS_PROJECT,
|
|
309
|
-
...config.azure
|
|
310
|
-
};
|
|
311
|
-
default:
|
|
312
|
-
return {};
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
module.exports = Config;
|
package/lib/utils/filesystem.js
DELETED
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Filesystem utility for cross-platform file operations
|
|
3
|
-
* Wraps fs-extra with additional safety and logging
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs-extra');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { glob } = require('glob');
|
|
9
|
-
|
|
10
|
-
class FileSystem {
|
|
11
|
-
constructor(logger) {
|
|
12
|
-
this.logger = logger;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Check if a path exists
|
|
17
|
-
*/
|
|
18
|
-
async exists(filepath) {
|
|
19
|
-
try {
|
|
20
|
-
await fs.access(filepath);
|
|
21
|
-
return true;
|
|
22
|
-
} catch {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Create directory with parents if needed
|
|
29
|
-
*/
|
|
30
|
-
async ensureDir(dirPath) {
|
|
31
|
-
try {
|
|
32
|
-
await fs.ensureDir(dirPath);
|
|
33
|
-
this.logger.debug(`Created directory: ${dirPath}`);
|
|
34
|
-
return true;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
this.logger.error(`Failed to create directory: ${dirPath}`, error);
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Copy file or directory
|
|
43
|
-
*/
|
|
44
|
-
async copy(src, dest, options = {}) {
|
|
45
|
-
try {
|
|
46
|
-
await fs.copy(src, dest, options);
|
|
47
|
-
this.logger.debug(`Copied: ${src} → ${dest}`);
|
|
48
|
-
return true;
|
|
49
|
-
} catch (error) {
|
|
50
|
-
this.logger.error(`Failed to copy: ${src} → ${dest}`, error);
|
|
51
|
-
const newError = new Error(`Failed to copy: ${error.message}`);
|
|
52
|
-
newError.code = error.code;
|
|
53
|
-
newError.src = src;
|
|
54
|
-
newError.dest = dest;
|
|
55
|
-
throw newError;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Move file or directory
|
|
61
|
-
*/
|
|
62
|
-
async move(src, dest, options = {}) {
|
|
63
|
-
try {
|
|
64
|
-
await fs.move(src, dest, options);
|
|
65
|
-
this.logger.debug(`Moved: ${src} → ${dest}`);
|
|
66
|
-
return true;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
this.logger.error(`Failed to move: ${src} → ${dest}`, error);
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Remove file or directory
|
|
75
|
-
*/
|
|
76
|
-
async remove(filepath) {
|
|
77
|
-
try {
|
|
78
|
-
await fs.remove(filepath);
|
|
79
|
-
this.logger.debug(`Removed: ${filepath}`);
|
|
80
|
-
return true;
|
|
81
|
-
} catch (error) {
|
|
82
|
-
this.logger.error(`Failed to remove: ${filepath}`, error);
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Read file content
|
|
89
|
-
*/
|
|
90
|
-
async readFile(filepath, encoding = 'utf8') {
|
|
91
|
-
try {
|
|
92
|
-
const content = await fs.readFile(filepath, encoding);
|
|
93
|
-
this.logger.debug(`Read file: ${filepath}`);
|
|
94
|
-
return content;
|
|
95
|
-
} catch (error) {
|
|
96
|
-
this.logger.error(`Failed to read file: ${filepath}`, error);
|
|
97
|
-
const newError = new Error(`Failed to read file: ${error.message}`);
|
|
98
|
-
newError.code = error.code;
|
|
99
|
-
newError.path = error.path;
|
|
100
|
-
throw newError;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Write file content
|
|
106
|
-
*/
|
|
107
|
-
async writeFile(filepath, content, options = {}) {
|
|
108
|
-
try {
|
|
109
|
-
await fs.writeFile(filepath, content, options);
|
|
110
|
-
this.logger.debug(`Wrote file: ${filepath}`);
|
|
111
|
-
return true;
|
|
112
|
-
} catch (error) {
|
|
113
|
-
this.logger.error(`Failed to write file: ${filepath}`, error);
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Read JSON file
|
|
120
|
-
*/
|
|
121
|
-
async readJson(filepath) {
|
|
122
|
-
try {
|
|
123
|
-
const data = await fs.readJson(filepath);
|
|
124
|
-
this.logger.debug(`Read JSON: ${filepath}`);
|
|
125
|
-
return data;
|
|
126
|
-
} catch (error) {
|
|
127
|
-
this.logger.error(`Failed to read JSON: ${filepath}`, error);
|
|
128
|
-
const newError = new Error(`Failed to read JSON: ${error.message}`);
|
|
129
|
-
newError.code = error.code;
|
|
130
|
-
newError.path = filepath;
|
|
131
|
-
throw newError;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Write JSON file
|
|
137
|
-
*/
|
|
138
|
-
async writeJson(filepath, data, options = { spaces: 2 }) {
|
|
139
|
-
try {
|
|
140
|
-
await fs.writeJson(filepath, data, options);
|
|
141
|
-
this.logger.debug(`Wrote JSON: ${filepath}`);
|
|
142
|
-
return true;
|
|
143
|
-
} catch (error) {
|
|
144
|
-
this.logger.error(`Failed to write JSON: ${filepath}`, error);
|
|
145
|
-
throw error;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* List directory contents
|
|
151
|
-
*/
|
|
152
|
-
async listDir(dirPath, options = {}) {
|
|
153
|
-
try {
|
|
154
|
-
const items = await fs.readdir(dirPath, options);
|
|
155
|
-
this.logger.debug(`Listed directory: ${dirPath}`);
|
|
156
|
-
return items;
|
|
157
|
-
} catch (error) {
|
|
158
|
-
this.logger.error(`Failed to list directory: ${dirPath}`, error);
|
|
159
|
-
const newError = new Error(`Failed to list directory: ${error.message}`);
|
|
160
|
-
newError.code = error.code;
|
|
161
|
-
newError.path = error.path;
|
|
162
|
-
throw newError;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Find files using glob pattern
|
|
168
|
-
*/
|
|
169
|
-
async findFiles(pattern, options = {}) {
|
|
170
|
-
try {
|
|
171
|
-
const files = await glob(pattern, options);
|
|
172
|
-
this.logger.debug(`Found ${files.length} files matching: ${pattern}`);
|
|
173
|
-
return files;
|
|
174
|
-
} catch (error) {
|
|
175
|
-
this.logger.error(`Failed to find files: ${pattern}`, error);
|
|
176
|
-
const newError = new Error(`Failed to find files: ${error.message}`);
|
|
177
|
-
newError.code = error.code;
|
|
178
|
-
newError.pattern = pattern;
|
|
179
|
-
throw newError;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Get file stats
|
|
185
|
-
*/
|
|
186
|
-
async stat(filepath) {
|
|
187
|
-
try {
|
|
188
|
-
const stats = await fs.stat(filepath);
|
|
189
|
-
this.logger.debug(`Got stats for: ${filepath}`);
|
|
190
|
-
return stats;
|
|
191
|
-
} catch (error) {
|
|
192
|
-
this.logger.error(`Failed to get stats: ${filepath}`, error);
|
|
193
|
-
const newError = new Error(`Failed to get stats: ${error.message}`);
|
|
194
|
-
newError.code = error.code;
|
|
195
|
-
newError.path = error.path;
|
|
196
|
-
throw newError;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Check if path is a directory
|
|
202
|
-
*/
|
|
203
|
-
async isDirectory(filepath) {
|
|
204
|
-
try {
|
|
205
|
-
const stats = await this.stat(filepath);
|
|
206
|
-
return stats.isDirectory();
|
|
207
|
-
} catch {
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Check if path is a file
|
|
214
|
-
*/
|
|
215
|
-
async isFile(filepath) {
|
|
216
|
-
try {
|
|
217
|
-
const stats = await this.stat(filepath);
|
|
218
|
-
return stats.isFile();
|
|
219
|
-
} catch {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Create backup with timestamp
|
|
226
|
-
*/
|
|
227
|
-
async backup(filepath) {
|
|
228
|
-
if (!await this.exists(filepath)) {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
233
|
-
const backupPath = `${filepath}.backup-${timestamp}`;
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
await this.copy(filepath, backupPath);
|
|
237
|
-
this.logger.info(`Created backup: ${backupPath}`);
|
|
238
|
-
return backupPath;
|
|
239
|
-
} catch (error) {
|
|
240
|
-
this.logger.error(`Failed to create backup: ${filepath}`, error);
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Set file permissions
|
|
247
|
-
*/
|
|
248
|
-
async chmod(filepath, mode) {
|
|
249
|
-
try {
|
|
250
|
-
await fs.chmod(filepath, mode);
|
|
251
|
-
this.logger.debug(`Changed permissions: ${filepath} to ${mode.toString(8)}`);
|
|
252
|
-
return true;
|
|
253
|
-
} catch (error) {
|
|
254
|
-
this.logger.error(`Failed to change permissions: ${filepath}`, error);
|
|
255
|
-
throw error;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Create symbolic link
|
|
261
|
-
*/
|
|
262
|
-
async symlink(target, linkPath) {
|
|
263
|
-
try {
|
|
264
|
-
await fs.ensureSymlink(target, linkPath);
|
|
265
|
-
this.logger.debug(`Created symlink: ${linkPath} → ${target}`);
|
|
266
|
-
return true;
|
|
267
|
-
} catch (error) {
|
|
268
|
-
this.logger.error(`Failed to create symlink: ${linkPath}`, error);
|
|
269
|
-
throw error;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Get absolute path
|
|
275
|
-
*/
|
|
276
|
-
resolvePath(...paths) {
|
|
277
|
-
return path.resolve(...paths);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Get relative path
|
|
282
|
-
*/
|
|
283
|
-
relativePath(from, to) {
|
|
284
|
-
return path.relative(from, to);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Join paths
|
|
289
|
-
*/
|
|
290
|
-
joinPath(...paths) {
|
|
291
|
-
return path.join(...paths);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Get directory name
|
|
296
|
-
*/
|
|
297
|
-
dirname(filepath) {
|
|
298
|
-
return path.dirname(filepath);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Get base name
|
|
303
|
-
*/
|
|
304
|
-
basename(filepath, ext) {
|
|
305
|
-
return path.basename(filepath, ext);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Get extension
|
|
310
|
-
*/
|
|
311
|
-
extname(filepath) {
|
|
312
|
-
return path.extname(filepath);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
module.exports = FileSystem;
|