prosicht 1.3.0 → 1.5.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.
- package/README.md +19 -5
- package/index.js +70 -32
- package/lib/files.js +16 -7
- package/lib/layout.js +11 -3
- package/lib/publish.js +34 -15
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# prosicht
|
|
2
2
|
|
|
3
|
-
Pro Sicht AI
|
|
3
|
+
Pro Sicht AI standartlarını, sunum ve blog altyapısını projeye indiren CLI. Şablonlar [prosicht/ai-standards](https://github.com/prosicht/ai-standards) deposundan canlı olarak çekilir.
|
|
4
4
|
|
|
5
5
|
## Kullanım
|
|
6
6
|
|
|
@@ -10,31 +10,35 @@ npx prosicht init
|
|
|
10
10
|
Önce ne yapmak istediğinizi sorar:
|
|
11
11
|
- **Uygulama:** Proje şablonunu seçersiniz (web, landing, mobile, backend); kurallar `AGENTS.md` olarak iner.
|
|
12
12
|
- **Sunum:** Etkileşimli, animasyonlu sunum altyapısı (çekirdek dosyalar, `AGENTS.md` ve çalışan bir örnek deste) bulunduğunuz klasöre iner.
|
|
13
|
+
- **Blog:** Etkileşimli, animasyonlu blog yazısı altyapısı (çekirdek dosyalar, `AGENTS.md` ve çalışan bir örnek yazı) bulunduğunuz klasöre iner.
|
|
13
14
|
|
|
14
15
|
Şablonu doğrudan vermek için:
|
|
15
16
|
```bash
|
|
16
17
|
npx prosicht init -t web
|
|
17
18
|
npx prosicht init -t presentation
|
|
19
|
+
npx prosicht init -t blog
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
Güncelleme (şablon, `AGENTS.md` başındaki etiketten otomatik tespit edilir):
|
|
21
23
|
```bash
|
|
22
24
|
npx prosicht update
|
|
23
25
|
```
|
|
24
|
-
Sunum klasörlerinde `update` yalnızca çekirdek dosyaları (`index.html`, `css/core/`, `js/core/`, `AGENTS.md`) yeniler; `js/scenes.js`, `js/sims/`, `css/theme.css`, `css/sims/`, `assets/` ve `sources.md` dokunulmadan kalır. Eski (1.x) klasör yapısındaki sunumlar onayla yeni yapıya taşınır (`--yes` sormadan taşır).
|
|
26
|
+
Sunum klasörlerinde `update` yalnızca çekirdek dosyaları (`index.html`, `css/core/`, `js/core/`, `AGENTS.md`) yeniler; `js/scenes.js`, `js/sims/`, `css/theme.css`, `css/sims/`, `assets/` ve `sources.md` dokunulmadan kalır. Blog klasörlerinde de yalnızca çekirdek yenilenir (`js/post.js`, `js/sims/`, `css/theme.css`, `css/sims/`, `assets/`, `sources.md` dokunulmaz). Eski (1.x) klasör yapısındaki sunumlar onayla yeni yapıya taşınır (`--yes` sormadan taşır). Sunum ve blog aynı çekirdek yollarını kullanır: bir kitin klasörüne diğeri yazılmadan önce onay istenir, `--yes` ile bu durumda işlem yapılmaz.
|
|
25
27
|
|
|
26
28
|
## Yayınlama
|
|
27
29
|
|
|
28
30
|
```bash
|
|
29
31
|
npx prosicht publish
|
|
30
32
|
```
|
|
31
|
-
1. deck.prosicht.com'da hesap açın ve **Yeni
|
|
32
|
-
2. Sunum klasöründe `npx prosicht publish` çalıştırın: **Sunum** → adres (varsayılan `deck.prosicht.com`) → key.
|
|
33
|
+
1. deck.prosicht.com'da hesap açın ve **Yeni ekle** ile, klasörünüzle aynı türü (**Sunum** ya da **Blog yazısı**) seçerek 32 karakterlik bir key alın.
|
|
34
|
+
2. Sunum veya blog klasöründe `npx prosicht publish` çalıştırın: **Sunum** / **Blog** (klasörden tespit edilip önceden seçilir) → adres (varsayılan `deck.prosicht.com`) → key.
|
|
33
35
|
3. Link terminalde verilir. Key ve adres isterseniz `.prosicht/publish.json` içine kaydedilir (git dışı); sonraki yayınlar sormaz.
|
|
34
36
|
|
|
37
|
+
Tür yüklemeyle birlikte gönderilir (`manifest.kind`); sunucu ayrıca dosyalardan da anlar (`js/post.js` → blog, `js/scenes.js` → sunum). Hiç yayınlanmamış bir kayıt yüklenen türü alır. Daha önce sunum olarak yayınlanmış bir kaydın keyiyle blog yayınlanamaz (ya da tersi): CLI bunu açıkça söyler; panelden doğru türde yeni bir kayıt açın.
|
|
38
|
+
|
|
35
39
|
Yalnızca `index.html`, `css/`, `js/` ve `assets/` yüklenir (en fazla 50 MB, dosya başına 25 MB, 500 dosya). `/` ile başlayan yollar için uyarı verilir. Kayıtlı key panelde yenilendiyse CLI yenisini sorar.
|
|
36
40
|
|
|
37
|
-
Seçenekler: `--key <key>`, `--host <url>`, `-y` (soruları atla).
|
|
41
|
+
Seçenekler: `--key <key>`, `--host <url>`, `-y` (soruları atla; tür klasörden tespit edilir).
|
|
38
42
|
|
|
39
43
|
## Sunum hazırlama
|
|
40
44
|
|
|
@@ -45,12 +49,22 @@ python3 -m http.server 8000 # http://localhost:8000
|
|
|
45
49
|
```
|
|
46
50
|
Ardından AI aracına konuyu söyleyin: *"KOBİ'ler için yapay zekâ konusunda 10 dakikalık sunum hazırla."*
|
|
47
51
|
|
|
52
|
+
## Blog yazısı hazırlama
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
mkdir yeni-yazi && cd yeni-yazi
|
|
56
|
+
npx prosicht init -t blog
|
|
57
|
+
python3 -m http.server 8000 # http://localhost:8000
|
|
58
|
+
```
|
|
59
|
+
Görselleri `assets/images/` içine koyun ya da yolunu verin, sonra AI aracına konuyu söyleyin: *"Ev kahvesi demleme yöntemleri hakkında, gorseller/ klasöründeki fotoğrafları kullanarak etkileşimli bir blog yazısı hazırla."*
|
|
60
|
+
|
|
48
61
|
## Geliştirme
|
|
49
62
|
|
|
50
63
|
Şablonları push etmeden yerel bir sunucudan denemek için (`PROSICHT_PUBLISH_URL` aynı şekilde yayın adresini değiştirir):
|
|
51
64
|
```bash
|
|
52
65
|
cd ai-standards/templates && python3 -m http.server 8765
|
|
53
66
|
PROSICHT_TEMPLATES_URL=http://127.0.0.1:8765 node /path/to/prosicht-cli/index.js init -t presentation
|
|
67
|
+
PROSICHT_TEMPLATES_URL=http://127.0.0.1:8765 node /path/to/prosicht-cli/index.js init -t blog
|
|
54
68
|
```
|
|
55
69
|
|
|
56
70
|
Node.js 18 veya üstü gerekir.
|
package/index.js
CHANGED
|
@@ -14,7 +14,8 @@ const BASE_URL = (process.env.PROSICHT_TEMPLATES_URL || 'https://raw.githubuserc
|
|
|
14
14
|
|
|
15
15
|
const KINDS = {
|
|
16
16
|
app: { title: 'Uygulama', description: 'Proje turune gore AGENTS.md (web, landing, mobile, backend)' },
|
|
17
|
-
presentation: { title: 'Sunum', description: 'Etkilesimli, animasyonlu sunum altyapisi + AGENTS.md' }
|
|
17
|
+
presentation: { title: 'Sunum', description: 'Etkilesimli, animasyonlu sunum altyapisi + AGENTS.md' },
|
|
18
|
+
blog: { title: 'Blog', description: 'Etkilesimli, animasyonlu blog yazisi altyapisi + AGENTS.md' }
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const APP_TEMPLATES = {
|
|
@@ -24,10 +25,35 @@ const APP_TEMPLATES = {
|
|
|
24
25
|
backend: { name: 'Backend API Service', file: 'backend.md' }
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
// Sunum tek dosya degil, klasor: dosya listesi templates
|
|
28
|
-
|
|
28
|
+
// Sunum ve blog tek dosya degil, klasor: dosya listesi templates/<dir>/manifest.json icinde.
|
|
29
|
+
// owned: update'in dokunmadigi icerik dosyalari; migrate: kit 1.x klasorleri tasinabilir
|
|
30
|
+
const KITS = {
|
|
31
|
+
presentation: {
|
|
32
|
+
name: 'Sunum altyapisi',
|
|
33
|
+
dir: 'presentation',
|
|
34
|
+
migrate: true,
|
|
35
|
+
owned: 'Sunuma ait dosyalar (js/scenes.js, js/sims/, css/theme.css, css/sims/, assets/, sources.md) degistirilmedi.',
|
|
36
|
+
nextSteps: [
|
|
37
|
+
'1. python3 -m http.server 8000 -> http://localhost:8000 (ornek sunum)',
|
|
38
|
+
'2. AI aracina konuyu soyleyin: "<konu> hakkinda 10 dakikalik sunum hazirla"',
|
|
39
|
+
'3. npx prosicht publish -> deck.prosicht.com uzerinde yayinlayin'
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
blog: {
|
|
43
|
+
name: 'Blog altyapisi',
|
|
44
|
+
dir: 'blog',
|
|
45
|
+
migrate: false,
|
|
46
|
+
owned: 'Yaziya ait dosyalar (js/post.js, js/sims/, css/theme.css, css/sims/, assets/, sources.md) degistirilmedi.',
|
|
47
|
+
nextSteps: [
|
|
48
|
+
'1. python3 -m http.server 8000 -> http://localhost:8000 (ornek yazi)',
|
|
49
|
+
'2. Gorselleri assets/images/ icine koyun ya da AI aracina yolunu verin',
|
|
50
|
+
'3. AI aracina konuyu soyleyin: "<konu> hakkinda, gorselleri kullanarak etkilesimli bir blog yazisi hazirla"',
|
|
51
|
+
'4. npx prosicht publish -> deck.prosicht.com uzerinde yayinlayin'
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
};
|
|
29
55
|
|
|
30
|
-
const TEMPLATE_KEYS = [...Object.keys(APP_TEMPLATES),
|
|
56
|
+
const TEMPLATE_KEYS = [...Object.keys(APP_TEMPLATES), ...Object.keys(KITS)];
|
|
31
57
|
const TAG_PATTERN = /<!--\s*template:\s*(\w+)\s*-->\n?/;
|
|
32
58
|
|
|
33
59
|
// Projedeki mevcut AGENTS.md dosyasından şablon türünü okur
|
|
@@ -80,11 +106,11 @@ async function askAppTemplate(message) {
|
|
|
80
106
|
return response.template;
|
|
81
107
|
}
|
|
82
108
|
|
|
83
|
-
// Önce tür (Uygulama / Sunum), uygulamaysa ardından şablon sorulur
|
|
109
|
+
// Önce tür (Uygulama / Sunum / Blog), uygulamaysa ardından şablon sorulur
|
|
84
110
|
async function askTemplate(message) {
|
|
85
111
|
const kind = await askKind();
|
|
86
112
|
if (!kind) return null;
|
|
87
|
-
if (kind
|
|
113
|
+
if (KITS[kind]) return kind;
|
|
88
114
|
return askAppTemplate(message);
|
|
89
115
|
}
|
|
90
116
|
|
|
@@ -116,21 +142,34 @@ function resolveInsideCwd(relPath) {
|
|
|
116
142
|
return target;
|
|
117
143
|
}
|
|
118
144
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
async function confirmMigration() {
|
|
122
|
-
const response = await prompts({
|
|
123
|
-
type: 'confirm',
|
|
124
|
-
name: 'ok',
|
|
125
|
-
initial: true,
|
|
126
|
-
message: 'Bu sunum eski klasor yapisinda (kit 1.x). Yeni yapiya (index.html + css/ js/ assets/) tasinsin mi?'
|
|
127
|
-
});
|
|
145
|
+
async function confirm(message, initial = true) {
|
|
146
|
+
const response = await prompts({ type: 'confirm', name: 'ok', initial, message });
|
|
128
147
|
return Boolean(response.ok);
|
|
129
148
|
}
|
|
130
149
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
// init: çekirdek + başlangıç dosyaları (mevcut başlangıç dosyaları korunur)
|
|
151
|
+
// update: yalnızca çekirdek dosyalar; sunum / yazı içeriğine dokunulmaz
|
|
152
|
+
async function installKit(key, mode, options = {}) {
|
|
153
|
+
const kit = KITS[key];
|
|
154
|
+
const base = `${BASE_URL}/${kit.dir}`;
|
|
155
|
+
|
|
156
|
+
// Sunum ve blog aynı çekirdek yollarını kullanır (index.html, js/core/app.js):
|
|
157
|
+
// başka bir kitin klasörüne yazmak onu bozar
|
|
158
|
+
const existing = detectExistingTemplate();
|
|
159
|
+
if (existing && existing !== key && (KITS[existing] || detectLayout(process.cwd()))) {
|
|
160
|
+
const label = KITS[existing] ? KITS[existing].name : `"${existing}" sablonu`;
|
|
161
|
+
if (options.yes) {
|
|
162
|
+
console.error(`Hata: Bu klasorde ${label} var; ${kit.name} cekirdek dosyalari ustune yazilmadi.`);
|
|
163
|
+
process.exitCode = 1;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!(await confirm(`Bu klasorde ${label} var. ${kit.name} cekirdek dosyalari (index.html, js/core/ ...) ustune yazilsin mi?`, false))) {
|
|
167
|
+
console.log('Iptal edildi; hicbir dosya degistirilmedi.\n');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`\n[Pro Sicht] ${kit.name} ${mode === 'update' ? 'guncelleniyor' : 'indiriliyor'}...`);
|
|
134
173
|
|
|
135
174
|
try {
|
|
136
175
|
const manifest = JSON.parse((await download(`${base}/manifest.json`)).toString('utf8'));
|
|
@@ -149,15 +188,15 @@ async function installPresentation(mode, options = {}) {
|
|
|
149
188
|
const url = `${base}/${job.file.split('/').map(encodeURIComponent).join('/')}`;
|
|
150
189
|
let data = await download(url);
|
|
151
190
|
if (job.file === 'AGENTS.md') {
|
|
152
|
-
data = Buffer.from(withTemplateTag(data.toString('utf8'),
|
|
191
|
+
data = Buffer.from(withTemplateTag(data.toString('utf8'), key), 'utf8');
|
|
153
192
|
}
|
|
154
193
|
return { ...job, data };
|
|
155
194
|
}));
|
|
156
195
|
|
|
157
196
|
// update: kit 1.x klasörünü önce yeni yapıya taşı (dosyalar indikten sonra, onayla).
|
|
158
197
|
// Yalnızca uzaktaki kit de yeni yapıdaysa: eski manifest eski yollara yazar.
|
|
159
|
-
if (mode === 'update' && detectLayout(process.cwd()) === 1 && Number(manifest.layout) >= 2) {
|
|
160
|
-
if (!options.yes && !(await
|
|
198
|
+
if (kit.migrate && mode === 'update' && detectLayout(process.cwd()) === 1 && Number(manifest.layout) >= 2) {
|
|
199
|
+
if (!options.yes && !(await confirm('Bu sunum eski klasor yapisinda (kit 1.x). Yeni yapiya (index.html + css/ js/ assets/) tasinsin mi?'))) {
|
|
161
200
|
console.log('Iptal edildi; hicbir dosya degistirilmedi.\n');
|
|
162
201
|
return;
|
|
163
202
|
}
|
|
@@ -171,14 +210,13 @@ async function installPresentation(mode, options = {}) {
|
|
|
171
210
|
}
|
|
172
211
|
for (const job of kept) console.log(` = ${job.file} (mevcut, korundu)`);
|
|
173
212
|
|
|
174
|
-
console.log(`Basarili: ${files.length} dosya yazildi (
|
|
213
|
+
console.log(`Basarili: ${files.length} dosya yazildi (${kit.name} v${manifest.version || '?'}).`);
|
|
175
214
|
if (mode === 'init') {
|
|
176
215
|
console.log('\nSonraki adimlar:');
|
|
177
|
-
|
|
178
|
-
console.log('
|
|
179
|
-
console.log(' 3. npx prosicht publish -> deck.prosicht.com uzerinde yayinlayin\n');
|
|
216
|
+
for (const step of kit.nextSteps) console.log(` ${step}`);
|
|
217
|
+
console.log('');
|
|
180
218
|
} else {
|
|
181
|
-
console.log(
|
|
219
|
+
console.log(`${kit.owned}\n`);
|
|
182
220
|
}
|
|
183
221
|
} catch (error) {
|
|
184
222
|
console.error('Indirme hatasi:', error.message);
|
|
@@ -191,7 +229,7 @@ async function install(templateKey, mode, options = {}) {
|
|
|
191
229
|
console.error(`Hata: Gecersiz sablon "${templateKey}". Gecerli: ${TEMPLATE_KEYS.join(', ')}`);
|
|
192
230
|
process.exit(1);
|
|
193
231
|
}
|
|
194
|
-
if (templateKey
|
|
232
|
+
if (KITS[templateKey]) return installKit(templateKey, mode, options);
|
|
195
233
|
return fetchAndSaveTemplate(templateKey);
|
|
196
234
|
}
|
|
197
235
|
|
|
@@ -202,7 +240,7 @@ program
|
|
|
202
240
|
|
|
203
241
|
program
|
|
204
242
|
.command('init')
|
|
205
|
-
.description('Projeye AI standartlarini (AGENTS.md) veya
|
|
243
|
+
.description('Projeye AI standartlarini (AGENTS.md), sunum veya blog altyapisini indirir')
|
|
206
244
|
.option('-t, --template <type>', `Sablon turu (${TEMPLATE_KEYS.join(', ')})`)
|
|
207
245
|
.action(async (options) => {
|
|
208
246
|
let templateKey = options.template;
|
|
@@ -217,9 +255,9 @@ program
|
|
|
217
255
|
|
|
218
256
|
program
|
|
219
257
|
.command('update')
|
|
220
|
-
.description('Projedeki AGENTS.md (ve sunum altyapisinin cekirdek dosyalarini) tespit edip gunceller')
|
|
258
|
+
.description('Projedeki AGENTS.md (ve sunum / blog altyapisinin cekirdek dosyalarini) tespit edip gunceller')
|
|
221
259
|
.option('-t, --template <type>', 'Sablon turunu zorla belirle')
|
|
222
|
-
.option('-y, --yes', '
|
|
260
|
+
.option('-y, --yes', 'Sormadan devam et: eski sunum yapisini yeni yapiya tasir')
|
|
223
261
|
.action(async (options) => {
|
|
224
262
|
let templateKey = options.template;
|
|
225
263
|
|
|
@@ -240,10 +278,10 @@ program
|
|
|
240
278
|
|
|
241
279
|
program
|
|
242
280
|
.command('publish')
|
|
243
|
-
.description('Sunumu deck.prosicht.com uzerinde yayinlar ve linki verir')
|
|
281
|
+
.description('Sunumu veya blog yazisini deck.prosicht.com uzerinde yayinlar ve linki verir')
|
|
244
282
|
.option('-k, --key <key>', 'Panelden alinan 32 karakterlik yayin keyi')
|
|
245
283
|
.option('--host <url>', 'Yayin adresi (varsayilan: https://deck.prosicht.com)')
|
|
246
|
-
.option('-y, --yes', 'Sorulari atla:
|
|
284
|
+
.option('-y, --yes', 'Sorulari atla: tur klasorden tespit edilir, key kaydedilir')
|
|
247
285
|
.action((options) => publish(options));
|
|
248
286
|
|
|
249
287
|
program.parse(process.argv);
|
package/lib/files.js
CHANGED
|
@@ -59,14 +59,23 @@ function findAbsolutePaths(files) {
|
|
|
59
59
|
return hits;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
// Başlık panelde ve paylaşım önizlemesinde görünür: sunumda js/scenes.js
|
|
63
|
+
// içindeki deck.title, blogda js/post.js içindeki post.title (ilk title alanı)
|
|
64
|
+
const CONTENT = { presentation: ['scenes.js', 'deck'], blog: ['post.js', 'post'] };
|
|
65
|
+
|
|
66
|
+
function readTitle(root, kind = 'presentation') {
|
|
67
|
+
const [fileName, name] = CONTENT[kind] || CONTENT.presentation;
|
|
68
|
+
const file = path.join(root, 'js', fileName);
|
|
65
69
|
if (!fs.existsSync(file)) return null;
|
|
66
70
|
const source = fs.readFileSync(file, 'utf8');
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
71
|
+
// önce satır başındaki kapanış (iç içe nesneler), sonra tek satırlık yazım
|
|
72
|
+
const head = `export\\s+const\\s+${name}\\s*=\\s*\\{([\\s\\S]*?)`;
|
|
73
|
+
const block = source.match(new RegExp(`${head}\\n\\};`)) || source.match(new RegExp(`${head}\\};`));
|
|
74
|
+
const title = block && block[1].match(/\btitle\s*:\s*(['"`])((?:(?!\1).)*)\1/);
|
|
75
|
+
if (!title) return null;
|
|
76
|
+
// <br> ve *vurgu* işaretleri başlıkta görünmesin
|
|
77
|
+
const plain = title[2].replace(/<br\s*\/?>/gi, ' ').replace(/\*/g, '').replace(/\s+/g, ' ').trim();
|
|
78
|
+
return plain.slice(0, 120) || null;
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
function formatBytes(n) {
|
|
@@ -75,4 +84,4 @@ function formatBytes(n) {
|
|
|
75
84
|
return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
76
85
|
}
|
|
77
86
|
|
|
78
|
-
module.exports = { collectFiles, findAbsolutePaths,
|
|
87
|
+
module.exports = { collectFiles, findAbsolutePaths, readTitle, formatBytes, MIME_TYPES };
|
package/lib/layout.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
// 2 = index.html + css/ js/ assets/ (kit 2.x). 0 =
|
|
4
|
+
// Kit klasör yapıları: 1 = kök dizinde app.js/sims/styles (sunum kit 1.x),
|
|
5
|
+
// 2 = index.html + css/ js/ assets/ (sunum kit 2.x ve blog). 0 = kit değil.
|
|
6
6
|
function detectLayout(root) {
|
|
7
7
|
const has = (rel) => fs.existsSync(path.join(root, rel));
|
|
8
8
|
if (has('js/core/app.js')) return 2;
|
|
@@ -10,6 +10,14 @@ function detectLayout(root) {
|
|
|
10
10
|
return 0;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// 2.x klasörünün türü: içerik dosyasından (blog: js/post.js, sunum: js/scenes.js)
|
|
14
|
+
function detectKit(root) {
|
|
15
|
+
const has = (rel) => fs.existsSync(path.join(root, rel));
|
|
16
|
+
if (has('js/post.js')) return 'blog';
|
|
17
|
+
if (has('js/scenes.js')) return 'presentation';
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
// Proje klasöründeki .gitignore'a satır ekler (yoksa oluşturur)
|
|
14
22
|
function ensureGitignore(root, line) {
|
|
15
23
|
const file = path.join(root, '.gitignore');
|
|
@@ -20,4 +28,4 @@ function ensureGitignore(root, line) {
|
|
|
20
28
|
return true;
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
module.exports = { detectLayout, ensureGitignore };
|
|
31
|
+
module.exports = { detectLayout, detectKit, ensureGitignore };
|
package/lib/publish.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const prompts = require('prompts');
|
|
4
|
-
const { detectLayout, ensureGitignore } = require('./layout');
|
|
5
|
-
const { collectFiles, findAbsolutePaths,
|
|
4
|
+
const { detectLayout, detectKit, ensureGitignore } = require('./layout');
|
|
5
|
+
const { collectFiles, findAbsolutePaths, readTitle, formatBytes, MIME_TYPES } = require('./files');
|
|
6
6
|
|
|
7
7
|
const DEFAULT_HOST = (process.env.PROSICHT_PUBLISH_URL || 'https://deck.prosicht.com').replace(/\/+$/, '');
|
|
8
8
|
const KEY_PATTERN = /^[A-Za-z0-9]{32}$/;
|
|
@@ -75,9 +75,10 @@ async function chooseKey(saved, flag) {
|
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
// Sunucu türü dosyalardan da anlar; kind yine de gönderilir (panelde rozet, MCP'de doğru kit)
|
|
79
|
+
async function upload(host, key, files, title, kind) {
|
|
79
80
|
const form = new FormData();
|
|
80
|
-
form.append('manifest', JSON.stringify({ title, files: files.map((f) => ({ path: f.path, size: f.size })) }));
|
|
81
|
+
form.append('manifest', JSON.stringify({ title, kind, files: files.map((f) => ({ path: f.path, size: f.size })) }));
|
|
81
82
|
for (const f of files) {
|
|
82
83
|
const type = MIME_TYPES[path.extname(f.path).toLowerCase()];
|
|
83
84
|
form.append('files', new Blob([fs.readFileSync(f.abs)], { type }), path.basename(f.path));
|
|
@@ -93,9 +94,19 @@ async function upload(host, key, files, title) {
|
|
|
93
94
|
return { status: response.status, body };
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
const NOUN = { presentation: 'sunum', blog: 'blog yazisi' };
|
|
98
|
+
const DATIVE = { presentation: 'sunuma', blog: 'blog yazisina' };
|
|
99
|
+
const PANEL_CHOICE = { presentation: 'Sunum', blog: 'Blog yazisi' };
|
|
100
|
+
|
|
96
101
|
function explain(status, body) {
|
|
97
102
|
if (status === 401) fail('Key gecersiz veya yenilenmis. Panelden guncel keyi alin.');
|
|
98
|
-
|
|
103
|
+
// key, daha önce başka türde yayınlanmış bir içeriğe ait
|
|
104
|
+
if (status === 409 && DATIVE[body.deckKind] && NOUN[body.kind]) {
|
|
105
|
+
fail(`Bu key bir ${DATIVE[body.deckKind]} ait; bu klasordeki icerik bir ${NOUN[body.kind]}.`, [
|
|
106
|
+
`Panelde "Yeni ekle" ile ${PANEL_CHOICE[body.kind]} secip yeni bir key alin ve --key ile verin.`
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
if (status === 413) fail('Dosyalar boyut sinirini asiyor (en fazla 50 MB).');
|
|
99
110
|
if (status === 400 || status === 422) fail(body.error || 'Dosyalar reddedildi.', body.details || []);
|
|
100
111
|
fail(`Sunucu hatasi (HTTP ${status})${body.error ? `: ${body.error}` : ''}`);
|
|
101
112
|
}
|
|
@@ -103,18 +114,26 @@ function explain(status, body) {
|
|
|
103
114
|
async function publish(options = {}) {
|
|
104
115
|
const root = process.cwd();
|
|
105
116
|
|
|
106
|
-
const
|
|
117
|
+
const detected = detectKit(root);
|
|
118
|
+
const choices = [
|
|
119
|
+
{ title: 'Sunum', value: 'presentation' },
|
|
120
|
+
{ title: 'Blog', value: 'blog' },
|
|
121
|
+
{ title: 'Uygulama', value: 'app', disabled: true, description: 'yakinda' }
|
|
122
|
+
];
|
|
123
|
+
const kind = options.yes ? (detected || 'presentation') : await ask({
|
|
107
124
|
type: 'select', name: 'kind', message: 'Ne yayinlamak istiyorsunuz?',
|
|
108
|
-
choices
|
|
109
|
-
|
|
110
|
-
{ title: 'Uygulama', value: 'app', disabled: true, description: 'yakinda' }
|
|
111
|
-
]
|
|
125
|
+
choices,
|
|
126
|
+
initial: Math.max(0, choices.findIndex((c) => c.value === detected))
|
|
112
127
|
});
|
|
113
|
-
if (kind !== 'presentation') process.exit(0);
|
|
128
|
+
if (kind !== 'presentation' && kind !== 'blog') process.exit(0);
|
|
129
|
+
const label = kind === 'blog' ? 'blog yazisi' : 'sunum';
|
|
114
130
|
|
|
115
131
|
const layout = detectLayout(root);
|
|
116
132
|
if (layout === 1) fail('Bu sunum eski klasor yapisinda. Once `npx prosicht update` ile yeni yapiya tasiyin.');
|
|
117
|
-
if (layout === 0) fail(
|
|
133
|
+
if (layout === 0) fail(`Bu klasorde bir ${label} bulunamadi (index.html ve js/core/app.js gerekli). ${kind === 'blog' ? 'Blog' : 'Sunum'} klasorunde calistirin.`);
|
|
134
|
+
if (detected && detected !== kind) {
|
|
135
|
+
fail(`Bu klasordeki icerik bir ${detected === 'blog' ? 'blog yazisi' : 'sunum'}; "${kind === 'blog' ? 'Blog' : 'Sunum'}" secildi.`);
|
|
136
|
+
}
|
|
118
137
|
|
|
119
138
|
const { files, total, errors } = collectFiles(root);
|
|
120
139
|
if (errors.length) fail('Yayin oncesi kontrol basarisiz:', errors);
|
|
@@ -129,11 +148,11 @@ async function publish(options = {}) {
|
|
|
129
148
|
let key = await chooseKey(options.key ? null : config.key, options.key);
|
|
130
149
|
if (!key) process.exit(0);
|
|
131
150
|
|
|
132
|
-
const title =
|
|
151
|
+
const title = readTitle(root, kind);
|
|
133
152
|
const send = async () => {
|
|
134
153
|
console.log(`\n[Pro Sicht] ${files.length} dosya (${formatBytes(total)}) yukleniyor...`);
|
|
135
154
|
try {
|
|
136
|
-
return await upload(host, key, files, title);
|
|
155
|
+
return await upload(host, key, files, title, kind);
|
|
137
156
|
} catch (error) {
|
|
138
157
|
return fail(`${host} adresine ulasilamadi: ${error.cause?.code || error.message}`);
|
|
139
158
|
}
|
|
@@ -149,7 +168,7 @@ async function publish(options = {}) {
|
|
|
149
168
|
if (result.status !== 200 || !result.body.url) explain(result.status, result.body);
|
|
150
169
|
|
|
151
170
|
const { url, privacy, version } = result.body;
|
|
152
|
-
console.log(`Yayinlandi (surum ${version}): ${url}`);
|
|
171
|
+
console.log(`Yayinlandi (${label}, surum ${version}): ${url}`);
|
|
153
172
|
if (privacy === 'private') {
|
|
154
173
|
console.log('Gizlilik: yalnizca siz gorebilirsiniz. Paylasmak icin panelden "Baglantiya sahip herkes" secin.');
|
|
155
174
|
} else {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosicht",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Pro Sicht AI Standards CLI: AGENTS.md sablonlari ve
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Pro Sicht AI Standards CLI: AGENTS.md sablonlari, sunum ve blog altyapisi",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"prosicht": "./index.js"
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"agents",
|
|
22
22
|
"cli",
|
|
23
23
|
"prosicht",
|
|
24
|
-
"presentation"
|
|
24
|
+
"presentation",
|
|
25
|
+
"blog"
|
|
25
26
|
],
|
|
26
27
|
"author": "Pro Sicht",
|
|
27
28
|
"license": "MIT",
|