@dreamlogic-ai/cli 2.0.7 → 2.0.8
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/dist/commands/install.js +54 -32
- package/dist/commands/update.js +16 -0
- package/dist/lib/agents.js +24 -2
- package/dist/lib/installer.js +28 -24
- package/dist/lib/ui.d.ts +1 -1
- package/dist/lib/ui.js +48 -9
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
package/dist/commands/install.js
CHANGED
|
@@ -111,7 +111,7 @@ export async function installCommand(skillIds, opts) {
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
-
// R4-FIX #8:
|
|
114
|
+
// R4-FIX #8 + R5-L02 FIX: Check disk space WITHOUT creating dir before confirmation
|
|
115
115
|
try {
|
|
116
116
|
const totalSize = selectedIds.reduce((sum, id) => {
|
|
117
117
|
const s = skills.find((s) => s.id === id);
|
|
@@ -119,11 +119,11 @@ export async function installCommand(skillIds, opts) {
|
|
|
119
119
|
}, 0);
|
|
120
120
|
if (totalSize > 0) {
|
|
121
121
|
const installPath = getInstallDir();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
122
|
+
// R5-L02: Only check existing parent dir, don't create install dir yet
|
|
123
|
+
const { existsSync: existsSyncFs } = await import("fs");
|
|
124
|
+
const checkPath = existsSyncFs(installPath) ? installPath : join(installPath, "..");
|
|
125
|
+
const fsStats = statfsSync(checkPath);
|
|
125
126
|
const freeSpace = fsStats.bsize * fsStats.bavail;
|
|
126
|
-
// 需要解压后约 2x 空间(压缩包 + 解压内容)
|
|
127
127
|
if (freeSpace < totalSize * 2) {
|
|
128
128
|
ui.warning(`磁盘剩余空间不足: ${ui.fileSize(freeSpace)} 可用, 预计需要 ${ui.fileSize(totalSize * 2)}`);
|
|
129
129
|
}
|
|
@@ -156,54 +156,76 @@ export async function installCommand(skillIds, opts) {
|
|
|
156
156
|
else {
|
|
157
157
|
ui.warning(`${successCount}/${selectedIds.length} 已安装,请检查上方错误信息。`);
|
|
158
158
|
}
|
|
159
|
-
// ── Agent Registration Step ──
|
|
159
|
+
// ── Agent Registration Step (skills.sh-style) ──
|
|
160
160
|
if (successCount > 0 && !opts.yes) {
|
|
161
161
|
console.log();
|
|
162
162
|
const { universal, detected } = detectAgents();
|
|
163
163
|
const totalAgents = universal.length + detected.length;
|
|
164
164
|
if (totalAgents > 0) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
//
|
|
165
|
+
ui.info(`共检测到 ${totalAgents} 个 AI Agent`);
|
|
166
|
+
console.log();
|
|
167
|
+
// Show universal agents as always-included (locked)
|
|
168
|
+
console.log(chalk.dim(" ── Universal (.agents/skills) ── 自动包含 ──────────"));
|
|
168
169
|
for (const agent of universal) {
|
|
169
|
-
|
|
170
|
-
label: agent.name,
|
|
171
|
-
value: `u:${agent.name}`,
|
|
172
|
-
hint: "universal (.agents/skills)",
|
|
173
|
-
});
|
|
170
|
+
console.log(` ${chalk.green("•")} ${agent.name}`);
|
|
174
171
|
}
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
172
|
+
// Build selectable options for additional agents
|
|
173
|
+
if (detected.length > 0) {
|
|
174
|
+
console.log();
|
|
175
|
+
console.log(chalk.dim(" ── 已检测的 Agent ──────────────────────────────"));
|
|
176
|
+
const additionalOptions = detected.map(agent => ({
|
|
177
|
+
label: `${agent.name}`,
|
|
178
|
+
value: agent.name,
|
|
179
|
+
hint: agent.skillsDir,
|
|
180
|
+
}));
|
|
181
|
+
const agentChoice = await clack.multiselect({
|
|
182
|
+
message: "选择要额外注册的 Agent(Universal 已自动包含):",
|
|
183
|
+
options: additionalOptions,
|
|
184
|
+
required: false,
|
|
181
185
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const allAgents = [...universal, ...detected];
|
|
191
|
-
const chosenAgents = allAgents.filter(a => chosenNames.includes(a.name));
|
|
186
|
+
// Always register universal + selected additional
|
|
187
|
+
const chosenAdditionalNames = (!clack.isCancel(agentChoice))
|
|
188
|
+
? agentChoice
|
|
189
|
+
: [];
|
|
190
|
+
const chosenAgents = [
|
|
191
|
+
...universal,
|
|
192
|
+
...detected.filter(a => chosenAdditionalNames.includes(a.name)),
|
|
193
|
+
];
|
|
192
194
|
// Register each installed skill with chosen agents
|
|
193
195
|
const installDir = getInstallDir();
|
|
194
196
|
for (const id of selectedIds) {
|
|
195
197
|
const skillPath = join(installDir, id);
|
|
196
198
|
const results = registerSkillWithAgents(id, skillPath, chosenAgents);
|
|
197
199
|
const created = results.filter(r => r.status === "created").length;
|
|
200
|
+
const existing = results.filter(r => r.status === "exists").length;
|
|
198
201
|
const errors = results.filter(r => r.status === "error");
|
|
199
|
-
if (created > 0) {
|
|
200
|
-
ui.ok(`${id}: 已注册到 ${created} 个 Agent`);
|
|
202
|
+
if (created > 0 || existing > 0) {
|
|
203
|
+
ui.ok(`${id}: 已注册到 ${created + existing} 个 Agent (${created} 新建, ${existing} 已存在)`);
|
|
201
204
|
}
|
|
202
205
|
for (const e of errors) {
|
|
203
206
|
ui.warning(`${e.agent}: ${e.error}`);
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
209
|
}
|
|
210
|
+
else {
|
|
211
|
+
// No additional agents detected — just register universal
|
|
212
|
+
console.log();
|
|
213
|
+
const registerUniversal = await clack.confirm({
|
|
214
|
+
message: "注册技能到 Universal Agent 目录?",
|
|
215
|
+
initialValue: true,
|
|
216
|
+
});
|
|
217
|
+
if (!clack.isCancel(registerUniversal) && registerUniversal) {
|
|
218
|
+
const installDir = getInstallDir();
|
|
219
|
+
for (const id of selectedIds) {
|
|
220
|
+
const skillPath = join(installDir, id);
|
|
221
|
+
const results = registerSkillWithAgents(id, skillPath, universal);
|
|
222
|
+
const created = results.filter(r => r.status === "created").length;
|
|
223
|
+
if (created > 0) {
|
|
224
|
+
ui.ok(`${id}: 已注册到 Universal Agent 目录`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
207
229
|
}
|
|
208
230
|
}
|
|
209
231
|
// Suggest MCP setup
|
package/dist/commands/update.js
CHANGED
|
@@ -119,6 +119,22 @@ export async function updateCommand(opts) {
|
|
|
119
119
|
console.log();
|
|
120
120
|
ui.header(`正在更新 ${u.name}`);
|
|
121
121
|
ui.line(`${u.currentVersion} → ${chalk.green(u.latestVersion)}`);
|
|
122
|
+
// Lossless upgrade: warn about user-modified files
|
|
123
|
+
const installDir = (await import("../lib/config.js")).getInstallDir();
|
|
124
|
+
const skillDir = (await import("path")).join(installDir, id);
|
|
125
|
+
const { existsSync: existsSyncCheck, readdirSync: readDirCheck } = await import("fs");
|
|
126
|
+
if (existsSyncCheck(skillDir)) {
|
|
127
|
+
try {
|
|
128
|
+
const userFiles = readDirCheck(skillDir, { recursive: true });
|
|
129
|
+
const knownUserDirs = ["memory", "signers", "output", "cache", "logs"];
|
|
130
|
+
const hasUserData = userFiles.some((f) => knownUserDirs.some(d => String(f).startsWith(d + "/") || String(f).startsWith(d + "\\") || String(f) === d));
|
|
131
|
+
if (hasUserData) {
|
|
132
|
+
ui.warning("检测到用户数据目录 — 升级将保留 memory/, signers/, output/ 等用户数据");
|
|
133
|
+
ui.line(chalk.dim(" 原版本已自动备份,可随时回滚: dreamlogic rollback " + id));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch { /* best-effort check */ }
|
|
137
|
+
}
|
|
122
138
|
try {
|
|
123
139
|
await installSkill(client, u.id, u.packageFile, u.sha256, u.latestVersion);
|
|
124
140
|
ui.ok(`已更新到 ${u.latestVersion}`);
|
package/dist/lib/agents.js
CHANGED
|
@@ -11,11 +11,14 @@ import { homedir, platform } from "os";
|
|
|
11
11
|
// ===== Agent Directory Map =====
|
|
12
12
|
// Universal agents: always shown (use .agents/skills as shared dir)
|
|
13
13
|
// Additional agents: shown only if detected on the system
|
|
14
|
+
// Based on skills.sh (vercel-labs/skills) comprehensive agent directory
|
|
14
15
|
const AGENT_CONFIGS = [
|
|
15
|
-
// ── Universal (.agents/skills) ──
|
|
16
|
+
// ── Universal (.agents/skills) ── always included ──
|
|
16
17
|
{ name: "Amp", skillsDir: ".agents/skills", group: "universal" },
|
|
18
|
+
{ name: "Antigravity", skillsDir: ".agents/skills", group: "universal" },
|
|
17
19
|
{ name: "Cline", skillsDir: ".agents/skills", group: "universal" },
|
|
18
20
|
{ name: "Codex", skillsDir: ".agents/skills", group: "universal", envOverride: "CODEX_HOME" },
|
|
21
|
+
{ name: "Command Code", skillsDir: ".agents/skills", group: "universal" },
|
|
19
22
|
{ name: "Cursor", skillsDir: ".agents/skills", group: "universal" },
|
|
20
23
|
{ name: "Deep Agents", skillsDir: ".agents/skills", group: "universal" },
|
|
21
24
|
{ name: "Firebender", skillsDir: ".agents/skills", group: "universal" },
|
|
@@ -24,7 +27,7 @@ const AGENT_CONFIGS = [
|
|
|
24
27
|
{ name: "Kimi Code CLI", skillsDir: ".agents/skills", group: "universal" },
|
|
25
28
|
{ name: "OpenCode", skillsDir: ".agents/skills", group: "universal" },
|
|
26
29
|
{ name: "Warp", skillsDir: ".agents/skills", group: "universal" },
|
|
27
|
-
// ── Additional agents ──
|
|
30
|
+
// ── Additional agents ── shown only if detected ──
|
|
28
31
|
{ name: "Augment", skillsDir: ".augment/skills", group: "additional", detect: ".augment" },
|
|
29
32
|
{ name: "Claude Code", skillsDir: ".claude/skills", group: "additional", detect: ".claude", envOverride: "CLAUDE_CONFIG_DIR" },
|
|
30
33
|
{ name: "OpenClaw", skillsDir: "skills", group: "additional", detect: "skills" },
|
|
@@ -34,10 +37,29 @@ const AGENT_CONFIGS = [
|
|
|
34
37
|
{ name: "Goose", skillsDir: ".config/goose/skills", group: "additional", detect: ".config/goose" },
|
|
35
38
|
{ name: "Roo Code", skillsDir: ".roo/skills", group: "additional", detect: ".roo" },
|
|
36
39
|
{ name: "Trae", skillsDir: ".trae/skills", group: "additional", detect: ".trae" },
|
|
40
|
+
{ name: "Trae CN", skillsDir: ".trae-cn/skills", group: "additional", detect: ".trae-cn" },
|
|
37
41
|
{ name: "Void", skillsDir: ".void/skills", group: "additional", detect: ".void" },
|
|
38
42
|
{ name: "Zed", skillsDir: ".config/zed/skills", group: "additional", detect: ".config/zed" },
|
|
39
43
|
{ name: "Aider", skillsDir: ".aider/skills", group: "additional", detect: ".aider" },
|
|
40
44
|
{ name: "Plandex", skillsDir: ".plandex/skills", group: "additional", detect: ".plandex" },
|
|
45
|
+
{ name: "IBM Bob", skillsDir: ".bob/skills", group: "additional", detect: ".bob" },
|
|
46
|
+
{ name: "Cortex Code", skillsDir: ".cortex/skills", group: "additional", detect: ".cortex" },
|
|
47
|
+
{ name: "Crush", skillsDir: ".crush/skills", group: "additional", detect: ".crush" },
|
|
48
|
+
{ name: "Droid", skillsDir: ".droid/skills", group: "additional", detect: ".droid" },
|
|
49
|
+
{ name: "iFlow CLI", skillsDir: ".iflow/skills", group: "additional", detect: ".iflow" },
|
|
50
|
+
{ name: "Junie", skillsDir: ".junie/skills", group: "additional", detect: ".junie" },
|
|
51
|
+
{ name: "Kilo", skillsDir: ".kilo/skills", group: "additional", detect: ".kilo" },
|
|
52
|
+
{ name: "Kiro CLI", skillsDir: ".kiro/skills", group: "additional", detect: ".kiro" },
|
|
53
|
+
{ name: "Kode", skillsDir: ".kode/skills", group: "additional", detect: ".kode" },
|
|
54
|
+
{ name: "Mistral Vibe", skillsDir: ".mistral-vibe/skills", group: "additional", detect: ".mistral-vibe" },
|
|
55
|
+
{ name: "Mux", skillsDir: ".mux/skills", group: "additional", detect: ".mux" },
|
|
56
|
+
{ name: "Neovate", skillsDir: ".neovate/skills", group: "additional", detect: ".neovate" },
|
|
57
|
+
{ name: "OpenHands", skillsDir: ".openhands/skills", group: "additional", detect: ".openhands" },
|
|
58
|
+
{ name: "Pi", skillsDir: ".pi/skills", group: "additional", detect: ".pi" },
|
|
59
|
+
{ name: "Qoder", skillsDir: ".qoder/skills", group: "additional", detect: ".qoder" },
|
|
60
|
+
{ name: "Qwen Code", skillsDir: ".qwen-code/skills", group: "additional", detect: ".qwen-code" },
|
|
61
|
+
{ name: "Replit", skillsDir: ".agents/skills", group: "additional", detect: ".replit" },
|
|
62
|
+
{ name: "ZenCoder", skillsDir: ".zencoder/skills", group: "additional", detect: ".zencoder" },
|
|
41
63
|
];
|
|
42
64
|
// R4-FIX: Shared safe skill ID pattern
|
|
43
65
|
const SAFE_SKILL_ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
package/dist/lib/installer.js
CHANGED
|
@@ -246,13 +246,19 @@ function extractZip(buffer, targetDir) {
|
|
|
246
246
|
}
|
|
247
247
|
let totalExtracted = 0;
|
|
248
248
|
let entryCount = 0;
|
|
249
|
+
let aborted = false; // R5-L06 FIX: Prevent reject-after-close race
|
|
250
|
+
const safeReject = (error) => {
|
|
251
|
+
if (aborted)
|
|
252
|
+
return;
|
|
253
|
+
aborted = true;
|
|
254
|
+
safeReject(error);
|
|
255
|
+
};
|
|
249
256
|
zipfile.readEntry();
|
|
250
257
|
zipfile.on("entry", (entry) => {
|
|
251
258
|
// R1-03: Entry count limit
|
|
252
259
|
entryCount++;
|
|
253
260
|
if (entryCount > MAX_ENTRY_COUNT) {
|
|
254
|
-
|
|
255
|
-
reject(new Error(`Too many ZIP entries (>${MAX_ENTRY_COUNT}) — possible zip bomb`));
|
|
261
|
+
safeReject(new Error(`Too many ZIP entries (>${MAX_ENTRY_COUNT}) — possible zip bomb`));
|
|
256
262
|
return;
|
|
257
263
|
}
|
|
258
264
|
const entryPath = normalize(entry.fileName);
|
|
@@ -261,8 +267,7 @@ function extractZip(buffer, targetDir) {
|
|
|
261
267
|
if (entryPath.startsWith("..") ||
|
|
262
268
|
entryPath.endsWith(`${sep}..`) ||
|
|
263
269
|
entryPath.includes(`${sep}..${sep}`)) {
|
|
264
|
-
|
|
265
|
-
reject(new Error(`Unsafe path in ZIP: ${entry.fileName}`));
|
|
270
|
+
safeReject(new Error(`Unsafe path in ZIP: ${entry.fileName}`));
|
|
266
271
|
return;
|
|
267
272
|
}
|
|
268
273
|
// R4-FIX #2: 拒绝 Windows Alternate Data Streams (filename:stream)
|
|
@@ -270,8 +275,7 @@ function extractZip(buffer, targetDir) {
|
|
|
270
275
|
const pathParts = entryPath.split(sep);
|
|
271
276
|
for (const part of pathParts) {
|
|
272
277
|
if (part.includes(":") && !/^[A-Za-z]:$/.test(part)) {
|
|
273
|
-
|
|
274
|
-
reject(new Error(`不安全的路径(可能包含 Windows ADS): ${entry.fileName}`));
|
|
278
|
+
safeReject(new Error(`不安全的路径(可能包含 Windows ADS): ${entry.fileName}`));
|
|
275
279
|
return;
|
|
276
280
|
}
|
|
277
281
|
}
|
|
@@ -279,20 +283,17 @@ function extractZip(buffer, targetDir) {
|
|
|
279
283
|
const externalAttrs = (entry.externalFileAttributes >>> 16) & 0xFFFF;
|
|
280
284
|
const S_IFLNK = 0xA000;
|
|
281
285
|
if ((externalAttrs & 0xF000) === S_IFLNK) {
|
|
282
|
-
|
|
283
|
-
reject(new Error(`Symlink entry rejected in ZIP: ${entry.fileName}`));
|
|
286
|
+
safeReject(new Error(`Symlink entry rejected in ZIP: ${entry.fileName}`));
|
|
284
287
|
return;
|
|
285
288
|
}
|
|
286
289
|
// R1-03: Pre-check declared size (actual bytes tracked in counting stream below)
|
|
287
290
|
if (entry.uncompressedSize > MAX_SINGLE_FILE_SIZE) {
|
|
288
|
-
|
|
289
|
-
reject(new Error(`File too large in ZIP: ${entry.fileName} (${entry.uncompressedSize} bytes)`));
|
|
291
|
+
safeReject(new Error(`File too large in ZIP: ${entry.fileName} (${entry.uncompressedSize} bytes)`));
|
|
290
292
|
return;
|
|
291
293
|
}
|
|
292
294
|
// R1-03: Pre-check cumulative declared size (actual bytes tracked in stream)
|
|
293
295
|
if (totalExtracted + entry.uncompressedSize > MAX_TOTAL_EXTRACT_SIZE) {
|
|
294
|
-
|
|
295
|
-
reject(new Error(`Total extracted size exceeds ${MAX_TOTAL_EXTRACT_SIZE / 1024 / 1024}MB — possible zip bomb`));
|
|
296
|
+
safeReject(new Error(`Total extracted size exceeds ${MAX_TOTAL_EXTRACT_SIZE / 1024 / 1024}MB — possible zip bomb`));
|
|
296
297
|
return;
|
|
297
298
|
}
|
|
298
299
|
const fullPath = join(targetDir, entryPath);
|
|
@@ -301,8 +302,7 @@ function extractZip(buffer, targetDir) {
|
|
|
301
302
|
// BUG-1 fix: use path.sep (not hardcoded "/") for Windows compatibility
|
|
302
303
|
const resolvedTarget = pathResolve(targetDir) + sep;
|
|
303
304
|
if (!resolvedPath.startsWith(resolvedTarget) && resolvedPath !== pathResolve(targetDir)) {
|
|
304
|
-
|
|
305
|
-
reject(new Error(`Path traversal detected: ${entry.fileName}`));
|
|
305
|
+
safeReject(new Error(`Path traversal detected: ${entry.fileName}`));
|
|
306
306
|
return;
|
|
307
307
|
}
|
|
308
308
|
if (entry.fileName.endsWith("/")) {
|
|
@@ -315,8 +315,7 @@ function extractZip(buffer, targetDir) {
|
|
|
315
315
|
mkdirSync(join(fullPath, ".."), { recursive: true });
|
|
316
316
|
zipfile.openReadStream(entry, (err, readStream) => {
|
|
317
317
|
if (err || !readStream) {
|
|
318
|
-
|
|
319
|
-
reject(err || new Error("Failed to read ZIP entry"));
|
|
318
|
+
safeReject(err || new Error("Failed to read ZIP entry"));
|
|
320
319
|
return;
|
|
321
320
|
}
|
|
322
321
|
// INS-01 FIX: Track actual bytes written (not declared uncompressedSize)
|
|
@@ -326,12 +325,18 @@ function extractZip(buffer, targetDir) {
|
|
|
326
325
|
fileBytes += chunk.length;
|
|
327
326
|
totalExtracted += chunk.length;
|
|
328
327
|
if (fileBytes > MAX_SINGLE_FILE_SIZE) {
|
|
329
|
-
|
|
328
|
+
if (!aborted) {
|
|
329
|
+
aborted = true;
|
|
330
|
+
zipfile.close();
|
|
331
|
+
}
|
|
330
332
|
callback(new Error(`Actual file size exceeds limit: ${entry.fileName}`));
|
|
331
333
|
return;
|
|
332
334
|
}
|
|
333
335
|
if (totalExtracted > MAX_TOTAL_EXTRACT_SIZE) {
|
|
334
|
-
|
|
336
|
+
if (!aborted) {
|
|
337
|
+
aborted = true;
|
|
338
|
+
zipfile.close();
|
|
339
|
+
}
|
|
335
340
|
callback(new Error(`Total extracted size exceeds ${MAX_TOTAL_EXTRACT_SIZE / 1024 / 1024}MB — possible zip bomb`));
|
|
336
341
|
return;
|
|
337
342
|
}
|
|
@@ -345,22 +350,21 @@ function extractZip(buffer, targetDir) {
|
|
|
345
350
|
try {
|
|
346
351
|
if (lstatSync(fullPath).isSymbolicLink()) {
|
|
347
352
|
unlinkSync(fullPath);
|
|
348
|
-
|
|
349
|
-
reject(new Error(`Post-extract symlink detected: ${entry.fileName}`));
|
|
353
|
+
safeReject(new Error(`Post-extract symlink detected: ${entry.fileName}`));
|
|
350
354
|
return;
|
|
351
355
|
}
|
|
352
356
|
}
|
|
353
357
|
catch { /* stat failed = file doesn't exist, ok to continue */ }
|
|
354
358
|
zipfile.readEntry();
|
|
355
359
|
});
|
|
356
|
-
writeStream.on("error", (e) => {
|
|
357
|
-
countingStream.on("error", (e) => {
|
|
358
|
-
readStream.on("error", (e) => {
|
|
360
|
+
writeStream.on("error", (e) => { safeReject(e instanceof Error ? e : new Error(String(e))); });
|
|
361
|
+
countingStream.on("error", (e) => { safeReject(e instanceof Error ? e : new Error(String(e))); });
|
|
362
|
+
readStream.on("error", (e) => { safeReject(e instanceof Error ? e : new Error(String(e))); });
|
|
359
363
|
});
|
|
360
364
|
}
|
|
361
365
|
});
|
|
362
366
|
zipfile.on("end", () => resolve());
|
|
363
|
-
zipfile.on("error",
|
|
367
|
+
zipfile.on("error", (e) => safeReject(e instanceof Error ? e : new Error(String(e))));
|
|
364
368
|
});
|
|
365
369
|
});
|
|
366
370
|
}
|
package/dist/lib/ui.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const ui: {
|
|
|
7
7
|
error: import("chalk").ChalkInstance;
|
|
8
8
|
dim: import("chalk").ChalkInstance;
|
|
9
9
|
muted: import("chalk").ChalkInstance;
|
|
10
|
-
/**
|
|
10
|
+
/** Gradient logo banner — ███ block-art style (inspired by skills.sh) */
|
|
11
11
|
banner(): void;
|
|
12
12
|
/** Section header with gradient bar */
|
|
13
13
|
header(text: string): void;
|
package/dist/lib/ui.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UI module — Dreamlogic CLI visual design system
|
|
3
|
-
* Uses @clack/prompts, gradient-string,
|
|
3
|
+
* Uses @clack/prompts, gradient-string, boxen, ora
|
|
4
4
|
*/
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import gradientString from "gradient-string";
|
|
8
|
-
import figlet from "figlet";
|
|
9
8
|
import boxen from "boxen";
|
|
10
9
|
import { CLI_NAME, CLI_AUTHOR, CLI_VERSION } from "../types.js";
|
|
11
10
|
// ===== Brand palette =====
|
|
@@ -36,16 +35,56 @@ export const ui = {
|
|
|
36
35
|
error,
|
|
37
36
|
dim,
|
|
38
37
|
muted,
|
|
39
|
-
/**
|
|
38
|
+
/** Gradient logo banner — ███ block-art style (inspired by skills.sh) */
|
|
40
39
|
banner() {
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
40
|
+
// Hardcoded Unicode box-drawing logo for pixel-perfect rendering across all terminals
|
|
41
|
+
const LOGO_LINES = [
|
|
42
|
+
"██████╗ ██████╗ ███████╗ █████╗ ███╗ ███╗",
|
|
43
|
+
"██╔══██╗██╔══██╗██╔════╝██╔══██╗████╗ ████║",
|
|
44
|
+
"██║ ██║██████╔╝█████╗ ███████║██╔████╔██║",
|
|
45
|
+
"██║ ██║██╔══██╗██╔══╝ ██╔══██║██║╚██╔╝██║",
|
|
46
|
+
"██████╔╝██║ ██║███████╗██║ ██║██║ ╚═╝ ██║",
|
|
47
|
+
"╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝",
|
|
48
|
+
"",
|
|
49
|
+
"██╗ ██████╗ ██████╗ ██╗ ██████╗",
|
|
50
|
+
"██║ ██╔═══██╗██╔════╝ ██║██╔════╝",
|
|
51
|
+
"██║ ██║ ██║██║ ███╗██║██║ ",
|
|
52
|
+
"██║ ██║ ██║██║ ██║██║██║ ",
|
|
53
|
+
"███████╗╚██████╔╝╚██████╔╝██║╚██████╗",
|
|
54
|
+
"╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝",
|
|
55
|
+
];
|
|
56
|
+
// ANSI 256-color gradient: purple → indigo → blue → cyan (6 per block)
|
|
57
|
+
const GRADIENT_COLORS = [
|
|
58
|
+
"\x1b[38;5;135m", // bright purple
|
|
59
|
+
"\x1b[38;5;134m",
|
|
60
|
+
"\x1b[38;5;99m", // indigo
|
|
61
|
+
"\x1b[38;5;98m",
|
|
62
|
+
"\x1b[38;5;63m", // blue
|
|
63
|
+
"\x1b[38;5;62m",
|
|
64
|
+
"\x1b[38;5;33m", // deep blue
|
|
65
|
+
"\x1b[38;5;38m", // teal
|
|
66
|
+
"\x1b[38;5;44m", // cyan
|
|
67
|
+
"\x1b[38;5;43m",
|
|
68
|
+
"\x1b[38;5;49m", // bright cyan
|
|
69
|
+
"\x1b[38;5;50m",
|
|
70
|
+
"\x1b[38;5;51m", // lightest cyan
|
|
71
|
+
];
|
|
72
|
+
const RESET = "\x1b[0m";
|
|
44
73
|
console.log();
|
|
45
|
-
|
|
46
|
-
|
|
74
|
+
if (hasTruecolor || chalk.level >= 2) {
|
|
75
|
+
LOGO_LINES.forEach((line, i) => {
|
|
76
|
+
const color = GRADIENT_COLORS[i % GRADIENT_COLORS.length];
|
|
77
|
+
console.log(` ${color}${line}${RESET}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Fallback: plain bold for 16-color terminals
|
|
82
|
+
LOGO_LINES.forEach(line => console.log(` ${chalk.bold.magenta(line)}`));
|
|
83
|
+
}
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(muted(" ") +
|
|
47
86
|
(hasTruecolor ? brandGradient(`${CLI_NAME} v${CLI_VERSION}`) : chalk.bold(`${CLI_NAME} v${CLI_VERSION}`)) +
|
|
48
|
-
muted("
|
|
87
|
+
muted(" │ ") +
|
|
49
88
|
muted(CLI_AUTHOR));
|
|
50
89
|
console.log();
|
|
51
90
|
},
|
package/dist/types.d.ts
CHANGED
|
@@ -34,6 +34,6 @@ export interface InstalledRegistry {
|
|
|
34
34
|
export declare const DEFAULT_SERVER = "https://skill.dreamlogic-claw.com";
|
|
35
35
|
export declare const DEFAULT_INSTALL_DIR_NAME = "dreamlogic-skills";
|
|
36
36
|
export declare const CONFIG_DIR_NAME = ".dreamlogic";
|
|
37
|
-
export declare const CLI_VERSION = "2.0.
|
|
37
|
+
export declare const CLI_VERSION = "2.0.8";
|
|
38
38
|
export declare const CLI_NAME = "Dreamlogic CLI";
|
|
39
39
|
export declare const CLI_AUTHOR = "Dreamlogic-ai by MAJORNINE";
|
package/dist/types.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
export const DEFAULT_SERVER = "https://skill.dreamlogic-claw.com";
|
|
3
3
|
export const DEFAULT_INSTALL_DIR_NAME = "dreamlogic-skills";
|
|
4
4
|
export const CONFIG_DIR_NAME = ".dreamlogic";
|
|
5
|
-
export const CLI_VERSION = "2.0.
|
|
5
|
+
export const CLI_VERSION = "2.0.8";
|
|
6
6
|
export const CLI_NAME = "Dreamlogic CLI";
|
|
7
7
|
export const CLI_AUTHOR = "Dreamlogic-ai by MAJORNINE";
|