aiwf 0.3.18 → 0.3.19

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 (33) hide show
  1. package/README.ko.md +84 -0
  2. package/README.md +87 -0
  3. package/ai-tools/README.md +105 -0
  4. package/ai-tools/augment/README.md +371 -0
  5. package/ai-tools/augment/config.json +30 -0
  6. package/ai-tools/augment/template/.augment/aiwf-integration.md +387 -0
  7. package/ai-tools/augment/template/.augment/augment.yaml +347 -0
  8. package/ai-tools/augment/template/augment.config.json +64 -0
  9. package/ai-tools/claude-code/README.md +151 -0
  10. package/ai-tools/claude-code/config.json +28 -0
  11. package/ai-tools/claude-code/template/CLAUDE.md +91 -0
  12. package/ai-tools/cursor/README.md +314 -0
  13. package/ai-tools/cursor/config.json +29 -0
  14. package/ai-tools/cursor/template/.cursorrules +362 -0
  15. package/ai-tools/github-copilot/README.md +195 -0
  16. package/ai-tools/github-copilot/config.json +24 -0
  17. package/ai-tools/github-copilot/template/.github/copilot-instructions.md +202 -0
  18. package/ai-tools/windsurf/README.md +355 -0
  19. package/ai-tools/windsurf/config.json +29 -0
  20. package/ai-tools/windsurf/template/.windsurf/aiwf-rules.md +260 -0
  21. package/ai-tools/windsurf/template/.windsurf/windsurf.config.js +312 -0
  22. package/ai-tools/windsurf/template/windsurf.config.json +63 -0
  23. package/docs/CODE_CLEANUP_GUIDE.ko.md +415 -0
  24. package/docs/CODE_CLEANUP_GUIDE.md +415 -0
  25. package/docs/VALIDATOR_API.ko.md +324 -0
  26. package/docs/VALIDATOR_API.md +324 -0
  27. package/package.json +3 -2
  28. package/src/cli/index.js +338 -0
  29. package/src/lib/backup-manager.js +4 -3
  30. package/src/lib/installer.js +126 -3
  31. package/src/lib/validator.js +78 -313
  32. package/src/utils/messages.js +16 -2
  33. package/src/utils/paths.js +1 -20
@@ -3,322 +3,17 @@ import path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { TOOL_DIRS, AIWF_DIRS, FILES } from '../utils/paths.js';
5
5
 
