create-byan-agent 1.1.2 → 1.2.0

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 (76) hide show
  1. package/CHANGELOG.md +250 -177
  2. package/LICENSE +21 -21
  3. package/README.md +1245 -421
  4. package/bin/create-byan-agent-backup.js +220 -220
  5. package/bin/create-byan-agent-fixed.js +301 -301
  6. package/bin/create-byan-agent.js +322 -301
  7. package/lib/errors.js +61 -0
  8. package/lib/exit-codes.js +54 -0
  9. package/lib/platforms/claude-code.js +113 -0
  10. package/lib/platforms/codex.js +92 -0
  11. package/lib/platforms/copilot-cli.js +123 -0
  12. package/lib/platforms/index.js +14 -0
  13. package/lib/platforms/vscode.js +51 -0
  14. package/lib/utils/config-loader.js +79 -0
  15. package/lib/utils/file-utils.js +104 -0
  16. package/lib/utils/git-detector.js +35 -0
  17. package/lib/utils/logger.js +64 -0
  18. package/lib/utils/node-detector.js +58 -0
  19. package/lib/utils/os-detector.js +74 -0
  20. package/lib/utils/yaml-utils.js +87 -0
  21. package/lib/yanstaller/backuper.js +308 -0
  22. package/lib/yanstaller/detector.js +141 -0
  23. package/lib/yanstaller/index.js +93 -0
  24. package/lib/yanstaller/installer.js +225 -0
  25. package/lib/yanstaller/interviewer.js +250 -0
  26. package/lib/yanstaller/recommender.js +298 -0
  27. package/lib/yanstaller/troubleshooter.js +498 -0
  28. package/lib/yanstaller/validator.js +578 -0
  29. package/lib/yanstaller/wizard.js +211 -0
  30. package/package.json +61 -55
  31. package/templates/.github/agents/bmad-agent-bmad-master.md +15 -15
  32. package/templates/.github/agents/bmad-agent-bmb-agent-builder.md +15 -15
  33. package/templates/.github/agents/bmad-agent-bmb-module-builder.md +15 -15
  34. package/templates/.github/agents/bmad-agent-bmb-workflow-builder.md +15 -15
  35. package/templates/.github/agents/bmad-agent-bmm-analyst.md +15 -15
  36. package/templates/.github/agents/bmad-agent-bmm-architect.md +15 -15
  37. package/templates/.github/agents/bmad-agent-bmm-dev.md +15 -15
  38. package/templates/.github/agents/bmad-agent-bmm-pm.md +15 -15
  39. package/templates/.github/agents/bmad-agent-bmm-quick-flow-solo-dev.md +15 -15
  40. package/templates/.github/agents/bmad-agent-bmm-quinn.md +15 -15
  41. package/templates/.github/agents/bmad-agent-bmm-sm.md +15 -15
  42. package/templates/.github/agents/bmad-agent-bmm-tech-writer.md +15 -15
  43. package/templates/.github/agents/bmad-agent-bmm-ux-designer.md +15 -15
  44. package/templates/.github/agents/bmad-agent-byan-test.md +32 -0
  45. package/templates/.github/agents/bmad-agent-byan.md +224 -224
  46. package/templates/.github/agents/bmad-agent-carmack.md +18 -0
  47. package/templates/.github/agents/bmad-agent-cis-brainstorming-coach.md +15 -15
  48. package/templates/.github/agents/bmad-agent-cis-creative-problem-solver.md +15 -15
  49. package/templates/.github/agents/bmad-agent-cis-design-thinking-coach.md +15 -15
  50. package/templates/.github/agents/bmad-agent-cis-innovation-strategist.md +15 -15
  51. package/templates/.github/agents/bmad-agent-cis-presentation-master.md +15 -15
  52. package/templates/.github/agents/bmad-agent-cis-storyteller.md +15 -15
  53. package/templates/.github/agents/bmad-agent-marc.md +48 -48
  54. package/templates/.github/agents/bmad-agent-patnote.md +48 -0
  55. package/templates/.github/agents/bmad-agent-rachid.md +47 -47
  56. package/templates/.github/agents/bmad-agent-tea-tea.md +15 -15
  57. package/templates/.github/agents/bmad-agent-test-dynamic.md +21 -0
  58. package/templates/.github/agents/expert-merise-agile.md +1 -0
  59. package/templates/.github/agents/franck.md +379 -0
  60. package/templates/_bmad/bmb/agents/agent-builder.md +59 -59
  61. package/templates/_bmad/bmb/agents/byan-test.md +116 -116
  62. package/templates/_bmad/bmb/agents/byan.md +215 -215
  63. package/templates/_bmad/bmb/agents/marc.md +303 -303
  64. package/templates/_bmad/bmb/agents/module-builder.md +60 -60
  65. package/templates/_bmad/bmb/agents/patnote.md +495 -495
  66. package/templates/_bmad/bmb/agents/rachid.md +184 -184
  67. package/templates/_bmad/bmb/agents/workflow-builder.md +61 -61
  68. package/templates/_bmad/bmb/workflows/byan/data/mantras.yaml +272 -272
  69. package/templates/_bmad/bmb/workflows/byan/data/templates.yaml +59 -59
  70. package/templates/_bmad/bmb/workflows/byan/delete-agent-workflow.md +657 -657
  71. package/templates/_bmad/bmb/workflows/byan/edit-agent-workflow.md +688 -688
  72. package/templates/_bmad/bmb/workflows/byan/interview-workflow.md +753 -753
  73. package/templates/_bmad/bmb/workflows/byan/quick-create-workflow.md +450 -450
  74. package/templates/_bmad/bmb/workflows/byan/templates/base-agent-template.md +79 -79
  75. package/templates/_bmad/bmb/workflows/byan/validate-agent-workflow.md +676 -676
  76. package/templates/_bmad/core/agents/carmack.md +238 -238
