@rubytech/create-realagent 1.0.864 → 1.0.866
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/package.json +1 -1
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js +67 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +44 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +17 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +62 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/package.json +4 -2
- package/payload/platform/plugins/admin/mcp/vitest.config.ts +9 -0
- package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +2 -0
- package/payload/platform/plugins/admin/skills/unzip-attachment/SKILL.md +2 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +17 -3
- package/payload/server/chunk-FHNFKJZN.js +2143 -0
- package/payload/server/chunk-ND23BDBM.js +11312 -0
- package/payload/server/chunk-TOLLHW7W.js +1155 -0
- package/payload/server/chunk-UXLZ5Z3Y.js +667 -0
- package/payload/server/client-pool-2IUOSYDF.js +34 -0
- package/payload/server/cloudflare-task-tracker-OCFIVXEJ.js +20 -0
- package/payload/server/maxy-edge.js +5 -6
- package/payload/server/public/assets/{admin-CCML_l4E.js → admin-CLp1xGlJ.js} +1 -1
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +132 -155
package/package.json
CHANGED
package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-read-skill-resolution.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/plugin-read-skill-resolution.test.ts"],"names":[],"mappings":""}
|
package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Task 964 — plugin-skill resolution gate.
|
|
2
|
+
//
|
|
3
|
+
// Locks in the contract for `findSkillOwners` (the helper backing the
|
|
4
|
+
// new `skill-find` MCP tool) and the sibling-hint that `plugin-read`
|
|
5
|
+
// appends to its "not found" error when the agent guesses the wrong
|
|
6
|
+
// pluginName for a skill that DOES live somewhere else under
|
|
7
|
+
// `plugins/*/skills/<name>/SKILL.md`.
|
|
8
|
+
//
|
|
9
|
+
// Tests build a temp PLATFORM_ROOT tree, point the helpers at it, and
|
|
10
|
+
// assert exact result shapes — no stdio transport, no SDK harness.
|
|
11
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
12
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { findSkillOwners, computePluginReadHint } from "../skill-resolution.js";
|
|
16
|
+
let root;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
root = mkdtempSync(join(tmpdir(), "skill-find-"));
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
rmSync(root, { recursive: true, force: false });
|
|
22
|
+
});
|
|
23
|
+
function seedSkill(plugin, skill) {
|
|
24
|
+
const dir = join(root, "plugins", plugin, "skills", skill);
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(join(dir, "SKILL.md"), `# ${skill}\n`);
|
|
27
|
+
}
|
|
28
|
+
describe("findSkillOwners", () => {
|
|
29
|
+
it("returns a unique owner when exactly one plugin holds the skill", () => {
|
|
30
|
+
seedSkill("cloudflare", "setup-tunnel");
|
|
31
|
+
seedSkill("admin", "onboarding");
|
|
32
|
+
const result = findSkillOwners(root, "setup-tunnel");
|
|
33
|
+
expect(result).toEqual({
|
|
34
|
+
status: "unique",
|
|
35
|
+
candidates: [{ pluginName: "cloudflare", file: "skills/setup-tunnel/SKILL.md" }],
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it("returns every candidate when the skill name is shared across plugins", () => {
|
|
39
|
+
seedSkill("admin", "shared-name");
|
|
40
|
+
seedSkill("cloudflare", "shared-name");
|
|
41
|
+
seedSkill("memory", "unrelated");
|
|
42
|
+
const result = findSkillOwners(root, "shared-name");
|
|
43
|
+
expect(result.status).toBe("ambiguous");
|
|
44
|
+
expect(result.candidates).toHaveLength(2);
|
|
45
|
+
const names = result.candidates.map(c => c.pluginName).sort();
|
|
46
|
+
expect(names).toEqual(["admin", "cloudflare"]);
|
|
47
|
+
expect(result.candidates.every(c => c.file === "skills/shared-name/SKILL.md")).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
it("returns not-found when no plugin owns the skill", () => {
|
|
50
|
+
seedSkill("admin", "onboarding");
|
|
51
|
+
const result = findSkillOwners(root, "absent-skill");
|
|
52
|
+
expect(result).toEqual({ status: "not-found", candidates: [] });
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("computePluginReadHint", () => {
|
|
56
|
+
it("returns the owning plugin name when exactly one sibling owns the skill", () => {
|
|
57
|
+
seedSkill("cloudflare", "setup-tunnel");
|
|
58
|
+
const hint = computePluginReadHint(root, "admin", "skills/setup-tunnel/SKILL.md");
|
|
59
|
+
expect(hint).toBe("cloudflare");
|
|
60
|
+
});
|
|
61
|
+
it("returns null when no plugin owns the requested skill", () => {
|
|
62
|
+
seedSkill("admin", "onboarding");
|
|
63
|
+
const hint = computePluginReadHint(root, "admin", "skills/imaginary/SKILL.md");
|
|
64
|
+
expect(hint).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=plugin-read-skill-resolution.test.js.map
|
package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-read-skill-resolution.test.js","sourceRoot":"","sources":["../../src/__tests__/plugin-read-skill-resolution.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,7 @@ import { homedir, hostname as osHostname } from "node:os";
|
|
|
19
19
|
import QRCode from "qrcode";
|
|
20
20
|
import { getSession, closeDriver } from "./lib/neo4j.js";
|
|
21
21
|
import { getOnboardingState, completeOnboardingStep } from "./lib/onboarding.js";
|
|
22
|
+
import { findSkillOwners, computePluginReadHint } from "./skill-resolution.js";
|
|
22
23
|
const server = new McpServer({
|
|
23
24
|
name: "admin",
|
|
24
25
|
version: "0.1.0",
|
|
@@ -1581,14 +1582,20 @@ server.tool("plugin-read", "Read a plugin definition (PLUGIN.md) or one of its r
|
|
|
1581
1582
|
}, async ({ pluginName, file }) => {
|
|
1582
1583
|
try {
|
|
1583
1584
|
const resolvedFile = file ?? "PLUGIN.md";
|
|
1584
|
-
console.log(`[plugin-read] ${pluginName}/${resolvedFile}`);
|
|
1585
1585
|
const pluginPath = resolve(PLATFORM_ROOT, "plugins", pluginName, resolvedFile);
|
|
1586
1586
|
if (!existsSync(pluginPath)) {
|
|
1587
|
+
// Task 964 — when the agent guesses the wrong pluginName for a file
|
|
1588
|
+
// path shaped like `skills/<n>/...`, surface the owning plugin so
|
|
1589
|
+
// the next call is deterministic instead of falling through to Glob.
|
|
1590
|
+
const owner = computePluginReadHint(PLATFORM_ROOT, pluginName, resolvedFile);
|
|
1591
|
+
const hintText = owner ? ` Did you mean pluginName="${owner}"?` : "";
|
|
1592
|
+
console.log(`[plugin-read] ${pluginName}/${resolvedFile} not-found hint=${owner ?? "none"}`);
|
|
1587
1593
|
return {
|
|
1588
|
-
content: [{ type: "text", text: `Plugin file not found: ${pluginPath}` }],
|
|
1594
|
+
content: [{ type: "text", text: `Plugin file not found: ${pluginPath}.${hintText}` }],
|
|
1589
1595
|
isError: true,
|
|
1590
1596
|
};
|
|
1591
1597
|
}
|
|
1598
|
+
console.log(`[plugin-read] ${pluginName}/${resolvedFile}`);
|
|
1592
1599
|
const rawContent = await readFile(pluginPath, "utf-8");
|
|
1593
1600
|
// Brand-substitute every plugin file before it reaches the agent —
|
|
1594
1601
|
// PLUGIN.md, skills/<name>/SKILL.md, references/*.md all flow through
|
|
@@ -1642,6 +1649,41 @@ server.tool("plugin-read", "Read a plugin definition (PLUGIN.md) or one of its r
|
|
|
1642
1649
|
};
|
|
1643
1650
|
}
|
|
1644
1651
|
});
|
|
1652
|
+
// Task 964 — agent-side `Glob` fallthrough when "set up cloudflare" maps a
|
|
1653
|
+
// SKILL.md to the wrong plugin: skill-find walks
|
|
1654
|
+
// `${PLATFORM_ROOT}/plugins/*/skills/<skillName>/SKILL.md` once and returns
|
|
1655
|
+
// the owner. With one tool call the agent learns the correct plugin name
|
|
1656
|
+
// and follows up with a deterministic `plugin-read`.
|
|
1657
|
+
server.tool("skill-find", "Find which plugin owns a skill by name. Walks plugins/*/skills/<skillName>/SKILL.md and returns the owning plugin (unique), every candidate (ambiguous), or not-found.", {
|
|
1658
|
+
skillName: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "skillName must be lowercase kebab-case (a-z, 0-9, -)"),
|
|
1659
|
+
}, async ({ skillName }) => {
|
|
1660
|
+
const start = Date.now();
|
|
1661
|
+
const result = findSkillOwners(PLATFORM_ROOT, skillName);
|
|
1662
|
+
const ms = Date.now() - start;
|
|
1663
|
+
console.log(`[skill-find] skillName=${skillName} result=${result.status} candidates=${result.candidates.length} ms=${ms}`);
|
|
1664
|
+
if (result.status === "unique") {
|
|
1665
|
+
const [{ pluginName, file }] = result.candidates;
|
|
1666
|
+
return {
|
|
1667
|
+
content: [{
|
|
1668
|
+
type: "text",
|
|
1669
|
+
text: `pluginName="${pluginName}" file="${file}"\nFollow up with: plugin-read pluginName="${pluginName}" file="${file}"`,
|
|
1670
|
+
}],
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
if (result.status === "ambiguous") {
|
|
1674
|
+
const lines = result.candidates.map(c => ` pluginName="${c.pluginName}" file="${c.file}"`).join("\n");
|
|
1675
|
+
return {
|
|
1676
|
+
content: [{
|
|
1677
|
+
type: "text",
|
|
1678
|
+
text: `Ambiguous — skill "${skillName}" exists in ${result.candidates.length} plugins:\n${lines}`,
|
|
1679
|
+
}],
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
return {
|
|
1683
|
+
content: [{ type: "text", text: `not_found — no plugin owns a skill named "${skillName}"` }],
|
|
1684
|
+
isError: true,
|
|
1685
|
+
};
|
|
1686
|
+
});
|
|
1645
1687
|
// Task 940 — `"rendered"` is a sentinel, not a persistence ack. The platform
|
|
1646
1688
|
// UI's stream-parser intercepts this tool_use upstream of the SDK reply
|
|
1647
1689
|
// (platform/ui/app/lib/claude-agent/stream-parser.ts:362) and emits a typed
|