@dreamlogic-ai/cli 2.0.0 → 2.0.2

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.
@@ -3,9 +3,11 @@
3
3
  * Supports: interactive multi-select, single skill by name, --from-file
4
4
  */
5
5
  import * as clack from "@clack/prompts";
6
+ import { join } from "path";
6
7
  import { ApiClient } from "../lib/api-client.js";
7
8
  import { getServer, getInstallDir, loadInstalled } from "../lib/config.js";
8
9
  import { installSkill } from "../lib/installer.js";
10
+ import { detectAgents, registerSkillWithAgents } from "../lib/agents.js";
9
11
  import { ui } from "../lib/ui.js";
10
12
  import { requireAuth } from "./helpers.js";
11
13
  import chalk from "chalk";
@@ -134,6 +136,56 @@ export async function installCommand(skillIds, opts) {
134
136
  else {
135
137
  ui.warning(`${successCount}/${selectedIds.length} installed. Check errors above.`);
136
138
  }
139
+ // ── Agent Registration Step ──
140
+ if (successCount > 0 && !opts.yes) {
141
+ console.log();
142
+ const { universal, detected } = detectAgents();
143
+ const totalAgents = universal.length + detected.length;
144
+ if (totalAgents > 0) {
145
+ // Build options grouped by category
146
+ const agentOptions = [];
147
+ // Universal agents header
148
+ for (const agent of universal) {
149
+ agentOptions.push({
150
+ label: agent.name,
151
+ value: `u:${agent.name}`,
152
+ hint: "universal (.agents/skills)",
153
+ });
154
+ }
155
+ // Detected additional agents
156
+ for (const agent of detected) {
157
+ agentOptions.push({
158
+ label: agent.name,
159
+ value: `a:${agent.name}`,
160
+ hint: `detected (${agent.skillsDir})`,
161
+ });
162
+ }
163
+ const agentChoice = await clack.multiselect({
164
+ message: `Register skills with AI agents? (${totalAgents} agents available)`,
165
+ options: agentOptions,
166
+ required: false,
167
+ });
168
+ if (!clack.isCancel(agentChoice) && agentChoice.length > 0) {
169
+ const chosenNames = agentChoice.map(v => v.replace(/^[ua]:/, ""));
170
+ const allAgents = [...universal, ...detected];
171
+ const chosenAgents = allAgents.filter(a => chosenNames.includes(a.name));
172
+ // Register each installed skill with chosen agents
173
+ const installDir = getInstallDir();
174
+ for (const id of selectedIds) {
175
+ const skillPath = join(installDir, id);
176
+ const results = registerSkillWithAgents(id, skillPath, chosenAgents);
177
+ const created = results.filter(r => r.status === "created").length;
178
+ const errors = results.filter(r => r.status === "error");
179
+ if (created > 0) {
180
+ ui.ok(`${id}: registered with ${created} agent location(s)`);
181
+ }
182
+ for (const e of errors) {
183
+ ui.warning(`${e.agent}: ${e.error}`);
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
137
189
  // Suggest MCP setup
138
190
  if (successCount > 0) {
139
191
  console.log();
@@ -40,7 +40,8 @@ function getAgents(server, key) {
40
40
  generate: () => ({
41
41
  mcpServers: {
42
42
  "dreamlogic-skills": {
43
- url: `${server}/sse?api_key=${key}`,
43
+ url: `${server}/sse`,
44
+ headers: { "Authorization": `Bearer ${key}` },
44
45
  },
45
46
  },
46
47
  }),
@@ -87,7 +88,7 @@ function getAgents(server, key) {
87
88
  },
88
89
  generate: () => ({
89
90
  // R1-02: Store args as array, not interpolated string
90
- args: ["mcp", "add", "dreamlogic-skills", "--url", `${server}/sse?api_key=${key}`],
91
+ args: ["mcp", "add", "dreamlogic-skills", "--url", `${server}/sse`, "--header", `Authorization: Bearer ${key}`],
91
92
  }),
92
93
  apply: (config) => {
93
94
  // R1-02: execFileSync with arg array — no shell injection possible
@@ -101,7 +102,8 @@ function getAgents(server, key) {
101
102
  generate: () => ({
102
103
  mcpServers: {
103
104
  "dreamlogic-skills": {
104
- url: `${server}/sse?api_key=${key}`,
105
+ url: `${server}/sse`,
106
+ headers: { "Authorization": `Bearer ${key}` },
105
107
  },
106
108
  },
107
109
  }),
@@ -170,7 +172,7 @@ export async function setupMcpCommand(opts) {
170
172
  const config = agent.generate(server, apiKey);
171
173
  // D-11: Show what will be written (R1-01: mask key in display)
172
174
  if (agent.name === "Claude CLI") {
173
- const maskedCmd = `claude mcp add dreamlogic-skills --url "${server}/sse?api_key=${maskKey(apiKey)}"`;
175
+ const maskedCmd = `claude mcp add dreamlogic-skills --url "${server}/sse" --header "Authorization: Bearer ${maskKey(apiKey)}"`;
174
176
  ui.line(` Command: ${chalk.cyan(maskedCmd)}`);
175
177
  }
176
178
  else {
@@ -0,0 +1,40 @@
1
+ export interface AgentConfig {
2
+ name: string;
3
+ skillsDir: string;
4
+ envOverride?: string;
5
+ group: "universal" | "additional";
6
+ detect?: string;
7
+ }
8
+ /**
9
+ * Detect which agents are installed on this system
10
+ */
11
+ export declare function detectAgents(): {
12
+ universal: AgentConfig[];
13
+ detected: AgentConfig[];
14
+ };
15
+ /**
16
+ * Get all available agents (universal + detected)
17
+ */
18
+ export declare function getAllAgents(): AgentConfig[];
19
+ /**
20
+ * Register a skill with specific agents by creating symlinks
21
+ * Returns array of { agent, path, status } results
22
+ */
23
+ export declare function registerSkillWithAgents(skillId: string, skillPath: string, agents: AgentConfig[]): Array<{
24
+ agent: string;
25
+ path: string;
26
+ status: "created" | "updated" | "exists" | "error";
27
+ error?: string;
28
+ }>;
29
+ /**
30
+ * Unregister a skill from all agents (remove symlinks only)
31
+ */
32
+ export declare function unregisterSkillFromAgents(skillId: string): number;
33
+ /**
34
+ * List which agents have a specific skill registered
35
+ */
36
+ export declare function getSkillAgentStatus(skillId: string): Array<{
37
+ agent: string;
38
+ registered: boolean;
39
+ path: string;
40
+ }>;
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Agent Registry — detect installed AI agents and register skills
3
+ * Inspired by skills.sh (vercel-labs/skills) agent directory mapping
4
+ *
5
+ * Pattern: Each agent has a known skills directory. We create symlinks
6
+ * from the agent's skills dir to the actual skill install location.
7
+ */
8
+ import { existsSync, mkdirSync, symlinkSync, lstatSync, readlinkSync, unlinkSync } from "fs";
9
+ import { join, resolve } from "path";
10
+ import { homedir, platform } from "os";
11
+ // ===== Agent Directory Map =====
12
+ // Universal agents: always shown (use .agents/skills as shared dir)
13
+ // Additional agents: shown only if detected on the system
14
+ const AGENT_CONFIGS = [
15
+ // ── Universal (.agents/skills) ──
16
+ { name: "Amp", skillsDir: ".agents/skills", group: "universal" },
17
+ { name: "Cline", skillsDir: ".agents/skills", group: "universal" },
18
+ { name: "Codex", skillsDir: ".agents/skills", group: "universal", envOverride: "CODEX_HOME" },
19
+ { name: "Cursor", skillsDir: ".agents/skills", group: "universal" },
20
+ { name: "Deep Agents", skillsDir: ".agents/skills", group: "universal" },
21
+ { name: "Firebender", skillsDir: ".agents/skills", group: "universal" },
22
+ { name: "Gemini CLI", skillsDir: ".agents/skills", group: "universal" },
23
+ { name: "GitHub Copilot", skillsDir: ".agents/skills", group: "universal" },
24
+ { name: "Kimi Code CLI", skillsDir: ".agents/skills", group: "universal" },
25
+ { name: "OpenCode", skillsDir: ".agents/skills", group: "universal" },
26
+ { name: "Warp", skillsDir: ".agents/skills", group: "universal" },
27
+ // ── Additional agents ──
28
+ { name: "Augment", skillsDir: ".augment/skills", group: "additional", detect: ".augment" },
29
+ { name: "Claude Code", skillsDir: ".claude/skills", group: "additional", detect: ".claude", envOverride: "CLAUDE_CONFIG_DIR" },
30
+ { name: "OpenClaw", skillsDir: "skills", group: "additional", detect: "skills" },
31
+ { name: "CodeBuddy", skillsDir: ".codebuddy/skills", group: "additional", detect: ".codebuddy" },
32
+ { name: "Continue", skillsDir: ".continue/skills", group: "additional", detect: ".continue" },
33
+ { name: "Windsurf", skillsDir: ".codeium/windsurf/skills", group: "additional", detect: ".codeium" },
34
+ { name: "Goose", skillsDir: ".config/goose/skills", group: "additional", detect: ".config/goose" },
35
+ { name: "Roo Code", skillsDir: ".roo/skills", group: "additional", detect: ".roo" },
36
+ { name: "Trae", skillsDir: ".trae/skills", group: "additional", detect: ".trae" },
37
+ { name: "Void", skillsDir: ".void/skills", group: "additional", detect: ".void" },
38
+ { name: "Zed", skillsDir: ".config/zed/skills", group: "additional", detect: ".config/zed" },
39
+ { name: "Aider", skillsDir: ".aider/skills", group: "additional", detect: ".aider" },
40
+ { name: "Plandex", skillsDir: ".plandex/skills", group: "additional", detect: ".plandex" },
41
+ ];
42
+ /**
43
+ * Resolve the skills directory for an agent (handles env overrides + home expansion)
44
+ */
45
+ function resolveAgentDir(agent) {
46
+ const home = homedir();
47
+ if (agent.envOverride && process.env[agent.envOverride]) {
48
+ return join(process.env[agent.envOverride], "skills");
49
+ }
50
+ return join(home, agent.skillsDir);
51
+ }
52
+ /**
53
+ * Detect which agents are installed on this system
54
+ */
55
+ export function detectAgents() {
56
+ const home = homedir();
57
+ const universal = AGENT_CONFIGS.filter(a => a.group === "universal");
58
+ const additional = AGENT_CONFIGS.filter(a => a.group === "additional");
59
+ const detected = additional.filter(agent => {
60
+ if (!agent.detect)
61
+ return false;
62
+ const checkPath = join(home, agent.detect);
63
+ return existsSync(checkPath);
64
+ });
65
+ return { universal, detected };
66
+ }
67
+ /**
68
+ * Get all available agents (universal + detected)
69
+ */
70
+ export function getAllAgents() {
71
+ const { universal, detected } = detectAgents();
72
+ return [...universal, ...detected];
73
+ }
74
+ /**
75
+ * Register a skill with specific agents by creating symlinks
76
+ * Returns array of { agent, path, status } results
77
+ */
78
+ export function registerSkillWithAgents(skillId, skillPath, agents) {
79
+ const results = [];
80
+ // R3-08: Validate skill ID to prevent path traversal via symlink target
81
+ const SAFE_SKILL_ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
82
+ if (!SAFE_SKILL_ID.test(skillId)) {
83
+ return [{ agent: "all", path: "", status: "error", error: "Invalid skill ID" }];
84
+ }
85
+ const resolvedSkillPath = resolve(skillPath);
86
+ // Deduplicate by resolved dir (universal agents share .agents/skills)
87
+ const seenDirs = new Set();
88
+ for (const agent of agents) {
89
+ const agentDir = resolveAgentDir(agent);
90
+ // Skip duplicates (multiple universal agents → same .agents/skills dir)
91
+ if (seenDirs.has(agentDir)) {
92
+ results.push({ agent: agent.name, path: agentDir, status: "exists" });
93
+ continue;
94
+ }
95
+ seenDirs.add(agentDir);
96
+ const targetLink = join(agentDir, skillId);
97
+ try {
98
+ // Ensure parent dir exists
99
+ mkdirSync(agentDir, { recursive: true });
100
+ // Check if link already exists
101
+ if (existsSync(targetLink)) {
102
+ try {
103
+ const stat = lstatSync(targetLink);
104
+ if (stat.isSymbolicLink()) {
105
+ const existing = readlinkSync(targetLink);
106
+ if (resolve(existing) === resolvedSkillPath) {
107
+ results.push({ agent: agent.name, path: targetLink, status: "exists" });
108
+ continue;
109
+ }
110
+ // Different target — update
111
+ unlinkSync(targetLink);
112
+ }
113
+ else {
114
+ // Not a symlink (real directory) — skip to avoid data loss
115
+ results.push({ agent: agent.name, path: targetLink, status: "exists" });
116
+ continue;
117
+ }
118
+ }
119
+ catch {
120
+ // Stat failed — try to proceed
121
+ }
122
+ }
123
+ // Create symlink
124
+ // On Windows, use junction for directories (no admin required)
125
+ const linkType = platform() === "win32" ? "junction" : "dir";
126
+ symlinkSync(resolvedSkillPath, targetLink, linkType);
127
+ results.push({ agent: agent.name, path: targetLink, status: "created" });
128
+ }
129
+ catch (err) {
130
+ results.push({
131
+ agent: agent.name,
132
+ path: targetLink,
133
+ status: "error",
134
+ error: err.message,
135
+ });
136
+ }
137
+ }
138
+ return results;
139
+ }
140
+ /**
141
+ * Unregister a skill from all agents (remove symlinks only)
142
+ */
143
+ export function unregisterSkillFromAgents(skillId) {
144
+ const home = homedir();
145
+ let removed = 0;
146
+ // Check all known agent dirs
147
+ const allDirs = new Set(AGENT_CONFIGS.map(a => resolveAgentDir(a)));
148
+ for (const dir of allDirs) {
149
+ const targetLink = join(dir, skillId);
150
+ try {
151
+ if (existsSync(targetLink) && lstatSync(targetLink).isSymbolicLink()) {
152
+ unlinkSync(targetLink);
153
+ removed++;
154
+ }
155
+ }
156
+ catch {
157
+ // Skip errors
158
+ }
159
+ }
160
+ return removed;
161
+ }
162
+ /**
163
+ * List which agents have a specific skill registered
164
+ */
165
+ export function getSkillAgentStatus(skillId) {
166
+ const results = [];
167
+ const seenDirs = new Set();
168
+ for (const agent of AGENT_CONFIGS) {
169
+ const agentDir = resolveAgentDir(agent);
170
+ if (seenDirs.has(agentDir))
171
+ continue;
172
+ seenDirs.add(agentDir);
173
+ const targetLink = join(agentDir, skillId);
174
+ const registered = existsSync(targetLink);
175
+ results.push({ agent: agent.name, registered, path: targetLink });
176
+ }
177
+ return results;
178
+ }
@@ -6,6 +6,21 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync, statSync
6
6
  import { join } from "path";
7
7
  import { homedir } from "os";
8
8
  import { CONFIG_DIR_NAME, DEFAULT_SERVER, DEFAULT_INSTALL_DIR_NAME, } from "../types.js";
9
+ /** CFG-01 FIX: Recursively strip prototype pollution keys from parsed JSON */
10
+ function sanitize(obj) {
11
+ if (typeof obj !== "object" || obj === null)
12
+ return obj;
13
+ const BANNED = new Set(["__proto__", "constructor", "prototype"]);
14
+ for (const key of Object.keys(obj)) {
15
+ if (BANNED.has(key)) {
16
+ delete obj[key];
17
+ }
18
+ else {
19
+ obj[key] = sanitize(obj[key]);
20
+ }
21
+ }
22
+ return obj;
23
+ }
9
24
  // R1-06: Key format validation applied everywhere
10
25
  const KEY_RE = /^sk-(admin|user)-[a-f0-9]{16,}$/;
11
26
  function getConfigDir() {
@@ -31,7 +46,7 @@ export function loadConfig() {
31
46
  if (!existsSync(path))
32
47
  return null;
33
48
  try {
34
- const data = JSON.parse(readFileSync(path, "utf-8"));
49
+ const data = sanitize(JSON.parse(readFileSync(path, "utf-8")));
35
50
  // R2-13: Basic shape validation
36
51
  if (typeof data !== "object" || data === null)
37
52
  return null;
@@ -81,7 +96,7 @@ export function loadInstalled() {
81
96
  if (!existsSync(path))
82
97
  return {};
83
98
  try {
84
- const data = JSON.parse(readFileSync(path, "utf-8"));
99
+ const data = sanitize(JSON.parse(readFileSync(path, "utf-8")));
85
100
  // R2-13: Basic shape validation
86
101
  if (typeof data !== "object" || data === null || Array.isArray(data))
87
102
  return {};
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { createWriteStream, existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSync } from "fs";
10
10
  import { join, normalize, resolve as pathResolve, sep } from "path";
11
- import { pipeline } from "stream/promises";
11
+ import { Transform as TransformStream } from "stream";
12
12
  import yauzl from "yauzl";
13
13
  import { loadInstalled, saveInstalled, getInstallDir } from "./config.js";
14
14
  import { ui } from "./ui.js";
@@ -155,6 +155,10 @@ export async function installSkill(client, skillId, packageFile, expectedSha256,
155
155
  * Rollback a skill to its backup (D-13)
156
156
  */
157
157
  export function rollbackSkill(skillId) {
158
+ // INS-03/RBK-01 FIX: Validate skill ID to prevent path traversal
159
+ if (!SAFE_SKILL_ID.test(skillId)) {
160
+ return false;
161
+ }
158
162
  const installDir = getInstallDir();
159
163
  const skillDir = join(installDir, skillId);
160
164
  let entries;
@@ -235,15 +239,22 @@ function extractZip(buffer, targetDir) {
235
239
  reject(new Error(`Unsafe path in ZIP: ${entry.fileName}`));
236
240
  return;
237
241
  }
238
- // Reject oversized single files
242
+ // INS-02 FIX: Reject symlink entries (external attributes bit 0xA000)
243
+ const externalAttrs = (entry.externalFileAttributes >>> 16) & 0xFFFF;
244
+ const S_IFLNK = 0xA000;
245
+ if ((externalAttrs & 0xF000) === S_IFLNK) {
246
+ zipfile.close();
247
+ reject(new Error(`Symlink entry rejected in ZIP: ${entry.fileName}`));
248
+ return;
249
+ }
250
+ // R1-03: Pre-check declared size (actual bytes tracked in counting stream below)
239
251
  if (entry.uncompressedSize > MAX_SINGLE_FILE_SIZE) {
240
252
  zipfile.close(); // R2-08
241
253
  reject(new Error(`File too large in ZIP: ${entry.fileName} (${entry.uncompressedSize} bytes)`));
242
254
  return;
243
255
  }
244
- // R1-03: Cumulative size limit
245
- totalExtracted += entry.uncompressedSize;
246
- if (totalExtracted > MAX_TOTAL_EXTRACT_SIZE) {
256
+ // R1-03: Pre-check cumulative declared size (actual bytes tracked in stream)
257
+ if (totalExtracted + entry.uncompressedSize > MAX_TOTAL_EXTRACT_SIZE) {
247
258
  zipfile.close(); // R2-08
248
259
  reject(new Error(`Total extracted size exceeds ${MAX_TOTAL_EXTRACT_SIZE / 1024 / 1024}MB — possible zip bomb`));
249
260
  return;
@@ -272,11 +283,31 @@ function extractZip(buffer, targetDir) {
272
283
  reject(err || new Error("Failed to read ZIP entry"));
273
284
  return;
274
285
  }
275
- const writeStream = createWriteStream(fullPath);
276
- pipeline(readStream, writeStream).then(() => zipfile.readEntry()).catch((e) => {
277
- zipfile.close(); // R2-08
278
- reject(e);
286
+ // INS-01 FIX: Track actual bytes written (not declared uncompressedSize)
287
+ let fileBytes = 0;
288
+ const countingStream = new TransformStream({
289
+ transform(chunk, _encoding, callback) {
290
+ fileBytes += chunk.length;
291
+ totalExtracted += chunk.length;
292
+ if (fileBytes > MAX_SINGLE_FILE_SIZE) {
293
+ zipfile.close();
294
+ callback(new Error(`Actual file size exceeds limit: ${entry.fileName}`));
295
+ return;
296
+ }
297
+ if (totalExtracted > MAX_TOTAL_EXTRACT_SIZE) {
298
+ zipfile.close();
299
+ callback(new Error(`Total extracted size exceeds ${MAX_TOTAL_EXTRACT_SIZE / 1024 / 1024}MB — possible zip bomb`));
300
+ return;
301
+ }
302
+ callback(null, chunk);
303
+ },
279
304
  });
305
+ const writeStream = createWriteStream(fullPath);
306
+ readStream.pipe(countingStream).pipe(writeStream);
307
+ writeStream.on("finish", () => zipfile.readEntry());
308
+ writeStream.on("error", (e) => { zipfile.close(); reject(e); });
309
+ countingStream.on("error", (e) => { zipfile.close(); reject(e); });
310
+ readStream.on("error", (e) => { zipfile.close(); reject(e); });
280
311
  });
281
312
  }
282
313
  });
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
- /** Animated gradient logo banner */
10
+ /** Animated gradient logo banner — block-art style */
11
11
  banner(): void;
12
12
  /** Section header with gradient bar */
13
13
  header(text: string): void;
package/dist/lib/ui.js CHANGED
@@ -29,16 +29,17 @@ export const ui = {
29
29
  error,
30
30
  dim,
31
31
  muted,
32
- /** Animated gradient logo banner */
32
+ /** Animated gradient logo banner — block-art style */
33
33
  banner() {
34
- const logo = figlet.textSync("DreamLogic", { font: "Small" });
34
+ const dream = figlet.textSync("DREAM", { font: "ANSI Shadow" });
35
+ const logic = figlet.textSync("LOGIC", { font: "ANSI Shadow" });
36
+ const fullLogo = dream + logic;
35
37
  console.log();
36
- console.log(hasTruecolor ? brandGradient.multiline(logo) : chalk.bold.magenta(logo));
37
- console.log(muted(" ") +
38
+ console.log(hasTruecolor ? brandGradient.multiline(fullLogo) : chalk.bold.magenta(fullLogo));
39
+ console.log(muted(" ") +
38
40
  (hasTruecolor ? brandGradient(`${CLI_NAME} v${CLI_VERSION}`) : chalk.bold(`${CLI_NAME} v${CLI_VERSION}`)) +
39
41
  muted(" | ") +
40
42
  muted(CLI_AUTHOR));
41
- console.log(muted(" " + "─".repeat(48)));
42
43
  console.log();
43
44
  },
44
45
  /** Section header with gradient bar */
package/dist/types.d.ts CHANGED
@@ -33,6 +33,6 @@ export interface InstalledRegistry {
33
33
  export declare const DEFAULT_SERVER = "https://skill.dreamlogic-claw.com";
34
34
  export declare const DEFAULT_INSTALL_DIR_NAME = "dreamlogic-skills";
35
35
  export declare const CONFIG_DIR_NAME = ".dreamlogic";
36
- export declare const CLI_VERSION = "2.0.0";
36
+ export declare const CLI_VERSION = "2.0.2";
37
37
  export declare const CLI_NAME = "Dreamlogic CLI";
38
38
  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.0";
5
+ export const CLI_VERSION = "2.0.2";
6
6
  export const CLI_NAME = "Dreamlogic CLI";
7
7
  export const CLI_AUTHOR = "Dreamlogic-ai by MAJORNINE";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamlogic-ai/cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Dreamlogic AI Skill Manager — Install, update and manage AI agent skills",
5
5
  "type": "module",
6
6
  "bin": {