@netnodeag/kraftwerk 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -20
- package/dist/cli/doctor.js +18 -1
- package/dist/cli/init.js +19 -12
- package/dist/cli/kraftwerk.js +3 -0
- package/dist/cli/projects.d.ts +14 -0
- package/dist/cli/projects.js +1 -1
- package/dist/cli/repos.d.ts +2 -0
- package/dist/cli/repos.js +115 -0
- package/dist/cli/routines.d.ts +10 -0
- package/dist/cli/routines.js +10 -10
- package/dist/config.d.ts +29 -2
- package/dist/config.js +56 -2
- package/dist/inspector/{team.d.ts → agents.d.ts} +21 -12
- package/dist/inspector/{team.js → agents.js} +34 -19
- package/dist/inspector/chat/backend.d.ts +1 -1
- package/dist/inspector/chat/sessions.js +72 -45
- package/dist/inspector/chat/store.js +13 -1
- package/dist/inspector/chat/types.d.ts +2 -2
- package/dist/inspector/git.d.ts +14 -0
- package/dist/inspector/git.js +11 -9
- package/dist/inspector/instances.d.ts +12 -0
- package/dist/inspector/instances.js +28 -0
- package/dist/inspector/repos.d.ts +118 -0
- package/dist/inspector/repos.js +249 -0
- package/dist/inspector/routines.d.ts +4 -4
- package/dist/inspector/routines.js +11 -11
- package/dist/inspector/search.d.ts +25 -0
- package/dist/inspector/search.js +31 -0
- package/dist/inspector/server.js +77 -30
- package/dist/inspector/settings.d.ts +7 -1
- package/dist/inspector/settings.js +32 -1
- package/dist/inspector/skills.d.ts +6 -6
- package/dist/inspector/skills.js +8 -8
- package/inspector/dist/assets/{editor-BABv2LmA.js → editor-vM6JSj5h.js} +3 -3
- package/inspector/dist/assets/{index-CCACy6uF.js → index-BAAjszPI.js} +1 -1
- package/inspector/dist/assets/{index-DuwmzZAr.js → index-BKcOp35r.js} +1 -1
- package/inspector/dist/assets/index-Ba8VU6Wz.js +217 -0
- package/inspector/dist/assets/{index-Ca9Qs5LZ.js → index-Bb08wK-l.js} +1 -1
- package/inspector/dist/assets/{index-ByWpqzAI.js → index-BrCwDkJZ.js} +1 -1
- package/inspector/dist/assets/{index-hkVov8T4.js → index-Bs1U0rWW.js} +1 -1
- package/inspector/dist/assets/{index-CdwTmXTV.js → index-C1UXlW32.js} +1 -1
- package/inspector/dist/assets/{index-HBdsdJhD.js → index-CQ2XKqmD.js} +1 -1
- package/inspector/dist/assets/{index-DCR7u4Xa.js → index-CReepirH.js} +1 -1
- package/inspector/dist/assets/{index-CzPQygR7.js → index-CUGfxoYt.js} +1 -1
- package/inspector/dist/assets/{index-sLxulPOm.js → index-CaKmxcD6.js} +1 -1
- package/inspector/dist/assets/{index-Bmm4zJNU.js → index-CtMGH62C.js} +1 -1
- package/inspector/dist/assets/{index-BXspF2KR.css → index-D4_mLTSH.css} +1 -1
- package/inspector/dist/assets/{index-BbTer62d.js → index-D_11IZBV.js} +1 -1
- package/inspector/dist/assets/{index-wkYwz0Tl.js → index-DfZT0IoM.js} +1 -1
- package/inspector/dist/assets/{index-Gx93brFq.js → index-DykDNrYm.js} +1 -1
- package/inspector/dist/assets/{index-DuPp3MUc.js → index-MWv5H1oP.js} +1 -1
- package/inspector/dist/assets/{index-CjlJeW9z.js → index-oIQqfbwm.js} +1 -1
- package/inspector/dist/assets/{index-DbWUNbt-.js → index-yGZsomcy.js} +1 -1
- package/inspector/dist/index.html +2 -2
- package/package.json +1 -1
- package/schema/kraftwerk.schema.json +15 -0
- package/inspector/dist/assets/index-uEig4--d.js +0 -217
package/dist/inspector/server.js
CHANGED
|
@@ -8,11 +8,13 @@ import { listWorkflows, getWorkflow } from "./workflows.js";
|
|
|
8
8
|
import { dockerStatus, triggerRun, stopRun } from "./runner.js";
|
|
9
9
|
import { cancelChat, createChat, deleteChat, disposeAllBackends, getChat, listChats, postMessage, resolvePermission, subscribeChat, } from "./chat/sessions.js";
|
|
10
10
|
import { bundleDetail, conceptDetail, createBundle, knowledgeIndex, putConcept, verifyFromUi, } from "./knowledge.js";
|
|
11
|
-
import {
|
|
11
|
+
import { deleteAgent, getAgent, listAgents, saveAgent, setAgentArchived, agentsRoot, } from "./agents.js";
|
|
12
12
|
import { deleteAgentSkill, getSkill, listAgentSkills, listSkills, readSkill, saveAgentSkill, skillsRoot, } from "./skills.js";
|
|
13
13
|
import { discoverWorkspaces, forgetProject, listWorkspacesDetailed, markProjectStopped, registerInstance, registerProject, startProject, stopProject, tildify, unregisterInstance, } from "./instances.js";
|
|
14
14
|
import { gitCommit, gitDiff, gitFetch, gitPull, gitPush, gitStatus, startGitSync, } from "./git.js";
|
|
15
15
|
import { getSettings, saveSettings } from "./settings.js";
|
|
16
|
+
import { addRepo, listRepos, openRepos, removeRepo, updateRepo } from "./repos.js";
|
|
17
|
+
import { searchAgents } from "./search.js";
|
|
16
18
|
import { deleteRoutine, routineStatuses, runRoutineNow, saveRoutine, startRoutineScheduler, } from "./routines.js";
|
|
17
19
|
/**
|
|
18
20
|
* The inspector server: a plain node:http server with no dependencies.
|
|
@@ -214,6 +216,7 @@ async function handleApi(req, res, url) {
|
|
|
214
216
|
projectRoot: project?.root ?? getProjectRoot(),
|
|
215
217
|
projectRootLabel: tildify(project?.root ?? getProjectRoot()),
|
|
216
218
|
git: (project?.config.git && project.config.git.enabled !== false) === true,
|
|
219
|
+
repos: (project?.config.repos && project.config.repos.enabled !== false) === true,
|
|
217
220
|
switcher,
|
|
218
221
|
});
|
|
219
222
|
}
|
|
@@ -249,6 +252,50 @@ async function handleApi(req, res, url) {
|
|
|
249
252
|
const result = await run();
|
|
250
253
|
return json(res, result, result.ok ? 200 : 409);
|
|
251
254
|
}
|
|
255
|
+
// GET /api/repos — every clone under the repos root, read live from git
|
|
256
|
+
if (seg.length === 2 && seg[1] === "repos" && method === "GET") {
|
|
257
|
+
return json(res, await listRepos());
|
|
258
|
+
}
|
|
259
|
+
// POST /api/repos {url, name?, branch?, depth?} — clone into the root
|
|
260
|
+
if (seg.length === 2 && seg[1] === "repos" && method === "POST") {
|
|
261
|
+
if ((await openRepos()).off)
|
|
262
|
+
return json(res, { error: "repositories are off" }, 409);
|
|
263
|
+
try {
|
|
264
|
+
const body = JSON.parse(await readBody(req));
|
|
265
|
+
const str = (v) => (typeof v === "string" ? v : undefined);
|
|
266
|
+
const depth = body.depth === undefined ? undefined : typeof body.depth === "number" ? body.depth : Number.NaN;
|
|
267
|
+
const repo = await addRepo({ url: str(body.url) ?? "", name: str(body.name), branch: str(body.branch), depth });
|
|
268
|
+
return json(res, repo, 201);
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
return json(res, { error: err.message }, 400);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// POST /api/repos/<slug>/update — fetch, fast-forward when clean
|
|
275
|
+
if (seg.length === 4 && seg[1] === "repos" && seg[3] === "update" && method === "POST") {
|
|
276
|
+
try {
|
|
277
|
+
const result = await updateRepo(seg[2]);
|
|
278
|
+
return json(res, result, result.ok ? 200 : result.repo || result.off ? 409 : 404);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
return json(res, { error: err.message }, 400);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// DELETE /api/repos/<slug>[?force=1] — remove the clone; 409 while it holds unpushed or uncommitted work
|
|
285
|
+
if (seg.length === 3 && seg[1] === "repos" && method === "DELETE") {
|
|
286
|
+
try {
|
|
287
|
+
const force = ["1", "true"].includes(url.searchParams.get("force") ?? "");
|
|
288
|
+
const result = await removeRepo(seg[2], force);
|
|
289
|
+
return json(res, result, result.ok ? 200 : result.conflict || result.off ? 409 : 404);
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
return json(res, { error: err.message }, 400);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// GET /api/search/agents — active agents of every reachable workspace (the ⌘K palette)
|
|
296
|
+
if (seg.length === 3 && seg[1] === "search" && seg[2] === "agents" && method === "GET") {
|
|
297
|
+
return json(res, await searchAgents());
|
|
298
|
+
}
|
|
252
299
|
// GET /api/projects — every known workspace incl. this one, with state + counts (admin screen)
|
|
253
300
|
if (seg.length === 2 && seg[1] === "projects" && method === "GET") {
|
|
254
301
|
return json(res, await listWorkspacesDetailed());
|
|
@@ -296,7 +343,7 @@ async function handleApi(req, res, url) {
|
|
|
296
343
|
if (seg.length === 2 && seg[1] === "settings" && method === "GET") {
|
|
297
344
|
return json(res, await getSettings());
|
|
298
345
|
}
|
|
299
|
-
// PUT /api/settings — write the UI-editable subset (name, icon, switcher, git) back
|
|
346
|
+
// PUT /api/settings — write the UI-editable subset (name, icon, switcher, git, repos) back
|
|
300
347
|
if (seg.length === 2 && seg[1] === "settings" && method === "PUT") {
|
|
301
348
|
try {
|
|
302
349
|
const input = JSON.parse(await readBody(req));
|
|
@@ -516,33 +563,33 @@ async function handleApi(req, res, url) {
|
|
|
516
563
|
const skill = await getSkill(decodeURIComponent(seg[2]));
|
|
517
564
|
return skill ? json(res, skill) : json(res, { error: "not found" }, 404);
|
|
518
565
|
}
|
|
519
|
-
// GET/POST /api/
|
|
520
|
-
if (seg.length === 2 && seg[1] === "
|
|
566
|
+
// GET/POST /api/agents — member list / create a member
|
|
567
|
+
if (seg.length === 2 && seg[1] === "agents") {
|
|
521
568
|
if (method === "GET") {
|
|
522
|
-
return json(res, { root: await
|
|
569
|
+
return json(res, { root: await agentsRoot(), agents: await listAgents() });
|
|
523
570
|
}
|
|
524
571
|
if (method === "POST") {
|
|
525
572
|
try {
|
|
526
573
|
const body = JSON.parse(await readBody(req));
|
|
527
|
-
return json(res, await
|
|
574
|
+
return json(res, await saveAgent({ ...body, slug: undefined }));
|
|
528
575
|
}
|
|
529
576
|
catch (err) {
|
|
530
577
|
return json(res, { error: err.message }, 400);
|
|
531
578
|
}
|
|
532
579
|
}
|
|
533
580
|
}
|
|
534
|
-
// POST /api/
|
|
535
|
-
if (seg.length === 4 && seg[1] === "
|
|
581
|
+
// POST /api/agents/:slug/archive — archive/unarchive a member ({ archived: boolean })
|
|
582
|
+
if (seg.length === 4 && seg[1] === "agents" && seg[3] === "archive" && method === "POST") {
|
|
536
583
|
try {
|
|
537
584
|
const body = JSON.parse(await readBody(req));
|
|
538
|
-
return json(res, await
|
|
585
|
+
return json(res, await setAgentArchived(decodeURIComponent(seg[2]), body.archived === true));
|
|
539
586
|
}
|
|
540
587
|
catch (err) {
|
|
541
588
|
return json(res, { error: err.message }, 400);
|
|
542
589
|
}
|
|
543
590
|
}
|
|
544
|
-
// GET/POST /api/
|
|
545
|
-
if (seg.length === 4 && seg[1] === "
|
|
591
|
+
// GET/POST /api/agents/:slug/skills — the agent's own skills / create or update one
|
|
592
|
+
if (seg.length === 4 && seg[1] === "agents" && seg[3] === "skills") {
|
|
546
593
|
const slug = decodeURIComponent(seg[2]);
|
|
547
594
|
try {
|
|
548
595
|
if (method === "GET")
|
|
@@ -556,8 +603,8 @@ async function handleApi(req, res, url) {
|
|
|
556
603
|
return json(res, { error: err.message }, 400);
|
|
557
604
|
}
|
|
558
605
|
}
|
|
559
|
-
// GET/DELETE /api/
|
|
560
|
-
if (seg.length === 5 && seg[1] === "
|
|
606
|
+
// GET/DELETE /api/agents/:slug/skills/:name — one agent skill with content / delete
|
|
607
|
+
if (seg.length === 5 && seg[1] === "agents" && seg[3] === "skills") {
|
|
561
608
|
const slug = decodeURIComponent(seg[2]);
|
|
562
609
|
const name = decodeURIComponent(seg[4]);
|
|
563
610
|
try {
|
|
@@ -576,8 +623,8 @@ async function handleApi(req, res, url) {
|
|
|
576
623
|
return json(res, { error: err.message }, 400);
|
|
577
624
|
}
|
|
578
625
|
}
|
|
579
|
-
// GET/POST /api/
|
|
580
|
-
if (seg.length === 4 && seg[1] === "
|
|
626
|
+
// GET/POST /api/agents/:slug/routines — list (with run state) / upsert
|
|
627
|
+
if (seg.length === 4 && seg[1] === "agents" && seg[3] === "routines") {
|
|
581
628
|
const slug = decodeURIComponent(seg[2]);
|
|
582
629
|
try {
|
|
583
630
|
if (method === "GET")
|
|
@@ -591,8 +638,8 @@ async function handleApi(req, res, url) {
|
|
|
591
638
|
return json(res, { error: err.message }, 400);
|
|
592
639
|
}
|
|
593
640
|
}
|
|
594
|
-
// DELETE /api/
|
|
595
|
-
if (seg.length >= 5 && seg[1] === "
|
|
641
|
+
// DELETE /api/agents/:slug/routines/:id | POST /api/agents/:slug/routines/:id/run
|
|
642
|
+
if (seg.length >= 5 && seg[1] === "agents" && seg[3] === "routines") {
|
|
596
643
|
const slug = decodeURIComponent(seg[2]);
|
|
597
644
|
const id = decodeURIComponent(seg[4]);
|
|
598
645
|
try {
|
|
@@ -608,20 +655,20 @@ async function handleApi(req, res, url) {
|
|
|
608
655
|
return json(res, { error: err.message }, 400);
|
|
609
656
|
}
|
|
610
657
|
}
|
|
611
|
-
// GET/PUT/DELETE /api/
|
|
612
|
-
if (seg.length === 3 && seg[1] === "
|
|
658
|
+
// GET/PUT/DELETE /api/agents/:slug
|
|
659
|
+
if (seg.length === 3 && seg[1] === "agents") {
|
|
613
660
|
const slug = decodeURIComponent(seg[2]);
|
|
614
661
|
try {
|
|
615
662
|
if (method === "GET") {
|
|
616
|
-
const
|
|
617
|
-
return
|
|
663
|
+
const agent = await getAgent(slug);
|
|
664
|
+
return agent ? json(res, agent) : json(res, { error: "not found" }, 404);
|
|
618
665
|
}
|
|
619
666
|
if (method === "PUT") {
|
|
620
667
|
const body = JSON.parse(await readBody(req));
|
|
621
|
-
return json(res, await
|
|
668
|
+
return json(res, await saveAgent({ ...body, slug }));
|
|
622
669
|
}
|
|
623
670
|
if (method === "DELETE") {
|
|
624
|
-
await
|
|
671
|
+
await deleteAgent(slug);
|
|
625
672
|
return json(res, { ok: true });
|
|
626
673
|
}
|
|
627
674
|
}
|
|
@@ -643,13 +690,13 @@ async function handleApi(req, res, url) {
|
|
|
643
690
|
}
|
|
644
691
|
let agent = body.agent;
|
|
645
692
|
let scope;
|
|
646
|
-
if (body.scope?.kind === "
|
|
647
|
-
//
|
|
648
|
-
const
|
|
649
|
-
if (!
|
|
650
|
-
return json(res, { error: "
|
|
651
|
-
scope = { kind: "
|
|
652
|
-
agent =
|
|
693
|
+
if (body.scope?.kind === "agent" && body.scope.slug) {
|
|
694
|
+
// Agent sessions run on the agent's configured harness.
|
|
695
|
+
const def = await getAgent(body.scope.slug).catch(() => null);
|
|
696
|
+
if (!def)
|
|
697
|
+
return json(res, { error: "agent not found" }, 404);
|
|
698
|
+
scope = { kind: "agent", slug: def.slug };
|
|
699
|
+
agent = def.harness;
|
|
653
700
|
}
|
|
654
701
|
else if (body.scope?.kind === "run" && body.scope.runId) {
|
|
655
702
|
scope = { kind: "run", runId: body.scope.runId };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ProjectConfig, type SwitcherEntry } from "../config.js";
|
|
2
2
|
/**
|
|
3
3
|
* Workspace settings: read kraftwerk.yml for the UI and write the
|
|
4
|
-
* UI-editable subset (name, icon, switcher, git) back. Edits go through the
|
|
4
|
+
* UI-editable subset (name, icon, switcher, git, repos) back. Edits go through the
|
|
5
5
|
* yaml Document API so comments and every other key survive untouched;
|
|
6
6
|
* a missing kraftwerk.yml is created at the project root on first save.
|
|
7
7
|
*/
|
|
@@ -24,6 +24,12 @@ export interface SaveSettingsInput {
|
|
|
24
24
|
switcher?: SwitcherEntry[];
|
|
25
25
|
/** Omitted = untouched. See cleanGit for how the block is written. */
|
|
26
26
|
git?: GitSettingsInput;
|
|
27
|
+
/** Omitted = untouched. Same rules as git: off with defaults = no block. */
|
|
28
|
+
repos?: ReposSettingsInput;
|
|
29
|
+
}
|
|
30
|
+
export interface ReposSettingsInput {
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
root?: string;
|
|
27
33
|
}
|
|
28
34
|
/** The git form as the settings screen posts it. Every field but `enabled` may be blank. */
|
|
29
35
|
export interface GitSettingsInput {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parseDocument } from "yaml";
|
|
4
|
-
import { resolveProject } from "../config.js";
|
|
4
|
+
import { ignoreEntryFor, REPOS_DEFAULT_ROOT, resolveProject } from "../config.js";
|
|
5
5
|
import { getProjectRoot } from "./context.js";
|
|
6
|
+
import { ensureReposRoot } from "./repos.js";
|
|
6
7
|
const GIT_DEFAULTS = { remote: "origin", interval: 300, autosync: "pull" };
|
|
7
8
|
export async function getSettings() {
|
|
8
9
|
const project = await resolveProject(getProjectRoot());
|
|
@@ -69,6 +70,25 @@ function cleanGit(value) {
|
|
|
69
70
|
return null;
|
|
70
71
|
return { enabled: false, ...out };
|
|
71
72
|
}
|
|
73
|
+
/** The repos block to write; null when the feature is off and nothing else is set. */
|
|
74
|
+
function cleanRepos(value, projectRoot) {
|
|
75
|
+
if (typeof value !== "object" || value === null)
|
|
76
|
+
throw new Error("repos must be an object");
|
|
77
|
+
if (typeof value.enabled !== "boolean")
|
|
78
|
+
throw new Error("repos.enabled must be true or false");
|
|
79
|
+
const out = {};
|
|
80
|
+
const root = typeof value.root === "string" ? value.root.trim().replace(/\/+$/, "") : "";
|
|
81
|
+
if (root && !ignoreEntryFor(projectRoot, root)) {
|
|
82
|
+
throw new Error('repos.root must be a directory inside the project (not ".", ".." or an absolute path outside it)');
|
|
83
|
+
}
|
|
84
|
+
if (root && root !== REPOS_DEFAULT_ROOT)
|
|
85
|
+
out.root = root;
|
|
86
|
+
if (value.enabled)
|
|
87
|
+
return out;
|
|
88
|
+
if (Object.keys(out).length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
return { enabled: false, ...out };
|
|
91
|
+
}
|
|
72
92
|
export async function saveSettings(input) {
|
|
73
93
|
if (input.name !== undefined && typeof input.name !== "string")
|
|
74
94
|
throw new Error("name must be a string");
|
|
@@ -99,6 +119,17 @@ export async function saveSettings(input) {
|
|
|
99
119
|
else
|
|
100
120
|
doc.set("git", git);
|
|
101
121
|
}
|
|
122
|
+
if (input.repos !== undefined) {
|
|
123
|
+
const repos = cleanRepos(input.repos, project.root);
|
|
124
|
+
if (repos === null)
|
|
125
|
+
doc.delete("repos");
|
|
126
|
+
else
|
|
127
|
+
doc.set("repos", repos);
|
|
128
|
+
}
|
|
102
129
|
await fs.writeFile(configPath, doc.toString());
|
|
130
|
+
// Turning repositories on prepares the folder so the first clone and the
|
|
131
|
+
// workspace git both find it in the right state.
|
|
132
|
+
if (input.repos?.enabled)
|
|
133
|
+
await ensureReposRoot().catch(() => { });
|
|
103
134
|
return getSettings();
|
|
104
135
|
}
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
* skill. Skills are surfaced to every chat as context (and expanded on
|
|
14
14
|
* /<name> invocation), so they work identically on claude, codex, and pi.
|
|
15
15
|
*
|
|
16
|
-
* On top of the shared roots, each
|
|
16
|
+
* On top of the shared roots, each agent can carry private skills in
|
|
17
17
|
*
|
|
18
18
|
* agents/<slug>/skills/<name>/SKILL.md (agent, git-tracked)
|
|
19
19
|
*
|
|
20
20
|
* Agent skills are visible only to that agent's sessions, always apply
|
|
21
|
-
* (the
|
|
21
|
+
* (the agent's allowlist narrows shared skills only), and shadow
|
|
22
22
|
* same-named shared skills.
|
|
23
23
|
*/
|
|
24
24
|
export interface SkillInfo {
|
|
@@ -31,7 +31,7 @@ export interface SkillInfo {
|
|
|
31
31
|
dir: string;
|
|
32
32
|
/** workspace = kraftwerk skills root; project = .claude/skills; user = ~/.claude/skills; agent = agents/<slug>/skills. */
|
|
33
33
|
source: "workspace" | "project" | "user" | "agent";
|
|
34
|
-
/** For source "agent": the owning
|
|
34
|
+
/** For source "agent": the owning agent's slug. */
|
|
35
35
|
agent?: string;
|
|
36
36
|
}
|
|
37
37
|
export interface SkillDetail extends SkillInfo {
|
|
@@ -42,15 +42,15 @@ export interface SkillDetail extends SkillInfo {
|
|
|
42
42
|
}
|
|
43
43
|
/** Absolute workspace skills root (kraftwerk.yml `skills`, default skills/). */
|
|
44
44
|
export declare function skillsRoot(): Promise<string>;
|
|
45
|
-
/** The private skills root of one
|
|
45
|
+
/** The private skills root of one agent: agents/<slug>/skills. */
|
|
46
46
|
export declare function agentSkillsRoot(slug: string): Promise<string>;
|
|
47
47
|
/** One agent's private skills (agents/<slug>/skills/<name>/SKILL.md). */
|
|
48
48
|
export declare function listAgentSkills(slug: string): Promise<SkillInfo[]>;
|
|
49
49
|
/**
|
|
50
|
-
* All discovered skills; agent (when
|
|
50
|
+
* All discovered skills; agent (when an agent slug is given) shadows workspace
|
|
51
51
|
* shadows project shadows user (global).
|
|
52
52
|
*/
|
|
53
|
-
export declare function listSkills(
|
|
53
|
+
export declare function listSkills(agent?: string): Promise<SkillInfo[]>;
|
|
54
54
|
/** Create or update one agent skill; writes agents/<slug>/skills/<name>/SKILL.md. */
|
|
55
55
|
export declare function saveAgentSkill(slug: string, name: string, content: string): Promise<SkillInfo & {
|
|
56
56
|
content: string;
|
package/dist/inspector/skills.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { parse } from "yaml";
|
|
5
5
|
import { resolveProject } from "../config.js";
|
|
6
6
|
import { getProjectRoot } from "./context.js";
|
|
7
|
-
import {
|
|
7
|
+
import { safeAgentSlug, agentsRoot } from "./agents.js";
|
|
8
8
|
const NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
9
9
|
function frontmatter(md) {
|
|
10
10
|
const m = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n|$)/.exec(md);
|
|
@@ -58,9 +58,9 @@ async function scanRoot(root, source) {
|
|
|
58
58
|
}));
|
|
59
59
|
return skills.filter(Boolean).sort((a, b) => a.name.localeCompare(b.name));
|
|
60
60
|
}
|
|
61
|
-
/** The private skills root of one
|
|
61
|
+
/** The private skills root of one agent: agents/<slug>/skills. */
|
|
62
62
|
export async function agentSkillsRoot(slug) {
|
|
63
|
-
return path.join(await
|
|
63
|
+
return path.join(await agentsRoot(), safeAgentSlug(slug), "skills");
|
|
64
64
|
}
|
|
65
65
|
/** One agent's private skills (agents/<slug>/skills/<name>/SKILL.md). */
|
|
66
66
|
export async function listAgentSkills(slug) {
|
|
@@ -68,17 +68,17 @@ export async function listAgentSkills(slug) {
|
|
|
68
68
|
return skills.map((s) => ({ ...s, agent: slug }));
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
|
-
* All discovered skills; agent (when
|
|
71
|
+
* All discovered skills; agent (when an agent slug is given) shadows workspace
|
|
72
72
|
* shadows project shadows user (global).
|
|
73
73
|
*/
|
|
74
|
-
export async function listSkills(
|
|
75
|
-
const [
|
|
76
|
-
|
|
74
|
+
export async function listSkills(agent) {
|
|
75
|
+
const [own, workspace, project, user] = await Promise.all([
|
|
76
|
+
agent ? listAgentSkills(agent).catch(() => []) : [],
|
|
77
77
|
skillsRoot().then((root) => scanRoot(root, "workspace")),
|
|
78
78
|
scanRoot(path.join(getProjectRoot(), ".claude", "skills"), "project"),
|
|
79
79
|
scanRoot(path.join(os.homedir(), ".claude", "skills"), "user"),
|
|
80
80
|
]);
|
|
81
|
-
const out = [...
|
|
81
|
+
const out = [...own];
|
|
82
82
|
const seen = new Set(out.map((s) => s.name.toLowerCase()));
|
|
83
83
|
for (const s of [...workspace, ...project, ...user]) {
|
|
84
84
|
if (seen.has(s.name.toLowerCase()))
|