@opencoreai/opencore 0.4.0 → 0.4.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.
package/bin/opencore.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import "dotenv/config";
3
3
  import { spawn } from "node:child_process";
4
- import { constants, promises as fs } from "node:fs";
4
+ import { promises as fs } from "node:fs";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import readline from "node:readline/promises";
@@ -16,10 +16,6 @@ const entry = path.join(rootDir, "src", "index.ts");
16
16
  const setupScript = path.join(rootDir, "scripts", "postinstall.mjs");
17
17
  const openCoreHome = path.join(os.homedir(), ".opencore");
18
18
  const settingsPath = path.join(openCoreHome, "configs", "settings.json");
19
- const runtimeRoot = path.join(openCoreHome, "runtime");
20
- const runtimeInstallRoot = path.join(runtimeRoot, "current");
21
- const localBinDir = path.join(openCoreHome, "bin");
22
- const zshRcPath = path.join(os.homedir(), ".zshrc");
23
19
  const publishedPackageName = "@opencoreai/opencore";
24
20
 
25
21
  const args = process.argv.slice(2);
@@ -95,140 +91,12 @@ async function runInitialSetupIfNeeded() {
95
91
  });
96
92
  }
97
93
 
98
- async function fileExists(targetPath) {
99
- try {
100
- await fs.access(targetPath);
101
- return true;
102
- } catch {
103
- return false;
104
- }
105
- }
106
-
107
- async function ensureDir(targetPath) {
108
- await fs.mkdir(targetPath, { recursive: true });
109
- }
110
-
111
- async function isWritableDirectory(targetPath) {
112
- try {
113
- await ensureDir(targetPath);
114
- await fs.access(targetPath, constants.W_OK);
115
- return true;
116
- } catch {
117
- return false;
118
- }
119
- }
120
-
121
- async function installPersistentRuntime() {
122
- await ensureDir(runtimeInstallRoot);
123
-
124
- await new Promise((resolve, reject) => {
125
- const installer = spawn(
126
- "npm",
127
- ["install", "--no-fund", "--no-audit", "--prefix", runtimeInstallRoot, rootDir],
128
- {
129
- cwd: rootDir,
130
- stdio: "inherit",
131
- env: process.env,
132
- },
133
- );
134
-
135
- installer.on("exit", (code, signal) => {
136
- if (signal) {
137
- reject(new Error(`Local install exited with signal ${signal}`));
138
- return;
139
- }
140
- if ((code ?? 1) !== 0) {
141
- reject(new Error(`Local install exited with code ${code ?? 1}`));
142
- return;
143
- }
144
- resolve();
145
- });
146
-
147
- installer.on("error", reject);
148
- });
149
- }
150
-
151
- function getPersistentEntryPath() {
152
- return path.join(runtimeInstallRoot, "node_modules", publishedPackageName, "bin", "opencore.mjs");
153
- }
154
-
155
- function renderLauncherScript(entryPath) {
156
- return `#!/bin/sh
157
- exec node "${entryPath}" "$@"
158
- `;
159
- }
160
-
161
- async function findPreferredLauncherDir() {
162
- const pathEntries = String(process.env.PATH || "")
163
- .split(path.delimiter)
164
- .map((item) => item.trim())
165
- .filter(Boolean);
166
-
167
- const preferred = [
168
- ...pathEntries,
169
- "/opt/homebrew/bin",
170
- "/usr/local/bin",
171
- path.join(os.homedir(), "bin"),
172
- path.join(os.homedir(), ".local", "bin"),
173
- localBinDir,
174
- ];
175
-
176
- const seen = new Set();
177
- for (const dir of preferred) {
178
- if (seen.has(dir)) continue;
179
- seen.add(dir);
180
- if (await isWritableDirectory(dir)) {
181
- return { dir, alreadyOnPath: pathEntries.includes(dir) };
182
- }
183
- }
184
-
185
- return { dir: localBinDir, alreadyOnPath: pathEntries.includes(localBinDir) };
186
- }
187
-
188
- async function ensureZshPathEntry(binDir) {
189
- const exportLine = `export PATH="${binDir}:$PATH"`;
190
- const existing = (await fs.readFile(zshRcPath, "utf8").catch(() => "")).toString();
191
- if (existing.includes(exportLine)) return false;
192
- const next = existing.trimEnd();
193
- const content = `${next ? `${next}\n\n` : ""}# OpenCore CLI\n${exportLine}\n`;
194
- await fs.writeFile(zshRcPath, content, "utf8");
195
- return true;
196
- }
197
-
198
- async function installPersistentLauncher() {
199
- const entryPath = getPersistentEntryPath();
200
- if (!(await fileExists(entryPath))) {
201
- throw new Error("Persistent OpenCore runtime was not installed correctly.");
202
- }
203
-
204
- const { dir, alreadyOnPath } = await findPreferredLauncherDir();
205
- await ensureDir(dir);
206
-
207
- const launcherPath = path.join(dir, "opencore");
208
- await fs.writeFile(launcherPath, renderLauncherScript(entryPath), "utf8");
209
- await fs.chmod(launcherPath, 0o755);
210
-
211
- let zshUpdated = false;
212
- if (!alreadyOnPath) {
213
- zshUpdated = await ensureZshPathEntry(dir);
214
- }
215
-
216
- return { launcherPath, binDir: dir, alreadyOnPath, zshUpdated };
217
- }
218
-
219
94
  async function runSetupCommand() {
220
95
  try {
221
- console.log("Installing persistent OpenCore launcher...");
222
- await installPersistentRuntime();
223
- const launcher = await installPersistentLauncher();
96
+ console.log("Running OpenCore onboarding...");
224
97
  await runInitialSetupIfNeeded();
225
98
  console.log("OpenCore setup is complete.");
226
- console.log(`OpenCore launcher: ${launcher.launcherPath}`);
227
- if (!launcher.alreadyOnPath) {
228
- console.log(`Added ${launcher.binDir} to ~/.zshrc`);
229
- console.log("Open a new terminal window or run: source ~/.zshrc");
230
- }
231
- console.log("You can then run: opencore engage");
99
+ console.log("You can now run: opencore engage");
232
100
  process.exit(0);
233
101
  } catch (error) {
234
102
  console.error(`OpenCore setup failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -238,6 +238,14 @@
238
238
  }
239
239
  }, [page]);
240
240
 
241
+ useEffect(() => {
242
+ if (page !== "skills") return undefined;
243
+ const timer = setInterval(() => {
244
+ loadSkills().catch(() => {});
245
+ }, 3000);
246
+ return () => clearInterval(timer);
247
+ }, [page]);
248
+
241
249
  async function sendChat() {
242
250
  const text = chatText.trim();
243
251
  if (!text || chatBusy) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencoreai/opencore",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "LicenseRef-OpenCore-Personal-Use-1.0",
@@ -26,6 +26,7 @@
26
26
  "opencore": "bin/opencore.mjs"
27
27
  },
28
28
  "scripts": {
29
+ "postinstall": "node scripts/install-migrate.mjs",
29
30
  "start": "tsx src/index.ts",
30
31
  "engage": "node bin/opencore.mjs engage"
31
32
  },
@@ -0,0 +1,63 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { promises as fs } from "node:fs";
4
+
5
+ const homeDir = os.homedir();
6
+ const openCoreBinPath = path.join(homeDir, ".opencore", "bin", "opencore");
7
+ const zshRcPath = path.join(homeDir, ".zshrc");
8
+ const legacyPathLine = 'export PATH="$HOME/.opencore/bin:$PATH"';
9
+ const legacyPathLineExpanded = `export PATH="${path.join(homeDir, ".opencore", "bin")}:$PATH"`;
10
+
11
+ async function removeLegacyLauncher() {
12
+ try {
13
+ const content = await fs.readFile(openCoreBinPath, "utf8");
14
+ const isLegacy =
15
+ content.includes(`${path.join(homeDir, ".opencore", "runtime", "current")}`) ||
16
+ content.includes(".opencore/runtime/current/node_modules/@opencoreai/opencore/bin/opencore.mjs");
17
+ if (!isLegacy) return false;
18
+ await fs.unlink(openCoreBinPath);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ async function cleanLegacyZshrc() {
26
+ try {
27
+ const existing = await fs.readFile(zshRcPath, "utf8");
28
+ const lines = existing.split("\n");
29
+ const cleaned = [];
30
+ let removed = false;
31
+ for (let i = 0; i < lines.length; i += 1) {
32
+ const line = lines[i];
33
+ const trimmed = line.trim();
34
+ if (trimmed === "# OpenCore CLI") {
35
+ const next = String(lines[i + 1] || "").trim();
36
+ if (next === legacyPathLine || next === legacyPathLineExpanded) {
37
+ removed = true;
38
+ i += 1;
39
+ continue;
40
+ }
41
+ }
42
+ if (trimmed === legacyPathLine || trimmed === legacyPathLineExpanded) {
43
+ removed = true;
44
+ continue;
45
+ }
46
+ cleaned.push(line);
47
+ }
48
+ if (!removed) return false;
49
+ await fs.writeFile(zshRcPath, `${cleaned.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}\n`, "utf8");
50
+ return true;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ async function main() {
57
+ const [removedLauncher, cleanedZshrc] = await Promise.all([removeLegacyLauncher(), cleanLegacyZshrc()]);
58
+ if (removedLauncher || cleanedZshrc) {
59
+ console.log("[OpenCore] Removed legacy ~/.opencore launcher migration artifacts.");
60
+ }
61
+ }
62
+
63
+ await main();
@@ -16,6 +16,9 @@ const __dirname = path.dirname(__filename);
16
16
  const ROOT_DIR = path.resolve(__dirname, "..");
17
17
  const TEMPLATE_DIR = path.join(ROOT_DIR, "templates");
18
18
  const OPENCORE_HOME = path.join(os.homedir(), ".opencore");
19
+ const AGENTS_DIR = path.join(OPENCORE_HOME, ".agents");
20
+ const SKILLS_DIR = path.join(AGENTS_DIR, "skills");
21
+ const LEGACY_SKILLS_DIR = path.join(OPENCORE_HOME, "skills");
19
22
  const SETTINGS_PATH = path.join(OPENCORE_HOME, "configs", "settings.json");
20
23
  const GUIDELINES_PATH = path.join(OPENCORE_HOME, "guidelines.md");
21
24
  const INSTRUCTIONS_PATH = path.join(OPENCORE_HOME, "instructions.md");
@@ -26,10 +29,11 @@ const PROFILE_SECTION_START = "<!-- OPENCORE_INSTALL_PROFILE_START -->";
26
29
  const PROFILE_SECTION_END = "<!-- OPENCORE_INSTALL_PROFILE_END -->";
27
30
  const DIRECTORIES = [
28
31
  OPENCORE_HOME,
32
+ AGENTS_DIR,
29
33
  path.join(OPENCORE_HOME, "configs"),
30
34
  path.join(OPENCORE_HOME, "logs"),
31
35
  path.join(OPENCORE_HOME, "cache"),
32
- path.join(OPENCORE_HOME, "skills"),
36
+ SKILLS_DIR,
33
37
  ];
34
38
  const execFileAsync = promisify(execFile);
35
39
 
@@ -213,13 +217,13 @@ async function configureTelegram(currentSettings) {
213
217
  }
214
218
 
215
219
  async function installSelectedSkills(skillIds) {
216
- await ensureDir(path.join(OPENCORE_HOME, "skills"));
220
+ await ensureDir(SKILLS_DIR);
217
221
  const installed = [];
218
222
  const skipped = [];
219
223
  for (const id of skillIds) {
220
224
  const skill = SKILL_CATALOG.find((item) => item.id === id);
221
225
  if (!skill) continue;
222
- const dir = path.join(OPENCORE_HOME, "skills", skill.id);
226
+ const dir = path.join(SKILLS_DIR, skill.id);
223
227
  const skillFile = path.join(dir, "SKILL.md");
224
228
  const configFile = path.join(dir, "config.json");
225
229
  try {
@@ -248,6 +252,25 @@ async function installSelectedSkills(skillIds) {
248
252
  return { installed, skipped };
249
253
  }
250
254
 
255
+ async function migrateLegacySkillsDir() {
256
+ try {
257
+ const entries = await fs.readdir(LEGACY_SKILLS_DIR, { withFileTypes: true });
258
+ await ensureDir(SKILLS_DIR);
259
+ for (const entry of entries) {
260
+ if (!entry.isDirectory()) continue;
261
+ const fromDir = path.join(LEGACY_SKILLS_DIR, entry.name);
262
+ const toDir = path.join(SKILLS_DIR, entry.name);
263
+ try {
264
+ await fs.access(toDir);
265
+ continue;
266
+ } catch {}
267
+ await fs.rename(fromDir, toDir).catch(async () => {
268
+ await fs.cp(fromDir, toDir, { recursive: true, force: false });
269
+ });
270
+ }
271
+ } catch {}
272
+ }
273
+
251
274
  async function promptSkillInstallation() {
252
275
  if (!(input.isTTY && output.isTTY)) return;
253
276
  const choices = [
@@ -407,6 +430,7 @@ async function run() {
407
430
  await ensureTemplateApplied(GUIDELINES_PATH, defaultGuidelines, ["# OpenCore Guidelines\n"]);
408
431
  await ensureTemplateApplied(INSTRUCTIONS_PATH, defaultInstructions, ["# OpenCore Instructions\n"]);
409
432
  await ensureFile(SETTINGS_PATH, `${JSON.stringify(DEFAULT_CONFIG, null, 2)}\n`);
433
+ await migrateLegacySkillsDir();
410
434
  await ensureCredentialStore();
411
435
  await ensureDefaultSkillsInstalled();
412
436
 
@@ -61,7 +61,7 @@ export class DashboardServer {
61
61
  this.options = options;
62
62
  this.app.disable("x-powered-by");
63
63
  this.dashboardDir = path.join(options.rootDir, "opencore dashboard");
64
- this.skillsDir = path.join(options.openCoreHome, "skills");
64
+ this.skillsDir = path.join(options.openCoreHome, ".agents", "skills");
65
65
  this.schedulesPath = path.join(options.openCoreHome, "configs", "schedules.json");
66
66
  this.chatLogPath = path.join(options.openCoreHome, "chat-history.json");
67
67
  this.vendorMap = {
@@ -164,6 +164,89 @@ export class DashboardServer {
164
164
  return installed;
165
165
  }
166
166
 
167
+ private extractFrontmatterField(content: string, key: string) {
168
+ const text = String(content || "");
169
+ if (!text.startsWith("---")) return "";
170
+ const end = text.indexOf("\n---", 3);
171
+ if (end === -1) return "";
172
+ const frontmatter = text.slice(3, end);
173
+ const match = frontmatter.match(new RegExp(`^${key}:\\s*(.+)$`, "m"));
174
+ return match ? String(match[1] || "").trim().replace(/^["']|["']$/g, "") : "";
175
+ }
176
+
177
+ private extractFirstHeading(content: string) {
178
+ const match = String(content || "").match(/^#\s+(.+)$/m);
179
+ return match ? String(match[1] || "").trim() : "";
180
+ }
181
+
182
+ private async listSkillsDetailed() {
183
+ const catalogMap = new Map(SKILL_CATALOG.map((skill) => [skill.id, skill]));
184
+ const items = new Map<string, any>();
185
+
186
+ for (const skill of SKILL_CATALOG) {
187
+ items.set(skill.id, {
188
+ id: skill.id,
189
+ name: skill.name,
190
+ description: skill.description,
191
+ installed: false,
192
+ external: false,
193
+ });
194
+ }
195
+
196
+ try {
197
+ const entries = await fs.readdir(this.skillsDir, { withFileTypes: true });
198
+ for (const entry of entries) {
199
+ if (!entry.isDirectory()) continue;
200
+ const id = entry.name;
201
+ const skillDir = path.join(this.skillsDir, id);
202
+ const skillFile = path.join(skillDir, "SKILL.md");
203
+ const configFile = path.join(skillDir, "config.json");
204
+ try {
205
+ await fs.access(skillFile);
206
+ } catch {
207
+ continue;
208
+ }
209
+
210
+ let config: any = {};
211
+ let markdown = "";
212
+ try {
213
+ const raw = await fs.readFile(configFile, "utf8");
214
+ config = JSON.parse(raw || "{}");
215
+ } catch {}
216
+ try {
217
+ markdown = await fs.readFile(skillFile, "utf8");
218
+ } catch {}
219
+
220
+ const catalogSkill = catalogMap.get(id);
221
+ const resolvedName =
222
+ String(config?.name || "").trim() ||
223
+ this.extractFrontmatterField(markdown, "name") ||
224
+ this.extractFirstHeading(markdown) ||
225
+ catalogSkill?.name ||
226
+ id;
227
+ const resolvedDescription =
228
+ String(config?.description || "").trim() ||
229
+ this.extractFrontmatterField(markdown, "description") ||
230
+ catalogSkill?.description ||
231
+ "External OpenCore skill.";
232
+
233
+ items.set(id, {
234
+ id,
235
+ name: resolvedName,
236
+ description: resolvedDescription,
237
+ installed: true,
238
+ external: !catalogSkill,
239
+ });
240
+ }
241
+ } catch {}
242
+
243
+ return Array.from(items.values()).sort((a, b) => {
244
+ if (a.installed !== b.installed) return a.installed ? -1 : 1;
245
+ if (a.external !== b.external) return a.external ? -1 : 1;
246
+ return String(a.name || a.id).localeCompare(String(b.name || b.id));
247
+ });
248
+ }
249
+
167
250
  private async installSkill(id: string) {
168
251
  const skill = SKILL_CATALOG.find((item) => item.id === id);
169
252
  if (!skill) {
@@ -267,15 +350,7 @@ export class DashboardServer {
267
350
  });
268
351
 
269
352
  this.app.get("/api/skills", async (_req, res) => {
270
- const installed = await this.listInstalledSkillIds();
271
- res.json({
272
- items: SKILL_CATALOG.map((skill) => ({
273
- id: skill.id,
274
- name: skill.name,
275
- description: skill.description,
276
- installed: installed.has(skill.id),
277
- })),
278
- });
353
+ res.json({ items: await this.listSkillsDetailed() });
279
354
  });
280
355
 
281
356
  this.app.get("/api/schedules", async (_req, res) => {
package/src/index.ts CHANGED
@@ -43,7 +43,9 @@ const INSTRUCTIONS_PATH = path.join(OPENCORE_HOME, "instructions.md");
43
43
  const SETTINGS_PATH = path.join(OPENCORE_HOME, "configs", "settings.json");
44
44
  const SCHEDULES_PATH = path.join(OPENCORE_HOME, "configs", "schedules.json");
45
45
  const SCREENSHOT_DIR = path.join(OPENCORE_HOME, "screenshots");
46
- const SKILLS_DIR = path.join(OPENCORE_HOME, "skills");
46
+ const AGENTS_DIR = path.join(OPENCORE_HOME, ".agents");
47
+ const SKILLS_DIR = path.join(AGENTS_DIR, "skills");
48
+ const LEGACY_SKILLS_DIR = path.join(OPENCORE_HOME, "skills");
47
49
  const INDICATOR_STATE_PATH = path.join(OPENCORE_HOME, "indicator-state.json");
48
50
  const SCHEDULER_LOG_PATH = path.join(OPENCORE_HOME, "logs", "scheduler.log");
49
51
  const DASHBOARD_PORT = Number(process.env.OPENCORE_DASHBOARD_PORT || 4111);
@@ -200,6 +202,7 @@ async function chmodIfPossible(targetPath: string, mode: number) {
200
202
  async function enforceOpenCorePermissions() {
201
203
  const dirs = [
202
204
  OPENCORE_HOME,
205
+ AGENTS_DIR,
203
206
  path.join(OPENCORE_HOME, "configs"),
204
207
  path.join(OPENCORE_HOME, "logs"),
205
208
  path.join(OPENCORE_HOME, "cache"),
@@ -344,6 +347,7 @@ async function ensureOpenCoreHome() {
344
347
  await fs.mkdir(path.join(OPENCORE_HOME, "configs"), { recursive: true });
345
348
  await fs.mkdir(path.join(OPENCORE_HOME, "logs"), { recursive: true });
346
349
  await fs.mkdir(path.join(OPENCORE_HOME, "cache"), { recursive: true });
350
+ await fs.mkdir(AGENTS_DIR, { recursive: true });
347
351
  await fs.mkdir(SCREENSHOT_DIR, { recursive: true });
348
352
  await fs.mkdir(SKILLS_DIR, { recursive: true });
349
353
 
@@ -394,10 +398,30 @@ async function ensureOpenCoreHome() {
394
398
  await fs.writeFile(SCHEDULES_PATH, "[]\n", "utf8");
395
399
  }
396
400
  await ensureDefaultOpenCoreSkills();
401
+ await migrateLegacySkillsDir();
397
402
  await ensureCredentialStore();
398
403
  await enforceOpenCorePermissions();
399
404
  }
400
405
 
406
+ async function migrateLegacySkillsDir() {
407
+ try {
408
+ const entries = await fs.readdir(LEGACY_SKILLS_DIR, { withFileTypes: true });
409
+ await fs.mkdir(SKILLS_DIR, { recursive: true });
410
+ for (const entry of entries) {
411
+ if (!entry.isDirectory()) continue;
412
+ const fromDir = path.join(LEGACY_SKILLS_DIR, entry.name);
413
+ const toDir = path.join(SKILLS_DIR, entry.name);
414
+ try {
415
+ await fs.access(toDir);
416
+ continue;
417
+ } catch {}
418
+ await fs.rename(fromDir, toDir).catch(async () => {
419
+ await fs.cp(fromDir, toDir, { recursive: true, force: false });
420
+ });
421
+ }
422
+ } catch {}
423
+ }
424
+
401
425
  async function ensureDefaultOpenCoreSkills() {
402
426
  for (const skill of DEFAULT_SYSTEM_SKILLS) {
403
427
  const dir = path.join(SKILLS_DIR, skill.id);
@@ -125,7 +125,7 @@
125
125
 
126
126
  if (self.statusGlowLayer == nil) {
127
127
  self.statusGlowLayer = [CALayer layer];
128
- self.statusGlowLayer.cornerRadius = 12.0;
128
+ self.statusGlowLayer.cornerRadius = 10.0;
129
129
  self.statusGlowLayer.shadowOpacity = 0.34f;
130
130
  self.statusGlowLayer.shadowRadius = 10.0f;
131
131
  self.statusGlowLayer.shadowOffset = CGSizeMake(0, 0);
@@ -135,7 +135,7 @@
135
135
 
136
136
  if (self.statusPillLayer == nil) {
137
137
  self.statusPillLayer = [CAGradientLayer layer];
138
- self.statusPillLayer.cornerRadius = 12.0;
138
+ self.statusPillLayer.cornerRadius = 10.0;
139
139
  self.statusPillLayer.borderWidth = 1.0;
140
140
  self.statusPillLayer.startPoint = CGPointMake(0.0, 0.5);
141
141
  self.statusPillLayer.endPoint = CGPointMake(1.0, 0.5);
@@ -210,7 +210,7 @@
210
210
  };
211
211
  CGFloat textWidth = ceil([title sizeWithAttributes:attributes].width);
212
212
  CGFloat iconWidth = button.image != nil ? 18.0 : 0.0;
213
- CGFloat totalWidth = textWidth + iconWidth + 22.0;
213
+ CGFloat totalWidth = textWidth + iconWidth + 24.0;
214
214
  CGFloat width = MAX(122.0, totalWidth);
215
215
  CGFloat height = 24.0;
216
216
  CGFloat x = floor((NSWidth(button.bounds) - width) / 2.0);
@@ -231,7 +231,7 @@
231
231
  self.statusPillLayer.borderColor = [[self.orangeBadgeBorderColor colorWithAlphaComponent:(active ? (self.hoverActive ? 1.0 : 0.85) : (self.hoverActive ? 0.86 : 0.60))] CGColor];
232
232
 
233
233
  self.statusShineLayer.frame = CGRectMake(8.0, 2.0, width - 16.0, height * 0.48);
234
- self.statusShineLayer.cornerRadius = 9.0;
234
+ self.statusShineLayer.cornerRadius = 8.0;
235
235
  self.statusShineLayer.opacity = active ? (self.hoverActive ? 1.0f : 0.95f) : (self.hoverActive ? 0.84f : 0.62f);
236
236
  }
237
237
 
@@ -17,7 +17,7 @@ description: Create or update OpenCore skills with focused triggers, reusable in
17
17
  # Skill Creator
18
18
 
19
19
  ## Purpose
20
- Create, repair, or improve reusable OpenCore skills under ~/.opencore/skills.
20
+ Create, repair, or improve reusable OpenCore skills under ~/.opencore/.agents/skills.
21
21
 
22
22
  ## Use This Skill When
23
23
  - User asks for a new OpenCore skill.
@@ -26,7 +26,7 @@ Create, repair, or improve reusable OpenCore skills under ~/.opencore/skills.
26
26
 
27
27
  ## Workflow
28
28
  1. Define the skill purpose, trigger phrases, and expected outcome.
29
- 2. Create or update ~/.opencore/skills/<skill-id>/SKILL.md with concise operating instructions.
29
+ 2. Create or update ~/.opencore/.agents/skills/<skill-id>/SKILL.md with concise operating instructions.
30
30
  3. Create or update config.json with id, name, description, version, category, and triggers.
31
31
  4. Keep the skill narrow enough to be reliable, but complete enough to be reusable.
32
32
  5. Summarize how the skill should be triggered and what it changes.
@@ -15,12 +15,14 @@
15
15
  - Computer Agent must not make independent policy decisions outside delegated scope.
16
16
 
17
17
  ## Skills Rules
18
- - OpenCore skills are stored under `~/.opencore/skills`.
18
+ - OpenCore agent assets are stored under `~/.opencore/.agents`.
19
+ - OpenCore skills are stored under `~/.opencore/.agents/skills`.
19
20
  - `skill-creator` is a built-in OpenCore skill and should exist by default.
20
21
  - `continuous-operations` is a built-in OpenCore skill and should exist by default.
21
22
  - `builder` is a built-in OpenCore skill and should exist by default.
22
23
  - `credential-operator` is a built-in OpenCore skill and should exist by default.
23
24
  - Manager Agent must check installed skills before planning and execution.
25
+ - Dashboard must also read installed skills from `~/.opencore/.agents/skills`.
24
26
  - If a relevant skill exists, Manager Agent should follow it.
25
27
  - If no relevant skill exists, proceed with default behavior.
26
28
  - Skills do not override core safety rules in this file.
@@ -8,13 +8,14 @@
8
8
  05. Computer Agent is only for macOS UI control tasks.
9
9
  06. If no UI control is needed, Manager should answer directly.
10
10
  07. If user asks to edit soul/memory/guidelines/instructions, Manager should edit locally.
11
- 07.1 Manager should also handle heartbeat.md and skills content under ~/.opencore/skills.
11
+ 07.1 Manager should also handle heartbeat.md and skills content under ~/.opencore/.agents/skills.
12
12
  08. Manager should delegate to Computer Agent only when interaction with apps/windows is required.
13
13
  08.1 For future-time or recurring requests, Manager should prefer cron-backed scheduling when possible.
14
14
  09. Manager should keep responses concise and operational.
15
15
  10. Manager should follow soul.md, guidelines.md, instructions.md, memory.md, and config context.
16
16
  10.05 Manager should also use computer-profile.md for durable machine facts such as installed apps, default browser, workspace, hardware profile, and permission-related environment facts.
17
- 10.1 Manager should inspect and follow installed OpenCore skills in ~/.opencore/skills when relevant.
17
+ 10.1 Manager should inspect and follow installed OpenCore skills in ~/.opencore/.agents/skills when relevant.
18
+ 10.15 Dashboard should read and display installed skills from ~/.opencore/.agents/skills.
18
19
  10.2 Built-in skills for skill creation, continuous operations, and internal tool building should be treated as always available if installed.
19
20
  10.3 The credential-operator skill should be treated as a built-in subsystem for login, sign-up, password reuse, verification handling, and saving credentials locally.
20
21
  11. Computer Agent should follow the same policy context while executing UI actions.