6
- // Essential files configuration for each tool
7
- const ESSENTIAL_FILES = {
8
- claudeCode: {
9
- baseDir: TOOL_DIRS.CLAUDE_COMMANDS,
10
- requiredFiles: [
11
- 'aiwf_initialize.md',
12
- 'aiwf_do_task.md',
13
- 'aiwf_commit.md',
14
- 'aiwf_create_sprint_tasks.md',
15
- 'aiwf_code_review.md'
16
- ],
17
- minFileCount: 5,
18
- description: 'Claude Code Commands'
19
- },
20
- geminiCLI: {
21
- baseDir: TOOL_DIRS.GEMINI_PROMPTS,
22
- requiredFiles: ['README.md'],
23
- minFileCount: 1,
24
- description: 'Gemini-CLI Prompts'
25
- },
26
- cursor: {
27
- baseDir: TOOL_DIRS.CURSOR_RULES,
28
- requiredFiles: [],
29
- minFileCount: 2,
30
- fileExtension: '.mdc',
31
- description: 'Cursor IDE Rules'
32
- },
33
- windsurf: {
34
- baseDir: TOOL_DIRS.WINDSURF_RULES,
35
- requiredFiles: [],
36
- minFileCount: 2,
37
- fileExtension: '.md',
38
- description: 'Windsurf IDE Rules'
39
- },
40
- aiwf: {
41
- baseDir: AIWF_DIRS.ROOT,
42
- requiredFiles: ['CLAUDE.md', '00_PROJECT_MANIFEST.md'],
43
- minFileCount: 2,
44
- description: 'AIWF Core Structure'
6
+ // Validation constants
7
+ const VALIDATION_CONSTANTS = {
8
+ MIN_FILE_SIZE: 10,
9
+ MIN_RULE_FILE_SIZE: 50,
10
+ MIN_FILE_COUNT: {
11
+ CURSOR_MDC: 2,
12
+ WINDSURF_MD: 2,
13
+ CLAUDE_COMMANDS: 4
45
14
  }
46
15
  };
47
16
 
48
- /**
49
- * Validate tool installation based on configuration
50
- * @param {Object} config - Tool configuration
51
- * @returns {Promise<Object>} Validation results
52
- */
53
- async function validateToolInstallation(config) {
54
- const results = {
55
- success: true,
56
- description: config.description,
57
- baseDir: config.baseDir,
58
- checks: {
59
- directoryExists: false,
60
- fileCount: 0,
61
- requiredFiles: [],
62
- totalSize: 0,
63
- issues: []
64
- }
65
- };
66
-
67
- try {
68
- // 1. Check directory exists
69
- await fs.access(config.baseDir);
70
- const dirStat = await fs.stat(config.baseDir);
71
- if (dirStat.isDirectory()) {
72
- results.checks.directoryExists = true;
73
- } else {
74
- results.checks.issues.push(`${config.baseDir} is not a directory`);
75
- results.success = false;
76
- return results;
77
- }
78
-
79
- // 2. Get file list
80
- const actualFiles = await fs.readdir(config.baseDir);
81
- results.checks.fileCount = actualFiles.length;
82
-
83
- // 3. Check required files
84
- for (const requiredFile of config.requiredFiles || []) {
85
- const filePath = path.join(config.baseDir, requiredFile);
86
- const fileCheck = {
87
- file: requiredFile,
88
- exists: false,
89
- size: 0,
90
- valid: false
91
- };
92
-
93
- if (actualFiles.includes(requiredFile)) {
94
- fileCheck.exists = true;
95
- try {
96
- const stats = await fs.stat(filePath);
97
- fileCheck.size = stats.size;
98
- results.checks.totalSize += stats.size;
99
-
100
- // Validate file size (minimum 10 bytes)
101
- if (stats.size < 10) {
102
- results.checks.issues.push(
103
- `File ${requiredFile} is too small (${stats.size} bytes)`
104
- );
105
- results.success = false;
106
- } else {
107
- fileCheck.valid = true;
108
- }
109
- } catch (error) {
110
- results.checks.issues.push(
111
- `Cannot read stats for ${requiredFile}: ${error.message}`
112
- );
113
- results.success = false;
114
- }
115
- } else {
116
- results.checks.issues.push(`Required file ${requiredFile} is missing`);
117
- results.success = false;
118
- }
119
-
120
- results.checks.requiredFiles.push(fileCheck);
121
- }
122
-
123
- // 4. File extension validation (Cursor/Windsurf)
124
- if (config.fileExtension) {
125
- const matchingFiles = actualFiles.filter(f => f.endsWith(config.fileExtension));
126
- if (matchingFiles.length < config.minFileCount) {
127
- results.checks.issues.push(
128
- `Expected at least ${config.minFileCount} ${config.fileExtension} files, found ${matchingFiles.length}`
129
- );
130
- results.success = false;
131
- }
132
-
133
- // Validate file sizes for extension files
134
- for (const file of matchingFiles) {
135
- const filePath = path.join(config.baseDir, file);
136
- try {
137
- const stats = await fs.stat(filePath);
138
- results.checks.totalSize += stats.size;
139
-
140
- if (stats.size < 50) {
141
- // Rules files should be at least 50 bytes
142
- results.checks.issues.push(
143
- `Rules file ${file} is too small (${stats.size} bytes)`
144
- );
145
- results.success = false;
146
- }
147
- } catch (error) {
148
- results.checks.issues.push(`Cannot read ${file}: ${error.message}`);
149
- results.success = false;
150
- }
151
- }
152
- }
153
-
154
- // 5. Minimum file count validation
155
- if (results.checks.fileCount < config.minFileCount) {
156
- results.checks.issues.push(
157
- `Expected at least ${config.minFileCount} files, found ${results.checks.fileCount}`
158
- );
159
- results.success = false;
160
- }
161
- } catch (error) {
162
- results.success = false;
163
- results.checks.issues.push(`Validation error: ${error.message}`);
164
- }
165
-
166
- return results;
167
- }
168
-
169
- /**
170
- * Validate Claude Code installation
171
- * @returns {Promise<Object>} Validation results
172
- */
173
- export async function validateClaudeInstall() {
174
- return await validateToolInstallation(ESSENTIAL_FILES.claudeCode);
175
- }
176
-
177
- /**
178
- * Validate Gemini CLI installation
179
- * @returns {Promise<Object>} Validation results
180
- */
181
- export async function validateGeminiInstall() {
182
- return await validateToolInstallation(ESSENTIAL_FILES.geminiCLI);
183
- }
184
-
185
- /**
186
- * Validate Cursor installation
187
- * @returns {Promise<Object>} Validation results
188
- */
189
- export async function validateCursorInstall() {
190
- return await validateToolInstallation(ESSENTIAL_FILES.cursor);
191
- }
192
-
193
- /**
194
- * Validate Windsurf installation
195
- * @returns {Promise<Object>} Validation results
196
- */
197
- export async function validateWindsurfInstall() {
198
- return await validateToolInstallation(ESSENTIAL_FILES.windsurf);
199
- }
200
-
201
- /**
202
- * Validate AIWF Core installation
203
- * @returns {Promise<Object>} Validation results
204
- */
205
- export async function validateAIWFCore() {
206
- return await validateToolInstallation(ESSENTIAL_FILES.aiwf);
207
- }
208
-
209
- /**
210
- * Validate installation with detailed results
211
- * @returns {Promise<Object>} Detailed validation results
212
- */
213
- export async function validateInstallationDetailed() {
214
- const validationResults = {
215
- overall: {
216
- success: true,
217
- toolsChecked: 0,
218
- toolsPassed: 0,
219
- toolsFailed: 0,
220
- totalIssues: 0
221
- },
222
- tools: {}
223
- };
224
-
225
- // Always validate AIWF core
226
- const aiwfResult = await validateAIWFCore();
227
- validationResults.tools.aiwf = aiwfResult;
228
- validationResults.overall.toolsChecked++;
229
-
230
- if (aiwfResult.success) {
231
- validationResults.overall.toolsPassed++;
232
- } else {
233
- validationResults.overall.toolsFailed++;
234
- validationResults.overall.success = false;
235
- validationResults.overall.totalIssues += aiwfResult.checks.issues.length;
236
- }
237
-
238
- // Validate all tools
239
- const toolValidators = {
240
- claudeCode: validateClaudeInstall,
241
- geminiCLI: validateGeminiInstall,
242
- cursor: validateCursorInstall,
243
- windsurf: validateWindsurfInstall
244
- };
245
-
246
- for (const [toolName, validator] of Object.entries(toolValidators)) {
247
- const result = await validator();
248
- validationResults.tools[toolName] = result;
249
- validationResults.overall.toolsChecked++;
250
-
251
- if (result.success) {
252
- validationResults.overall.toolsPassed++;
253
- } else {
254
- validationResults.overall.toolsFailed++;
255
- validationResults.overall.success = false;
256
- validationResults.overall.totalIssues += result.checks.issues.length;
257
- }
258
- }
259
-
260
- return validationResults;
261
- }
262
-
263
- /**
264
- * Enhanced installation validation with tool selection
265
- * @param {Array|null} selectedTools - Tools to validate
266
- * @param {string} language - Language code
267
- * @returns {Promise<Object>} Validation results
268
- */
269
- export async function validateInstallationEnhanced(selectedTools = null, language = 'en') {
270
- const validationResults = {
271
- overall: {
272
- success: true,
273
- toolsChecked: 0,
274
- toolsPassed: 0,
275
- toolsFailed: 0,
276
- totalIssues: 0
277
- },
278
- tools: {}
279
- };
280
-
281
- // Always validate AIWF core
282
- const aiwfResult = await validateAIWFCore();
283
- validationResults.tools.aiwf = aiwfResult;
284
- validationResults.overall.toolsChecked++;
285
-
286
- if (aiwfResult.success) {
287
- validationResults.overall.toolsPassed++;
288
- } else {
289
- validationResults.overall.toolsFailed++;
290
- validationResults.overall.success = false;
291
- validationResults.overall.totalIssues += aiwfResult.checks.issues.length;
292
- }
293
-
294
- // Determine tools to validate
295
- const toolsToValidate = selectedTools || ['claudeCode', 'geminiCLI', 'cursor', 'windsurf'];
296
-
297
- const toolValidators = {
298
- claudeCode: validateClaudeInstall,
299
- geminiCLI: validateGeminiInstall,
300
- cursor: validateCursorInstall,
301
- windsurf: validateWindsurfInstall
302
- };
303
-
304
- for (const toolName of toolsToValidate) {
305
- if (toolValidators[toolName]) {
306
- const result = await toolValidators[toolName]();
307
- validationResults.tools[toolName] = result;
308
- validationResults.overall.toolsChecked++;
309
-
310
- if (result.success) {
311
- validationResults.overall.toolsPassed++;
312
- } else {
313
- validationResults.overall.toolsFailed++;
314
- validationResults.overall.success = false;
315
- validationResults.overall.totalIssues += result.checks.issues.length;
316
- }
317
- }
318
- }
319
-
320
- return validationResults;
321
- }
322
17
 
323
18
  /**
324
19
  * Simplified validation with clean interface
@@ -440,6 +135,16 @@ async function validateClaudeCode() {
440
135
  if (!exists) {
441
136
  return { success: false, reason: `Missing file: ${filePath}` };
442
137
  }
138
+
139
+ // Check file size
140
+ try {
141
+ const stats = await fs.stat(filePath);
142
+ if (stats.size < VALIDATION_CONSTANTS.MIN_FILE_SIZE) {
143
+ return { success: false, reason: `File ${fileName} is too small (${stats.size} bytes)` };
144
+ }
145
+ } catch (error) {
146
+ return { success: false, reason: `Cannot read file ${fileName}: ${error.message}` };
147
+ }
443
148
  }
444
149
 
445
150
  return { success: true };
@@ -485,6 +190,36 @@ async function validateCursorTool() {
485
190
  return { success: false, reason: `Missing Cursor rules directory: ${TOOL_DIRS.CURSOR_RULES}` };
486
191
  }
487
192
 
193
+ // Enhanced validation: Check file count and types
194
+ const files = await fs.readdir(TOOL_DIRS.CURSOR_RULES);
195
+ const mdcFiles = files.filter(file => file.endsWith('.mdc'));
196
+
197
+ if (mdcFiles.length < VALIDATION_CONSTANTS.MIN_FILE_COUNT.CURSOR_MDC) {
198
+ return {
199
+ success: false,
200
+ reason: `Cursor rules: Expected at least ${VALIDATION_CONSTANTS.MIN_FILE_COUNT.CURSOR_MDC} .mdc files, found ${mdcFiles.length}`
201
+ };
202
+ }
203
+
204
+ // Validate file sizes
205
+ for (const file of mdcFiles) {
206
+ const filePath = path.join(TOOL_DIRS.CURSOR_RULES, file);
207
+ try {
208
+ const stats = await fs.stat(filePath);
209
+ if (stats.size < VALIDATION_CONSTANTS.MIN_RULE_FILE_SIZE) {
210
+ return {
211
+ success: false,
212
+ reason: `Cursor rules file ${file} is too small (${stats.size} bytes, minimum ${VALIDATION_CONSTANTS.MIN_RULE_FILE_SIZE})`
213
+ };
214
+ }
215
+ } catch (error) {
216
+ return {
217
+ success: false,
218
+ reason: `Cannot read Cursor rules file ${file}: ${error.message}`
219
+ };
220
+ }
221
+ }
222
+
488
223
  return { success: true };
489
224
  } catch (error) {
490
225
  return { success: false, reason: `Cursor validation error: ${error.message}` };
@@ -505,6 +240,36 @@ async function validateWindsurfTool() {
505
240
  return { success: false, reason: `Missing Windsurf rules directory: ${TOOL_DIRS.WINDSURF_RULES}` };
506
241
  }
507
242
 
243
+ // Enhanced validation: Check file count and types
244
+ const files = await fs.readdir(TOOL_DIRS.WINDSURF_RULES);
245
+ const mdFiles = files.filter(file => file.endsWith('.md'));
246
+
247
+ if (mdFiles.length < VALIDATION_CONSTANTS.MIN_FILE_COUNT.WINDSURF_MD) {
248
+ return {
249
+ success: false,
250
+ reason: `Windsurf rules: Expected at least ${VALIDATION_CONSTANTS.MIN_FILE_COUNT.WINDSURF_MD} .md files, found ${mdFiles.length}`
251
+ };
252
+ }
253
+
254
+ // Validate file sizes
255
+ for (const file of mdFiles) {
256
+ const filePath = path.join(TOOL_DIRS.WINDSURF_RULES, file);
257
+ try {
258
+ const stats = await fs.stat(filePath);
259
+ if (stats.size < VALIDATION_CONSTANTS.MIN_RULE_FILE_SIZE) {
260
+ return {
261
+ success: false,
262
+ reason: `Windsurf rules file ${file} is too small (${stats.size} bytes, minimum ${VALIDATION_CONSTANTS.MIN_RULE_FILE_SIZE})`
263
+ };
264
+ }
265
+ } catch (error) {
266
+ return {
267
+ success: false,
268
+ reason: `Cannot read Windsurf rules file ${file}: ${error.message}`
269
+ };
270
+ }
271
+ }
272
+
508
273
  return { success: true };
509
274
  } catch (error) {
510
275
  return { success: false, reason: `Windsurf validation error: ${error.message}` };
@@ -69,7 +69,14 @@ export const messages = {
69
69
  checkingInstallation: 'AIWF 설치 상태를 확인하는 중...',
70
70
  creatingDirectories: 'AIWF 디렉토리 구조를 생성하는 중...',
71
71
  restoringBackup: '백업에서 파일을 복원하는 중...',
72
- backupRestored: '{count}개 파일이 복원되었습니다'
72
+ backupRestored: '{count}개 파일이 복원되었습니다',
73
+ checkingExistingTools: '기존 도구 디렉토리를 확인하는 중...',
74
+ foundExistingCursor: 'Cursor 규칙 파일이 발견되었습니다',
75
+ foundExistingWindsurf: 'Windsurf 규칙 파일이 발견되었습니다',
76
+ backingUpToolFiles: '기존 도구 파일을 백업하는 중...',
77
+ toolBackupCreated: '도구 파일 백업이 생성되었습니다',
78
+ installingToolRules: '도구 규칙을 설치하는 중...',
79
+ toolRulesInstalled: '도구 규칙이 성공적으로 설치되었습니다',
73
80
  },
74
81
  en: {
75
82
  welcome: '\n🎉 Welcome to AIWF!\n',
@@ -138,7 +145,14 @@ export const messages = {
138
145
  checkingInstallation: 'Checking AIWF installation status...',
139
146
  creatingDirectories: 'Creating AIWF directory structure...',
140
147
  restoringBackup: 'Restoring files from backup...',
141
- backupRestored: '{count} files restored'
148
+ backupRestored: '{count} files restored',
149
+ checkingExistingTools: 'Checking existing tool directories...',
150
+ foundExistingCursor: 'Found existing Cursor rules',
151
+ foundExistingWindsurf: 'Found existing Windsurf rules',
152
+ backingUpToolFiles: 'Backing up existing tool files...',
153
+ toolBackupCreated: 'Tool files backup created',
154
+ installingToolRules: 'Installing tool rules...',
155
+ toolRulesInstalled: 'Tool rules installed successfully',
142
156
  }
143
157
  };
144
158
 
@@ -56,11 +56,6 @@ export const FILES = {
56
56
  }
57
57
  };
58
58
 
59
- // Temporary directories
60
- export const TEMP_DIRS = {
61
- RULES_GLOBAL: path.join(AIWF_DIRS.ROOT, '_tmp_rules_global'),
62
- RULES_MANUAL: path.join(AIWF_DIRS.ROOT, '_tmp_rules_manual')
63
- };
64
59
 
65
60
  // User cache directory (for global cache)
66
61
  export const USER_CACHE_DIR = path.join(
@@ -103,18 +98,6 @@ export function getToolDirectory(tool) {
103
98
  return mapping[tool] || null;
104
99
  }
105
100
 
106
- // Get backup directory name with timestamp
107
- export function getBackupDirName() {
108
- const now = new Date();
109
- const pad = n => n.toString().padStart(2, '0');
110
- const y = now.getFullYear();
111
- const m = pad(now.getMonth() + 1);
112
- const d = pad(now.getDate());
113
- const h = pad(now.getHours());
114
- const min = pad(now.getMinutes());
115
- const s = pad(now.getSeconds());
116
- return path.join(AIWF_DIRS.ROOT, `backup_${y}${m}${d}_${h}${min}${s}`);
117
- }
118
101
 
119
102
  // Export all for convenience
120
103
  export default {
@@ -122,10 +105,8 @@ export default {
122
105
  AIWF_DIRS,
123
106
  TOOL_DIRS,
124
107
  FILES,
125
- TEMP_DIRS,
126
108
  USER_CACHE_DIR,
127
109
  getAllAiwfDirectories,
128
110
  getAllClaudeMdFiles,
129
- getToolDirectory,
130
- getBackupDirName
111
+ getToolDirectory
131
112
  };