tsoft-cli 2.6.15 → 3.4.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.
@@ -0,0 +1,162 @@
1
+ import chalk from 'chalk';
2
+ import fs from 'fs/promises';
3
+ import {createWriteStream} from 'fs';
4
+ import path from 'path';
5
+ import AdmZip from 'adm-zip';
6
+ import {createApiClient} from '../api-client.js';
7
+ import {slugify, getActiveStore} from '../storage.js';
8
+ import {brandedConfirm, brandedSelect, showWarning, showInfo, showError} from '../ui/prompt-wrapper.js';
9
+ import {normalizeStoreDomain} from '../storage.js';
10
+ import {isJsonMode, jsonOut} from '../output-mode.js';
11
+ import {mapError} from '../errors/error-mapper.js';
12
+ import { t } from '../i18n.js';
13
+
14
+ export async function themeInitCommand(fromSlug) {
15
+ try {
16
+ const apiClient = await createApiClient();
17
+ const response = await apiClient.get('/theme');
18
+ const themes = response.data || [];
19
+
20
+ let sourceTheme;
21
+
22
+ if (fromSlug && fromSlug !== true) {
23
+ // Slug provided: find theme by slug or uuid
24
+ sourceTheme = themes.find(t => slugify(t.name) === fromSlug || t.theme_folder === fromSlug);
25
+
26
+ if (!sourceTheme) {
27
+ if (isJsonMode()) {
28
+ jsonOut({ status: 'error', command: 'theme init', error: { message: t('init.theme_not_found', { slug: fromSlug }), hint: null } });
29
+ process.exit(1);
30
+ }
31
+ showError(t('init.theme_not_found', { slug: fromSlug }));
32
+ return;
33
+ }
34
+ } else {
35
+ // Interactive mode: show theme picker
36
+ if (isJsonMode()) {
37
+ jsonOut({ status: 'error', command: 'theme init', error: { message: t('init.json_error.message'), hint: t('init.json_error.hint') } });
38
+ process.exit(1);
39
+ }
40
+
41
+ if (themes.length === 0) {
42
+ showWarning(t('init.no_themes'));
43
+ return;
44
+ }
45
+
46
+ const themeChoices = themes.map(theme => ({
47
+ name: `${theme.name} ${chalk.gray('v' + theme.version)}${theme.organization_id ? chalk.dim(' [Org #' + theme.organization_id + ']') : ''}`,
48
+ value: theme,
49
+ description: theme.theme_folder
50
+ }));
51
+
52
+ sourceTheme = await brandedSelect({
53
+ message: t('init.select_prompt'),
54
+ choices: themeChoices
55
+ }, t('init.select_title'));
56
+ }
57
+
58
+ // JSON modda onay sormadan devam et
59
+ if (!isJsonMode()) {
60
+ const confirmed = await brandedConfirm({
61
+ message: t('init.confirm_prompt', { name: sourceTheme.name, version: sourceTheme.version }),
62
+ default: true
63
+ }, t('init.confirm_title'));
64
+
65
+ if (!confirmed) {
66
+ showInfo(t('init.cancelled'));
67
+ return;
68
+ }
69
+ }
70
+
71
+ // Fork API call
72
+ if (!isJsonMode()) {
73
+ console.log(chalk.yellow(t('init.forking')));
74
+ }
75
+ const forkResult = await apiClient.forkTheme(sourceTheme.theme_folder);
76
+ const forkedData = forkResult.data;
77
+
78
+ // Generate local path
79
+ const store = await getActiveStore();
80
+ const storeSlug = normalizeStoreDomain(store);
81
+ const forkedSlug = slugify(forkedData.name);
82
+ const themePath = path.join(process.cwd(), storeSlug, forkedSlug);
83
+
84
+ // Check if directory exists
85
+ const exists = await fs.access(themePath).then(() => true).catch(() => false);
86
+ if (exists) {
87
+ if (isJsonMode()) {
88
+ // JSON modda otomatik overwrite
89
+ await fs.rm(themePath, { recursive: true, force: true });
90
+ } else {
91
+ const overwrite = await brandedConfirm({
92
+ message: t('init.dir_exists_prompt', { path: themePath }),
93
+ default: false,
94
+ }, t('init.dir_exists_title'));
95
+
96
+ if (!overwrite) {
97
+ showWarning(t('init.dir_cancelled'));
98
+ return;
99
+ }
100
+ await fs.rm(themePath, { recursive: true, force: true });
101
+ }
102
+ }
103
+
104
+ // Download and extract forked theme files
105
+ if (!isJsonMode()) {
106
+ console.log(chalk.yellow(t('init.downloading')));
107
+ }
108
+ await fs.mkdir(themePath, { recursive: true });
109
+ const downloadResponse = await apiClient.download(`/theme/${forkedData.uuid}/download`, {});
110
+ const zipPath = path.join(themePath, 'theme.zip');
111
+ const writer = createWriteStream(zipPath);
112
+ downloadResponse.data.pipe(writer);
113
+ await new Promise((resolve, reject) => {
114
+ writer.on('finish', resolve);
115
+ writer.on('error', reject);
116
+ });
117
+
118
+ const zip = new AdmZip(zipPath);
119
+ zip.extractAllTo(themePath, true);
120
+ await fs.unlink(zipPath);
121
+
122
+ // JSON mode success output
123
+ if (isJsonMode()) {
124
+ jsonOut({
125
+ status: 'success',
126
+ command: 'theme init',
127
+ data: {
128
+ uuid: forkedData.uuid || forkedData.theme_folder,
129
+ name: forkedData.name,
130
+ version: forkedData.version || '0.0.1',
131
+ source: {
132
+ uuid: sourceTheme.theme_folder,
133
+ name: sourceTheme.name,
134
+ version: sourceTheme.version,
135
+ },
136
+ path: `${storeSlug}/${forkedSlug}/`,
137
+ }
138
+ });
139
+ return;
140
+ }
141
+
142
+ // Human-readable success message
143
+ console.log();
144
+ console.log(chalk.green(t('init.success')));
145
+ console.log();
146
+ console.log(chalk.cyan(' ' + t('init.theme_label') + ' ') + chalk.white(forkedData.name));
147
+ console.log(chalk.cyan(' ' + t('init.source_label') + ' ') + chalk.white(`${forkedData.source.name} v${forkedData.source.version}`));
148
+ console.log(chalk.cyan(' ' + t('init.location_label') + ' ') + chalk.white(`${storeSlug}/${forkedSlug}/`));
149
+ console.log();
150
+ console.log(chalk.gray(t('init.dev_tip', { slug: forkedSlug })));
151
+ console.log();
152
+
153
+ } catch (error) {
154
+ if (isJsonMode()) {
155
+ const mapped = mapError(error, { command: 'theme init' });
156
+ jsonOut({ status: 'error', command: 'theme init', error: { message: mapped.message, hint: mapped.hint || null } });
157
+ process.exit(1);
158
+ }
159
+ console.error(chalk.red('Hata: ') + error.message);
160
+ process.exit(1);
161
+ }
162
+ }
@@ -0,0 +1,12 @@
1
+ import chalk from 'chalk';
2
+ import { themePushCommand } from './theme-push.js';
3
+ import { t } from '../i18n.js';
4
+
5
+ /**
6
+ * Tema publish komutu (deprecated) - Push'a yonlendirir
7
+ */
8
+ export async function themePublishCommand(themeName) {
9
+ console.log(chalk.yellow('\n' + t('publish.deprecated')));
10
+ console.log('');
11
+ await themePushCommand(themeName);
12
+ }
@@ -0,0 +1,197 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { createApiClient } from '../api-client.js';
4
+ import { getActiveTheme } from '../storage.js';
5
+ import { mapPushError } from '../errors/error-mapper.js';
6
+ import { isJsonMode, jsonOut } from '../output-mode.js';
7
+ import { t } from '../i18n.js';
8
+
9
+ /**
10
+ * Change type'lari kategorilere grupla
11
+ */
12
+ const CHANGE_CATEGORIES = {
13
+ 'Sections': ['section_added', 'section_removed'],
14
+ 'Templates': ['template_added', 'template_removed'],
15
+ 'Meta': ['meta_object_added', 'meta_object_removed', 'meta_field_added', 'meta_field_removed'],
16
+ };
17
+
18
+ /**
19
+ * Diff ozeti goster (gruplu)
20
+ * @param {Object} data - Push response data
21
+ * @param {string|null} previousVersion - Onceki versiyon
22
+ */
23
+ function displayDiffSummary(data, previousVersion) {
24
+ const { version, bump_type: bumpType, action, changes, partner_panel_url: partnerUrl } = data;
25
+
26
+ console.log('');
27
+
28
+ // Versiyon gecisi
29
+ if (action === 'created' && !previousVersion) {
30
+ // Ilk push
31
+ console.log(chalk.green(` ${version} ${t('push.first_push_note')}`));
32
+ console.log(chalk.gray(' ' + t('push.admin_approval')));
33
+ } else if (previousVersion && previousVersion !== version) {
34
+ console.log(chalk.green(` ${previousVersion} -> ${version} (${bumpType})`));
35
+ } else {
36
+ console.log(chalk.green(` ${version}`));
37
+ }
38
+
39
+ // Action satiri
40
+ const actionText = action === 'created' ? t('push.draft_created') : t('push.draft_updated');
41
+ console.log(chalk.green(` ${actionText}`));
42
+
43
+ // Grouped changes
44
+ if (changes && changes.length > 0) {
45
+ console.log('');
46
+
47
+ for (const [category, types] of Object.entries(CHANGE_CATEGORIES)) {
48
+ const addedTypes = types.filter(t => t.endsWith('_added'));
49
+ const removedTypes = types.filter(t => t.endsWith('_removed'));
50
+
51
+ const addedCount = changes.filter(c => addedTypes.includes(c.type)).length;
52
+ const removedCount = changes.filter(c => removedTypes.includes(c.type)).length;
53
+
54
+ if (addedCount === 0 && removedCount === 0) continue;
55
+
56
+ const parts = [];
57
+ if (addedCount > 0) parts.push(chalk.green(t('push.changes_added', { count: addedCount })));
58
+ if (removedCount > 0) parts.push(chalk.red(t('push.changes_removed', { count: removedCount })));
59
+
60
+ console.log(` ${chalk.bold(category)}: ${parts.join(', ')}`);
61
+ }
62
+ }
63
+
64
+ // Partner panel URL
65
+ if (partnerUrl) {
66
+ console.log('');
67
+ console.log(chalk.cyan(` ${partnerUrl}`));
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Tema push komutu
73
+ * @param {string|null} themeName - Tema adi/UUID (opsiyonel)
74
+ * @param {Object} options - Komut opsiyonlari
75
+ * @param {boolean} options.verbose - Detayli hata ciktisi
76
+ */
77
+ export async function themePushCommand(themeName, options = {}) {
78
+ const verbose = options.verbose || false;
79
+
80
+ // Tema belirleme
81
+ let themeUuid;
82
+ if (themeName) {
83
+ themeUuid = themeName;
84
+ } else {
85
+ themeUuid = await getActiveTheme();
86
+ if (!themeUuid) {
87
+ console.error(chalk.red(t('push.no_active_theme')));
88
+ console.log(chalk.gray(t('push.usage')));
89
+ console.log(chalk.gray(t('push.use_tip')));
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+ const spinner = ora({
95
+ text: t('push.connecting'),
96
+ color: 'cyan',
97
+ stream: isJsonMode() ? process.stderr : process.stdout,
98
+ }).start();
99
+
100
+ let previousVersion = null;
101
+
102
+ try {
103
+ // 1. Dev status kontrol
104
+ spinner.text = t('push.checking_status');
105
+ const apiClient = await createApiClient();
106
+
107
+ try {
108
+ const statusResponse = await apiClient.getThemeDevStatus(themeUuid);
109
+ previousVersion = statusResponse.data?.current_version || null;
110
+ } catch (devStatusError) {
111
+ // dev-status hatasi kritik degil, devam et
112
+ }
113
+
114
+ // 2. Push
115
+ spinner.text = t('push.pushing');
116
+ const result = await apiClient.pushTheme(themeUuid);
117
+
118
+ // 3. No-op kontrolu
119
+ if (result.data?.no_changes === true) {
120
+ // JSON mode
121
+ if (isJsonMode()) {
122
+ spinner.stop();
123
+ jsonOut({
124
+ status: 'no_op',
125
+ command: 'theme push',
126
+ data: {
127
+ reason: 'no_changes',
128
+ partner_panel_url: result.data?.partner_panel_url || null,
129
+ }
130
+ });
131
+ return;
132
+ }
133
+ // Insan-okunabilir
134
+ spinner.info(t('push.no_changes'));
135
+ if (result.data?.partner_panel_url) {
136
+ console.log(chalk.gray(` ${result.data.partner_panel_url}`));
137
+ }
138
+ return;
139
+ }
140
+
141
+ // 4. Basari
142
+ spinner.succeed(t('push.success'));
143
+
144
+ // JSON mode basari output
145
+ if (isJsonMode()) {
146
+ jsonOut({
147
+ status: 'success',
148
+ command: 'theme push',
149
+ data: {
150
+ version: result.data.version,
151
+ bump_type: result.data.bump_type,
152
+ action: result.data.action,
153
+ changes: result.data.changes || [],
154
+ partner_panel_url: result.data.partner_panel_url || null,
155
+ }
156
+ });
157
+ return;
158
+ }
159
+
160
+ // 5. Diff summary (Insan-okunabilir)
161
+ displayDiffSummary(result.data, previousVersion);
162
+
163
+ } catch (error) {
164
+ spinner.stop();
165
+
166
+ // JSON mode error output
167
+ if (isJsonMode()) {
168
+ const mapped = mapPushError(error, false);
169
+ jsonOut({
170
+ status: 'error',
171
+ command: 'theme push',
172
+ error: {
173
+ message: mapped.message,
174
+ hint: mapped.hint || null,
175
+ }
176
+ });
177
+ process.exit(1);
178
+ }
179
+
180
+ // Push hatasi (axios error, error.response mevcut)
181
+ if (error.response) {
182
+ const mapped = mapPushError(error, verbose);
183
+ console.error(chalk.red(`X ${mapped.message}`));
184
+ if (mapped.hint) {
185
+ console.log(chalk.yellow(` ${mapped.hint}`));
186
+ }
187
+ if (mapped.raw) {
188
+ console.log(chalk.gray(JSON.stringify(mapped.raw, null, 2)));
189
+ }
190
+ } else {
191
+ // Genel hata (network, vs.)
192
+ console.error(chalk.red(`X ${error.message}`));
193
+ }
194
+
195
+ process.exit(1);
196
+ }
197
+ }
@@ -0,0 +1,317 @@
1
+ import chalk from 'chalk';
2
+ import {checkbox} from '@inquirer/prompts';
3
+ import fs from 'fs/promises';
4
+ import {createWriteStream} from 'fs';
5
+ import path from 'path';
6
+ import AdmZip from 'adm-zip';
7
+ import {createApiClient} from '../api-client.js';
8
+ import {getActiveTheme, getThemeUuidBySlug, normalizeStoreDomain, getActiveStore} from '../storage.js';
9
+ import {brandedConfirm, brandedSelect, showWarning} from '../ui/prompt-wrapper.js';
10
+ import { t } from '../i18n.js';
11
+
12
+ export async function themeSectionListCommand() {
13
+ try {
14
+ // Aktif temayı al
15
+ const activeTheme = await getActiveTheme();
16
+ if (!activeTheme) {
17
+ showWarning(t('section.list.no_active_theme'));
18
+ return;
19
+ }
20
+
21
+ console.log(chalk.cyan(t('section.list.starting') + '\n'));
22
+ console.log(chalk.gray(t('section.list.active_theme', { theme: activeTheme }) + '\n'));
23
+
24
+ // Tema UUID'sini al
25
+ const themeInfo = await getThemeUuidBySlug(activeTheme);
26
+
27
+ // Section'ları çek
28
+ const apiClient = await createApiClient();
29
+ const response = await apiClient.getSections(themeInfo.uuid);
30
+ const blocks = response.data || [];
31
+
32
+ if (blocks.length === 0) {
33
+ console.log(chalk.yellow(t('section.list.no_blocks') + '\n'));
34
+ return;
35
+ }
36
+
37
+ console.log(chalk.bold.cyan(t('section.list.count', { count: blocks.length }) + '\n'));
38
+
39
+ blocks.forEach((block) => {
40
+ // Block başlığı
41
+ console.log(chalk.bold.cyan(`📦 ${block.name}`));
42
+ console.log(chalk.gray(` ${t('section.list.id_label')} ${block.id}`));
43
+ console.log(chalk.gray(` ${t('section.list.folder_label')} ${block.folder}`));
44
+
45
+ // Section'lar
46
+ if (block.default_sections && block.default_sections.length > 0) {
47
+ console.log(chalk.yellow(' ' + t('section.list.sections_label', { count: block.default_sections.length })));
48
+
49
+ block.default_sections.forEach((section) => {
50
+ console.log(chalk.white(` • ${section.name} ${chalk.gray(`(ID: ${section.id}, v${section.version})`)}`));
51
+
52
+ // Meta bilgiler varsa göster
53
+ if (section.meta) {
54
+ if (section.meta.description) {
55
+ console.log(chalk.gray(` ${section.meta.description}`));
56
+ }
57
+ if (section.meta.tags && section.meta.tags.length > 0) {
58
+ console.log(chalk.gray(` ${t('section.list.tags_label')} ${section.meta.tags.join(', ')}`));
59
+ }
60
+ }
61
+
62
+ console.log(chalk.gray(` ${t('section.list.file_label')} ${section.content}`));
63
+ });
64
+ } else {
65
+ console.log(chalk.red(' ' + t('section.list.no_sections')));
66
+ }
67
+
68
+ console.log('');
69
+ });
70
+
71
+ } catch (error) {
72
+ console.error(chalk.red('\n❌ Hata: ') + error.message);
73
+ process.exit(1);
74
+ }
75
+ }
76
+
77
+ export async function themeSectionAddCommand() {
78
+ try {
79
+ // Aktif temayı al
80
+ const activeTheme = await getActiveTheme();
81
+ if (!activeTheme) {
82
+ showWarning(t('section.add.no_active_theme'));
83
+ return;
84
+ }
85
+
86
+ console.log(chalk.cyan(t('section.add.starting') + '\n'));
87
+ console.log(chalk.gray(t('section.add.active_theme', { theme: activeTheme }) + '\n'));
88
+
89
+ // Tema UUID'sini al
90
+ const themeInfo = await getThemeUuidBySlug(activeTheme);
91
+
92
+ // Section'ları çek
93
+ const apiClient = await createApiClient();
94
+ const response = await apiClient.getSections(themeInfo.uuid);
95
+ const blocks = response.data || [];
96
+
97
+ if (blocks.length === 0) {
98
+ showWarning(t('section.add.no_blocks'));
99
+ return;
100
+ }
101
+
102
+ // Sadece section'ı olan block'ları filtrele
103
+ const availableBlocks = blocks.filter(b => b.default_sections && b.default_sections.length > 0);
104
+
105
+ if (availableBlocks.length === 0) {
106
+ showWarning(t('section.add.no_sections'));
107
+ return;
108
+ }
109
+
110
+ // Block seçimi
111
+ const blockChoices = availableBlocks.map(block => ({
112
+ name: `${block.name} ${chalk.gray(`(${block.default_sections.length} section)`)}`,
113
+ value: block,
114
+ description: `${t('section.list.folder_label')} ${block.folder}`
115
+ }));
116
+
117
+ const selectedBlock = await brandedSelect({
118
+ message: t('section.add.block_select_prompt'),
119
+ choices: blockChoices,
120
+ }, t('section.add.block_select_title'));
121
+
122
+ console.log(chalk.cyan('\n' + t('section.add.block_selected', { name: selectedBlock.name }) + '\n'));
123
+
124
+ // Tek section varsa direkt ekle, çoklu varsa mod seçimi
125
+ let selectedSections = [];
126
+
127
+ if (selectedBlock.default_sections.length === 1) {
128
+ // Tek section - direkt ekle
129
+ selectedSections = [selectedBlock.default_sections[0]];
130
+ } else {
131
+ // Çoklu section - kullanıcıya sor
132
+ const addMode = await brandedSelect({
133
+ message: t('section.add.mode_prompt'),
134
+ choices: [
135
+ {name: t('section.add.mode_all'), value: 'all'},
136
+ {name: t('section.add.mode_selective'), value: 'selective'}
137
+ ]
138
+ }, t('section.add.mode_title'));
139
+
140
+ if (addMode === 'all') {
141
+ selectedSections = selectedBlock.default_sections;
142
+ } else {
143
+ // Checkbox ile seçim
144
+ const sectionChoices = selectedBlock.default_sections.map(section => ({
145
+ name: `${section.name} ${chalk.gray(`v${section.version}`)}`,
146
+ value: section,
147
+ checked: false
148
+ }));
149
+
150
+ selectedSections = await checkbox({
151
+ message: t('section.add.checkbox_prompt'),
152
+ choices: sectionChoices,
153
+ validate: (answer) => {
154
+ if (answer.length === 0) {
155
+ return t('section.add.min_one');
156
+ }
157
+ return true;
158
+ }
159
+ });
160
+ }
161
+ }
162
+
163
+ // Özet göster
164
+ console.log(chalk.yellow('\n' + t('section.add.summary', { count: selectedSections.length })));
165
+ selectedSections.forEach(s => {
166
+ console.log(chalk.gray(` • ${s.name} (v${s.version})`));
167
+ });
168
+
169
+ // Confirmation
170
+ const confirm = await brandedConfirm({
171
+ message: t('section.add.confirm_prompt', { count: selectedSections.length, theme: themeInfo.name }),
172
+ default: true
173
+ }, t('section.add.confirm_title'));
174
+
175
+ if (!confirm) {
176
+ showWarning(t('section.add.cancelled'));
177
+ return;
178
+ }
179
+
180
+ // Section'ları ekle
181
+ console.log(chalk.yellow('\n' + t('section.add.adding') + '\n'));
182
+
183
+ let successCount = 0;
184
+ let errorCount = 0;
185
+ let overwriteAll = false;
186
+
187
+ for (const section of selectedSections) {
188
+ try {
189
+ let force = overwriteAll;
190
+
191
+ // İlk deneme
192
+ try {
193
+ await apiClient.addSection(
194
+ themeInfo.uuid,
195
+ selectedBlock.id,
196
+ section.id,
197
+ force
198
+ );
199
+ console.log(chalk.green(t('section.add.added', { name: section.name })));
200
+ successCount++;
201
+ } catch (error) {
202
+ // 422 hatası - section zaten var
203
+ if (error.message.startsWith('VALIDATION_ERROR:')) {
204
+ const errorMsg = error.message.replace('VALIDATION_ERROR:', '');
205
+
206
+ if (errorMsg.includes('already exists') || errorMsg.includes('zaten mevcut')) {
207
+ // Üzerine yazma sorgusu
208
+ console.log(chalk.yellow('\n' + t('section.add.already_exists', { name: section.name })));
209
+
210
+ let shouldOverwrite = false;
211
+
212
+ if (!overwriteAll && selectedSections.length > 1) {
213
+ const overwriteChoice = await brandedSelect({
214
+ message: t('section.add.overwrite_prompt'),
215
+ choices: [
216
+ {name: t('section.add.overwrite'), value: 'overwrite'},
217
+ {name: t('section.add.overwrite_all'), value: 'overwrite_all'},
218
+ {name: t('section.add.skip'), value: 'skip'}
219
+ ]
220
+ }, t('section.add.overwrite_title'));
221
+
222
+ if (overwriteChoice === 'overwrite_all') {
223
+ overwriteAll = true;
224
+ shouldOverwrite = true;
225
+ } else if (overwriteChoice === 'overwrite') {
226
+ shouldOverwrite = true;
227
+ }
228
+ } else if (!overwriteAll) {
229
+ shouldOverwrite = await brandedConfirm({
230
+ message: t('section.add.single_overwrite_prompt', { name: section.name }),
231
+ default: false
232
+ }, t('section.add.single_overwrite_title'));
233
+ } else {
234
+ shouldOverwrite = true;
235
+ }
236
+
237
+ if (shouldOverwrite) {
238
+ // Force ile tekrar dene
239
+ await apiClient.addSection(
240
+ themeInfo.uuid,
241
+ selectedBlock.id,
242
+ section.id,
243
+ true
244
+ );
245
+ console.log(chalk.green(t('section.add.overwritten', { name: section.name })));
246
+ successCount++;
247
+ } else {
248
+ console.log(chalk.gray(t('section.add.skipped', { name: section.name })));
249
+ }
250
+ } else {
251
+ throw error;
252
+ }
253
+ } else {
254
+ throw error;
255
+ }
256
+ }
257
+ } catch (error) {
258
+ console.error(chalk.red(t('section.add.failed', { name: section.name, error: error.message })));
259
+ errorCount++;
260
+ }
261
+ }
262
+
263
+ // Sonuç özeti
264
+ console.log(chalk.gray('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
265
+ if (successCount > 0) {
266
+ console.log(chalk.green(t('section.add.success_count', { count: successCount })));
267
+ }
268
+ if (errorCount > 0) {
269
+ console.log(chalk.red(t('section.add.error_count', { count: errorCount })));
270
+ }
271
+
272
+ // Otomatik tema indirme
273
+ if (successCount > 0) {
274
+ console.log(chalk.yellow('\n' + t('section.add.updating_files') + '\n'));
275
+
276
+ const store = await getActiveStore();
277
+ const storeSlug = normalizeStoreDomain(store);
278
+ const themePath = path.join(process.cwd(), storeSlug, activeTheme);
279
+
280
+ try {
281
+ // Temayı indir
282
+ const downloadResponse = await apiClient.download(`/theme/${themeInfo.uuid}/download`, {});
283
+
284
+ const zipPath = path.join(themePath, 'theme-update.zip');
285
+ const writer = createWriteStream(zipPath);
286
+
287
+ downloadResponse.data.pipe(writer);
288
+
289
+ await new Promise((resolve, reject) => {
290
+ writer.on('finish', resolve);
291
+ writer.on('error', reject);
292
+ });
293
+
294
+ console.log(chalk.green(t('section.add.files_downloaded')));
295
+ console.log(chalk.yellow(t('section.add.files_updating')));
296
+
297
+ // Extract
298
+ const zip = new AdmZip(zipPath);
299
+ zip.extractAllTo(themePath, true);
300
+
301
+ await fs.unlink(zipPath);
302
+
303
+ console.log(chalk.green(t('section.add.files_updated') + '\n'));
304
+ console.log(chalk.bold.green(t('section.add.complete')));
305
+ console.log(chalk.gray(' ' + t('section.add.location', { path: `${storeSlug}/${activeTheme}/` }) + '\n'));
306
+ } catch (downloadError) {
307
+ console.error(chalk.red(t('section.add.download_error')), downloadError.message);
308
+ console.log(chalk.yellow('\n' + t('section.add.manual_tip')));
309
+ console.log(chalk.white(` ${t('section.add.manual_command')}\n`));
310
+ }
311
+ }
312
+
313
+ } catch (error) {
314
+ console.error(chalk.red('\n❌ Hata: ') + error.message);
315
+ process.exit(1);
316
+ }
317
+ }