package/lib/errors.js ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Custom Error Classes
3
+ *
4
+ * @module errors
5
+ */
6
+
7
+ class YanInstallerError extends Error {
8
+ constructor(message, options) {
9
+ super(message, options);
10
+ this.name = 'YanInstallerError';
11
+ }
12
+ }
13
+
14
+ class NodeVersionError extends YanInstallerError {
15
+ constructor(required, current) {
16
+ super(`Node.js ${required}+ required, got ${current}`);
17
+ this.name = 'NodeVersionError';
18
+ this.required = required;
19
+ this.current = current;
20
+ }
21
+ }
22
+
23
+ class PlatformNotFoundError extends YanInstallerError {
24
+ constructor(platform) {
25
+ super(`Platform not found: ${platform}`);
26
+ this.name = 'PlatformNotFoundError';
27
+ this.platform = platform;
28
+ }
29
+ }
30
+
31
+ class PermissionError extends YanInstallerError {
32
+ constructor(path) {
33
+ super(`Permission denied: ${path}`);
34
+ this.name = 'PermissionError';
35
+ this.path = path;
36
+ }
37
+ }
38
+
39
+ class ValidationError extends YanInstallerError {
40
+ constructor(message, failures) {
41
+ super(message);
42
+ this.name = 'ValidationError';
43
+ this.failures = failures;
44
+ }
45
+ }
46
+
47
+ class BackupError extends YanInstallerError {
48
+ constructor(message, options) {
49
+ super(message, options);
50
+ this.name = 'BackupError';
51
+ }
52
+ }
53
+
54
+ module.exports = {
55
+ YanInstallerError,
56
+ NodeVersionError,
57
+ PlatformNotFoundError,
58
+ PermissionError,
59
+ ValidationError,
60
+ BackupError
61
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Exit Codes
3
+ *
4
+ * Standard exit codes for YANSTALLER CLI.
5
+ *
6
+ * @module exit-codes
7
+ */
8
+
9
+ module.exports = {
10
+ /**
11
+ * Success - Installation completed without errors
12
+ */
13
+ SUCCESS: 0,
14
+
15
+ /**
16
+ * Node.js version too old (< 18.0.0)
17
+ */
18
+ NODE_VERSION_ERROR: 1,
19
+
20
+ /**
21
+ * Permission denied (file system access)
22
+ */
23
+ PERMISSION_ERROR: 2,
24
+
25
+ /**
26
+ * Post-installation validation failed
27
+ */
28
+ VALIDATION_FAILED: 3,
29
+
30
+ /**
31
+ * Installation process failed
32
+ */
33
+ INSTALLATION_FAILED: 4,
34
+
35
+ /**
36
+ * Backup operation failed
37
+ */
38
+ BACKUP_FAILED: 5,
39
+
40
+ /**
41
+ * Platform not found or not supported
42
+ */
43
+ PLATFORM_ERROR: 6,
44
+
45
+ /**
46
+ * User cancelled installation
47
+ */
48
+ USER_CANCELLED: 7,
49
+
50
+ /**
51
+ * Unknown error
52
+ */
53
+ UNKNOWN_ERROR: 99
54
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Claude Code Platform Support
3
+ *
4
+ * Detects and installs MCP server config for Claude Code.
5
+ *
6
+ * @module platforms/claude-code
7
+ */
8
+
9
+ const path = require('path');
10
+ const os = require('os');
11
+ const fileUtils = require('../utils/file-utils');
12
+
13
+ const PLATFORM_NAME = 'Claude Code';
14
+
15
+ /**
16
+ * Get config path for current platform
17
+ *
18
+ * @returns {string|null}
19
+ */
20
+ function getConfigPath() {
21
+ const platform = os.platform();
22
+ const home = os.homedir();
23
+
24
+ switch (platform) {
25
+ case 'darwin':
26
+ return path.join(home, 'Library/Application Support/Claude/claude_desktop_config.json');
27
+ case 'win32':
28
+ return path.join(home, 'AppData/Roaming/Claude/claude_desktop_config.json');
29
+ case 'linux':
30
+ return path.join(home, '.config/Claude/claude_desktop_config.json');
31
+ default:
32
+ return null;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Detect if Claude Code is installed
38
+ *
39
+ * @returns {Promise<boolean>}
40
+ */
41
+ async function detect() {
42
+ const configPath = getConfigPath();
43
+ if (!configPath) return false;
44
+
45
+ return fileUtils.exists(configPath);
46
+ }
47
+
48
+ /**
49
+ * Install MCP server config for Claude Code
50
+ *
51
+ * @param {string} projectRoot - Project root directory
52
+ * @param {string[]} agents - Agent names to install
53
+ * @param {Object} config - Installation config
54
+ * @returns {Promise<{success: boolean, installed: number}>}
55
+ */
56
+ async function install(projectRoot, agents, config) {
57
+ const configPath = getConfigPath();
58
+
59
+ if (!configPath) {
60
+ throw new Error(`Unsupported platform: ${os.platform()}`);
61
+ }
62
+
63
+ // Read existing config or create new
64
+ let desktopConfig = {};
65
+ if (await fileUtils.exists(configPath)) {
66
+ const content = await fileUtils.readFile(configPath, 'utf8');
67
+ desktopConfig = JSON.parse(content);
68
+ }
69
+
70
+ // Ensure mcpServers exists
71
+ if (!desktopConfig.mcpServers) {
72
+ desktopConfig.mcpServers = {};
73
+ }
74
+
75
+ // Add BYAN MCP server if not exists
76
+ if (!desktopConfig.mcpServers.byan) {
77
+ desktopConfig.mcpServers.byan = {
78
+ command: 'npx',
79
+ args: ['-y', '@byan/mcp-server'],
80
+ env: {
81
+ BYAN_PROJECT_ROOT: projectRoot
82
+ }
83
+ };
84
+ }
85
+
86
+ // Write updated config
87
+ await fileUtils.ensureDir(path.dirname(configPath));
88
+ await fileUtils.writeFile(
89
+ configPath,
90
+ JSON.stringify(desktopConfig, null, 2)
91
+ );
92
+
93
+ return {
94
+ success: true,
95
+ installed: agents.length
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Get platform installation path
101
+ *
102
+ * @returns {string}
103
+ */
104
+ function getPath() {
105
+ return getConfigPath() || 'unknown';
106
+ }
107
+
108
+ module.exports = {
109
+ name: PLATFORM_NAME,
110
+ detect,
111
+ install,
112
+ getPath
113
+ };
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Codex Platform Support
3
+ *
4
+ * Detects and installs agents for Codex.
5
+ *
6
+ * @module platforms/codex
7
+ */
8
+
9
+ const path = require('path');
10
+ const fileUtils = require('../utils/file-utils');
11
+
12
+ const PLATFORM_NAME = 'Codex';
13
+ const PROMPTS_DIR = '.codex/prompts';
14
+
15
+ /**
16
+ * Detect if Codex is configured
17
+ *
18
+ * @returns {Promise<boolean>}
19
+ */
20
+ async function detect() {
21
+ // Check if .codex/prompts/ directory exists
22
+ return fileUtils.exists(PROMPTS_DIR);
23
+ }
24
+
25
+ /**
26
+ * Install agents for Codex
27
+ *
28
+ * @param {string} projectRoot - Project root directory
29
+ * @param {string[]} agents - Agent names to install
30
+ * @param {Object} config - Installation config
31
+ * @returns {Promise<{success: boolean, installed: number}>}
32
+ */
33
+ async function install(projectRoot, agents, config) {
34
+ const promptsDir = path.join(projectRoot, PROMPTS_DIR);
35
+ await fileUtils.ensureDir(promptsDir);
36
+
37
+ let installed = 0;
38
+
39
+ for (const agentName of agents) {
40
+ await generatePrompt(promptsDir, agentName, config);
41
+ installed++;
42
+ }
43
+
44
+ return {
45
+ success: true,
46
+ installed
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Generate Codex prompt file
52
+ *
53
+ * @param {string} promptsDir - Prompts directory path
54
+ * @param {string} agentName - Agent name
55
+ * @param {Object} config - Installation config
56
+ * @returns {Promise<void>}
57
+ */
58
+ async function generatePrompt(promptsDir, agentName, config) {
59
+ const promptPath = path.join(promptsDir, `${agentName}.md`);
60
+
61
+ const content = `# ${agentName} Agent
62
+
63
+ You must fully embody this agent's persona and follow all activation instructions exactly as specified.
64
+
65
+ <agent-activation CRITICAL="TRUE">
66
+ 1. LOAD the FULL agent file from {project-root}/_bmad/*/agents/${agentName}.md
67
+ 2. READ its entire contents - this contains the complete agent persona, menu, and instructions
68
+ 3. FOLLOW every step in the <activation> section precisely
69
+ 4. DISPLAY the welcome/greeting as instructed
70
+ 5. PRESENT the numbered menu
71
+ 6. WAIT for user input before proceeding
72
+ </agent-activation>
73
+ `;
74
+
75
+ await fileUtils.writeFile(promptPath, content);
76
+ }
77
+
78
+ /**
79
+ * Get platform installation path
80
+ *
81
+ * @returns {string}
82
+ */
83
+ function getPath() {
84
+ return PROMPTS_DIR;
85
+ }
86
+
87
+ module.exports = {
88
+ name: PLATFORM_NAME,
89
+ detect,
90
+ install,
91
+ getPath
92
+ };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * GitHub Copilot CLI Platform Support
3
+ *
4
+ * Detects and installs agents for GitHub Copilot CLI.
5
+ *
6
+ * @module platforms/copilot-cli
7
+ */
8
+
9
+ const path = require('path');
10
+ const fileUtils = require('../utils/file-utils');
11
+ const yamlUtils = require('../utils/yaml-utils');
12
+ const { execSync } = require('child_process');
13
+
14
+ const PLATFORM_NAME = 'GitHub Copilot CLI';
15
+ const STUB_DIR = '.github/agents';
16
+
17
+ /**
18
+ * Detect if Copilot CLI is installed
19
+ *
20
+ * With 10s timeout protection to prevent hanging.
21
+ *
22
+ * @returns {Promise<boolean|{detected: boolean, error: string}>}
23
+ */
24
+ async function detect() {
25
+ // Timeout promise (10 seconds)
26
+ const timeoutPromise = new Promise((resolve) =>
27
+ setTimeout(() => resolve({
28
+ detected: false,
29
+ error: 'Detection timeout after 10s'
30
+ }), 10000)
31
+ );
32
+
33
+ // Detection promise
34
+ const detectionPromise = (async () => {
35
+ try {
36
+ // Check if github-copilot-cli is installed
37
+ execSync('which github-copilot-cli', { encoding: 'utf8', stdio: 'ignore' });
38
+ return true;
39
+ } catch {
40
+ // Also check for .github/agents/ directory
41
+ return fileUtils.exists(STUB_DIR);
42
+ }
43
+ })();
44
+
45
+ // Race between detection and timeout
46
+ return Promise.race([detectionPromise, timeoutPromise]);
47
+ }
48
+
49
+ /**
50
+ * Install agents for Copilot CLI
51
+ *
52
+ * @param {string} projectRoot - Project root directory
53
+ * @param {string[]} agents - Agent names to install
54
+ * @param {Object} config - Installation config
55
+ * @returns {Promise<{success: boolean, installed: number}>}
56
+ */
57
+ async function install(projectRoot, agents, config) {
58
+ const stubsDir = path.join(projectRoot, STUB_DIR);
59
+ await fileUtils.ensureDir(stubsDir);
60
+
61
+ let installed = 0;
62
+
63
+ for (const agentName of agents) {
64
+ await generateStub(stubsDir, agentName, config);
65
+ installed++;
66
+ }
67
+
68
+ return {
69
+ success: true,
70
+ installed
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Generate Copilot CLI stub with YAML frontmatter
76
+ *
77
+ * @param {string} stubsDir - Stubs directory path
78
+ * @param {string} agentName - Agent name
79
+ * @param {Object} config - Installation config
80
+ * @returns {Promise<void>}
81
+ */
82
+ async function generateStub(stubsDir, agentName, config) {
83
+ const stubPath = path.join(stubsDir, `${agentName}.md`);
84
+
85
+ const frontmatter = {
86
+ name: agentName,
87
+ description: `${agentName} agent from BYAN platform`
88
+ };
89
+
90
+ const content = `---
91
+ ${yamlUtils.dump(frontmatter).trim()}
92
+ ---
93
+
94
+ You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
95
+
96
+ <agent-activation CRITICAL="TRUE">
97
+ 1. LOAD the FULL agent file from {project-root}/_bmad/*/agents/${agentName}.md
98
+ 2. READ its entire contents - this contains the complete agent persona, menu, and instructions
99
+ 3. FOLLOW every step in the <activation> section precisely
100
+ 4. DISPLAY the welcome/greeting as instructed
101
+ 5. PRESENT the numbered menu
102
+ 6. WAIT for user input before proceeding
103
+ </agent-activation>
104
+ `;
105
+
106
+ await fileUtils.writeFile(stubPath, content);
107
+ }
108
+
109
+ /**
110
+ * Get platform installation path
111
+ *
112
+ * @returns {string}
113
+ */
114
+ function getPath() {
115
+ return STUB_DIR;
116
+ }
117
+
118
+ module.exports = {
119
+ name: PLATFORM_NAME,
120
+ detect,
121
+ install,
122
+ getPath
123
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Platforms Index
3
+ *
4
+ * Exports all platform modules.
5
+ *
6
+ * @module platforms
7
+ */
8
+
9
+ module.exports = {
10
+ 'copilot-cli': require('./copilot-cli'),
11
+ 'vscode': require('./vscode'),
12
+ 'claude': require('./claude-code'),
13
+ 'codex': require('./codex')
14
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * VSCode Copilot Extension Platform Support
3
+ *
4
+ * Detects and installs agents for VSCode Copilot Extension.
5
+ * Same format as Copilot CLI.
6
+ *
7
+ * @module platforms/vscode
8
+ */
9
+
10
+ const copilotCli = require('./copilot-cli');
11
+
12
+ const PLATFORM_NAME = 'VSCode Copilot Extension';
13
+
14
+ /**
15
+ * Detect if VSCode with Copilot extension is installed
16
+ *
17
+ * @returns {Promise<boolean>}
18
+ */
19
+ async function detect() {
20
+ // VSCode uses same stub format as Copilot CLI
21
+ return copilotCli.detect();
22
+ }
23
+
24
+ /**
25
+ * Install agents for VSCode
26
+ *
27
+ * @param {string} projectRoot - Project root directory
28
+ * @param {string[]} agents - Agent names to install
29
+ * @param {Object} config - Installation config
30
+ * @returns {Promise<{success: boolean, installed: number}>}
31
+ */
32
+ async function install(projectRoot, agents, config) {
33
+ // Reuse Copilot CLI installation (same format)
34
+ return copilotCli.install(projectRoot, agents, config);
35
+ }
36
+
37
+ /**
38
+ * Get platform installation path
39
+ *
40
+ * @returns {string}
41
+ */
42
+ function getPath() {
43
+ return copilotCli.getPath();
44
+ }
45
+
46
+ module.exports = {
47
+ name: PLATFORM_NAME,
48
+ detect,
49
+ install,
50
+ getPath
51
+ };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Config Loader Utility
3
+ *
4
+ * Loads configuration files with variable resolution.
5
+ *
6
+ * @module utils/config-loader
7
+ */
8
+
9
+ const path = require('path');
10
+ const yamlUtils = require('./yaml-utils');
11
+
12
+ /**
13
+ * Load config file and resolve variables
14
+ *
15
+ * @param {string} configPath - Path to config.yaml
16
+ * @param {Object} context - Context for variable resolution
17
+ * @returns {Promise<Object>}
18
+ */
19
+ async function loadConfig(configPath, context = {}) {
20
+ const config = await yamlUtils.readYAML(configPath);
21
+ return resolveVariables(config, context);
22
+ }
23
+
24
+ /**
25
+ * Resolve variables in config object
26
+ *
27
+ * @param {Object} config - Config object
28
+ * @param {Object} context - Context for variable resolution
29
+ * @returns {Object}
30
+ */
31
+ function resolveVariables(config, context) {
32
+ const resolved = {};
33
+
34
+ for (const [key, value] of Object.entries(config)) {
35
+ if (typeof value === 'string') {
36
+ resolved[key] = resolveVariable(value, context);
37
+ } else if (typeof value === 'object' && value !== null) {
38
+ resolved[key] = resolveVariables(value, context);
39
+ } else {
40
+ resolved[key] = value;
41
+ }
42
+ }
43
+
44
+ return resolved;
45
+ }
46
+
47
+ /**
48
+ * Resolve single variable string
49
+ *
50
+ * @param {string} value - Value with potential variables
51
+ * @param {Object} context - Context for variable resolution
52
+ * @returns {string}
53
+ */
54
+ function resolveVariable(value, context) {
55
+ let resolved = value;
56
+
57
+ // Replace {project-root}
58
+ if (context.projectRoot) {
59
+ resolved = resolved.replace(/\{project-root\}/g, context.projectRoot);
60
+ }
61
+
62
+ // Replace {output_folder}
63
+ if (context.outputFolder) {
64
+ resolved = resolved.replace(/\{output_folder\}/g, context.outputFolder);
65
+ }
66
+
67
+ // Replace {user_name}
68
+ if (context.userName) {
69
+ resolved = resolved.replace(/\{user_name\}/g, context.userName);
70
+ }
71
+
72
+ return resolved;
73
+ }
74
+
75
+ module.exports = {
76
+ loadConfig,
77
+ resolveVariables,
78
+ resolveVariable
79
+ };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * File Utilities
3
+ *
4
+ * Wrapper around fs-extra for common file operations.
5
+ *
6
+ * @module utils/file-utils
7
+ */
8
+
9
+ const fs = require('fs-extra');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * Copy file or directory
14
+ *
15
+ * @param {string} src - Source path
16
+ * @param {string} dest - Destination path
17
+ * @returns {Promise<void>}
18
+ */
19
+ async function copy(src, dest) {
20
+ await fs.copy(src, dest);
21
+ }
22
+
23
+ /**
24
+ * Check if path exists
25
+ *
26
+ * @param {string} filePath - File or directory path
27
+ * @returns {Promise<boolean>}
28
+ */
29
+ async function exists(filePath) {
30
+ return fs.pathExists(filePath);
31
+ }
32
+
33
+ /**
34
+ * Ensure directory exists (create if not)
35
+ *
36
+ * @param {string} dirPath - Directory path
37
+ * @returns {Promise<void>}
38
+ */
39
+ async function ensureDir(dirPath) {
40
+ await fs.ensureDir(dirPath);
41
+ }
42
+
43
+ /**
44
+ * Remove file or directory
45
+ *
46
+ * @param {string} filePath - File or directory path
47
+ * @returns {Promise<void>}
48
+ */
49
+ async function remove(filePath) {
50
+ await fs.remove(filePath);
51
+ }
52
+
53
+ /**
54
+ * Read JSON file
55
+ *
56
+ * @param {string} filePath - JSON file path
57
+ * @returns {Promise<Object>}
58
+ */
59
+ async function readJSON(filePath) {
60
+ return fs.readJSON(filePath);
61
+ }
62
+
63
+ /**
64
+ * Write JSON file
65
+ *
66
+ * @param {string} filePath - JSON file path
67
+ * @param {Object} data - Data to write
68
+ * @returns {Promise<void>}
69
+ */
70
+ async function writeJSON(filePath, data) {
71
+ await fs.writeJSON(filePath, data, { spaces: 2 });
72
+ }
73
+
74
+ /**
75
+ * Read text file
76
+ *
77
+ * @param {string} filePath - Text file path
78
+ * @returns {Promise<string>}
79
+ */
80
+ async function readFile(filePath) {
81
+ return fs.readFile(filePath, 'utf8');
82
+ }
83
+
84
+ /**
85
+ * Write text file
86
+ *
87
+ * @param {string} filePath - Text file path
88
+ * @param {string} content - Content to write
89
+ * @returns {Promise<void>}
90
+ */
91
+ async function writeFile(filePath, content) {
92
+ await fs.writeFile(filePath, content, 'utf8');
93
+ }
94
+
95
+ module.exports = {
96
+ copy,
97
+ exists,
98
+ ensureDir,
99
+ remove,
100
+ readJSON,
101
+ writeJSON,
102
+ readFile,
103
+ writeFile
104
+ };