aiwf 0.3.3 → 0.3.4
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/.aiwf/config/language.json +1 -1
- package/.aiwf/sessions/session_1752877859489_1t7n2fhdb.json +7 -0
- package/.aiwf/sessions/session_1752877859515_cwdv211ry.json +7 -0
- package/.aiwf/sessions/session_1752877860664_huziadmbo.json +7 -0
- package/.aiwf/sessions/session_1752877860666_kyr5u1np6.json +7 -0
- package/.aiwf/sessions/session_1752877861848_9yinepw3t.json +7 -0
- package/.aiwf/sessions/session_1752877861874_y32sputuc.json +7 -0
- package/.aiwf/sessions/session_1752877862685_ivxbl5rhz.json +7 -0
- package/.aiwf/sessions/session_1752877862820_wnq4glvxi.json +7 -0
- package/.aiwf/sessions/session_1752877864632_3ebnpaelb.json +7 -0
- package/.aiwf/sessions/session_1752877864879_b9zgjcb7d.json +7 -0
- package/.aiwf/sessions/session_1752877865533_x14m1vodi.json +7 -0
- package/.aiwf/sessions/session_1752877865562_gkoh19n7d.json +7 -0
- package/.aiwf/sessions/session_1752877866333_wp8qpdcoh.json +7 -0
- package/.aiwf/sessions/session_1752877866509_dawntfpab.json +7 -0
- package/.aiwf/sessions/session_1752877866890_plc5vfucl.json +7 -0
- package/.aiwf/sessions/session_1752877866895_ga8hmq3oi.json +7 -0
- package/.aiwf/sessions/session_1752877867761_ayjp4v5f5.json +7 -0
- package/.aiwf/sessions/session_1752877867892_lfz6jy252.json +7 -0
- package/.aiwf/sessions/session_1752877868139_7bqd9ubli.json +7 -0
- package/.aiwf/sessions/session_1752877868162_zq3ofjbsh.json +7 -0
- package/.aiwf/sessions/session_1752877869054_7rlgm9jt6.json +7 -0
- package/.aiwf/sessions/session_1752877869477_ax3nhbxzr.json +7 -0
- package/.aiwf/sessions/session_1752877870095_6hgp4vl1e.json +7 -0
- package/.aiwf/sessions/session_1752877870381_qfcrdg4li.json +7 -0
- package/.aiwf/sessions/session_1752877871895_pgd5vllhz.json +7 -0
- package/.aiwf/sessions/session_1752877872199_jbhv2g2da.json +7 -0
- package/.aiwf/sessions/session_1752877872680_x04wpffu6.json +7 -0
- package/.aiwf/sessions/session_1752877872895_3y4vbeauo.json +7 -0
- package/.aiwf/sessions/session_1752877873178_3maolzc6b.json +7 -0
- package/.aiwf/sessions/session_1752877873180_ytm83j07f.json +7 -0
- package/.aiwf/sessions/session_1752877874217_dp56c9t42.json +7 -0
- package/.aiwf/sessions/session_1752877874415_epwp1g1og.json +7 -0
- package/.aiwf/sessions/session_1752877874454_heicyuryb.json +7 -0
- package/.aiwf/sessions/session_1752877874869_1c2illeu5.json +7 -0
- package/.aiwf/sessions/session_1752877874879_k1vo4vxyx.json +7 -0
- package/.aiwf/sessions/session_1752877874913_bwgyiz9bp.json +7 -0
- package/.aiwf/sessions/session_1752877876218_44807kztz.json +7 -0
- package/.aiwf/sessions/session_1752877876515_qkhjv5kpw.json +7 -0
- package/.aiwf/sessions/session_1752877876543_p605aud1m.json +7 -0
- package/.aiwf/sessions/session_1752877876819_l65w7ifck.json +7 -0
- package/.aiwf/sessions/session_1752877876867_xrhzss4we.json +7 -0
- package/.aiwf/sessions/session_1752877876872_u7aso54qb.json +7 -0
- package/.aiwf/sessions/session_1752877877705_telnfg4o6.json +7 -0
- package/.aiwf/sessions/session_1752877878285_6s45im2ib.json +7 -0
- package/.aiwf/sessions/session_1752877878506_v0dvguqhz.json +7 -0
- package/.aiwf/sessions/session_1752877878581_3ri3yc66a.json +7 -0
- package/.aiwf/sessions/session_1752877878756_a82x9s2pd.json +7 -0
- package/.aiwf/sessions/session_1752877878886_20pjsfour.json +7 -0
- package/.aiwf/sessions/session_1752877880266_fm1ydjw0g.json +7 -0
- package/.aiwf/sessions/session_1752877881149_xbat4z03d.json +7 -0
- package/.aiwf/sessions/session_1752877881551_qy5sic9qs.json +7 -0
- package/.aiwf/sessions/session_1752877881554_24ugd96wu.json +7 -0
- package/.aiwf/sessions/session_1752877881797_e9mm25uh0.json +7 -0
- package/.aiwf/sessions/session_1752877881822_bxvjj8c9g.json +7 -0
- package/.aiwf/sessions/session_1752877881890_jv7l6jve3.json +7 -0
- package/.aiwf/sessions/session_1752877881991_f0qugdtcq.json +7 -0
- package/.aiwf/sessions/session_1752877883387_0bvw9rvnx.json +7 -0
- package/.aiwf/sessions/session_1752877883820_q6g2md2up.json +7 -0
- package/.aiwf/sessions/session_1752877884393_sqr7trvjl.json +7 -0
- package/.aiwf/sessions/session_1752877884500_whtypmzlf.json +7 -0
- package/.aiwf/sessions/session_1752877884645_y9mvvxli1.json +7 -0
- package/.aiwf/sessions/session_1752877884733_hk46eenel.json +7 -0
- package/.aiwf/sessions/session_1752877884738_c62j6214r.json +7 -0
- package/.aiwf/sessions/session_1752877884771_temhxehoz.json +7 -0
- package/.aiwf/sessions/session_1752877885643_6v5fcktc7.json +7 -0
- package/.aiwf/sessions/session_1752877886426_ee8di2fqh.json +7 -0
- package/.aiwf/sessions/session_1752877886549_3xj6tdoew.json +7 -0
- package/.aiwf/sessions/session_1752877886604_aj80yyfco.json +7 -0
- package/.aiwf/sessions/session_1752877886702_2b8841vyg.json +7 -0
- package/.aiwf/sessions/session_1752877886712_ntqc8b8bz.json +7 -0
- package/.aiwf/sessions/session_1752877886968_946m8tbw6.json +7 -0
- package/.aiwf/sessions/session_1752877887008_wg6zfg8dn.json +7 -0
- package/.aiwf/sessions/session_1752877888479_ciiu2qiqs.json +7 -0
- package/.aiwf/sessions/session_1752877889330_ravg8l66k.json +7 -0
- package/.aiwf/sessions/session_1752877889542_7zepkhjkc.json +7 -0
- package/.aiwf/sessions/session_1752877889638_ev4htqiu0.json +7 -0
- package/.aiwf/sessions/session_1752877889956_szphy2qma.json +7 -0
- package/.aiwf/sessions/session_1752877890220_o6aaa57tt.json +7 -0
- package/.aiwf/sessions/session_1752877890527_v2loy5vu1.json +7 -0
- package/.aiwf/sessions/session_1752877890572_cwq8fiuma.json +7 -0
- package/.aiwf/sessions/session_1752877892200_sgcwq1alt.json +7 -0
- package/.aiwf/sessions/session_1752877892268_v8kexpbc5.json +7 -0
- package/.aiwf/sessions/session_1752877892384_1gbbgrp4y.json +7 -0
- package/.aiwf/sessions/session_1752877893568_9p2n5p31a.json +7 -0
- package/.aiwf/sessions/session_1752877893727_un34a5tvp.json +7 -0
- package/.aiwf/sessions/session_1752877893778_01j79sw9i.json +7 -0
- package/.aiwf/sessions/session_1752877894053_jrhba6wko.json +7 -0
- package/.aiwf/sessions/session_1752877894082_x3bghawox.json +7 -0
- package/.aiwf/sessions/session_1752877894500_iytiav2ku.json +7 -0
- package/.aiwf/sessions/session_1752877894667_ucn0gc2ge.json +7 -0
- package/.aiwf/test-results-s02.json +4 -4
- package/.claude/commands/aiwf/aiwf_changelog.md +82 -0
- package/.claude/commands/aiwf/aiwf_code_review.md +88 -0
- package/.claude/commands/aiwf/aiwf_commit.md +160 -0
- package/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +53 -0
- package/.claude/commands/aiwf/aiwf_compress_context.md +116 -0
- package/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +48 -0
- package/.claude/commands/aiwf/aiwf_compress_stats.md +79 -0
- package/.claude/commands/aiwf/aiwf_compress_with_persona.md +51 -0
- package/.claude/commands/aiwf/aiwf_create_feature_ledger.md +54 -0
- package/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
- package/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
- package/.claude/commands/aiwf/aiwf_create_prd.md +280 -0
- package/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
- package/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
- package/.claude/commands/aiwf/aiwf_default_mode.md +44 -0
- package/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
- package/.claude/commands/aiwf/aiwf_do_task.md +109 -0
- package/.claude/commands/aiwf/aiwf_docs.md +225 -0
- package/.claude/commands/aiwf/aiwf_evaluate.md +92 -0
- package/.claude/commands/aiwf/aiwf_get_feature_details.md +78 -0
- package/.claude/commands/aiwf/aiwf_infinite.md +202 -0
- package/.claude/commands/aiwf/aiwf_initialize.md +134 -0
- package/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
- package/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +57 -0
- package/.claude/commands/aiwf/aiwf_list_features.md +54 -0
- package/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
- package/.claude/commands/aiwf/aiwf_persona_architect.md +48 -0
- package/.claude/commands/aiwf/aiwf_persona_backend.md +37 -0
- package/.claude/commands/aiwf/aiwf_persona_data_analyst.md +37 -0
- package/.claude/commands/aiwf/aiwf_persona_frontend.md +37 -0
- package/.claude/commands/aiwf/aiwf_persona_security.md +48 -0
- package/.claude/commands/aiwf/aiwf_persona_status.md +50 -0
- package/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
- package/.claude/commands/aiwf/aiwf_prime.md +9 -0
- package/.claude/commands/aiwf/aiwf_project_review.md +261 -0
- package/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
- package/.claude/commands/aiwf/aiwf_test.md +124 -0
- package/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
- package/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +210 -0
- package/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
- package/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
- package/.claude/commands/aiwf/aiwf_ultrathink_general.md +305 -0
- package/.claude/commands/aiwf/aiwf_update_docs_kr.md +25 -0
- package/.claude/commands/aiwf/aiwf_update_feature_status.md +53 -0
- package/.claude/commands/aiwf/aiwf_yolo.md +300 -0
- package/.eslintrc.json +26 -0
- package/.prettierrc +10 -0
- package/CLAUDE.md +154 -54
- package/README.ko.md +25 -0
- package/README.md +25 -0
- package/TEST_RECOVERY_PLAN.md +68 -0
- package/docs/CLEANUP_RECOMMENDATIONS.md +86 -0
- package/docs/CLI_USAGE_GUIDE.md +479 -0
- package/package.json +9 -9
- package/scripts/update-feature-status.js +70 -0
- package/{lib → src/cli}/cache-cli.js +4 -4
- package/src/cli/index.js +165 -0
- package/{language-cli.js → src/cli/language-cli.js} +11 -4
- package/src/commands/cache-templates.js +209 -0
- package/src/commands/create-offline.js +86 -0
- package/src/commands/create-project.js +151 -0
- package/src/lib/backup-manager.js +270 -0
- package/src/lib/file-downloader.js +208 -0
- package/src/lib/installer.js +676 -0
- package/src/lib/rollback-manager.js +418 -0
- package/src/lib/validator.js +561 -0
- package/src/utils/git-integration.js +173 -0
- package/{language-utils.js → src/utils/language-utils.js} +2 -1
- package/src/utils/messages.js +136 -0
- package/src/utils/paths.js +130 -0
- package/tests/application.test.js +2 -2
- package/tests/installer.test.js +2 -2
- package/tests/integration/ai-persona-system.test.js +4 -4
- package/tests/integration/test-persona-system/docs/README.md +1 -0
- package/tests/integration/test-persona-system/package.json +1 -0
- package/tests/integration/test-persona-system/src/auth.js +1 -0
- package/tests/integration/test-persona-system/src/index.js +1 -0
- package/tests/integration/test-persona-system/test/test.js +1 -0
- package/tests/language-utils.test.js +1 -1
- package/tests/temp_invalid_json/invalid.json +1 -0
- package/tests/unit/ai-persona-manager.test.js +7 -7
- package/.aiwf/05_DOCUMENTATION/performance-report.md +0 -33
- package/.aiwf/test-cache/86c7f9e80251abb8bedd58953ab0d1779e46b4f43ea33b63bffdb778106373b0.json +0 -6
- package/.aiwf/test-cache/9e252a90b80fce0585d7ed0646e7a9b4343e109f661f42a3ae678ba1ec5e22c7.json +0 -7
- package/.aiwf/token-data/alerts.json +0 -293
- package/.aiwf/token-data/monitor-config.json +0 -12
- package/CHANGELOG_M02.md +0 -52
- package/claude-code/aiwf/ko/templates/web-app/template/package-lock.json +0 -10022
- package/claude-code/aiwf/ko/token-data/alerts.json +0 -293
- package/claude-code/aiwf/ko/token-data/monitor-config.json +0 -12
- package/docs/m02-dod-checklist.md +0 -125
- package/docs/m02-gap-analysis.md +0 -214
- package/docs/m02-milestone-completion-report.md +0 -186
- package/docs/m02-milestone-executive-summary.md +0 -68
- package/index.js +0 -1951
- package/lib/cache-integration.js +0 -401
- package/lib/file-batch-processor.js +0 -401
- package/lib/performance-benchmark.js +0 -621
- package/lib/template-manager.js +0 -310
- package/scripts/validate-commands.cjs +0 -338
- /package/{lib → src/lib}/ai-persona-manager.js +0 -0
- /package/{lib → src/lib}/cache-system.js +0 -0
- /package/{lib → src/lib}/context-engine.js +0 -0
- /package/{lib → src/lib}/memory-profiler.js +0 -0
- /package/{lib → src/lib}/metrics-collector.js +0 -0
- /package/{lib → src/lib}/offline-detector.js +0 -0
- /package/{lib → src/lib}/task-analyzer.js +0 -0
- /package/{lib → src/lib}/template-cache-system.js +0 -0
- /package/{lib → src/lib}/template-downloader.js +0 -0
- /package/{lib → src/lib}/template-version-manager.js +0 -0
- /package/{lib → src/lib}/token-optimizer.js +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import {
|
|
6
|
+
TOOL_DIRS,
|
|
7
|
+
AIWF_DIRS,
|
|
8
|
+
FILES,
|
|
9
|
+
getBackupDirName as getBackupDirNamePath
|
|
10
|
+
} from '../utils/paths.js';
|
|
11
|
+
|
|
12
|
+
let BACKUP_DIR = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate backup directory name with timestamp
|
|
16
|
+
* @returns {string} Backup directory name
|
|
17
|
+
*/
|
|
18
|
+
function getBackupDirName() {
|
|
19
|
+
return getBackupDirNamePath();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Backup a single file with proper naming convention
|
|
24
|
+
* @param {string} filePath - File to backup
|
|
25
|
+
* @param {string} backupDir - Backup directory path
|
|
26
|
+
* @returns {Promise<string|null>} Backup file path or null
|
|
27
|
+
*/
|
|
28
|
+
async function backupFile(filePath, backupDir) {
|
|
29
|
+
try {
|
|
30
|
+
const exists = await fs.access(filePath).then(() => true).catch(() => false);
|
|
31
|
+
if (exists) {
|
|
32
|
+
await fs.mkdir(backupDir, { recursive: true });
|
|
33
|
+
const backupPath = `${filePath}.bak`;
|
|
34
|
+
await fs.copyFile(filePath, backupPath);
|
|
35
|
+
|
|
36
|
+
// Tool-specific backup file naming rules
|
|
37
|
+
let bakFileName;
|
|
38
|
+
if (filePath.startsWith(TOOL_DIRS.CLAUDE_COMMANDS + '/')) {
|
|
39
|
+
// Claude Code command files
|
|
40
|
+
const relativePath = filePath.substring(TOOL_DIRS.CLAUDE_COMMANDS.length + 1);
|
|
41
|
+
bakFileName = `claude-commands-aiwf-${relativePath.replace(/\//g, '-')}.bak`;
|
|
42
|
+
} else if (filePath.startsWith(TOOL_DIRS.GEMINI_PROMPTS + '/')) {
|
|
43
|
+
// Gemini CLI prompt files
|
|
44
|
+
const relativePath = filePath.substring(TOOL_DIRS.GEMINI_PROMPTS.length + 1);
|
|
45
|
+
bakFileName = `gemini-prompts-aiwf-${relativePath.replace(/\//g, '-')}.bak`;
|
|
46
|
+
} else if (filePath.startsWith(TOOL_DIRS.CURSOR_RULES + '/')) {
|
|
47
|
+
// Cursor rule files
|
|
48
|
+
const relativePath = filePath.substring(TOOL_DIRS.CURSOR_RULES.length + 1);
|
|
49
|
+
bakFileName = `cursor-rules-${relativePath.replace(/\//g, '-')}.bak`;
|
|
50
|
+
} else if (filePath.startsWith(TOOL_DIRS.WINDSURF_RULES + '/')) {
|
|
51
|
+
// Windsurf rule files
|
|
52
|
+
const relativePath = filePath.substring(TOOL_DIRS.WINDSURF_RULES.length + 1);
|
|
53
|
+
bakFileName = `windsurf-rules-${relativePath.replace(/\//g, '-')}.bak`;
|
|
54
|
+
} else if (filePath.startsWith(AIWF_DIRS.ROOT + '/')) {
|
|
55
|
+
// AIWF core files
|
|
56
|
+
const relativePath = filePath.substring(AIWF_DIRS.ROOT.length + 1);
|
|
57
|
+
bakFileName = `aiwf-${relativePath.replace(/\//g, '-')}.bak`;
|
|
58
|
+
} else {
|
|
59
|
+
// Other files
|
|
60
|
+
bakFileName = path.basename(filePath) + '.bak';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const destPath = path.join(backupDir, bakFileName);
|
|
64
|
+
await fs.rename(backupPath, destPath);
|
|
65
|
+
return destPath;
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// Backup failed, but continue
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Backup commands and documentation files
|
|
75
|
+
* @param {Object} messages - Localized messages
|
|
76
|
+
* @param {string} [commandsDir] - Commands directory
|
|
77
|
+
* @returns {Promise<Array<string>>} Array of backed up file paths
|
|
78
|
+
*/
|
|
79
|
+
async function backupCommandsAndDocs(messages, commandsDir = TOOL_DIRS.CLAUDE_COMMANDS) {
|
|
80
|
+
if (!BACKUP_DIR) BACKUP_DIR = getBackupDirName();
|
|
81
|
+
const spinner = ora(messages.backingUp).start();
|
|
82
|
+
const backedUpFiles = [];
|
|
83
|
+
await fs.mkdir(BACKUP_DIR, { recursive: true });
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// Files that will be updated and need backup
|
|
87
|
+
const filesToBackup = Object.values(FILES.CLAUDE_MD);
|
|
88
|
+
|
|
89
|
+
// Backup CLAUDE.md files
|
|
90
|
+
for (const file of filesToBackup) {
|
|
91
|
+
const backupPath = await backupFile(file, BACKUP_DIR);
|
|
92
|
+
if (backupPath) {
|
|
93
|
+
backedUpFiles.push(backupPath);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Backup all command files
|
|
98
|
+
const commandsExist = await fs.access(commandsDir).then(() => true).catch(() => false);
|
|
99
|
+
if (commandsExist) {
|
|
100
|
+
try {
|
|
101
|
+
const commandFiles = await fs.readdir(commandsDir);
|
|
102
|
+
for (const file of commandFiles) {
|
|
103
|
+
const filePath = path.join(commandsDir, file);
|
|
104
|
+
const stat = await fs.stat(filePath);
|
|
105
|
+
if (stat.isFile() && file.endsWith('.md')) {
|
|
106
|
+
const backupPath = await backupFile(filePath, BACKUP_DIR);
|
|
107
|
+
if (backupPath) {
|
|
108
|
+
backedUpFiles.push(backupPath);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
// Commands directory might be empty or have issues
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Backup IDE rule files (Cursor)
|
|
118
|
+
const cursorRulesExist = await fs.access(TOOL_DIRS.CURSOR_RULES).then(() => true).catch(() => false);
|
|
119
|
+
if (cursorRulesExist) {
|
|
120
|
+
try {
|
|
121
|
+
const cursorFiles = await fs.readdir(TOOL_DIRS.CURSOR_RULES);
|
|
122
|
+
for (const file of cursorFiles) {
|
|
123
|
+
if (file.endsWith('.mdc')) {
|
|
124
|
+
const filePath = path.join(TOOL_DIRS.CURSOR_RULES, file);
|
|
125
|
+
const backupPath = await backupFile(filePath, BACKUP_DIR);
|
|
126
|
+
if (backupPath) {
|
|
127
|
+
backedUpFiles.push(backupPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
// Cursor rules directory might have issues
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Backup IDE rule files (Windsurf)
|
|
137
|
+
const windsurfRulesExist = await fs.access(TOOL_DIRS.WINDSURF_RULES).then(() => true).catch(() => false);
|
|
138
|
+
if (windsurfRulesExist) {
|
|
139
|
+
try {
|
|
140
|
+
const windsurfFiles = await fs.readdir(TOOL_DIRS.WINDSURF_RULES);
|
|
141
|
+
for (const file of windsurfFiles) {
|
|
142
|
+
if (file.endsWith('.md')) {
|
|
143
|
+
const filePath = path.join(TOOL_DIRS.WINDSURF_RULES, file);
|
|
144
|
+
const backupPath = await backupFile(filePath, BACKUP_DIR);
|
|
145
|
+
if (backupPath) {
|
|
146
|
+
backedUpFiles.push(backupPath);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
// Windsurf rules directory might have issues
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (backedUpFiles.length > 0) {
|
|
156
|
+
spinner.succeed(chalk.green(messages.backupComplete.replace('{count}', backedUpFiles.length)));
|
|
157
|
+
} else {
|
|
158
|
+
spinner.succeed(chalk.gray(messages.noFilesToBackup));
|
|
159
|
+
}
|
|
160
|
+
return backedUpFiles;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
spinner.fail(chalk.red(messages.backupFailed));
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Restore files from backup
|
|
169
|
+
* @param {Object} spinner - Ora spinner instance
|
|
170
|
+
* @param {Object} messages - Localized messages
|
|
171
|
+
* @returns {Promise<void>}
|
|
172
|
+
*/
|
|
173
|
+
async function restoreFromBackup(spinner, messages) {
|
|
174
|
+
if (!BACKUP_DIR) return;
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
spinner.text = messages.restoringBackup;
|
|
178
|
+
|
|
179
|
+
// Get all backup files
|
|
180
|
+
const backupFiles = await fs.readdir(BACKUP_DIR);
|
|
181
|
+
let restoredCount = 0;
|
|
182
|
+
|
|
183
|
+
for (const bakFile of backupFiles) {
|
|
184
|
+
if (bakFile.endsWith('.bak')) {
|
|
185
|
+
const backupPath = path.join(BACKUP_DIR, bakFile);
|
|
186
|
+
let originalPath;
|
|
187
|
+
|
|
188
|
+
// Determine original path from backup filename
|
|
189
|
+
if (bakFile.startsWith('claude-commands-aiwf-')) {
|
|
190
|
+
const relativePath = bakFile
|
|
191
|
+
.replace('claude-commands-aiwf-', '')
|
|
192
|
+
.replace('.bak', '')
|
|
193
|
+
.replace(/-/g, '/');
|
|
194
|
+
originalPath = path.join(TOOL_DIRS.CLAUDE_COMMANDS, relativePath);
|
|
195
|
+
} else if (bakFile.startsWith('gemini-prompts-aiwf-')) {
|
|
196
|
+
const relativePath = bakFile
|
|
197
|
+
.replace('gemini-prompts-aiwf-', '')
|
|
198
|
+
.replace('.bak', '')
|
|
199
|
+
.replace(/-/g, '/');
|
|
200
|
+
originalPath = path.join(TOOL_DIRS.GEMINI_PROMPTS, relativePath);
|
|
201
|
+
} else if (bakFile.startsWith('cursor-rules-')) {
|
|
202
|
+
const relativePath = bakFile
|
|
203
|
+
.replace('cursor-rules-', '')
|
|
204
|
+
.replace('.bak', '')
|
|
205
|
+
.replace(/-/g, '/');
|
|
206
|
+
originalPath = path.join(TOOL_DIRS.CURSOR_RULES, relativePath);
|
|
207
|
+
} else if (bakFile.startsWith('windsurf-rules-')) {
|
|
208
|
+
const relativePath = bakFile
|
|
209
|
+
.replace('windsurf-rules-', '')
|
|
210
|
+
.replace('.bak', '')
|
|
211
|
+
.replace(/-/g, '/');
|
|
212
|
+
originalPath = path.join(TOOL_DIRS.WINDSURF_RULES, relativePath);
|
|
213
|
+
} else if (bakFile.startsWith('aiwf-')) {
|
|
214
|
+
const relativePath = bakFile
|
|
215
|
+
.replace('aiwf-', '')
|
|
216
|
+
.replace('.bak', '')
|
|
217
|
+
.replace(/-/g, '/');
|
|
218
|
+
originalPath = path.join(AIWF_DIRS.ROOT, relativePath);
|
|
219
|
+
} else {
|
|
220
|
+
// Simple filename.ext.bak format
|
|
221
|
+
originalPath = bakFile.replace('.bak', '');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
// Ensure directory exists
|
|
226
|
+
await fs.mkdir(path.dirname(originalPath), { recursive: true });
|
|
227
|
+
// Copy backup to original location
|
|
228
|
+
await fs.copyFile(backupPath, originalPath);
|
|
229
|
+
restoredCount++;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
// Individual file restore failed, continue with others
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Clean up backup directory
|
|
237
|
+
await fs.rm(BACKUP_DIR, { recursive: true, force: true });
|
|
238
|
+
BACKUP_DIR = null;
|
|
239
|
+
|
|
240
|
+
spinner.text = messages.backupRestored.replace('{count}', restoredCount);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
spinner.fail(chalk.red(messages.restoreFailed));
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get current backup directory
|
|
249
|
+
* @returns {string|null} Current backup directory path
|
|
250
|
+
*/
|
|
251
|
+
function getCurrentBackupDir() {
|
|
252
|
+
return BACKUP_DIR;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Set backup directory
|
|
257
|
+
* @param {string} dir - Backup directory path
|
|
258
|
+
*/
|
|
259
|
+
function setBackupDir(dir) {
|
|
260
|
+
BACKUP_DIR = dir;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export {
|
|
264
|
+
getBackupDirName,
|
|
265
|
+
backupFile,
|
|
266
|
+
backupCommandsAndDocs,
|
|
267
|
+
restoreFromBackup,
|
|
268
|
+
getCurrentBackupDir,
|
|
269
|
+
setBackupDir
|
|
270
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import https from 'https';
|
|
2
|
+
import { createWriteStream } from 'fs';
|
|
3
|
+
import { pipeline } from 'stream/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
const GITHUB_RAW_URL = 'https://raw.githubusercontent.com/moonklabs/aiwf/main';
|
|
7
|
+
const GITHUB_CONTENT_PREFIX = 'claude-code/aiwf';
|
|
8
|
+
|
|
9
|
+
// Pre-defined list of command files
|
|
10
|
+
const COMMAND_FILES = [
|
|
11
|
+
'aiwf_changelog.md',
|
|
12
|
+
'aiwf_code_review.md',
|
|
13
|
+
'aiwf_commit.md',
|
|
14
|
+
'aiwf_compress_aggressive_persona.md',
|
|
15
|
+
'aiwf_compress_context.md',
|
|
16
|
+
'aiwf_compress_minimal_persona.md',
|
|
17
|
+
'aiwf_compress_stats.md',
|
|
18
|
+
'aiwf_compress_with_persona.md',
|
|
19
|
+
'aiwf_create_feature_ledger.md',
|
|
20
|
+
'aiwf_create_general_task.md',
|
|
21
|
+
'aiwf_create_milestone_plan.md',
|
|
22
|
+
'aiwf_create_prd.md',
|
|
23
|
+
'aiwf_create_sprint_tasks.md',
|
|
24
|
+
'aiwf_create_sprints_from_milestone.md',
|
|
25
|
+
'aiwf_default_mode.md',
|
|
26
|
+
'aiwf_discuss_review.md',
|
|
27
|
+
'aiwf_do_task.md',
|
|
28
|
+
'aiwf_docs.md',
|
|
29
|
+
'aiwf_evaluate.md',
|
|
30
|
+
'aiwf_get_feature_details.md',
|
|
31
|
+
'aiwf_infinite.md',
|
|
32
|
+
'aiwf_initialize.md',
|
|
33
|
+
'aiwf_issue_create.md',
|
|
34
|
+
'aiwf_link_feature_to_milestone.md',
|
|
35
|
+
'aiwf_list_features.md',
|
|
36
|
+
'aiwf_mermaid.md',
|
|
37
|
+
'aiwf_persona_architect.md',
|
|
38
|
+
'aiwf_persona_backend.md',
|
|
39
|
+
'aiwf_persona_data_analyst.md',
|
|
40
|
+
'aiwf_persona_frontend.md',
|
|
41
|
+
'aiwf_persona_security.md',
|
|
42
|
+
'aiwf_persona_status.md',
|
|
43
|
+
'aiwf_pr_create.md',
|
|
44
|
+
'aiwf_prime.md',
|
|
45
|
+
'aiwf_project_review.md',
|
|
46
|
+
'aiwf_switch_language.md',
|
|
47
|
+
'aiwf_test.md',
|
|
48
|
+
'aiwf_testing_review.md',
|
|
49
|
+
'aiwf_tm-run-all-subtask.md',
|
|
50
|
+
'aiwf_ultrathink_code_advanced.md',
|
|
51
|
+
'aiwf_ultrathink_code_basic.md',
|
|
52
|
+
'aiwf_ultrathink_general.md',
|
|
53
|
+
'aiwf_update_docs_kr.md',
|
|
54
|
+
'aiwf_update_feature_status.md',
|
|
55
|
+
'aiwf_yolo.md'
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// Pre-defined list of global rules files
|
|
59
|
+
const GLOBAL_RULES_FILES = [
|
|
60
|
+
'code-style-guide.md',
|
|
61
|
+
'coding-principles.md',
|
|
62
|
+
'development-process.md',
|
|
63
|
+
'global-rules.md'
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
// Pre-defined list of manual rules files
|
|
67
|
+
const MANUAL_RULES_FILES = [
|
|
68
|
+
'generate-plan-docs.md'
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// Pre-defined list of template files (these might not exist in the repo)
|
|
72
|
+
const TEMPLATE_FILES = [
|
|
73
|
+
// Add template files here when they are available
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// Pre-defined list of prompt files (these might not exist in the repo)
|
|
77
|
+
const PROMPT_FILES = [
|
|
78
|
+
// Add prompt files here when they are available
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Fetch content from URL
|
|
83
|
+
* @param {string} url - The URL to fetch from
|
|
84
|
+
* @returns {Promise<string>} The response content
|
|
85
|
+
*/
|
|
86
|
+
async function fetchContent(url) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
https.get(url, { headers: { 'User-Agent': 'aiwf' } }, (res) => {
|
|
89
|
+
let data = '';
|
|
90
|
+
res.on('data', chunk => data += chunk);
|
|
91
|
+
res.on('end', () => {
|
|
92
|
+
if (res.statusCode === 200) {
|
|
93
|
+
resolve(data);
|
|
94
|
+
} else {
|
|
95
|
+
reject(new Error(`Failed to fetch ${url}: ${res.statusCode}`));
|
|
96
|
+
}
|
|
97
|
+
res.destroy();
|
|
98
|
+
});
|
|
99
|
+
}).on('error', (err) => {
|
|
100
|
+
reject(err);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Download a file from URL to destination path
|
|
107
|
+
* @param {string} url - The URL to download from
|
|
108
|
+
* @param {string} destPath - The destination file path
|
|
109
|
+
* @returns {Promise<void>}
|
|
110
|
+
*/
|
|
111
|
+
async function downloadFile(url, destPath) {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
https.get(url, { headers: { 'User-Agent': 'aiwf' } }, (response) => {
|
|
114
|
+
if (response.statusCode !== 200) {
|
|
115
|
+
response.destroy();
|
|
116
|
+
reject(new Error(`Failed to download ${url}: ${response.statusCode}`));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const file = createWriteStream(destPath);
|
|
120
|
+
pipeline(response, file)
|
|
121
|
+
.then(() => {
|
|
122
|
+
resolve();
|
|
123
|
+
})
|
|
124
|
+
.catch((error) => {
|
|
125
|
+
reject(error);
|
|
126
|
+
});
|
|
127
|
+
}).on('error', (err) => {
|
|
128
|
+
reject(err);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Download files from a predefined list
|
|
135
|
+
* @param {string} githubPath - GitHub repository path
|
|
136
|
+
* @param {string} localPath - Local destination path
|
|
137
|
+
* @param {Array<string>} fileList - List of files to download
|
|
138
|
+
* @param {Object} spinner - Ora spinner instance
|
|
139
|
+
* @param {Object} messages - Localized messages
|
|
140
|
+
* @returns {Promise<void>}
|
|
141
|
+
*/
|
|
142
|
+
async function downloadFileList(githubPath, localPath, fileList, spinner, messages) {
|
|
143
|
+
for (const fileName of fileList) {
|
|
144
|
+
try {
|
|
145
|
+
const destPath = path.join(localPath, fileName);
|
|
146
|
+
const downloadUrl = `${GITHUB_RAW_URL}/${githubPath}/${fileName}`;
|
|
147
|
+
|
|
148
|
+
await downloadFile(downloadUrl, destPath);
|
|
149
|
+
if (spinner) {
|
|
150
|
+
spinner.text = `${messages.downloading}: ${fileName}`;
|
|
151
|
+
}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
// Skip files that don't exist, but log the error
|
|
154
|
+
if (spinner) {
|
|
155
|
+
spinner.text = `${messages.skipping || 'Skipping'}: ${fileName}`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Download directory contents using predefined file lists
|
|
163
|
+
* @param {string} githubPath - GitHub repository path
|
|
164
|
+
* @param {string} localPath - Local destination path
|
|
165
|
+
* @param {Object} spinner - Ora spinner instance
|
|
166
|
+
* @param {Object} messages - Localized messages
|
|
167
|
+
* @param {string} [selectedLanguage='en'] - Selected language
|
|
168
|
+
* @returns {Promise<void>}
|
|
169
|
+
*/
|
|
170
|
+
async function downloadDirectory(githubPath, localPath, spinner, messages, selectedLanguage = 'en') {
|
|
171
|
+
try {
|
|
172
|
+
// Determine which file list to use based on the path
|
|
173
|
+
let fileList = [];
|
|
174
|
+
|
|
175
|
+
if (githubPath.includes('.claude/commands/aiwf')) {
|
|
176
|
+
fileList = COMMAND_FILES;
|
|
177
|
+
} else if (githubPath.includes('rules/global')) {
|
|
178
|
+
fileList = GLOBAL_RULES_FILES;
|
|
179
|
+
} else if (githubPath.includes('rules/manual')) {
|
|
180
|
+
fileList = MANUAL_RULES_FILES;
|
|
181
|
+
} else if (githubPath.includes('98_PROMPTS')) {
|
|
182
|
+
fileList = PROMPT_FILES;
|
|
183
|
+
} else if (githubPath.includes('99_TEMPLATES')) {
|
|
184
|
+
fileList = TEMPLATE_FILES;
|
|
185
|
+
} else {
|
|
186
|
+
// For unknown directories, try to download common files
|
|
187
|
+
fileList = ['README.md', 'index.md'];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
await downloadFileList(githubPath, localPath, fileList, spinner, messages);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
throw new Error(`Failed to download directory ${githubPath}: ${error.message}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export {
|
|
197
|
+
fetchContent,
|
|
198
|
+
downloadFile,
|
|
199
|
+
downloadFileList,
|
|
200
|
+
downloadDirectory,
|
|
201
|
+
GITHUB_RAW_URL,
|
|
202
|
+
GITHUB_CONTENT_PREFIX,
|
|
203
|
+
COMMAND_FILES,
|
|
204
|
+
GLOBAL_RULES_FILES,
|
|
205
|
+
MANUAL_RULES_FILES,
|
|
206
|
+
TEMPLATE_FILES,
|
|
207
|
+
PROMPT_FILES
|
|
208
|
+
};
|