@rbinar/dev-kit 1.0.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 +103 -0
- package/bin/cli.js +98 -0
- package/bundle/agents-bundle.enc +0 -0
- package/bundle/manifest.json +23 -0
- package/lib/commands/doctor.js +251 -0
- package/lib/commands/init.js +184 -0
- package/lib/commands/status.js +50 -0
- package/lib/commands/update.js +186 -0
- package/lib/constants.js +120 -0
- package/lib/crypto.js +64 -0
- package/lib/installer.js +234 -0
- package/lib/scaffold.js +109 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @blinkbrosai/dev-kit
|
|
2
|
+
|
|
3
|
+
VS Code Copilot Agent Mode için blink.dev ajan sistemini projelerinize kuran CLI aracı.
|
|
4
|
+
|
|
5
|
+
## Kurulum
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @blinkbrosai/dev-kit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> Node.js ≥18 gerektirir.
|
|
12
|
+
|
|
13
|
+
## Kullanım
|
|
14
|
+
|
|
15
|
+
### İlk kurulum
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
blink-dev init --key=SIFRE
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Bu komut:
|
|
22
|
+
- `.github/agents/` altına 14 uzmanlaşmış ajan dosyasını kurar
|
|
23
|
+
- `.github/prompts/` altına prompt dosyalarını kurar
|
|
24
|
+
- `.github/copilot-instructions.md` dosyasını oluşturur (mevcut içeriği korur)
|
|
25
|
+
- `.blink/` scaffold dizinini oluşturur
|
|
26
|
+
|
|
27
|
+
### Güncelleme
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
blink-dev update --key=SIFRE
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Mevcut kurulumu en son sürüme günceller. Güncelleme öncesi otomatik backup alır.
|
|
34
|
+
|
|
35
|
+
### Durum kontrolü
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
blink-dev status
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Kurulu versiyon, ajan sayısı ve kurulum tarihini gösterir.
|
|
42
|
+
|
|
43
|
+
### Sağlık kontrolü
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
blink-dev doctor
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Kurulumun bütünlüğünü doğrular: eksik dosyalar, bozuk frontmatter, kırık handoff'lar.
|
|
50
|
+
|
|
51
|
+
## Seçenekler
|
|
52
|
+
|
|
53
|
+
| Seçenek | Komutlar | Açıklama |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `--key <şifre>` | init, update | Şifreleme anahtarı |
|
|
56
|
+
| `--force` | init | Mevcut kurulumu ez |
|
|
57
|
+
| `--dry-run` | init, update | Değişiklikleri göster, uygulamadan |
|
|
58
|
+
|
|
59
|
+
## Ortam Değişkenleri
|
|
60
|
+
|
|
61
|
+
| Değişken | Açıklama |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `BLINK_DEV_KEY` | `--key` yerine kullanılabilir |
|
|
64
|
+
|
|
65
|
+
## Proje Yapısı (kurulum sonrası)
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
projeniz/
|
|
69
|
+
├── .github/
|
|
70
|
+
│ ├── agents/ ← 14 ajan dosyası
|
|
71
|
+
│ ├── prompts/ ← prompt dosyaları
|
|
72
|
+
│ └── copilot-instructions.md
|
|
73
|
+
├── .blink/ ← proje konvansiyonları (scaffold)
|
|
74
|
+
│ ├── architecture.md
|
|
75
|
+
│ ├── conventions.md
|
|
76
|
+
│ ├── stack.md
|
|
77
|
+
│ ├── testing.md
|
|
78
|
+
│ └── todo.md
|
|
79
|
+
└── .blink-dev-version ← versiyon takip dosyası
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Ajanlar
|
|
83
|
+
|
|
84
|
+
| Ajan | Rol |
|
|
85
|
+
|---|---|
|
|
86
|
+
| blink.dev.Triage | Görev seçimi ve önceliklendirme |
|
|
87
|
+
| blink.dev.Planner | Araştırma ve plan üretimi |
|
|
88
|
+
| blink.dev.Coordinator | Paralel yürütme |
|
|
89
|
+
| blink.dev.Reviewer | Kalite kapısı |
|
|
90
|
+
| blink.dev.Shipper | Build, PR, merge, tag |
|
|
91
|
+
| blink.dev.Debugger | Hata ayıklama |
|
|
92
|
+
| blink.dev.Security | Güvenlik denetimi |
|
|
93
|
+
| blink.dev.Analyst | Kod sağlığı analizi |
|
|
94
|
+
| blink.dev.Scribe | Dokümantasyon |
|
|
95
|
+
| blink.dev.Critic | Özellik eleştirisi |
|
|
96
|
+
| blink.dev.Brainstorm | Fikir üretimi |
|
|
97
|
+
| blink.dev.Ops | Sunucu teşhisi |
|
|
98
|
+
| blink.dev.Initializer | Proje scaffold |
|
|
99
|
+
| blink.dev.Guardian | Risk değerlendirme |
|
|
100
|
+
|
|
101
|
+
## Lisans
|
|
102
|
+
|
|
103
|
+
Tüm hakları saklıdır. Yeniden dağıtım yasaktır.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { Command } = require('commander');
|
|
5
|
+
const { version } = require('../package.json');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Anahtar çözümleme: option → env → interaktif prompt
|
|
10
|
+
*/
|
|
11
|
+
async function resolveKey(options) {
|
|
12
|
+
if (options.key) return options.key;
|
|
13
|
+
if (process.env.BLINK_DEV_KEY) return process.env.BLINK_DEV_KEY;
|
|
14
|
+
|
|
15
|
+
const rl = readline.createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
rl.question('Anahtar: ', (answer) => {
|
|
22
|
+
rl.close();
|
|
23
|
+
if (!answer || !answer.trim()) {
|
|
24
|
+
console.error('Hata: Anahtar belirtilmedi.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
resolve(answer.trim());
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const program = new Command();
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.name('blink-dev')
|
|
36
|
+
.description('blink.dev agent dosyalarını yönetme CLI aracı')
|
|
37
|
+
.version(version);
|
|
38
|
+
|
|
39
|
+
// init
|
|
40
|
+
program
|
|
41
|
+
.command('init')
|
|
42
|
+
.description('Agent dosyalarını projeye kur')
|
|
43
|
+
.option('--key <password>', 'Şifre')
|
|
44
|
+
.option('--force', 'Mevcut kurulumu ez', false)
|
|
45
|
+
.option('--dry-run', 'Sadece göster, değişiklik yapma', false)
|
|
46
|
+
.action(async (options) => {
|
|
47
|
+
try {
|
|
48
|
+
const key = await resolveKey(options);
|
|
49
|
+
await require('../lib/commands/init')({ key, force: options.force, dryRun: options.dryRun });
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error('init hatası:', err.message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// update
|
|
57
|
+
program
|
|
58
|
+
.command('update')
|
|
59
|
+
.description('Mevcut kurulumu güncelle')
|
|
60
|
+
.option('--key <password>', 'Şifre')
|
|
61
|
+
.option('--dry-run', 'Sadece göster, değişiklik yapma', false)
|
|
62
|
+
.action(async (options) => {
|
|
63
|
+
try {
|
|
64
|
+
const key = await resolveKey(options);
|
|
65
|
+
await require('../lib/commands/update')({ key, dryRun: options.dryRun });
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error('update hatası:', err.message);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// status
|
|
73
|
+
program
|
|
74
|
+
.command('status')
|
|
75
|
+
.description('Kurulum durumunu göster')
|
|
76
|
+
.action(async () => {
|
|
77
|
+
try {
|
|
78
|
+
await require('../lib/commands/status')();
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error('status hatası:', err.message);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// doctor
|
|
86
|
+
program
|
|
87
|
+
.command('doctor')
|
|
88
|
+
.description('Kurulumu doğrula ve sorunları raporla')
|
|
89
|
+
.action(async () => {
|
|
90
|
+
try {
|
|
91
|
+
await require('../lib/commands/doctor')();
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error('doctor hatası:', err.message);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
program.parse();
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"createdAt": "2026-04-05T15:05:51.237Z",
|
|
4
|
+
"files": [
|
|
5
|
+
"agents/Analyst.agent.md",
|
|
6
|
+
"agents/Brainstorm.agent.md",
|
|
7
|
+
"agents/Coordinator.agent.md",
|
|
8
|
+
"agents/Critic.agent.md",
|
|
9
|
+
"agents/Debugger.agent.md",
|
|
10
|
+
"agents/Guardian.agent.md",
|
|
11
|
+
"agents/Initializer.agent.md",
|
|
12
|
+
"agents/Ops.agent.md",
|
|
13
|
+
"agents/Planner.agent.md",
|
|
14
|
+
"agents/Reviewer.agent.md",
|
|
15
|
+
"agents/Scribe.agent.md",
|
|
16
|
+
"agents/Security.agent.md",
|
|
17
|
+
"agents/Shipper.agent.md",
|
|
18
|
+
"agents/Triage.agent.md",
|
|
19
|
+
"prompts/blink-dev-close-issue.prompt.md",
|
|
20
|
+
"copilot-instructions.md"
|
|
21
|
+
],
|
|
22
|
+
"checksum": "ee5f3bdae4c8f6689997769770b28d0c42b5523b902cbe7d6bf3407958a627bc"
|
|
23
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { isInitialized } = require('../installer');
|
|
6
|
+
const {
|
|
7
|
+
TARGET_AGENTS_DIR,
|
|
8
|
+
TARGET_BLINK_DIR,
|
|
9
|
+
TARGET_INSTRUCTIONS,
|
|
10
|
+
EXPECTED_AGENTS,
|
|
11
|
+
MARKER_START,
|
|
12
|
+
MARKER_END,
|
|
13
|
+
SCAFFOLD_FILES,
|
|
14
|
+
} = require('../constants');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Basit frontmatter kontrolü — `name:` ve `description:` satırlarını arar.
|
|
18
|
+
* Tam YAML parser kullanmaz.
|
|
19
|
+
*/
|
|
20
|
+
function checkFrontmatter(content) {
|
|
21
|
+
const lines = content.split('\n');
|
|
22
|
+
if (lines[0] !== '---') return false;
|
|
23
|
+
|
|
24
|
+
const endIdx = lines.indexOf('---', 1);
|
|
25
|
+
if (endIdx === -1) return false;
|
|
26
|
+
|
|
27
|
+
const block = lines.slice(1, endIdx).join('\n');
|
|
28
|
+
const hasName = /^name\s*:/m.test(block);
|
|
29
|
+
const hasDescription = /^description\s*:/m.test(block);
|
|
30
|
+
|
|
31
|
+
return hasName && hasDescription;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Agent dosyasındaki handoff referanslarını çıkarır.
|
|
36
|
+
* `agent: blink.dev.XYZ` → 'XYZ'
|
|
37
|
+
*/
|
|
38
|
+
function extractHandoffTargets(content) {
|
|
39
|
+
const targets = [];
|
|
40
|
+
const re = /^\s*agent\s*:\s*blink\.dev\.(\w+)/gm;
|
|
41
|
+
let match;
|
|
42
|
+
while ((match = re.exec(content)) !== null) {
|
|
43
|
+
targets.push(match[1]);
|
|
44
|
+
}
|
|
45
|
+
return targets;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function doctor() {
|
|
49
|
+
const cwd = process.cwd();
|
|
50
|
+
const checks = [];
|
|
51
|
+
|
|
52
|
+
// ── Kontrol 1: Kurulum dosyası mevcut mu ──
|
|
53
|
+
const initialized = isInitialized(cwd);
|
|
54
|
+
checks.push({
|
|
55
|
+
name: 'Kurulum dosyası',
|
|
56
|
+
status: initialized ? 'ok' : 'error',
|
|
57
|
+
detail: initialized ? 'Kurulum dosyası mevcut' : 'Kurulum dosyası bulunamadı',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ── Kontrol 2: Agent dizini mevcut mu ──
|
|
61
|
+
const agentsDir = path.join(cwd, TARGET_AGENTS_DIR);
|
|
62
|
+
const agentsDirExists = fs.existsSync(agentsDir);
|
|
63
|
+
checks.push({
|
|
64
|
+
name: 'Agent dizini',
|
|
65
|
+
status: agentsDirExists ? 'ok' : 'error',
|
|
66
|
+
detail: agentsDirExists ? 'Agent dizini mevcut' : 'Agent dizini bulunamadı',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// ── Kontrol 3: Beklenen agent dosyaları ──
|
|
70
|
+
const missingAgents = [];
|
|
71
|
+
for (const agent of EXPECTED_AGENTS) {
|
|
72
|
+
if (!fs.existsSync(path.join(agentsDir, agent))) {
|
|
73
|
+
missingAgents.push(agent.replace('.agent.md', ''));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const foundCount = EXPECTED_AGENTS.length - missingAgents.length;
|
|
77
|
+
const totalCount = EXPECTED_AGENTS.length;
|
|
78
|
+
if (missingAgents.length === 0) {
|
|
79
|
+
checks.push({
|
|
80
|
+
name: 'Agent dosyaları',
|
|
81
|
+
status: 'ok',
|
|
82
|
+
detail: `${totalCount}/${totalCount} agent dosyası mevcut`,
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
checks.push({
|
|
86
|
+
name: 'Agent dosyaları',
|
|
87
|
+
status: 'warn',
|
|
88
|
+
detail: `${foundCount}/${totalCount} agent dosyası mevcut (eksik: ${missingAgents.join(', ')})`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Kontrol 4: Agent YAML frontmatter geçerli mi ──
|
|
93
|
+
const brokenFrontmatter = [];
|
|
94
|
+
if (agentsDirExists) {
|
|
95
|
+
for (const agent of EXPECTED_AGENTS) {
|
|
96
|
+
const filePath = path.join(agentsDir, agent);
|
|
97
|
+
try {
|
|
98
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
99
|
+
if (!checkFrontmatter(content)) {
|
|
100
|
+
brokenFrontmatter.push(agent.replace('.agent.md', ''));
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
// Dosya okunamadıysa frontmatter kontrolü atla (eksik dosya zaten Kontrol 3'te raporlandı)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (brokenFrontmatter.length === 0) {
|
|
108
|
+
checks.push({
|
|
109
|
+
name: 'Agent frontmatter',
|
|
110
|
+
status: 'ok',
|
|
111
|
+
detail: 'Tüm agent dosyalarının frontmatter\'ı geçerli',
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
checks.push({
|
|
115
|
+
name: 'Agent frontmatter',
|
|
116
|
+
status: 'error',
|
|
117
|
+
detail: `${brokenFrontmatter.length} dosyada frontmatter bozuk: ${brokenFrontmatter.join(', ')}`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Kontrol 5: copilot-instructions.md mevcut ve marker'lar sağlam mı ──
|
|
122
|
+
const instructionsPath = path.join(cwd, TARGET_INSTRUCTIONS);
|
|
123
|
+
if (!fs.existsSync(instructionsPath)) {
|
|
124
|
+
checks.push({
|
|
125
|
+
name: 'copilot-instructions.md',
|
|
126
|
+
status: 'error',
|
|
127
|
+
detail: 'Dosya bulunamadı',
|
|
128
|
+
});
|
|
129
|
+
} else {
|
|
130
|
+
try {
|
|
131
|
+
const content = fs.readFileSync(instructionsPath, 'utf8');
|
|
132
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
133
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
134
|
+
|
|
135
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
136
|
+
checks.push({
|
|
137
|
+
name: 'copilot-instructions.md',
|
|
138
|
+
status: 'warn',
|
|
139
|
+
detail: 'Marker\'lar eksik',
|
|
140
|
+
});
|
|
141
|
+
} else if (startIdx >= endIdx) {
|
|
142
|
+
checks.push({
|
|
143
|
+
name: 'copilot-instructions.md',
|
|
144
|
+
status: 'warn',
|
|
145
|
+
detail: 'Marker sırası bozuk',
|
|
146
|
+
});
|
|
147
|
+
} else {
|
|
148
|
+
checks.push({
|
|
149
|
+
name: 'copilot-instructions.md',
|
|
150
|
+
status: 'ok',
|
|
151
|
+
detail: 'copilot-instructions.md sağlam',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
checks.push({
|
|
156
|
+
name: 'copilot-instructions.md',
|
|
157
|
+
status: 'error',
|
|
158
|
+
detail: 'Dosya okunamadı',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ── Kontrol 6: .blink/ scaffold mevcut mi ──
|
|
164
|
+
const blinkDir = path.join(cwd, TARGET_BLINK_DIR);
|
|
165
|
+
const scaffoldKeys = Object.keys(SCAFFOLD_FILES);
|
|
166
|
+
const missingScaffold = [];
|
|
167
|
+
if (!fs.existsSync(blinkDir)) {
|
|
168
|
+
missingScaffold.push(...scaffoldKeys);
|
|
169
|
+
} else {
|
|
170
|
+
for (const file of scaffoldKeys) {
|
|
171
|
+
if (!fs.existsSync(path.join(blinkDir, file))) {
|
|
172
|
+
missingScaffold.push(file);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const scaffoldFound = scaffoldKeys.length - missingScaffold.length;
|
|
177
|
+
const scaffoldTotal = scaffoldKeys.length;
|
|
178
|
+
if (missingScaffold.length === 0) {
|
|
179
|
+
checks.push({
|
|
180
|
+
name: '.blink/ scaffold',
|
|
181
|
+
status: 'ok',
|
|
182
|
+
detail: `.blink/ scaffold tam (${scaffoldTotal}/${scaffoldTotal})`,
|
|
183
|
+
});
|
|
184
|
+
} else {
|
|
185
|
+
checks.push({
|
|
186
|
+
name: '.blink/ scaffold',
|
|
187
|
+
status: 'warn',
|
|
188
|
+
detail: `${missingScaffold.length}/${scaffoldTotal} dosya eksik`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── Kontrol 7: Handoff hedefleri tutarlı mı ──
|
|
193
|
+
const brokenHandoffs = [];
|
|
194
|
+
if (agentsDirExists) {
|
|
195
|
+
for (const agent of EXPECTED_AGENTS) {
|
|
196
|
+
const filePath = path.join(agentsDir, agent);
|
|
197
|
+
try {
|
|
198
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
199
|
+
const targets = extractHandoffTargets(content);
|
|
200
|
+
for (const target of targets) {
|
|
201
|
+
const targetFile = `${target}.agent.md`;
|
|
202
|
+
if (!fs.existsSync(path.join(agentsDir, targetFile))) {
|
|
203
|
+
const source = agent.replace('.agent.md', '');
|
|
204
|
+
const entry = `blink.dev.${source} → ${targetFile} bulunamadı`;
|
|
205
|
+
if (!brokenHandoffs.includes(entry)) {
|
|
206
|
+
brokenHandoffs.push(entry);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
// Dosya okunamadıysa handoff kontrolü atla
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (brokenHandoffs.length === 0) {
|
|
216
|
+
checks.push({
|
|
217
|
+
name: 'Handoff hedefleri',
|
|
218
|
+
status: 'ok',
|
|
219
|
+
detail: 'Tüm handoff hedefleri mevcut',
|
|
220
|
+
});
|
|
221
|
+
} else {
|
|
222
|
+
checks.push({
|
|
223
|
+
name: 'Handoff hedefleri',
|
|
224
|
+
status: 'error',
|
|
225
|
+
detail: `${brokenHandoffs.length} kırık handoff: ${brokenHandoffs.join(', ')}`,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── Çıktı ──
|
|
230
|
+
const icons = { ok: '✅', warn: '⚠️ ', error: '❌' };
|
|
231
|
+
const lines = ['blink-dev sağlık raporu', '═══════════════════════', ''];
|
|
232
|
+
|
|
233
|
+
for (const check of checks) {
|
|
234
|
+
lines.push(`${icons[check.status]} ${check.detail}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const okCount = checks.filter((c) => c.status === 'ok').length;
|
|
238
|
+
const warnCount = checks.filter((c) => c.status === 'warn').length;
|
|
239
|
+
const errorCount = checks.filter((c) => c.status === 'error').length;
|
|
240
|
+
|
|
241
|
+
lines.push('');
|
|
242
|
+
lines.push(`Sonuç: ${okCount} başarılı, ${warnCount} uyarı, ${errorCount} hata`);
|
|
243
|
+
|
|
244
|
+
console.log(lines.join('\n'));
|
|
245
|
+
|
|
246
|
+
if (errorCount > 0) {
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
module.exports = doctor;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const zlib = require('node:zlib');
|
|
7
|
+
const { Readable } = require('node:stream');
|
|
8
|
+
const { pipeline } = require('node:stream/promises');
|
|
9
|
+
const tar = require('tar');
|
|
10
|
+
|
|
11
|
+
const { decrypt } = require('../crypto');
|
|
12
|
+
const {
|
|
13
|
+
installFiles,
|
|
14
|
+
mergeCopilotInstructions,
|
|
15
|
+
createScaffold,
|
|
16
|
+
isInitialized,
|
|
17
|
+
writeVersion,
|
|
18
|
+
} = require('../installer');
|
|
19
|
+
const {
|
|
20
|
+
BUNDLE_PATH,
|
|
21
|
+
TARGET_AGENTS_DIR,
|
|
22
|
+
TARGET_PROMPTS_DIR,
|
|
23
|
+
} = require('../constants');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tarball buffer'ından dosya haritası çıkarır.
|
|
27
|
+
* @param {Buffer} tarBuffer - Gunzip edilmiş .tar.gz buffer
|
|
28
|
+
* @returns {Promise<{ fileMap: Map<string, Buffer>, copilotInstructions: string | null }>}
|
|
29
|
+
*/
|
|
30
|
+
async function extractFileMap(tarBuffer) {
|
|
31
|
+
const fileMap = new Map();
|
|
32
|
+
let copilotInstructions = null;
|
|
33
|
+
|
|
34
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'blink-dev-'));
|
|
35
|
+
const tmpTarGz = path.join(tmpDir, 'bundle.tar.gz');
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// 1. Buffer'ı geçici dosyaya yaz
|
|
39
|
+
fs.writeFileSync(tmpTarGz, tarBuffer);
|
|
40
|
+
|
|
41
|
+
// 2. Geçici dizine extract et
|
|
42
|
+
const extractDir = path.join(tmpDir, 'extracted');
|
|
43
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
44
|
+
|
|
45
|
+
await tar.x({
|
|
46
|
+
file: tmpTarGz,
|
|
47
|
+
cwd: extractDir,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 3. Extract edilen dosyaları oku ve fileMap'e ekle
|
|
51
|
+
function walkDir(dir, prefix) {
|
|
52
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const fullPath = path.join(dir, entry.name);
|
|
55
|
+
const relPath = prefix ? path.join(prefix, entry.name) : entry.name;
|
|
56
|
+
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
walkDir(fullPath, relPath);
|
|
59
|
+
} else if (entry.isFile()) {
|
|
60
|
+
const content = fs.readFileSync(fullPath);
|
|
61
|
+
const mapped = mapFilePath(relPath);
|
|
62
|
+
|
|
63
|
+
if (mapped === null) {
|
|
64
|
+
// copilot-instructions.md — ayrı tut
|
|
65
|
+
copilotInstructions = content.toString('utf8');
|
|
66
|
+
} else if (mapped) {
|
|
67
|
+
fileMap.set(mapped, content);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
walkDir(extractDir, '');
|
|
74
|
+
} finally {
|
|
75
|
+
// Geçici dosyaları temizle
|
|
76
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { fileMap, copilotInstructions };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Tarball içi yolu hedef yola dönüştürür.
|
|
84
|
+
* @param {string} entryPath - Tarball içi dosya yolu
|
|
85
|
+
* @returns {string | null} - null = copilot-instructions.md (ayrı merge)
|
|
86
|
+
*/
|
|
87
|
+
function mapFilePath(entryPath) {
|
|
88
|
+
// Normalize separators
|
|
89
|
+
const normalized = entryPath.split(path.sep).join('/');
|
|
90
|
+
|
|
91
|
+
if (normalized === 'copilot-instructions.md') {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (normalized.startsWith('agents/')) {
|
|
96
|
+
return path.join(TARGET_AGENTS_DIR, normalized.slice('agents/'.length));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (normalized.startsWith('prompts/')) {
|
|
100
|
+
return path.join(TARGET_PROMPTS_DIR, normalized.slice('prompts/'.length));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Diğer dosyalar olduğu gibi kalsın
|
|
104
|
+
return normalized;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* blink-dev init --key=SIFRE komutu handler'ı.
|
|
109
|
+
* @param {{ key: string, force?: boolean, dryRun?: boolean }} options
|
|
110
|
+
*/
|
|
111
|
+
async function init({ key, force = false, dryRun = false }) {
|
|
112
|
+
const cwd = process.cwd();
|
|
113
|
+
|
|
114
|
+
// 1. Kurulum kontrolü
|
|
115
|
+
if (isInitialized(cwd) && !force) {
|
|
116
|
+
console.error('Bu proje zaten blink-dev ile kurulmuş. --force ile yeniden kurabilirsiniz.');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 2. Bundle oku
|
|
121
|
+
let encBuffer;
|
|
122
|
+
try {
|
|
123
|
+
encBuffer = fs.readFileSync(BUNDLE_PATH);
|
|
124
|
+
} catch (_) {
|
|
125
|
+
console.error('Bundle dosyası bulunamadı: ' + BUNDLE_PATH);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 3. Decrypt
|
|
130
|
+
let tarBuffer;
|
|
131
|
+
try {
|
|
132
|
+
tarBuffer = decrypt(encBuffer, key);
|
|
133
|
+
} catch (_) {
|
|
134
|
+
console.error('Hata: Geçersiz anahtar.');
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 4. Extract
|
|
139
|
+
const { fileMap, copilotInstructions } = await extractFileMap(tarBuffer);
|
|
140
|
+
|
|
141
|
+
// 5. Dry-run kontrolü
|
|
142
|
+
if (dryRun) {
|
|
143
|
+
const result = installFiles(fileMap, cwd, { dryRun: true });
|
|
144
|
+
if (result.installed.length > 0) {
|
|
145
|
+
console.log('Kurulacak dosyalar:');
|
|
146
|
+
for (const f of result.installed) {
|
|
147
|
+
console.log(' ' + f);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (result.skipped.length > 0) {
|
|
151
|
+
console.log('Atlanacak dosyalar:');
|
|
152
|
+
for (const f of result.skipped) {
|
|
153
|
+
console.log(' ' + f);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 6. Dosyaları yaz
|
|
160
|
+
const { installed, skipped } = installFiles(fileMap, cwd, { force });
|
|
161
|
+
|
|
162
|
+
// 7. Copilot instructions merge
|
|
163
|
+
if (copilotInstructions) {
|
|
164
|
+
mergeCopilotInstructions(copilotInstructions, cwd);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 8. Scaffold oluştur
|
|
168
|
+
const scaffold = createScaffold(cwd);
|
|
169
|
+
|
|
170
|
+
// 9. Versiyon yaz
|
|
171
|
+
writeVersion(cwd, require('../../package.json').version);
|
|
172
|
+
|
|
173
|
+
// 10. Başarı mesajı
|
|
174
|
+
console.log('');
|
|
175
|
+
console.log('✅ blink-dev başarıyla kuruldu!');
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log('📁 Kurulan dosyalar: ' + installed.length);
|
|
178
|
+
console.log('⏩ Atlanan dosyalar: ' + skipped.length);
|
|
179
|
+
console.log('📝 .blink/ scaffold: ' + scaffold.created.length + ' dosya oluşturuldu');
|
|
180
|
+
console.log('');
|
|
181
|
+
console.log('Sonraki adım: VS Code Agent Mode\'u açın ve ajanları kullanmaya başlayın.');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = init;
|