prosicht 1.1.0 → 1.2.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.
Files changed (4) hide show
  1. package/README.md +43 -0
  2. package/index.js +144 -52
  3. package/package.json +16 -3
  4. package/tasks/todo.md +0 -14
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # prosicht
2
+
3
+ Pro Sicht AI standartlarını ve sunum altyapısını projeye indiren CLI. Şablonlar [prosicht/ai-standards](https://github.com/prosicht/ai-standards) deposundan canlı olarak çekilir.
4
+
5
+ ## Kullanım
6
+
7
+ ```bash
8
+ npx prosicht init
9
+ ```
10
+ Önce ne yapmak istediğinizi sorar:
11
+ - **Uygulama:** Proje şablonunu seçersiniz (web, landing, mobile, backend); kurallar `AGENTS.md` olarak iner.
12
+ - **Sunum:** Etkileşimli, animasyonlu sunum altyapısı (çekirdek dosyalar, `AGENTS.md` ve çalışan bir örnek deste) bulunduğunuz klasöre iner.
13
+
14
+ Şablonu doğrudan vermek için:
15
+ ```bash
16
+ npx prosicht init -t web
17
+ npx prosicht init -t presentation
18
+ ```
19
+
20
+ Güncelleme (şablon, `AGENTS.md` başındaki etiketten otomatik tespit edilir):
21
+ ```bash
22
+ npx prosicht update
23
+ ```
24
+ Sunum klasörlerinde `update` yalnızca çekirdek dosyaları yeniler; `scenes.js`, `sims/`, `styles/theme.css` ve `sources.md` dokunulmadan kalır.
25
+
26
+ ## Sunum hazırlama
27
+
28
+ ```bash
29
+ mkdir yeni-sunum && cd yeni-sunum
30
+ npx prosicht init -t presentation
31
+ python3 -m http.server 8000 # http://localhost:8000
32
+ ```
33
+ Ardından AI aracına konuyu söyleyin: *"KOBİ'ler için yapay zekâ konusunda 10 dakikalık sunum hazırla."*
34
+
35
+ ## Geliştirme
36
+
37
+ Şablonları push etmeden yerel bir sunucudan denemek için:
38
+ ```bash
39
+ cd ai-standards/templates && python3 -m http.server 8765
40
+ PROSICHT_TEMPLATES_URL=http://127.0.0.1:8765 node /path/to/prosicht-cli/index.js init -t presentation
41
+ ```
42
+
43
+ Node.js 18 veya üstü gerekir.
package/index.js CHANGED
@@ -4,95 +4,196 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { program } = require('commander');
6
6
  const prompts = require('prompts');
7
+ const { version } = require('./package.json');
7
8
 
8
- const BASE_URL = 'https://raw.githubusercontent.com/ProSicht/ai-standards/main/templates';
9
+ // PROSICHT_TEMPLATES_URL: sablonlari push etmeden yerel bir sunucudan denemek icin
10
+ const BASE_URL = (process.env.PROSICHT_TEMPLATES_URL || 'https://raw.githubusercontent.com/ProSicht/ai-standards/main/templates').replace(/\/+$/, '');
9
11
 
10
- const TEMPLATES = {
12
+ const KINDS = {
13
+ app: { title: 'Uygulama', description: 'Proje turune gore AGENTS.md (web, landing, mobile, backend)' },
14
+ presentation: { title: 'Sunum', description: 'Etkilesimli, animasyonlu sunum altyapisi + AGENTS.md' }
15
+ };
16
+
17
+ const APP_TEMPLATES = {
11
18
  web: { name: 'Web Application (Next.js, Shadcn, Docker)', file: 'web.md' },
12
19
  landing: { name: 'Landing Page (SEO & Speed Focused)', file: 'landing.md' },
13
20
  mobile: { name: 'Mobile Application', file: 'mobile.md' },
14
21
  backend: { name: 'Backend API Service', file: 'backend.md' }
15
22
  };
16
23
 
24
+ // Sunum tek dosya degil, klasor: dosya listesi templates/presentation/manifest.json icinde
25
+ const PRESENTATION = { key: 'presentation', name: 'Sunum Altyapisi', dir: 'presentation' };
26
+
27
+ const TEMPLATE_KEYS = [...Object.keys(APP_TEMPLATES), PRESENTATION.key];
28
+ const TAG_PATTERN = /<!--\s*template:\s*(\w+)\s*-->\n?/;
29
+
17
30
  // Projedeki mevcut AGENTS.md dosyasından şablon türünü okur
18
31
  function detectExistingTemplate() {
19
32
  const targetPath = path.join(process.cwd(), 'AGENTS.md');
20
33
  if (!fs.existsSync(targetPath)) return null;
21
34
 
22
35
  const content = fs.readFileSync(targetPath, 'utf8');
23
- const match = content.match(/<!--\s*template:\s*(\w+)\s*-->/);
36
+ const match = content.match(TAG_PATTERN);
24
37
  return match ? match[1] : null;
25
38
  }
26
39
 
27
- async function fetchAndSaveTemplate(templateKey) {
28
- const selected = TEMPLATES[templateKey];
29
- if (!selected) {
30
- console.error(`Hata: Gecersiz sablon "${templateKey}".`);
31
- process.exit(1);
40
+ // En üste gizli metadata etiketini ekler; önceki etiket varsa temizler
41
+ function withTemplateTag(content, templateKey) {
42
+ return `<!-- template: ${templateKey} -->\n` + content.replace(TAG_PATTERN, '');
43
+ }
44
+
45
+ async function download(url) {
46
+ const response = await fetch(url);
47
+ if (!response.ok) {
48
+ throw new Error(`HTTP ${response.status}: Dosya bulunamadi (${url})`);
32
49
  }
50
+ return Buffer.from(await response.arrayBuffer());
51
+ }
52
+
53
+ async function askKind() {
54
+ const response = await prompts({
55
+ type: 'select',
56
+ name: 'kind',
57
+ message: 'Ne yapmak istiyorsunuz?',
58
+ choices: Object.entries(KINDS).map(([value, kind]) => ({
59
+ title: kind.title,
60
+ description: kind.description,
61
+ value
62
+ }))
63
+ });
64
+ return response.kind;
65
+ }
66
+
67
+ async function askAppTemplate(message) {
68
+ const response = await prompts({
69
+ type: 'select',
70
+ name: 'template',
71
+ message,
72
+ choices: Object.entries(APP_TEMPLATES).map(([key, value]) => ({
73
+ title: value.name,
74
+ value: key
75
+ }))
76
+ });
77
+ return response.template;
78
+ }
33
79
 
80
+ // Önce tür (Uygulama / Sunum), uygulamaysa ardından şablon sorulur
81
+ async function askTemplate(message) {
82
+ const kind = await askKind();
83
+ if (!kind) return null;
84
+ if (kind === 'presentation') return PRESENTATION.key;
85
+ return askAppTemplate(message);
86
+ }
87
+
88
+ async function fetchAndSaveTemplate(templateKey) {
89
+ const selected = APP_TEMPLATES[templateKey];
34
90
  const fileUrl = `${BASE_URL}/${selected.file}`;
35
91
  console.log(`\n[Pro Sicht] "${selected.name}" sablonu indiriliyor...`);
36
92
 
37
93
  try {
38
- const response = await fetch(fileUrl);
39
- if (!response.ok) {
40
- throw new Error(`HTTP ${response.status}: Dosya bulunamadi (${fileUrl})`);
41
- }
42
-
43
- let content = await response.text();
44
-
45
- // En üste gizli metadata etiketini ekle
46
- const metadataHeader = `<!-- template: ${templateKey} -->\n`;
47
-
48
- // Eğer dosya önceden indirildiyse eski etiketi temizle, yenisini bas
49
- content = content.replace(/<!--\s*template:\s*\w+\s*-->\n?/, '');
50
- const finalContent = metadataHeader + content;
51
-
94
+ const content = (await download(fileUrl)).toString('utf8');
52
95
  const targetPath = path.join(process.cwd(), 'AGENTS.md');
53
- fs.writeFileSync(targetPath, finalContent, 'utf8');
54
-
96
+ fs.writeFileSync(targetPath, withTemplateTag(content, templateKey), 'utf8');
97
+
55
98
  console.log(`Basarili: AGENTS.md kaydedildi! (Sablon: ${templateKey})`);
56
99
  console.log(`Konum: ${targetPath}\n`);
57
-
58
100
  } catch (error) {
59
101
  console.error('Indirme hatasi:', error.message);
102
+ process.exitCode = 1;
103
+ }
104
+ }
105
+
106
+ // Manifest uzaktan gelir: proje klasörünün dışına yazan yolları reddet
107
+ function resolveInsideCwd(relPath) {
108
+ const root = process.cwd();
109
+ const target = path.resolve(root, relPath);
110
+ if (typeof relPath !== 'string' || path.isAbsolute(relPath) || !target.startsWith(root + path.sep)) {
111
+ throw new Error(`Gecersiz dosya yolu: ${relPath}`);
112
+ }
113
+ return target;
114
+ }
115
+
116
+ // init: çekirdek + başlangıç dosyaları (mevcut başlangıç dosyaları korunur)
117
+ // update: yalnızca çekirdek dosyalar; sunum içeriğine dokunulmaz
118
+ async function installPresentation(mode) {
119
+ const base = `${BASE_URL}/${PRESENTATION.dir}`;
120
+ console.log(`\n[Pro Sicht] Sunum altyapisi ${mode === 'update' ? 'guncelleniyor' : 'indiriliyor'}...`);
121
+
122
+ try {
123
+ const manifest = JSON.parse((await download(`${base}/manifest.json`)).toString('utf8'));
124
+ const core = Array.isArray(manifest.core) ? manifest.core : [];
125
+ const starter = mode === 'init' && Array.isArray(manifest.starter) ? manifest.starter : [];
126
+
127
+ const jobs = [
128
+ ...core.map((file) => ({ file, starter: false })),
129
+ ...starter.map((file) => ({ file, starter: true }))
130
+ ].map((job) => ({ ...job, target: resolveInsideCwd(job.file) }));
131
+ const kept = jobs.filter((job) => job.starter && fs.existsSync(job.target));
132
+ const toWrite = jobs.filter((job) => !kept.includes(job));
133
+
134
+ // Önce hepsini indir, sonra yaz: ağ hatasında yarım kurulum kalmasın
135
+ const files = await Promise.all(toWrite.map(async (job) => {
136
+ const url = `${base}/${job.file.split('/').map(encodeURIComponent).join('/')}`;
137
+ let data = await download(url);
138
+ if (job.file === 'AGENTS.md') {
139
+ data = Buffer.from(withTemplateTag(data.toString('utf8'), PRESENTATION.key), 'utf8');
140
+ }
141
+ return { ...job, data };
142
+ }));
143
+
144
+ for (const file of files) {
145
+ fs.mkdirSync(path.dirname(file.target), { recursive: true });
146
+ fs.writeFileSync(file.target, file.data);
147
+ console.log(` + ${file.file}`);
148
+ }
149
+ for (const job of kept) console.log(` = ${job.file} (mevcut, korundu)`);
150
+
151
+ console.log(`Basarili: ${files.length} dosya yazildi (Sunum altyapisi v${manifest.version || '?'}).`);
152
+ if (mode === 'init') {
153
+ console.log('\nSonraki adimlar:');
154
+ console.log(' 1. python3 -m http.server 8000 -> http://localhost:8000 (ornek sunum)');
155
+ console.log(' 2. AI aracina konuyu soyleyin: "<konu> hakkinda 10 dakikalik sunum hazirla"\n');
156
+ } else {
157
+ console.log('Sunum icerigi (scenes.js, sims/, styles/theme.css, sources.md) degistirilmedi.\n');
158
+ }
159
+ } catch (error) {
160
+ console.error('Indirme hatasi:', error.message);
161
+ process.exitCode = 1;
162
+ }
163
+ }
164
+
165
+ async function install(templateKey, mode) {
166
+ if (!TEMPLATE_KEYS.includes(templateKey)) {
167
+ console.error(`Hata: Gecersiz sablon "${templateKey}". Gecerli: ${TEMPLATE_KEYS.join(', ')}`);
168
+ process.exit(1);
60
169
  }
170
+ if (templateKey === PRESENTATION.key) return installPresentation(mode);
171
+ return fetchAndSaveTemplate(templateKey);
61
172
  }
62
173
 
63
174
  program
64
175
  .name('prosicht')
65
176
  .description('Pro Sicht AI Standards CLI')
66
- .version('1.1.0');
177
+ .version(version);
67
178
 
68
179
  program
69
180
  .command('init')
70
- .description('Projeye AI standartlari dosyasini (AGENTS.md) indirir')
71
- .option('-t, --template <type>', 'Sablon turu (web, landing, mobile, backend)')
181
+ .description('Projeye AI standartlarini (AGENTS.md) veya sunum altyapisini indirir')
182
+ .option('-t, --template <type>', `Sablon turu (${TEMPLATE_KEYS.join(', ')})`)
72
183
  .action(async (options) => {
73
184
  let templateKey = options.template;
74
185
 
75
186
  if (!templateKey) {
76
- const response = await prompts({
77
- type: 'select',
78
- name: 'template',
79
- message: 'Lutfen bir Pro Sicht AI sablonu secin:',
80
- choices: Object.entries(TEMPLATES).map(([key, value]) => ({
81
- title: value.name,
82
- value: key
83
- }))
84
- });
85
-
86
- templateKey = response.template;
187
+ templateKey = await askTemplate('Lutfen bir Pro Sicht AI sablonu secin:');
87
188
  if (!templateKey) process.exit(0);
88
189
  }
89
190
 
90
- await fetchAndSaveTemplate(templateKey);
191
+ await install(templateKey, 'init');
91
192
  });
92
193
 
93
194
  program
94
195
  .command('update')
95
- .description('Projedeki AGENTS.md dosyasini tespit edip otomatik gunceller')
196
+ .description('Projedeki AGENTS.md (ve sunum altyapisinin cekirdek dosyalarini) tespit edip gunceller')
96
197
  .option('-t, --template <type>', 'Sablon turunu zorla belirle')
97
198
  .action(async (options) => {
98
199
  let templateKey = options.template;
@@ -104,21 +205,12 @@ program
104
205
  console.log(`🔍 Tespit edilen mevcut sablon: "${templateKey}"`);
105
206
  } else {
106
207
  console.log('⚠️ Projede mevcut bir AGENTS.md veya sablon etiketi bulunamadi.');
107
- const response = await prompts({
108
- type: 'select',
109
- name: 'template',
110
- message: 'Guncellemek istediginiz sablonu secin:',
111
- choices: Object.entries(TEMPLATES).map(([key, value]) => ({
112
- title: value.name,
113
- value: key
114
- }))
115
- });
116
- templateKey = response.template;
208
+ templateKey = await askTemplate('Guncellemek istediginiz sablonu secin:');
117
209
  if (!templateKey) process.exit(0);
118
210
  }
119
211
  }
120
212
 
121
- await fetchAndSaveTemplate(templateKey);
213
+ await install(templateKey, 'update');
122
214
  });
123
215
 
124
216
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,17 +1,30 @@
1
1
  {
2
2
  "name": "prosicht",
3
- "version": "1.1.0",
4
- "description": "Pro Sicht AI Standards CLI",
3
+ "version": "1.2.0",
4
+ "description": "Pro Sicht AI Standards CLI: AGENTS.md sablonlari ve sunum altyapisi",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "prosicht": "./index.js"
8
8
  },
9
+ "files": [
10
+ "index.js"
11
+ ],
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
9
15
  "scripts": {
10
16
  "test": "echo \"Error: no test specified\" && exit 1"
11
17
  },
12
- "keywords": ["ai", "agents", "cli", "prosicht"],
18
+ "keywords": [
19
+ "ai",
20
+ "agents",
21
+ "cli",
22
+ "prosicht",
23
+ "presentation"
24
+ ],
13
25
  "author": "Pro Sicht",
14
26
  "license": "MIT",
27
+ "homepage": "https://github.com/prosicht/ai-standards#readme",
15
28
  "dependencies": {
16
29
  "commander": "^11.1.0",
17
30
  "prompts": "^2.4.2"
package/tasks/todo.md DELETED
@@ -1,14 +0,0 @@
1
- # Şablon etiketi ve otomatik update
2
-
3
- ## Plan
4
-
5
- - [x] `index.js`: init sırasında `<!-- template: xxx -->` yaz
6
- - [x] `index.js`: update mevcut etiketi okuyup doğru şablonu çeksin
7
- - [x] `package.json` sürümünü `1.1.0` yap (CLI `--version` ile eşleşsin)
8
- - [x] Lokal test: init `-t web` + etiket; update `-t` olmadan tespit
9
-
10
- ## Review
11
-
12
- Init, `AGENTS.md` başına `<!-- template: web -->` yazıyor. `prosicht update` (`-t` yok) etiketi okuyup web şablonunu tekrar indirdi; dosyada tek etiket kaldı.
13
-
14
- Sürüm lokal `1.1.0`. npm'deki yayın hâlâ `1.0.0` — yeni davranış için `npm publish` gerekir.