@lumerahq/cli 0.29.1 → 0.30.0-dev.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/dist/{chunk-QPVRRJFJ.js → chunk-FLFEF2BN.js} +1 -1
- package/dist/{chunk-KC3VBP6R.js → chunk-LVIIWE44.js} +1 -1
- package/dist/{chunk-FHHWIV4C.js → chunk-U3ZLIXGD.js} +32 -0
- package/dist/{chunk-7ZTCXKII.js → chunk-YMWDIAUG.js} +1 -1
- package/dist/{deps-IYUORT55.js → deps-NHVL4DMD.js} +2 -2
- package/dist/{dev-OJEV76DA.js → dev-IWTA5G5M.js} +3 -3
- package/dist/{functions-KP2NO4RC.js → functions-55J6E2RF.js} +1 -1
- package/dist/index.js +14 -14
- package/dist/{init-ZR2SLJ3K.js → init-I4FECTEL.js} +1 -1
- package/dist/{project-OPA5UUMQ.js → project-6POVRBZL.js} +2 -2
- package/dist/{register-HRLBT4FI.js → register-OBQZPOGC.js} +1 -1
- package/dist/{resources-P4VA2JPE.js → resources-YEOQ7YN6.js} +455 -55
- package/dist/{run-WHVUVIYB.js → run-SLLDX3RF.js} +1 -1
- package/package.json +1 -1
- package/templates/default/platform/skills/.gitkeep +0 -0
- package/templates/default/pyproject.toml +1 -1
|
@@ -4,7 +4,7 @@ import { existsSync } from "fs";
|
|
|
4
4
|
import { join, resolve } from "path";
|
|
5
5
|
import pc from "picocolors";
|
|
6
6
|
var SUPPORTED_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "inspect", "invoke", "test"]);
|
|
7
|
-
var FUNCTIONS_SDK_REQUIREMENT = "lumera[functions]>=0.
|
|
7
|
+
var FUNCTIONS_SDK_REQUIREMENT = "lumera[functions]>=0.33.0,<0.34.0";
|
|
8
8
|
var FUNCTIONS_RUNNER_PROTOCOL_VERSION = 1;
|
|
9
9
|
function errorMessage(error, fallback) {
|
|
10
10
|
return error instanceof Error && error.message ? error.message : fallback;
|
|
@@ -299,6 +299,38 @@ var ApiClient = class {
|
|
|
299
299
|
method: "DELETE"
|
|
300
300
|
});
|
|
301
301
|
}
|
|
302
|
+
// Project skills
|
|
303
|
+
async listAgentSkills(opts) {
|
|
304
|
+
const params = new URLSearchParams({ limit: "100" });
|
|
305
|
+
if (opts?.project_id) params.set("project_id", opts.project_id);
|
|
306
|
+
if (opts?.include_global) params.set("include_global", "true");
|
|
307
|
+
const all = [];
|
|
308
|
+
let offset = 0;
|
|
309
|
+
for (; ; ) {
|
|
310
|
+
params.set("offset", String(offset));
|
|
311
|
+
const result = await this.request(`/api/lm_agent_skills?${params}`);
|
|
312
|
+
const skills = result.skills || [];
|
|
313
|
+
all.push(...skills);
|
|
314
|
+
if (!result.has_more || skills.length === 0) break;
|
|
315
|
+
offset += skills.length;
|
|
316
|
+
}
|
|
317
|
+
return all;
|
|
318
|
+
}
|
|
319
|
+
async createAgentSkill(def) {
|
|
320
|
+
return this.request("/api/lm_agent_skills", {
|
|
321
|
+
method: "POST",
|
|
322
|
+
body: JSON.stringify(def)
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
async updateAgentSkill(id, def) {
|
|
326
|
+
return this.request(`/api/lm_agent_skills/${id}`, {
|
|
327
|
+
method: "PATCH",
|
|
328
|
+
body: JSON.stringify(def)
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
async deleteAgentSkill(id) {
|
|
332
|
+
await this.request(`/api/lm_agent_skills/${id}`, { method: "DELETE" });
|
|
333
|
+
}
|
|
302
334
|
// Resolve a list of refs (slugs or IDs) to skill records. Chunks
|
|
303
335
|
// requests so callers don't have to think about the server-side
|
|
304
336
|
// per-request cap.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deps,
|
|
3
3
|
syncDeps
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YMWDIAUG.js";
|
|
5
5
|
import "./chunk-2CR762KB.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-U3ZLIXGD.js";
|
|
7
7
|
import "./chunk-JLVVHTBY.js";
|
|
8
8
|
import "./chunk-FJFIWC7G.js";
|
|
9
9
|
import "./chunk-PNKVD2UK.js";
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dev
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LVIIWE44.js";
|
|
4
4
|
import {
|
|
5
5
|
syncDeps
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YMWDIAUG.js";
|
|
7
7
|
import {
|
|
8
8
|
loadEnv
|
|
9
9
|
} from "./chunk-2CR762KB.js";
|
|
10
10
|
import {
|
|
11
11
|
createApiClient
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-U3ZLIXGD.js";
|
|
13
13
|
import {
|
|
14
14
|
findProjectRoot,
|
|
15
15
|
getApiUrl,
|
package/dist/index.js
CHANGED
|
@@ -240,49 +240,49 @@ async function main() {
|
|
|
240
240
|
switch (command) {
|
|
241
241
|
// Resource commands
|
|
242
242
|
case "plan":
|
|
243
|
-
await import("./resources-
|
|
243
|
+
await import("./resources-YEOQ7YN6.js").then((m) => m.plan(args.slice(1)));
|
|
244
244
|
break;
|
|
245
245
|
case "apply":
|
|
246
|
-
await import("./resources-
|
|
246
|
+
await import("./resources-YEOQ7YN6.js").then((m) => m.apply(args.slice(1)));
|
|
247
247
|
break;
|
|
248
248
|
case "pull":
|
|
249
|
-
await import("./resources-
|
|
249
|
+
await import("./resources-YEOQ7YN6.js").then((m) => m.pull(args.slice(1)));
|
|
250
250
|
break;
|
|
251
251
|
case "destroy":
|
|
252
|
-
await import("./resources-
|
|
252
|
+
await import("./resources-YEOQ7YN6.js").then((m) => m.destroy(args.slice(1)));
|
|
253
253
|
break;
|
|
254
254
|
case "list":
|
|
255
|
-
await import("./resources-
|
|
255
|
+
await import("./resources-YEOQ7YN6.js").then((m) => m.list(args.slice(1)));
|
|
256
256
|
break;
|
|
257
257
|
case "show":
|
|
258
|
-
await import("./resources-
|
|
258
|
+
await import("./resources-YEOQ7YN6.js").then((m) => m.show(args.slice(1)));
|
|
259
259
|
break;
|
|
260
260
|
case "diff":
|
|
261
|
-
await import("./resources-
|
|
261
|
+
await import("./resources-YEOQ7YN6.js").then((m) => m.diff(args.slice(1)));
|
|
262
262
|
break;
|
|
263
263
|
// Development
|
|
264
264
|
case "dev":
|
|
265
|
-
await import("./dev-
|
|
265
|
+
await import("./dev-IWTA5G5M.js").then((m) => m.dev(args.slice(1)));
|
|
266
266
|
break;
|
|
267
267
|
case "run":
|
|
268
|
-
await import("./run-
|
|
268
|
+
await import("./run-SLLDX3RF.js").then((m) => m.run(args.slice(1)));
|
|
269
269
|
break;
|
|
270
270
|
case "functions":
|
|
271
|
-
await import("./functions-
|
|
271
|
+
await import("./functions-55J6E2RF.js").then(
|
|
272
272
|
(m) => m.functions(subcommand, args.slice(2))
|
|
273
273
|
);
|
|
274
274
|
break;
|
|
275
275
|
case "project":
|
|
276
|
-
await import("./project-
|
|
276
|
+
await import("./project-6POVRBZL.js").then(
|
|
277
277
|
(m) => m.project(subcommand, args.slice(2))
|
|
278
278
|
);
|
|
279
279
|
break;
|
|
280
280
|
// Project
|
|
281
281
|
case "init":
|
|
282
|
-
await import("./init-
|
|
282
|
+
await import("./init-I4FECTEL.js").then((m) => m.init(args.slice(1)));
|
|
283
283
|
break;
|
|
284
284
|
case "register":
|
|
285
|
-
await import("./register-
|
|
285
|
+
await import("./register-OBQZPOGC.js").then((m) => m.register(args.slice(1)));
|
|
286
286
|
break;
|
|
287
287
|
case "templates":
|
|
288
288
|
await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
|
|
@@ -299,7 +299,7 @@ async function main() {
|
|
|
299
299
|
break;
|
|
300
300
|
// Dependencies
|
|
301
301
|
case "deps":
|
|
302
|
-
await import("./deps-
|
|
302
|
+
await import("./deps-NHVL4DMD.js").then((m) => m.deps(args.slice(1)));
|
|
303
303
|
break;
|
|
304
304
|
// Feature flags (read the in-sandbox snapshot)
|
|
305
305
|
case "flags":
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
findFunctionsProjectRoot
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FLFEF2BN.js";
|
|
4
4
|
import "./chunk-PNKVD2UK.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/project.ts
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
8
|
import pc from "picocolors";
|
|
9
|
-
var RELEASE_SDK_REQUIREMENT = "lumera[release]>=0.
|
|
9
|
+
var RELEASE_SDK_REQUIREMENT = "lumera[release]>=0.33.0,<0.34.0";
|
|
10
10
|
var RELEASE_PROTOCOL_VERSION = 1;
|
|
11
11
|
var RELEASE_COMMANDS = /* @__PURE__ */ new Set([
|
|
12
12
|
"build",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deploy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LVIIWE44.js";
|
|
4
4
|
import {
|
|
5
5
|
syncDeps
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YMWDIAUG.js";
|
|
7
7
|
import {
|
|
8
8
|
loadEnv
|
|
9
9
|
} from "./chunk-2CR762KB.js";
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
createApiClient,
|
|
13
13
|
isApiErrorStatus,
|
|
14
14
|
validateCollectionNameLength
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-U3ZLIXGD.js";
|
|
16
16
|
import {
|
|
17
17
|
findProjectRoot,
|
|
18
18
|
getApiUrl,
|
|
@@ -1287,6 +1287,8 @@ ${pc2.dim("Resources:")}
|
|
|
1287
1287
|
hooks Plan only hooks
|
|
1288
1288
|
agents Plan only agents
|
|
1289
1289
|
agents/<name> Plan single agent
|
|
1290
|
+
skills Plan only project skills
|
|
1291
|
+
skills/<slug> Plan single project skill
|
|
1290
1292
|
mailboxes Plan only mailboxes
|
|
1291
1293
|
mailboxes/<slug> Plan single mailbox
|
|
1292
1294
|
app Plan app deployment
|
|
@@ -1315,6 +1317,8 @@ ${pc2.dim("Resources:")}
|
|
|
1315
1317
|
hooks Apply only hooks
|
|
1316
1318
|
agents Apply only agents
|
|
1317
1319
|
agents/<name> Apply single agent
|
|
1320
|
+
skills Apply only project skills
|
|
1321
|
+
skills/<slug> Apply single project skill
|
|
1318
1322
|
mailboxes Apply only mailboxes
|
|
1319
1323
|
mailboxes/<slug> Apply single mailbox
|
|
1320
1324
|
app Deploy the frontend app
|
|
@@ -1359,6 +1363,8 @@ ${pc2.dim("Resources:")}
|
|
|
1359
1363
|
hooks Pull only hooks
|
|
1360
1364
|
agents Pull only agents
|
|
1361
1365
|
agents/<name> Pull single agent
|
|
1366
|
+
skills Pull only project skills
|
|
1367
|
+
skills/<slug> Pull single project skill
|
|
1362
1368
|
mailboxes Pull only mailboxes
|
|
1363
1369
|
mailboxes/<slug> Pull single mailbox
|
|
1364
1370
|
|
|
@@ -1386,6 +1392,8 @@ ${pc2.dim("Resources:")}
|
|
|
1386
1392
|
hooks Destroy only hooks
|
|
1387
1393
|
agents Destroy only agents
|
|
1388
1394
|
agents/<name> Destroy single agent
|
|
1395
|
+
skills Destroy only project skills
|
|
1396
|
+
skills/<slug> Destroy single project skill
|
|
1389
1397
|
app Delete app registration
|
|
1390
1398
|
|
|
1391
1399
|
${pc2.dim("Options:")}
|
|
@@ -1412,6 +1420,7 @@ ${pc2.dim("Types:")}
|
|
|
1412
1420
|
automations List only automations
|
|
1413
1421
|
hooks List only hooks
|
|
1414
1422
|
agents List only agents
|
|
1423
|
+
skills List only project skills
|
|
1415
1424
|
mailboxes List only mailboxes
|
|
1416
1425
|
|
|
1417
1426
|
${pc2.dim("Options:")}
|
|
@@ -1436,6 +1445,7 @@ ${pc2.dim("Resources:")}
|
|
|
1436
1445
|
automations/<name> Show automation details
|
|
1437
1446
|
hooks/<name> Show hook details
|
|
1438
1447
|
agents/<name> Show agent details
|
|
1448
|
+
skills/<slug> Show project skill details
|
|
1439
1449
|
mailboxes/<slug> Show mailbox details
|
|
1440
1450
|
app Show app details
|
|
1441
1451
|
|
|
@@ -1452,7 +1462,7 @@ function parseResource(resourcePath) {
|
|
|
1452
1462
|
const parts = resourcePath.split("/");
|
|
1453
1463
|
const type = parts[0];
|
|
1454
1464
|
const name = parts.slice(1).join("/") || null;
|
|
1455
|
-
const validTypes = ["collections", "automations", "hooks", "agents", "mailboxes", "app"];
|
|
1465
|
+
const validTypes = ["collections", "automations", "hooks", "agents", "skills", "mailboxes", "app"];
|
|
1456
1466
|
if (!validTypes.includes(type)) {
|
|
1457
1467
|
console.log(pc2.red(` Unknown resource type "${type}". Valid types: ${validTypes.join(", ")}`));
|
|
1458
1468
|
process.exit(1);
|
|
@@ -2475,6 +2485,246 @@ ${hook.script.split("\n").map((line) => " " + line).join("\n")}
|
|
|
2475
2485
|
console.log(pc2.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
|
|
2476
2486
|
}
|
|
2477
2487
|
}
|
|
2488
|
+
var SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2489
|
+
var SKILL_NAME_MAX_LENGTH = 100;
|
|
2490
|
+
var SKILL_SLUG_MAX_LENGTH = 100;
|
|
2491
|
+
var SKILL_SUMMARY_MAX_LENGTH = 500;
|
|
2492
|
+
function loadLocalSkills(platformDir, filterName) {
|
|
2493
|
+
const skillsDir = join2(platformDir, "skills");
|
|
2494
|
+
if (!existsSync2(skillsDir)) return [];
|
|
2495
|
+
const skills = [];
|
|
2496
|
+
const errors = [];
|
|
2497
|
+
for (const entry of readdirSync2(skillsDir, { withFileTypes: true })) {
|
|
2498
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
2499
|
+
if (filterName && entry.name !== filterName) continue;
|
|
2500
|
+
if (!SKILL_SLUG_RE.test(entry.name)) {
|
|
2501
|
+
errors.push(`${entry.name}: directory name must be a lowercase slug`);
|
|
2502
|
+
continue;
|
|
2503
|
+
}
|
|
2504
|
+
const path = join2(skillsDir, entry.name, "SKILL.md");
|
|
2505
|
+
if (!existsSync2(path)) {
|
|
2506
|
+
errors.push(`${entry.name}: missing SKILL.md`);
|
|
2507
|
+
continue;
|
|
2508
|
+
}
|
|
2509
|
+
const content = readFileSync2(path, "utf-8");
|
|
2510
|
+
const separator = /\r?\n---\r?\n/.exec(content);
|
|
2511
|
+
const parts = separator ? [content.slice(0, separator.index), content.slice(separator.index + separator[0].length)] : [content];
|
|
2512
|
+
if (parts.length !== 2) {
|
|
2513
|
+
errors.push(`${entry.name}: SKILL.md must separate the summary and instructions with ---`);
|
|
2514
|
+
continue;
|
|
2515
|
+
}
|
|
2516
|
+
const headerLines = parts[0].trim().split(/\r?\n/);
|
|
2517
|
+
const titleLine = headerLines[0]?.trim().startsWith("# ") ? headerLines[0] : "";
|
|
2518
|
+
const name = titleLine.trim().slice(2).trim();
|
|
2519
|
+
const summaryLines = [];
|
|
2520
|
+
for (const line of headerLines.slice(1)) {
|
|
2521
|
+
const trimmed = line.trim();
|
|
2522
|
+
if (summaryLines.length === 0 && trimmed === "") continue;
|
|
2523
|
+
if (summaryLines.length > 0 && trimmed === "") break;
|
|
2524
|
+
summaryLines.push(trimmed);
|
|
2525
|
+
}
|
|
2526
|
+
const summary = summaryLines.join(" ");
|
|
2527
|
+
const instructions = parts[1].trim();
|
|
2528
|
+
if (!name) errors.push(`${entry.name}: SKILL.md must start with a # Title`);
|
|
2529
|
+
if (name.length > SKILL_NAME_MAX_LENGTH) errors.push(`${entry.name}: title must be at most ${SKILL_NAME_MAX_LENGTH} characters`);
|
|
2530
|
+
if (!summary) errors.push(`${entry.name}: SKILL.md must include a one-paragraph summary`);
|
|
2531
|
+
if (summary.length > SKILL_SUMMARY_MAX_LENGTH) errors.push(`${entry.name}: summary must be at most ${SKILL_SUMMARY_MAX_LENGTH} characters`);
|
|
2532
|
+
if (!instructions) errors.push(`${entry.name}: SKILL.md must include instructions after ---`);
|
|
2533
|
+
if (name && name.length <= SKILL_NAME_MAX_LENGTH && summary && summary.length <= SKILL_SUMMARY_MAX_LENGTH && instructions) {
|
|
2534
|
+
skills.push({ slug: entry.name, name, summary, instructions });
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
if (errors.length > 0) {
|
|
2538
|
+
console.log(pc2.red(" Skill errors:"));
|
|
2539
|
+
for (const error3 of errors) console.log(pc2.red(` \u2717 ${error3}`));
|
|
2540
|
+
throw new Error(`Found ${errors.length} skill error(s)`);
|
|
2541
|
+
}
|
|
2542
|
+
return skills;
|
|
2543
|
+
}
|
|
2544
|
+
function requireProjectSkillsProjectID(projectId) {
|
|
2545
|
+
if (!projectId) {
|
|
2546
|
+
throw new Error("Project skills require lumera.project_id in package.json. Register the project before applying skills.");
|
|
2547
|
+
}
|
|
2548
|
+
return projectId;
|
|
2549
|
+
}
|
|
2550
|
+
function skillMarkdown(skill) {
|
|
2551
|
+
return `# ${skill.name}
|
|
2552
|
+
|
|
2553
|
+
${skill.summary}
|
|
2554
|
+
|
|
2555
|
+
---
|
|
2556
|
+
|
|
2557
|
+
${skill.instructions}
|
|
2558
|
+
`;
|
|
2559
|
+
}
|
|
2560
|
+
function scopedProjectSkillSlug(appName, localSlug) {
|
|
2561
|
+
return appName ? `${appName}:${localSlug}` : localSlug;
|
|
2562
|
+
}
|
|
2563
|
+
function localProjectSkillSlug(appName, storedSlug) {
|
|
2564
|
+
const prefix = appName ? `${appName}:` : "";
|
|
2565
|
+
return prefix && storedSlug.startsWith(prefix) ? storedSlug.slice(prefix.length) : storedSlug;
|
|
2566
|
+
}
|
|
2567
|
+
function remoteSkillsByLocalSlug(remoteSkills, appName) {
|
|
2568
|
+
const byLocalSlug = /* @__PURE__ */ new Map();
|
|
2569
|
+
for (const remote of remoteSkills) {
|
|
2570
|
+
const localSlug = localProjectSkillSlug(appName, remote.slug);
|
|
2571
|
+
const existing = byLocalSlug.get(localSlug);
|
|
2572
|
+
if (!existing || remote.slug === scopedProjectSkillSlug(appName, localSlug)) {
|
|
2573
|
+
byLocalSlug.set(localSlug, remote);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
return byLocalSlug;
|
|
2577
|
+
}
|
|
2578
|
+
function projectSkillRefCandidates(refs, appName) {
|
|
2579
|
+
return [...new Set(refs.flatMap((ref) => appName && SKILL_SLUG_RE.test(ref) ? [scopedProjectSkillSlug(appName, ref), ref] : [ref]))];
|
|
2580
|
+
}
|
|
2581
|
+
async function resolveAgentSkillRefs(api, refs, projectId, appName, pendingLocalSkillSlugs = /* @__PURE__ */ new Set()) {
|
|
2582
|
+
const refToId = /* @__PURE__ */ new Map();
|
|
2583
|
+
const skillIdToSlug = /* @__PURE__ */ new Map();
|
|
2584
|
+
const blockedRefs = /* @__PURE__ */ new Set();
|
|
2585
|
+
const outsideProjectRefs = /* @__PURE__ */ new Set();
|
|
2586
|
+
if (refs.length === 0) return { refToId, skillIdToSlug, blockedRefs, outsideProjectRefs };
|
|
2587
|
+
const skills = await api.lookupAgentSkills(projectSkillRefCandidates(refs, appName));
|
|
2588
|
+
const byID = new Map(skills.map((skill) => [skill.id, skill]));
|
|
2589
|
+
const bySlug = new Map(skills.map((skill) => [skill.slug, skill]));
|
|
2590
|
+
const projectByLocalSlug = /* @__PURE__ */ new Map();
|
|
2591
|
+
for (const skill of skills) {
|
|
2592
|
+
const inRuntimeProject = !!projectId && skill.project_id === projectId;
|
|
2593
|
+
const inScope = !skill.project_id || inRuntimeProject;
|
|
2594
|
+
if (inScope && isAvailableToCustomAgents(skill)) {
|
|
2595
|
+
skillIdToSlug.set(skill.id, inRuntimeProject ? localProjectSkillSlug(appName, skill.slug) : skill.slug);
|
|
2596
|
+
}
|
|
2597
|
+
if (!inRuntimeProject) continue;
|
|
2598
|
+
const localSlug = localProjectSkillSlug(appName, skill.slug);
|
|
2599
|
+
const existing = projectByLocalSlug.get(localSlug);
|
|
2600
|
+
if (!existing || skill.slug === scopedProjectSkillSlug(appName, localSlug)) {
|
|
2601
|
+
projectByLocalSlug.set(localSlug, skill);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
for (const ref of refs) {
|
|
2605
|
+
let selected = byID.get(ref);
|
|
2606
|
+
if (!selected && SKILL_SLUG_RE.test(ref)) {
|
|
2607
|
+
selected = projectByLocalSlug.get(ref);
|
|
2608
|
+
if (!selected && pendingLocalSkillSlugs.has(ref)) {
|
|
2609
|
+
continue;
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
selected ||= bySlug.get(ref);
|
|
2613
|
+
if (!selected) continue;
|
|
2614
|
+
if (selected.project_id && selected.project_id !== projectId) {
|
|
2615
|
+
outsideProjectRefs.add(ref);
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
if (!isAvailableToCustomAgents(selected)) {
|
|
2619
|
+
blockedRefs.add(ref);
|
|
2620
|
+
continue;
|
|
2621
|
+
}
|
|
2622
|
+
refToId.set(ref, selected.id);
|
|
2623
|
+
}
|
|
2624
|
+
return { refToId, skillIdToSlug, blockedRefs, outsideProjectRefs };
|
|
2625
|
+
}
|
|
2626
|
+
function validateProjectSkillStorageSlugs(localSkills, appName) {
|
|
2627
|
+
for (const skill of localSkills) {
|
|
2628
|
+
const storedSlug = scopedProjectSkillSlug(appName, skill.slug);
|
|
2629
|
+
if (storedSlug.length > SKILL_SLUG_MAX_LENGTH) {
|
|
2630
|
+
throw new Error(`Project skill slug "${storedSlug}" must be at most ${SKILL_SLUG_MAX_LENGTH} characters including the project prefix`);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
async function planSkills(api, localSkills, projectId, appName) {
|
|
2635
|
+
if (localSkills.length === 0) return [];
|
|
2636
|
+
validateProjectSkillStorageSlugs(localSkills, appName);
|
|
2637
|
+
const scopedProjectID = requireProjectSkillsProjectID(projectId);
|
|
2638
|
+
const remoteSkills = await api.listAgentSkills({ project_id: scopedProjectID });
|
|
2639
|
+
const remoteBySlug = remoteSkillsByLocalSlug(remoteSkills, appName);
|
|
2640
|
+
const changes = [];
|
|
2641
|
+
for (const skill of localSkills) {
|
|
2642
|
+
const remote = remoteBySlug.get(skill.slug);
|
|
2643
|
+
if (!remote) {
|
|
2644
|
+
changes.push({ type: "create", resource: "skill", id: skill.slug, name: skill.name });
|
|
2645
|
+
continue;
|
|
2646
|
+
}
|
|
2647
|
+
const changed = [];
|
|
2648
|
+
if (remote.name !== skill.name) changed.push("name");
|
|
2649
|
+
if ((remote.summary || "").trim() !== skill.summary.trim()) changed.push("summary");
|
|
2650
|
+
if ((remote.instructions || "").trim() !== skill.instructions.trim()) changed.push("instructions");
|
|
2651
|
+
if (changed.length > 0) {
|
|
2652
|
+
changes.push({
|
|
2653
|
+
type: "update",
|
|
2654
|
+
resource: "skill",
|
|
2655
|
+
id: skill.slug,
|
|
2656
|
+
name: skill.name,
|
|
2657
|
+
details: `changed: ${changed.join(", ")}`,
|
|
2658
|
+
textDiffs: changed.includes("instructions") || changed.includes("summary") ? [{ field: "SKILL.md", oldText: skillMarkdown({ slug: skill.slug, name: remote.name, summary: remote.summary || "", instructions: remote.instructions || "" }), newText: skillMarkdown(skill) }] : void 0
|
|
2659
|
+
});
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
return changes;
|
|
2663
|
+
}
|
|
2664
|
+
async function applySkills(api, localSkills, projectId, appName) {
|
|
2665
|
+
if (localSkills.length === 0) return 0;
|
|
2666
|
+
validateProjectSkillStorageSlugs(localSkills, appName);
|
|
2667
|
+
const scopedProjectID = requireProjectSkillsProjectID(projectId);
|
|
2668
|
+
let errors = 0;
|
|
2669
|
+
const remoteSkills = await api.listAgentSkills({ project_id: scopedProjectID });
|
|
2670
|
+
const remoteBySlug = remoteSkillsByLocalSlug(remoteSkills, appName);
|
|
2671
|
+
for (const skill of localSkills) {
|
|
2672
|
+
const remote = remoteBySlug.get(skill.slug);
|
|
2673
|
+
try {
|
|
2674
|
+
if (remote) {
|
|
2675
|
+
await api.updateAgentSkill(remote.id, {
|
|
2676
|
+
name: skill.name,
|
|
2677
|
+
summary: skill.summary,
|
|
2678
|
+
instructions: skill.instructions
|
|
2679
|
+
});
|
|
2680
|
+
console.log(pc2.green(" \u2713"), `${skill.name} (updated)`);
|
|
2681
|
+
} else {
|
|
2682
|
+
await api.createAgentSkill({
|
|
2683
|
+
...skill,
|
|
2684
|
+
slug: scopedProjectSkillSlug(appName, skill.slug),
|
|
2685
|
+
project_id: scopedProjectID
|
|
2686
|
+
});
|
|
2687
|
+
console.log(pc2.green(" \u2713"), `${skill.name} (created)`);
|
|
2688
|
+
}
|
|
2689
|
+
} catch (error3) {
|
|
2690
|
+
console.log(pc2.red(" \u2717"), `${skill.name}: ${error3}`);
|
|
2691
|
+
errors++;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
return errors;
|
|
2695
|
+
}
|
|
2696
|
+
async function pullSkills(api, platformDir, filterName, projectId, appName) {
|
|
2697
|
+
if (!projectId) {
|
|
2698
|
+
console.log(pc2.dim(" Skipped \u2014 project not registered (run `lumera register`)"));
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
const scopedProjectID = requireProjectSkillsProjectID(projectId);
|
|
2702
|
+
const skillsDir = join2(platformDir, "skills");
|
|
2703
|
+
const remoteSkills = remoteSkillsByLocalSlug(
|
|
2704
|
+
await api.listAgentSkills({ project_id: scopedProjectID }),
|
|
2705
|
+
appName
|
|
2706
|
+
);
|
|
2707
|
+
for (const [localSlug, skill] of remoteSkills) {
|
|
2708
|
+
if (!SKILL_SLUG_RE.test(localSlug)) {
|
|
2709
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${skill.name}: skill slug ${skill.slug} cannot be represented in platform/skills`));
|
|
2710
|
+
continue;
|
|
2711
|
+
}
|
|
2712
|
+
if (filterName && localSlug !== filterName && skill.slug !== filterName && skill.name !== filterName) continue;
|
|
2713
|
+
if (!(skill.summary || "").trim() || !(skill.instructions || "").trim()) {
|
|
2714
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${skill.name}: source-managed skills require both summary and instructions`));
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2717
|
+
const skillDir = join2(skillsDir, localSlug);
|
|
2718
|
+
mkdirSync(skillDir, { recursive: true });
|
|
2719
|
+
writeFileSync(join2(skillDir, "SKILL.md"), skillMarkdown({
|
|
2720
|
+
slug: localSlug,
|
|
2721
|
+
name: skill.name,
|
|
2722
|
+
summary: skill.summary || "",
|
|
2723
|
+
instructions: skill.instructions || ""
|
|
2724
|
+
}));
|
|
2725
|
+
console.log(pc2.green(" \u2713"), `${skill.name} \u2192 skills/${localSlug}/`);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2478
2728
|
function loadLocalAgents(platformDir, filterName, appName) {
|
|
2479
2729
|
const agentsDir = join2(platformDir, "agents");
|
|
2480
2730
|
if (!existsSync2(agentsDir)) return [];
|
|
@@ -2545,44 +2795,38 @@ function loadLocalAgents(platformDir, filterName, appName) {
|
|
|
2545
2795
|
}
|
|
2546
2796
|
return agents;
|
|
2547
2797
|
}
|
|
2548
|
-
async function planAgents(api, localAgents, projectId) {
|
|
2798
|
+
async function planAgents(api, localAgents, projectId, localSkillSlugs = /* @__PURE__ */ new Set(), appName) {
|
|
2549
2799
|
const changes = [];
|
|
2550
2800
|
const remoteAgents = await api.listAgents(projectId ? { project_id: projectId } : void 0);
|
|
2551
2801
|
const remoteByExternalId = new Map(
|
|
2552
2802
|
remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
|
|
2553
2803
|
);
|
|
2554
|
-
const refToId = /* @__PURE__ */ new Map();
|
|
2555
|
-
const skillIdToSlug = /* @__PURE__ */ new Map();
|
|
2556
|
-
const blockedRefs = /* @__PURE__ */ new Set();
|
|
2557
2804
|
const planRefs = [
|
|
2558
2805
|
.../* @__PURE__ */ new Set([
|
|
2559
2806
|
...localAgents.flatMap((a) => a.agent.skills ?? []),
|
|
2560
2807
|
...remoteAgents.flatMap((a) => a.skill_ids ?? [])
|
|
2561
2808
|
])
|
|
2562
2809
|
];
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
blockedRefs.add(s.id);
|
|
2574
|
-
}
|
|
2575
|
-
}
|
|
2576
|
-
} catch (e) {
|
|
2577
|
-
console.log(pc2.yellow(` \u26A0 Could not resolve skills for plan: ${e}`));
|
|
2578
|
-
}
|
|
2810
|
+
let resolution = {
|
|
2811
|
+
refToId: /* @__PURE__ */ new Map(),
|
|
2812
|
+
skillIdToSlug: /* @__PURE__ */ new Map(),
|
|
2813
|
+
blockedRefs: /* @__PURE__ */ new Set(),
|
|
2814
|
+
outsideProjectRefs: /* @__PURE__ */ new Set()
|
|
2815
|
+
};
|
|
2816
|
+
try {
|
|
2817
|
+
resolution = await resolveAgentSkillRefs(api, planRefs, projectId, appName, localSkillSlugs);
|
|
2818
|
+
} catch (e) {
|
|
2819
|
+
console.log(pc2.yellow(` \u26A0 Could not resolve skills for plan: ${e}`));
|
|
2579
2820
|
}
|
|
2821
|
+
const { refToId, skillIdToSlug, blockedRefs, outsideProjectRefs } = resolution;
|
|
2580
2822
|
for (const { agent, systemPrompt, policyScript } of localAgents) {
|
|
2581
2823
|
const remote = remoteByExternalId.get(agent.external_id);
|
|
2582
2824
|
for (const ref of agent.skills ?? []) {
|
|
2583
|
-
if (
|
|
2825
|
+
if (outsideProjectRefs.has(ref)) {
|
|
2826
|
+
console.log(pc2.red(` \u2717 Skill "${ref}" belongs to another project \u2014 apply will fail for this agent`));
|
|
2827
|
+
} else if (blockedRefs.has(ref)) {
|
|
2584
2828
|
console.log(pc2.red(` \u2717 Skill "${ref}" is a Lumera platform skill, only available to Lumera-built agents \u2014 apply will fail for this agent`));
|
|
2585
|
-
} else if (!refToId.has(ref)) {
|
|
2829
|
+
} else if (!refToId.has(ref) && !localSkillSlugs.has(ref)) {
|
|
2586
2830
|
console.log(pc2.red(` \u2717 Skill "${ref}" not found \u2014 apply will fail for this agent`));
|
|
2587
2831
|
}
|
|
2588
2832
|
}
|
|
@@ -2600,12 +2844,14 @@ async function planAgents(api, localAgents, projectId) {
|
|
|
2600
2844
|
if ((remote.policy_enabled || false) !== (agent.policy_enabled || false)) diffs.push("policy_enabled");
|
|
2601
2845
|
if ((remote.policy_description || "") !== (agent.policy_description || "")) diffs.push("policy_description");
|
|
2602
2846
|
const localSkillIds = (agent.skills || []).map((s) => refToId.get(s)).filter((id) => !!id).sort();
|
|
2847
|
+
const pendingLocalSkillSlugs = (agent.skills || []).filter((ref) => localSkillSlugs.has(ref) && !refToId.has(ref));
|
|
2603
2848
|
const remoteSkillIds = [...remote.skill_ids || []].sort();
|
|
2604
|
-
if (localSkillIds.join(",") !== remoteSkillIds.join(",")) {
|
|
2849
|
+
if (localSkillIds.join(",") !== remoteSkillIds.join(",") || pendingLocalSkillSlugs.length > 0) {
|
|
2605
2850
|
const addedSlugs = localSkillIds.filter((id) => !remoteSkillIds.includes(id)).map((id) => skillIdToSlug.get(id) || id);
|
|
2606
2851
|
const removedSlugs = remoteSkillIds.filter((id) => !localSkillIds.includes(id)).map((id) => skillIdToSlug.get(id) || id);
|
|
2607
2852
|
const parts = [];
|
|
2608
2853
|
if (addedSlugs.length) parts.push(`+${addedSlugs.join(", +")}`);
|
|
2854
|
+
if (pendingLocalSkillSlugs.length) parts.push(`+${pendingLocalSkillSlugs.join(", +")}`);
|
|
2609
2855
|
if (removedSlugs.length) parts.push(`-${removedSlugs.join(", -")}`);
|
|
2610
2856
|
diffs.push(`skills (${parts.join(", ")})`);
|
|
2611
2857
|
}
|
|
@@ -2628,34 +2874,29 @@ function isAvailableToCustomAgents(skill) {
|
|
|
2628
2874
|
if (!skill.managed) return true;
|
|
2629
2875
|
return (skill.tags ?? []).includes(TAG_AVAILABLE_TO_CUSTOM_AGENTS);
|
|
2630
2876
|
}
|
|
2631
|
-
async function applyAgents(api, localAgents, projectId) {
|
|
2877
|
+
async function applyAgents(api, localAgents, projectId, localSkillSlugs = /* @__PURE__ */ new Set(), appName) {
|
|
2632
2878
|
let errors = 0;
|
|
2633
2879
|
const remoteAgents = await api.listAgents(projectId ? { project_id: projectId } : void 0);
|
|
2634
2880
|
const remoteByExternalId = new Map(
|
|
2635
2881
|
remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
|
|
2636
2882
|
);
|
|
2637
|
-
const refToId = /* @__PURE__ */ new Map();
|
|
2638
|
-
const blockedRefs = /* @__PURE__ */ new Set();
|
|
2639
2883
|
const refs = [...new Set(localAgents.flatMap((a) => a.agent.skills ?? []))];
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
}
|
|
2651
|
-
}
|
|
2652
|
-
} catch (e) {
|
|
2653
|
-
console.log(pc2.yellow(` \u26A0 Could not resolve skills: ${e}`));
|
|
2654
|
-
}
|
|
2884
|
+
let resolution = {
|
|
2885
|
+
refToId: /* @__PURE__ */ new Map(),
|
|
2886
|
+
skillIdToSlug: /* @__PURE__ */ new Map(),
|
|
2887
|
+
blockedRefs: /* @__PURE__ */ new Set(),
|
|
2888
|
+
outsideProjectRefs: /* @__PURE__ */ new Set()
|
|
2889
|
+
};
|
|
2890
|
+
try {
|
|
2891
|
+
resolution = await resolveAgentSkillRefs(api, refs, projectId, appName, localSkillSlugs);
|
|
2892
|
+
} catch (e) {
|
|
2893
|
+
console.log(pc2.yellow(` \u26A0 Could not resolve skills: ${e}`));
|
|
2655
2894
|
}
|
|
2895
|
+
const { refToId, blockedRefs, outsideProjectRefs } = resolution;
|
|
2656
2896
|
for (const { agent, systemPrompt, policyScript } of localAgents) {
|
|
2657
2897
|
const remote = remoteByExternalId.get(agent.external_id);
|
|
2658
2898
|
const skillIds = [];
|
|
2899
|
+
const outsideProjectForAgent = [];
|
|
2659
2900
|
const blockedForAgent = [];
|
|
2660
2901
|
const notFoundForAgent = [];
|
|
2661
2902
|
if (agent.skills) {
|
|
@@ -2663,6 +2904,8 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
2663
2904
|
const id = refToId.get(ref);
|
|
2664
2905
|
if (id) {
|
|
2665
2906
|
skillIds.push(id);
|
|
2907
|
+
} else if (outsideProjectRefs.has(ref)) {
|
|
2908
|
+
outsideProjectForAgent.push(ref);
|
|
2666
2909
|
} else if (blockedRefs.has(ref)) {
|
|
2667
2910
|
blockedForAgent.push(ref);
|
|
2668
2911
|
} else {
|
|
@@ -2670,8 +2913,11 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
2670
2913
|
}
|
|
2671
2914
|
}
|
|
2672
2915
|
}
|
|
2673
|
-
if (blockedForAgent.length > 0 || notFoundForAgent.length > 0) {
|
|
2916
|
+
if (outsideProjectForAgent.length > 0 || blockedForAgent.length > 0 || notFoundForAgent.length > 0) {
|
|
2674
2917
|
console.log(pc2.red(" \u2717"), `${agent.name}:`);
|
|
2918
|
+
if (outsideProjectForAgent.length > 0) {
|
|
2919
|
+
console.log(pc2.red(` skills outside the agent project: ${outsideProjectForAgent.join(", ")}`));
|
|
2920
|
+
}
|
|
2675
2921
|
if (blockedForAgent.length > 0) {
|
|
2676
2922
|
console.log(pc2.red(` skills not available for custom agents: ${blockedForAgent.join(", ")}`));
|
|
2677
2923
|
}
|
|
@@ -2711,7 +2957,7 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
2711
2957
|
}
|
|
2712
2958
|
return errors;
|
|
2713
2959
|
}
|
|
2714
|
-
async function pullAgents(api, platformDir, filterName, projectId) {
|
|
2960
|
+
async function pullAgents(api, platformDir, filterName, projectId, appName) {
|
|
2715
2961
|
const agentsDir = join2(platformDir, "agents");
|
|
2716
2962
|
mkdirSync(agentsDir, { recursive: true });
|
|
2717
2963
|
const agents = await api.listAgents(projectId ? { project_id: projectId } : void 0);
|
|
@@ -2720,7 +2966,9 @@ async function pullAgents(api, platformDir, filterName, projectId) {
|
|
|
2720
2966
|
if (pullRefs.length > 0) {
|
|
2721
2967
|
try {
|
|
2722
2968
|
const skills = await api.lookupAgentSkills(pullRefs);
|
|
2723
|
-
for (const s of skills)
|
|
2969
|
+
for (const s of skills) {
|
|
2970
|
+
skillIdToSlug.set(s.id, s.project_id === projectId ? localProjectSkillSlug(appName, s.slug) : s.slug);
|
|
2971
|
+
}
|
|
2724
2972
|
} catch (e) {
|
|
2725
2973
|
console.log(pc2.yellow(` \u26A0 Could not resolve skills for pull: ${e}`));
|
|
2726
2974
|
console.log(pc2.yellow(` Pulled configs may omit skills \u2014 review before re-applying.`));
|
|
@@ -2875,6 +3123,28 @@ async function listResources(api, platformDir, filterType, appName, projectId) {
|
|
|
2875
3123
|
}
|
|
2876
3124
|
}
|
|
2877
3125
|
}
|
|
3126
|
+
if (!filterType || filterType === "skills") {
|
|
3127
|
+
const localSkills = loadLocalSkills(platformDir);
|
|
3128
|
+
if (localSkills.length > 0) requireProjectSkillsProjectID(projectId);
|
|
3129
|
+
const remoteSkills = projectId ? await api.listAgentSkills({ project_id: projectId }) : [];
|
|
3130
|
+
const remoteBySlug = remoteSkillsByLocalSlug(remoteSkills, appName);
|
|
3131
|
+
const localSlugs = new Set(localSkills.map((skill) => skill.slug));
|
|
3132
|
+
for (const skill of localSkills) {
|
|
3133
|
+
const remote = remoteBySlug.get(skill.slug);
|
|
3134
|
+
if (!remote) {
|
|
3135
|
+
results.push({ name: skill.name, type: "skills", status: "local-only" });
|
|
3136
|
+
} else if (remote.name !== skill.name || (remote.summary || "").trim() !== skill.summary.trim() || (remote.instructions || "").trim() !== skill.instructions.trim()) {
|
|
3137
|
+
results.push({ name: skill.name, type: "skills", status: "changed", details: "SKILL.md" });
|
|
3138
|
+
} else {
|
|
3139
|
+
results.push({ name: skill.name, type: "skills", status: "synced" });
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
for (const remote of remoteSkills) {
|
|
3143
|
+
if (!localSlugs.has(localProjectSkillSlug(appName, remote.slug))) {
|
|
3144
|
+
results.push({ name: remote.name, type: "skills", status: "remote-only" });
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
2878
3148
|
if (!filterType || filterType === "mailboxes") {
|
|
2879
3149
|
const localMailboxes = loadLocalMailboxes(platformDir);
|
|
2880
3150
|
const remoteMailboxes = await api.listMailboxes();
|
|
@@ -3022,6 +3292,20 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
3022
3292
|
} catch {
|
|
3023
3293
|
}
|
|
3024
3294
|
}
|
|
3295
|
+
if (!resourceType || resourceType === "skills") {
|
|
3296
|
+
const localSkills = loadLocalSkills(platformDir, resourceName || void 0);
|
|
3297
|
+
if (localSkills.length > 0) {
|
|
3298
|
+
const scopedProjectID = requireProjectSkillsProjectID(projectId);
|
|
3299
|
+
const remoteSkills = await api.listAgentSkills({ project_id: scopedProjectID });
|
|
3300
|
+
const remoteBySlug = remoteSkillsByLocalSlug(remoteSkills, appName);
|
|
3301
|
+
for (const skill of localSkills) {
|
|
3302
|
+
const remote = remoteBySlug.get(skill.slug);
|
|
3303
|
+
if (remote) {
|
|
3304
|
+
toDelete.push({ type: "skill", id: skill.slug, name: skill.name, remoteId: remote.id });
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3025
3309
|
if (toDelete.length === 0) {
|
|
3026
3310
|
console.log(pc2.green(" \u2713 No resources found to delete"));
|
|
3027
3311
|
return;
|
|
@@ -3045,6 +3329,7 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
3045
3329
|
}
|
|
3046
3330
|
}
|
|
3047
3331
|
const agents = toDelete.filter((r) => r.type === "agent");
|
|
3332
|
+
const skills = toDelete.filter((r) => r.type === "skill");
|
|
3048
3333
|
const hooks = toDelete.filter((r) => r.type === "hook");
|
|
3049
3334
|
const automations = toDelete.filter((r) => r.type === "automation");
|
|
3050
3335
|
const collections = toDelete.filter((r) => r.type === "collection");
|
|
@@ -3058,6 +3343,15 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
3058
3343
|
errors++;
|
|
3059
3344
|
}
|
|
3060
3345
|
}
|
|
3346
|
+
for (const resource of skills) {
|
|
3347
|
+
try {
|
|
3348
|
+
await api.deleteAgentSkill(resource.remoteId);
|
|
3349
|
+
console.log(pc2.green(" \u2713"), `Deleted skill: ${resource.name}`);
|
|
3350
|
+
} catch (e) {
|
|
3351
|
+
console.log(pc2.red(" \u2717"), `Failed to delete skill ${resource.name}: ${e}`);
|
|
3352
|
+
errors++;
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3061
3355
|
for (const resource of hooks) {
|
|
3062
3356
|
try {
|
|
3063
3357
|
await api.deleteHook(resource.remoteId);
|
|
@@ -3314,6 +3608,31 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
3314
3608
|
console.log(` Skills: ${skills.join(", ")}`);
|
|
3315
3609
|
}
|
|
3316
3610
|
console.log();
|
|
3611
|
+
} else if (resourceType === "skills") {
|
|
3612
|
+
const localSkills = loadLocalSkills(platformDir, resourceName);
|
|
3613
|
+
if (localSkills.length > 0) requireProjectSkillsProjectID(projectId);
|
|
3614
|
+
const remoteSkills = projectId ? await api.listAgentSkills({ project_id: projectId }) : [];
|
|
3615
|
+
const local = localSkills[0];
|
|
3616
|
+
const remote = remoteSkillsByLocalSlug(remoteSkills, appName).get(resourceName) || remoteSkills.find((skill) => skill.name === resourceName);
|
|
3617
|
+
if (!local && !remote) {
|
|
3618
|
+
console.log(pc2.red(` Skill "${resourceName}" not found`));
|
|
3619
|
+
process.exit(1);
|
|
3620
|
+
}
|
|
3621
|
+
console.log();
|
|
3622
|
+
console.log(pc2.bold(` Skill: ${local?.name || remote?.name}`));
|
|
3623
|
+
console.log();
|
|
3624
|
+
if (local && remote) {
|
|
3625
|
+
const changed = local.name !== remote.name || local.summary.trim() !== (remote.summary || "").trim() || local.instructions.trim() !== (remote.instructions || "").trim();
|
|
3626
|
+
console.log(` Status: ${changed ? pc2.yellow("changed") : pc2.green("synced")}`);
|
|
3627
|
+
} else if (local) {
|
|
3628
|
+
console.log(` Status: ${pc2.yellow("local only")}`);
|
|
3629
|
+
} else {
|
|
3630
|
+
console.log(` Status: ${pc2.cyan("remote only")}`);
|
|
3631
|
+
}
|
|
3632
|
+
console.log(` Slug: ${local?.slug || (remote ? localProjectSkillSlug(appName, remote.slug) : "")}`);
|
|
3633
|
+
const summary = local?.summary || remote?.summary;
|
|
3634
|
+
if (summary) console.log(` Summary: ${summary}`);
|
|
3635
|
+
console.log();
|
|
3317
3636
|
} else if (resourceType === "mailboxes") {
|
|
3318
3637
|
const localMailboxes = loadLocalMailboxes(platformDir, resourceName);
|
|
3319
3638
|
const remoteMailboxes = await api.listMailboxes();
|
|
@@ -3430,10 +3749,15 @@ async function plan(args) {
|
|
|
3430
3749
|
allChanges.push(...changes);
|
|
3431
3750
|
}
|
|
3432
3751
|
}
|
|
3752
|
+
const localSkills = !type || type === "skills" ? loadLocalSkills(platformDir, name || void 0) : [];
|
|
3753
|
+
const localSkillSlugs = new Set(!type || type === "skills" || type === "agents" ? (localSkills.length > 0 || type !== "agents" ? localSkills : loadLocalSkills(platformDir)).map((skill) => skill.slug) : []);
|
|
3754
|
+
if (localSkills.length > 0) {
|
|
3755
|
+
allChanges.push(...await planSkills(api, localSkills, projectId, appName));
|
|
3756
|
+
}
|
|
3433
3757
|
if (!type || type === "agents") {
|
|
3434
3758
|
const localAgents = loadLocalAgents(platformDir, name || void 0, appName);
|
|
3435
3759
|
if (localAgents.length > 0) {
|
|
3436
|
-
const changes = await planAgents(api, localAgents, projectId);
|
|
3760
|
+
const changes = await planAgents(api, localAgents, projectId, localSkillSlugs, appName);
|
|
3437
3761
|
allChanges.push(...changes);
|
|
3438
3762
|
}
|
|
3439
3763
|
}
|
|
@@ -3556,15 +3880,18 @@ async function apply(args) {
|
|
|
3556
3880
|
const localCollections = !type || type === "collections" ? loadLocalCollections(platformDir, name || void 0) : [];
|
|
3557
3881
|
const localAutomations = !type || type === "automations" ? loadLocalAutomations(platformDir, name || void 0, appName) : [];
|
|
3558
3882
|
const localHooks = !type || type === "hooks" ? loadLocalHooks(platformDir, name || void 0, appName) : [];
|
|
3883
|
+
const localSkills = !type || type === "skills" ? loadLocalSkills(platformDir, name || void 0) : [];
|
|
3884
|
+
const localSkillSlugs = new Set(!type || type === "skills" || type === "agents" ? (localSkills.length > 0 || type !== "agents" ? localSkills : loadLocalSkills(platformDir)).map((skill) => skill.slug) : []);
|
|
3559
3885
|
const localAgents = !type || type === "agents" ? loadLocalAgents(platformDir, name || void 0, appName) : [];
|
|
3560
3886
|
const localMailboxes = !type || type === "mailboxes" ? loadLocalMailboxes(platformDir, name || void 0) : [];
|
|
3561
3887
|
if (localCollections.length > 0) allChanges.push(...await planCollections(api, localCollections, appName, projectId));
|
|
3562
3888
|
if (localAutomations.length > 0) allChanges.push(...await planAutomations(api, localAutomations, projectId));
|
|
3563
3889
|
if (localHooks.length > 0) allChanges.push(...await planHooks(api, localHooks, collections, projectId));
|
|
3564
|
-
if (
|
|
3890
|
+
if (localSkills.length > 0) allChanges.push(...await planSkills(api, localSkills, projectId, appName));
|
|
3891
|
+
if (localAgents.length > 0) allChanges.push(...await planAgents(api, localAgents, projectId, localSkillSlugs, appName));
|
|
3565
3892
|
if (localMailboxes.length > 0) allChanges.push(...await planMailboxes(api, localMailboxes));
|
|
3566
3893
|
if (name) {
|
|
3567
|
-
const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localAgents.length > 0 || localMailboxes.length > 0;
|
|
3894
|
+
const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localSkills.length > 0 || localAgents.length > 0 || localMailboxes.length > 0;
|
|
3568
3895
|
if (!hasLocal) {
|
|
3569
3896
|
console.log();
|
|
3570
3897
|
console.log(pc2.red(` Resource "${name}" not found locally`));
|
|
@@ -3651,9 +3978,14 @@ async function apply(args) {
|
|
|
3651
3978
|
totalErrors += await applyHooks(api, localHooks, collections, projectId);
|
|
3652
3979
|
console.log();
|
|
3653
3980
|
}
|
|
3981
|
+
if (localSkills.length > 0) {
|
|
3982
|
+
console.log(pc2.bold(" Skills:"));
|
|
3983
|
+
totalErrors += await applySkills(api, localSkills, projectId, appName);
|
|
3984
|
+
console.log();
|
|
3985
|
+
}
|
|
3654
3986
|
if (localAgents.length > 0) {
|
|
3655
3987
|
console.log(pc2.bold(" Agents:"));
|
|
3656
|
-
totalErrors += await applyAgents(api, localAgents, projectId);
|
|
3988
|
+
totalErrors += await applyAgents(api, localAgents, projectId, localSkillSlugs, appName);
|
|
3657
3989
|
console.log();
|
|
3658
3990
|
}
|
|
3659
3991
|
if (localMailboxes.length > 0) {
|
|
@@ -3706,10 +4038,25 @@ async function pull(args) {
|
|
|
3706
4038
|
} catch {
|
|
3707
4039
|
collections = /* @__PURE__ */ new Map();
|
|
3708
4040
|
}
|
|
4041
|
+
if (projectId && (!type || type === "skills")) {
|
|
4042
|
+
const localSkills = loadLocalSkills(platformDir, name || void 0);
|
|
4043
|
+
if (localSkills.length > 0) {
|
|
4044
|
+
const changes = await planSkills(api, localSkills, projectId, appName);
|
|
4045
|
+
for (const change of changes) {
|
|
4046
|
+
if (change.type === "update") conflicts.push(`skills/${change.id}`);
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
3709
4050
|
if (!type || type === "agents") {
|
|
3710
4051
|
const localAgents = loadLocalAgents(platformDir, name || void 0, appName);
|
|
3711
4052
|
if (localAgents.length > 0) {
|
|
3712
|
-
const changes = await planAgents(
|
|
4053
|
+
const changes = await planAgents(
|
|
4054
|
+
api,
|
|
4055
|
+
localAgents,
|
|
4056
|
+
projectId,
|
|
4057
|
+
new Set(loadLocalSkills(platformDir).map((skill) => skill.slug)),
|
|
4058
|
+
appName
|
|
4059
|
+
);
|
|
3713
4060
|
for (const c of changes) {
|
|
3714
4061
|
if (c.type === "update") conflicts.push(`agents/${c.name}`);
|
|
3715
4062
|
}
|
|
@@ -3770,9 +4117,14 @@ async function pull(args) {
|
|
|
3770
4117
|
await pullHooks(api, platformDir, name || void 0, appName, projectId);
|
|
3771
4118
|
console.log();
|
|
3772
4119
|
}
|
|
4120
|
+
if (!type || type === "skills") {
|
|
4121
|
+
console.log(pc2.bold(" Skills:"));
|
|
4122
|
+
await pullSkills(api, platformDir, name || void 0, projectId, appName);
|
|
4123
|
+
console.log();
|
|
4124
|
+
}
|
|
3773
4125
|
if (!type || type === "agents") {
|
|
3774
4126
|
console.log(pc2.bold(" Agents:"));
|
|
3775
|
-
await pullAgents(api, platformDir, name || void 0, projectId);
|
|
4127
|
+
await pullAgents(api, platformDir, name || void 0, projectId, appName);
|
|
3776
4128
|
console.log();
|
|
3777
4129
|
}
|
|
3778
4130
|
if (!type || type === "mailboxes") {
|
|
@@ -3924,6 +4276,7 @@ ${pc2.dim("Usage:")}
|
|
|
3924
4276
|
|
|
3925
4277
|
${pc2.dim("Resources:")}
|
|
3926
4278
|
agents/<name> Diff agent (system_prompt, policy_script)
|
|
4279
|
+
skills/<slug> Diff project skill instructions
|
|
3927
4280
|
automations/<name> Diff automation code
|
|
3928
4281
|
hooks/<name> Diff hook script
|
|
3929
4282
|
|
|
@@ -4072,23 +4425,70 @@ async function diff(args) {
|
|
|
4072
4425
|
} else if (scriptChanged) {
|
|
4073
4426
|
renderFullDiff(local.fileName, remote.script || "", local.script);
|
|
4074
4427
|
}
|
|
4428
|
+
} else if (type === "skills") {
|
|
4429
|
+
const localSkills = loadLocalSkills(platformDir, name);
|
|
4430
|
+
if (localSkills.length > 0) requireProjectSkillsProjectID(projectId);
|
|
4431
|
+
const remoteSkills = projectId ? await api.listAgentSkills({ project_id: projectId }) : [];
|
|
4432
|
+
const local = localSkills[0];
|
|
4433
|
+
const remote = remoteSkillsByLocalSlug(remoteSkills, appName).get(name) || remoteSkills.find((skill) => skill.name === name);
|
|
4434
|
+
if (!local && !remote) {
|
|
4435
|
+
console.log(pc2.red(` Skill "${name}" not found locally or remotely`));
|
|
4436
|
+
process.exit(1);
|
|
4437
|
+
}
|
|
4438
|
+
if (!local) {
|
|
4439
|
+
console.log(pc2.cyan(` Skill "${name}" exists only remotely`));
|
|
4440
|
+
process.exit(0);
|
|
4441
|
+
}
|
|
4442
|
+
if (!remote) {
|
|
4443
|
+
console.log(pc2.yellow(` Skill "${name}" exists only locally (not yet deployed)`));
|
|
4444
|
+
process.exit(0);
|
|
4445
|
+
}
|
|
4446
|
+
console.log(pc2.bold(` Skill: ${local.name}`));
|
|
4447
|
+
console.log();
|
|
4448
|
+
if (remote.name !== local.name) {
|
|
4449
|
+
console.log(` name: ${pc2.red(remote.name)} \u2192 ${pc2.green(local.name)}`);
|
|
4450
|
+
}
|
|
4451
|
+
if ((remote.summary || "").trim() !== local.summary.trim()) {
|
|
4452
|
+
console.log(` summary: ${pc2.red(remote.summary || "(empty)")} \u2192 ${pc2.green(local.summary)}`);
|
|
4453
|
+
}
|
|
4454
|
+
const remoteMarkdown = skillMarkdown({
|
|
4455
|
+
slug: localProjectSkillSlug(appName, remote.slug),
|
|
4456
|
+
name: remote.name,
|
|
4457
|
+
summary: remote.summary || "",
|
|
4458
|
+
instructions: remote.instructions || ""
|
|
4459
|
+
});
|
|
4460
|
+
const localMarkdown = skillMarkdown(local);
|
|
4461
|
+
if (remoteMarkdown === localMarkdown) {
|
|
4462
|
+
console.log(pc2.green(" \u2713 No changes"));
|
|
4463
|
+
} else {
|
|
4464
|
+
renderFullDiff("SKILL.md", remoteMarkdown, localMarkdown);
|
|
4465
|
+
}
|
|
4075
4466
|
} else {
|
|
4076
|
-
console.log(pc2.red(` Diff not supported for "${type}" \u2014 use agents, automations, or hooks`));
|
|
4467
|
+
console.log(pc2.red(` Diff not supported for "${type}" \u2014 use agents, skills, automations, or hooks`));
|
|
4077
4468
|
process.exit(1);
|
|
4078
4469
|
}
|
|
4079
4470
|
console.log();
|
|
4080
4471
|
}
|
|
4081
4472
|
export {
|
|
4082
4473
|
apply,
|
|
4474
|
+
applyAgents,
|
|
4083
4475
|
applyCollections,
|
|
4476
|
+
applySkills,
|
|
4084
4477
|
convertCollectionToApiFormat,
|
|
4085
4478
|
destroy,
|
|
4086
4479
|
diff,
|
|
4087
4480
|
list,
|
|
4088
4481
|
loadLocalCollections,
|
|
4482
|
+
loadLocalSkills,
|
|
4483
|
+
localProjectSkillSlug,
|
|
4089
4484
|
plan,
|
|
4090
4485
|
planCollections,
|
|
4486
|
+
planSkills,
|
|
4487
|
+
projectSkillRefCandidates,
|
|
4091
4488
|
pull,
|
|
4092
4489
|
pullCollections,
|
|
4490
|
+
pullSkills,
|
|
4491
|
+
resolveAgentSkillRefs,
|
|
4492
|
+
scopedProjectSkillSlug,
|
|
4093
4493
|
show
|
|
4094
4494
|
};
|
package/package.json
CHANGED
|
File without changes
|