prosicht 1.5.0 → 1.7.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 +22 -1
- package/index.js +30 -2
- package/lib/overrides.js +42 -0
- package/lib/publish.js +48 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -25,6 +25,18 @@ npx prosicht update
|
|
|
25
25
|
```
|
|
26
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.
|
|
27
27
|
|
|
28
|
+
### Projeye özel kurallar
|
|
29
|
+
|
|
30
|
+
`init` ve `update`, `AGENTS.md`'yi şablondan baştan yazar. Projeye özel kuralları (ör. Next.js şablonunu Nuxt'a uyarlayan eşlemeler) şu işaretler arasına yazın; CLI bu bloğu olduğu gibi korur ve yeni şablonda ilk `## ` başlığının önüne (başlık ve giriş paragrafından sonra) koyar:
|
|
31
|
+
|
|
32
|
+
```markdown
|
|
33
|
+
<!-- BEGIN:project-overrides -->
|
|
34
|
+
## 0. Project Overrides
|
|
35
|
+
- ...
|
|
36
|
+
<!-- END:project-overrides -->
|
|
37
|
+
```
|
|
38
|
+
İşaretler eksik, ters sırada ya da birden fazlaysa CLI hata verir ve `AGENTS.md`'ye dokunmaz. Uygulama şablonlarında ve sunum / blog kitlerinde aynı şekilde çalışır (CLI 1.7+).
|
|
39
|
+
|
|
28
40
|
## Yayınlama
|
|
29
41
|
|
|
30
42
|
```bash
|
|
@@ -38,7 +50,16 @@ Tür yüklemeyle birlikte gönderilir (`manifest.kind`); sunucu ayrıca dosyalar
|
|
|
38
50
|
|
|
39
51
|
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.
|
|
40
52
|
|
|
41
|
-
Seçenekler: `--key <key>`, `--host <url>`, `-y` (soruları atla; tür klasörden tespit edilir).
|
|
53
|
+
Seçenekler: `--key <key>`, `--host <url>`, `-y` (soruları atla; tür klasörden tespit edilir), `-f, --force` (aşağıdaki iki denetimi atla, üzerine yaz).
|
|
54
|
+
|
|
55
|
+
### Ortak çalışma (CLI 1.6+)
|
|
56
|
+
|
|
57
|
+
İçeriğin sahibi Pro veya Ekip planındaysa panelden başka hesapları (ücretsiz olabilir) içeriğe ekler. Eklenen kişi panelde **Benimle paylaşılanlar** altından:
|
|
58
|
+
1. **Kaynak dosyalarını indir** ile yayındaki sürümü ZIP olarak alır ve açar (içinde `AGENTS.md` ve key'siz `.prosicht/publish.json` var),
|
|
59
|
+
2. **Yayın keyi al** ile kendine ait keyi alır,
|
|
60
|
+
3. klasörde `npx prosicht publish` çalıştırır; key ilk seferde sorulur.
|
|
61
|
+
|
|
62
|
+
Her yayından sonra klasörün hangi sürümün üzerine kurulu olduğu `.prosicht/publish.json`'a (`baseVersion`) yazılır ve sonraki yayında gönderilir. Bu arada başka biri (sahip ya da başka bir ortak çalışan) yeni sürüm yayınladıysa yayın reddedilir; güncel kaynağı indirip değişiklikleri aktarın ya da bilerek üzerine yazmak için `--force` kullanın. Klasörün ait olduğu içerikten başka bir içeriğin keyi girilirse de yayın reddedilir (`--force` ile o içeriğe yayınlanır). Sahibin planı ortak çalışmayı artık içermiyorsa ortak çalışanların yayını reddedilir.
|
|
42
63
|
|
|
43
64
|
## Sunum hazırlama
|
|
44
65
|
|
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const { version } = require('./package.json');
|
|
|
8
8
|
const { detectLayout } = require('./lib/layout');
|
|
9
9
|
const { migrateV1 } = require('./lib/migrate');
|
|
10
10
|
const { publish } = require('./lib/publish');
|
|
11
|
+
const { insertOverrides, readExistingOverrides } = require('./lib/overrides');
|
|
11
12
|
|
|
12
13
|
// PROSICHT_TEMPLATES_URL: sablonlari push etmeden yerel bir sunucudan denemek icin
|
|
13
14
|
const BASE_URL = (process.env.PROSICHT_TEMPLATES_URL || 'https://raw.githubusercontent.com/ProSicht/ai-standards/main/templates').replace(/\/+$/, '');
|
|
@@ -71,6 +72,26 @@ function withTemplateTag(content, templateKey) {
|
|
|
71
72
|
return `<!-- template: ${templateKey} -->\n` + content.replace(TAG_PATTERN, '');
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
// Yeni AGENTS.md: şablon + etiket + projedeki mevcut AGENTS.md'nin proje bloğu (varsa)
|
|
76
|
+
function buildAgentsMd(content, templateKey, overrides) {
|
|
77
|
+
return withTemplateTag(overrides ? insertOverrides(content, overrides) : content, templateKey);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Mevcut AGENTS.md'nin proje bloğu; işaretler bozuksa hatayı yazar ve undefined döner (hiçbir şey yazılmaz)
|
|
81
|
+
function loadOverrides() {
|
|
82
|
+
try {
|
|
83
|
+
return readExistingOverrides(process.cwd());
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error(`Hata: ${error.message} AGENTS.md degistirilmedi.`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function reportOverrides(overrides) {
|
|
92
|
+
if (overrides) console.log('Projeye ozel blok (project-overrides) korundu.');
|
|
93
|
+
}
|
|
94
|
+
|
|
74
95
|
async function download(url) {
|
|
75
96
|
const response = await fetch(url);
|
|
76
97
|
if (!response.ok) {
|
|
@@ -117,14 +138,17 @@ async function askTemplate(message) {
|
|
|
117
138
|
async function fetchAndSaveTemplate(templateKey) {
|
|
118
139
|
const selected = APP_TEMPLATES[templateKey];
|
|
119
140
|
const fileUrl = `${BASE_URL}/${selected.file}`;
|
|
141
|
+
const overrides = loadOverrides();
|
|
142
|
+
if (overrides === undefined) return;
|
|
120
143
|
console.log(`\n[Pro Sicht] "${selected.name}" sablonu indiriliyor...`);
|
|
121
144
|
|
|
122
145
|
try {
|
|
123
146
|
const content = (await download(fileUrl)).toString('utf8');
|
|
124
147
|
const targetPath = path.join(process.cwd(), 'AGENTS.md');
|
|
125
|
-
fs.writeFileSync(targetPath,
|
|
148
|
+
fs.writeFileSync(targetPath, buildAgentsMd(content, templateKey, overrides), 'utf8');
|
|
126
149
|
|
|
127
150
|
console.log(`Basarili: AGENTS.md kaydedildi! (Sablon: ${templateKey})`);
|
|
151
|
+
reportOverrides(overrides);
|
|
128
152
|
console.log(`Konum: ${targetPath}\n`);
|
|
129
153
|
} catch (error) {
|
|
130
154
|
console.error('Indirme hatasi:', error.message);
|
|
@@ -169,6 +193,8 @@ async function installKit(key, mode, options = {}) {
|
|
|
169
193
|
}
|
|
170
194
|
}
|
|
171
195
|
|
|
196
|
+
const overrides = loadOverrides();
|
|
197
|
+
if (overrides === undefined) return;
|
|
172
198
|
console.log(`\n[Pro Sicht] ${kit.name} ${mode === 'update' ? 'guncelleniyor' : 'indiriliyor'}...`);
|
|
173
199
|
|
|
174
200
|
try {
|
|
@@ -188,7 +214,7 @@ async function installKit(key, mode, options = {}) {
|
|
|
188
214
|
const url = `${base}/${job.file.split('/').map(encodeURIComponent).join('/')}`;
|
|
189
215
|
let data = await download(url);
|
|
190
216
|
if (job.file === 'AGENTS.md') {
|
|
191
|
-
data = Buffer.from(
|
|
217
|
+
data = Buffer.from(buildAgentsMd(data.toString('utf8'), key, overrides), 'utf8');
|
|
192
218
|
}
|
|
193
219
|
return { ...job, data };
|
|
194
220
|
}));
|
|
@@ -211,6 +237,7 @@ async function installKit(key, mode, options = {}) {
|
|
|
211
237
|
for (const job of kept) console.log(` = ${job.file} (mevcut, korundu)`);
|
|
212
238
|
|
|
213
239
|
console.log(`Basarili: ${files.length} dosya yazildi (${kit.name} v${manifest.version || '?'}).`);
|
|
240
|
+
if (files.some((file) => file.file === 'AGENTS.md')) reportOverrides(overrides);
|
|
214
241
|
if (mode === 'init') {
|
|
215
242
|
console.log('\nSonraki adimlar:');
|
|
216
243
|
for (const step of kit.nextSteps) console.log(` ${step}`);
|
|
@@ -282,6 +309,7 @@ program
|
|
|
282
309
|
.option('-k, --key <key>', 'Panelden alinan 32 karakterlik yayin keyi')
|
|
283
310
|
.option('--host <url>', 'Yayin adresi (varsayilan: https://deck.prosicht.com)')
|
|
284
311
|
.option('-y, --yes', 'Sorulari atla: tur klasorden tespit edilir, key kaydedilir')
|
|
312
|
+
.option('-f, --force', 'Bu klasorden sonra yayinlanan surumun ve klasorun ait oldugu icerigin denetimini atla (uzerine yazar)')
|
|
285
313
|
.action((options) => publish(options));
|
|
286
314
|
|
|
287
315
|
program.parse(process.argv);
|
package/lib/overrides.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// Projeye özel kurallar AGENTS.md içinde bu işaretler arasında durur; init ve update
|
|
5
|
+
// şablonu yeniden yazarken bloğu olduğu gibi korur (ör. Next.js şablonunu Nuxt'a uyarlayan kurallar)
|
|
6
|
+
const BEGIN = '<!-- BEGIN:project-overrides -->';
|
|
7
|
+
const END = '<!-- END:project-overrides -->';
|
|
8
|
+
|
|
9
|
+
function count(text, marker) {
|
|
10
|
+
return text.split(marker).length - 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Bloğu (işaretler dahil) döndürür; blok yoksa null. Bozuk işaretlerde hata fırlatır:
|
|
14
|
+
// yazmaya devam etmek bloğu sessizce siler.
|
|
15
|
+
function extractOverrides(content) {
|
|
16
|
+
const begins = count(content, BEGIN);
|
|
17
|
+
const ends = count(content, END);
|
|
18
|
+
if (begins === 0 && ends === 0) return null;
|
|
19
|
+
const start = content.indexOf(BEGIN);
|
|
20
|
+
const end = content.indexOf(END);
|
|
21
|
+
if (begins !== 1 || ends !== 1 || end < start) {
|
|
22
|
+
throw new Error(`AGENTS.md icindeki proje blogu bozuk: tam olarak bir "${BEGIN}" ve ondan sonra bir "${END}" olmali.`);
|
|
23
|
+
}
|
|
24
|
+
return content.slice(start, end + END.length);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Bloğu şablonun ilk "## " başlığının önüne koyar (başlık ve giriş paragrafından sonra);
|
|
28
|
+
// başlık yoksa sona ekler
|
|
29
|
+
function insertOverrides(content, block) {
|
|
30
|
+
const heading = content.search(/^## /m);
|
|
31
|
+
if (heading === -1) return `${content.replace(/\n*$/, '')}\n\n${block}\n`;
|
|
32
|
+
return `${content.slice(0, heading)}${block}\n\n${content.slice(heading)}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Projedeki mevcut AGENTS.md'den blok; dosya yoksa null
|
|
36
|
+
function readExistingOverrides(root) {
|
|
37
|
+
const file = path.join(root, 'AGENTS.md');
|
|
38
|
+
if (!fs.existsSync(file)) return null;
|
|
39
|
+
return extractOverrides(fs.readFileSync(file, 'utf8'));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { BEGIN, END, extractOverrides, insertOverrides, readExistingOverrides };
|
package/lib/publish.js
CHANGED
|
@@ -62,12 +62,17 @@ async function chooseHost(saved, flag) {
|
|
|
62
62
|
return normalizeHost(custom);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
async function chooseKey(saved, flag) {
|
|
65
|
+
async function chooseKey(saved, flag, config = {}) {
|
|
66
66
|
if (flag) return KEY_PATTERN.test(flag) ? flag : fail('Key 32 karakter olmali (harf ve rakam).');
|
|
67
67
|
if (saved && KEY_PATTERN.test(saved)) {
|
|
68
68
|
console.log(`Kayitli key kullaniliyor (....${saved.slice(-4)}). Degistirmek icin: --key`);
|
|
69
69
|
return saved;
|
|
70
70
|
}
|
|
71
|
+
// panelden indirilen klasör: hangi içeriğe ait olduğu belli, key kişiye özel
|
|
72
|
+
if (config.deckId && config.url) {
|
|
73
|
+
console.log(`Bu klasor ${config.url} icerigine ait. Keyinizi panelden alin:`);
|
|
74
|
+
console.log(' sahibiyseniz Iceriklerim, ortak calisiyorsaniz Benimle paylasilanlar -> Yayin keyi al.');
|
|
75
|
+
}
|
|
71
76
|
return ask({
|
|
72
77
|
type: 'password', name: 'key', message: 'Lutfen keyi giriniz',
|
|
73
78
|
validate: (v) => (KEY_PATTERN.test(String(v).trim()) ? true : 'Key 32 karakter olmali (harf ve rakam)'),
|
|
@@ -75,10 +80,13 @@ async function chooseKey(saved, flag) {
|
|
|
75
80
|
});
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
// Sunucu türü dosyalardan da anlar; kind yine de gönderilir (panelde rozet, MCP'de doğru kit)
|
|
79
|
-
|
|
83
|
+
// Sunucu türü dosyalardan da anlar; kind yine de gönderilir (panelde rozet, MCP'de doğru kit).
|
|
84
|
+
// base: klasörün geldiği içerik ve sürüm; arada başkası yayınladıysa sunucu reddeder (409)
|
|
85
|
+
async function upload(host, key, files, title, kind, base) {
|
|
80
86
|
const form = new FormData();
|
|
81
|
-
|
|
87
|
+
const manifest = { title, kind, files: files.map((f) => ({ path: f.path, size: f.size })) };
|
|
88
|
+
if (base) Object.assign(manifest, { deckId: base.deckId, baseVersion: base.version });
|
|
89
|
+
form.append('manifest', JSON.stringify(manifest));
|
|
82
90
|
for (const f of files) {
|
|
83
91
|
const type = MIME_TYPES[path.extname(f.path).toLowerCase()];
|
|
84
92
|
form.append('files', new Blob([fs.readFileSync(f.abs)], { type }), path.basename(f.path));
|
|
@@ -100,6 +108,22 @@ const PANEL_CHOICE = { presentation: 'Sunum', blog: 'Blog yazisi' };
|
|
|
100
108
|
|
|
101
109
|
function explain(status, body) {
|
|
102
110
|
if (status === 401) fail('Key gecersiz veya yenilenmis. Panelden guncel keyi alin.');
|
|
111
|
+
if (status === 403 && body.code === 'collaboration_inactive') {
|
|
112
|
+
fail('Icerigin sahibinin plani ortak calismayi icermiyor (Pro veya Ekip gerekir); su an yalnizca sahibi yayinlayabilir.');
|
|
113
|
+
}
|
|
114
|
+
// indirildikten (ya da son yayından) sonra başka biri yeni sürüm yayınlamış
|
|
115
|
+
if (status === 409 && body.code === 'version_conflict') {
|
|
116
|
+
fail(`Bu klasor surum ${body.baseVersion} uzerine kurulu, ama bu arada surum ${body.version} yayinlanmis. Yayinlarsaniz o surumdeki degisiklikler kaybolur.`, [
|
|
117
|
+
'Panelden "Kaynak dosyalarini indir" ile guncel surumu alip degisikliklerinizi ona aktarin, sonra tekrar yayinlayin.',
|
|
118
|
+
'Ya da bilerek uzerine yazmak icin: npx prosicht publish --force'
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
if (status === 409 && body.code === 'deck_mismatch') {
|
|
122
|
+
fail(`Bu klasor ${body.folderDeckId} icerigine ait, girilen key ise ${body.deckId} icerigine.`, [
|
|
123
|
+
'Klasorun ait oldugu icerigin keyini --key ile verin.',
|
|
124
|
+
`Klasoru bilerek ${body.deckId} uzerine yayinlamak icin: npx prosicht publish --force`
|
|
125
|
+
]);
|
|
126
|
+
}
|
|
103
127
|
// key, daha önce başka türde yayınlanmış bir içeriğe ait
|
|
104
128
|
if (status === 409 && DATIVE[body.deckKind] && NOUN[body.kind]) {
|
|
105
129
|
fail(`Bu key bir ${DATIVE[body.deckKind]} ait; bu klasordeki icerik bir ${NOUN[body.kind]}.`, [
|
|
@@ -145,14 +169,18 @@ async function publish(options = {}) {
|
|
|
145
169
|
|
|
146
170
|
const config = readConfig(root);
|
|
147
171
|
const host = await chooseHost(options.host ? null : config.host, options.host);
|
|
148
|
-
let key = await chooseKey(options.key ? null : config.key, options.key);
|
|
172
|
+
let key = await chooseKey(options.key ? null : config.key, options.key, config);
|
|
149
173
|
if (!key) process.exit(0);
|
|
150
174
|
|
|
175
|
+
// --force: çakışma denetimi yok, klasör keyin içeriğine yayınlanır
|
|
176
|
+
const base = !options.force && config.deckId && Number.isInteger(config.baseVersion)
|
|
177
|
+
? { deckId: config.deckId, version: config.baseVersion }
|
|
178
|
+
: null;
|
|
151
179
|
const title = readTitle(root, kind);
|
|
152
180
|
const send = async () => {
|
|
153
181
|
console.log(`\n[Pro Sicht] ${files.length} dosya (${formatBytes(total)}) yukleniyor...`);
|
|
154
182
|
try {
|
|
155
|
-
return await upload(host, key, files, title, kind);
|
|
183
|
+
return await upload(host, key, files, title, kind, base);
|
|
156
184
|
} catch (error) {
|
|
157
185
|
return fail(`${host} adresine ulasilamadi: ${error.cause?.code || error.message}`);
|
|
158
186
|
}
|
|
@@ -161,28 +189,37 @@ async function publish(options = {}) {
|
|
|
161
189
|
// kayıtlı key panelden yenilenmişse bir kez yenisini sor ve tekrar dene
|
|
162
190
|
if (result.status === 401 && key === config.key && !options.key && !options.yes) {
|
|
163
191
|
console.log('Kayitli key gecersiz; panelde yenilenmis olabilir.');
|
|
164
|
-
key = await chooseKey(null, null);
|
|
192
|
+
key = await chooseKey(null, null, config);
|
|
165
193
|
if (!key) process.exit(0);
|
|
166
194
|
result = await send();
|
|
167
195
|
}
|
|
168
196
|
if (result.status !== 200 || !result.body.url) explain(result.status, result.body);
|
|
169
197
|
|
|
170
|
-
const { url, privacy, version } = result.body;
|
|
198
|
+
const { url, privacy, version, deckId, role } = result.body;
|
|
171
199
|
console.log(`Yayinlandi (${label}, surum ${version}): ${url}`);
|
|
172
|
-
|
|
200
|
+
// ortak çalışan gizliliği değiştiremez; yalnızca sahibi
|
|
201
|
+
if (role === 'collaborator') {
|
|
202
|
+
console.log(privacy === 'private'
|
|
203
|
+
? 'Gizlilik: yalnizca sahibi ve ortak calisanlar gorebilir. Paylasmayi sahibi panelden acar.'
|
|
204
|
+
: `Gizlilik: ${privacy === 'password' ? 'baglanti ve sifreyle' : 'baglantiya sahip herkes'} gorebilir (sahibinin ayari).`);
|
|
205
|
+
} else if (privacy === 'private') {
|
|
173
206
|
console.log('Gizlilik: yalnizca siz gorebilirsiniz. Paylasmak icin panelden "Baglantiya sahip herkes" secin.');
|
|
174
207
|
} else {
|
|
175
208
|
console.log('Gizlilik: baglantiya sahip herkes gorebilir.');
|
|
176
209
|
}
|
|
177
210
|
|
|
211
|
+
// baseVersion her yayında güncellenir: sonraki yayın bu sürümün üzerine kurulu sayılır
|
|
212
|
+
const published = { deckId, url, baseVersion: version };
|
|
178
213
|
if (config.key !== key || config.host !== host) {
|
|
179
214
|
const remember = options.yes ? true : await ask({
|
|
180
215
|
type: 'confirm', name: 'save', initial: true,
|
|
181
216
|
message: 'Key ve adres bu klasor icin kaydedilsin mi? (.prosicht/, git disi)'
|
|
182
217
|
});
|
|
183
|
-
|
|
218
|
+
const rest = { ...config };
|
|
219
|
+
delete rest.key;
|
|
220
|
+
saveConfig(root, remember ? { ...rest, host, key, ...published } : { ...rest, host, ...published });
|
|
184
221
|
} else {
|
|
185
|
-
saveConfig(root, { ...config,
|
|
222
|
+
saveConfig(root, { ...config, ...published });
|
|
186
223
|
}
|
|
187
224
|
console.log('');
|
|
188
225
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosicht",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Pro Sicht AI Standards CLI: AGENTS.md sablonlari, sunum ve blog altyapisi",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"node": ">=18"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "
|
|
17
|
+
"test": "node --test"
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
20
20
|
"ai",
|