@rubytech/create-maxy-code 0.1.214 → 0.1.216

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.
Files changed (64) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  3. package/payload/platform/plugins/admin/PLUGIN.md +4 -1
  4. package/payload/platform/plugins/admin/hooks/__tests__/post-tool-use-agent.test.sh +97 -1
  5. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +53 -12
  6. package/payload/platform/plugins/admin/hooks/lib/hook-emit.sh +30 -0
  7. package/payload/platform/plugins/admin/hooks/post-tool-use-agent.sh +81 -29
  8. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +34 -17
  9. package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.d.ts +2 -0
  10. package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.d.ts.map +1 -0
  11. package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js +130 -0
  12. package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js.map +1 -0
  13. package/payload/platform/plugins/admin/mcp/dist/index.js +63 -0
  14. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.d.ts +14 -0
  16. package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.d.ts.map +1 -0
  17. package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.js +50 -0
  18. package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.js.map +1 -0
  19. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +28 -0
  20. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -0
  21. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +68 -0
  22. package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -0
  23. package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +31 -0
  24. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +30 -14
  25. package/payload/platform/plugins/docs/references/platform.md +19 -9
  26. package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
  27. package/payload/platform/scripts/check-architecture-skill-no-drift.mjs +89 -42
  28. package/payload/platform/scripts/check-roles-doc-completeness.mjs +96 -0
  29. package/payload/platform/scripts/check-specialist-tool-surface.mjs +1 -1
  30. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  31. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  32. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  33. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  34. package/payload/platform/services/claude-session-manager/dist/http-server.js +77 -0
  35. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  36. package/payload/platform/services/claude-session-manager/dist/index.js +30 -0
  37. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  38. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +18 -0
  39. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -1
  40. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +63 -0
  41. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -1
  42. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +5 -0
  43. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  44. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +29 -0
  45. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  46. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts +80 -0
  47. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts.map +1 -0
  48. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js +306 -0
  49. package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js.map +1 -0
  50. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts +9 -0
  51. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
  52. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +6 -2
  53. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
  54. package/payload/platform/templates/agents/admin/IDENTITY.md +3 -1
  55. package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
  56. package/payload/server/public/assets/AdminShell-CPWbJ_I8.js +1 -0
  57. package/payload/server/public/assets/{admin-DMo5DWKH.js → admin-DjGGOx2K.js} +1 -1
  58. package/payload/server/public/assets/{data-D6IQdm0R.js → data-WBEvtRV-.js} +1 -1
  59. package/payload/server/public/assets/{graph-PkZKVkWD.js → graph-DsOLsIPf.js} +1 -1
  60. package/payload/server/public/data.html +2 -2
  61. package/payload/server/public/graph.html +2 -2
  62. package/payload/server/public/index.html +2 -2
  63. package/payload/server/server.js +6 -0
  64. package/payload/server/public/assets/AdminShell-BcxEZnpu.js +0 -1
@@ -0,0 +1,50 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { resolve, join } from "node:path";
3
+ function listSpecialists(dir) {
4
+ if (!existsSync(dir))
5
+ return [];
6
+ const out = [];
7
+ for (const entry of readdirSync(dir)) {
8
+ const full = join(dir, entry);
9
+ const st = statSync(full);
10
+ if (st.isFile() && entry.endsWith(".md"))
11
+ out.push(entry.replace(/\.md$/, ""));
12
+ else if (st.isDirectory() && existsSync(join(full, "IDENTITY.md")))
13
+ out.push(entry);
14
+ }
15
+ return out;
16
+ }
17
+ function slugify(s) {
18
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
19
+ }
20
+ export function runCatalogueCensus(input) {
21
+ const platformSpecs = listSpecialists(resolve(input.platformRoot, "templates", "specialists", "agents"));
22
+ const premiumSpecs = [];
23
+ for (const bundle of input.enabledBundles) {
24
+ const dir = resolve(input.premiumPluginsRoot, bundle, "agents");
25
+ premiumSpecs.push(...listSpecialists(dir));
26
+ }
27
+ const disk = [...platformSpecs, ...premiumSpecs];
28
+ const body = existsSync(input.shippedSkillPath)
29
+ ? readFileSync(input.shippedSkillPath, "utf8")
30
+ : "";
31
+ const bodySlugs = new Set();
32
+ for (const match of body.matchAll(/^\|\s*([^|]+)\s*\|/gm)) {
33
+ const cell = match[1].trim();
34
+ if (!cell || cell.startsWith("---") || cell === "Role" || cell === "Plugin")
35
+ continue;
36
+ bodySlugs.add(slugify(cell));
37
+ }
38
+ const missing = disk.filter((s) => !bodySlugs.has(slugify(s)));
39
+ // Stale signal arrives via the per-brand drift gate and the
40
+ // check-roles-doc-completeness gate; this census closes the F5 leak —
41
+ // disk-but-undocumented — which the drift gates cannot see.
42
+ const stale = [];
43
+ return {
44
+ diskCount: disk.length,
45
+ documentedCount: bodySlugs.size,
46
+ missing,
47
+ stale,
48
+ };
49
+ }
50
+ //# sourceMappingURL=catalogue-census.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue-census.js","sourceRoot":"","sources":["../../src/lib/catalogue-census.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export interface CapabilitiesHereInput {
2
+ accountId: string;
3
+ accountConfigPath: string;
4
+ brandConfigPath: string;
5
+ platformRoot: string;
6
+ premiumPluginsRoot: string;
7
+ claudeConfigDir: string;
8
+ }
9
+ export interface CapabilitiesHereResult {
10
+ accountId: string;
11
+ tier: string | null;
12
+ adminModel: string | null;
13
+ publicModel: string | null;
14
+ enabledPlugins: string[];
15
+ installedPlatformSpecialists: string[];
16
+ installedPremiumSpecialists: Array<{
17
+ bundle: string;
18
+ name: string;
19
+ }>;
20
+ registeredAgentTypes: string[];
21
+ brand: {
22
+ productName: string | null;
23
+ shipsPremiumBundles: string[];
24
+ };
25
+ capturedAt: string;
26
+ }
27
+ export declare function resolveCapabilitiesHere(input: CapabilitiesHereInput, nowIso: string): Promise<CapabilitiesHereResult>;
28
+ //# sourceMappingURL=capabilities-here.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities-here.d.ts","sourceRoot":"","sources":["../../src/tools/capabilities-here.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,4BAA4B,EAAE,MAAM,EAAE,CAAC;IACvC,2BAA2B,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;KAC/B,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;CACpB;AA0BD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,sBAAsB,CAAC,CAgDjC"}
@@ -0,0 +1,68 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { existsSync, readdirSync, statSync } from "node:fs";
3
+ import { resolve, join } from "node:path";
4
+ function listSpecialistFiles(dir) {
5
+ if (!existsSync(dir))
6
+ return [];
7
+ const out = [];
8
+ for (const entry of readdirSync(dir)) {
9
+ const full = join(dir, entry);
10
+ const st = statSync(full);
11
+ if (st.isFile() && entry.endsWith(".md")) {
12
+ out.push(entry.replace(/\.md$/, ""));
13
+ }
14
+ else if (st.isDirectory() && existsSync(join(full, "IDENTITY.md"))) {
15
+ out.push(entry);
16
+ }
17
+ }
18
+ return out.sort();
19
+ }
20
+ function listAgentRegistry(dir) {
21
+ if (!existsSync(dir))
22
+ return [];
23
+ const out = [];
24
+ for (const entry of readdirSync(dir)) {
25
+ if (entry.endsWith(".md"))
26
+ out.push(entry.replace(/\.md$/, ""));
27
+ }
28
+ return out.sort();
29
+ }
30
+ export async function resolveCapabilitiesHere(input, nowIso) {
31
+ const accountRaw = await readFile(input.accountConfigPath, "utf-8");
32
+ const account = JSON.parse(accountRaw);
33
+ const brandRaw = await readFile(input.brandConfigPath, "utf-8");
34
+ const brand = JSON.parse(brandRaw);
35
+ const enabledPlugins = Array.isArray(account.enabledPlugins)
36
+ ? account.enabledPlugins.slice().sort()
37
+ : [];
38
+ const tier = typeof account.tier === "string" ? account.tier : null;
39
+ const adminModel = typeof account.adminModel === "string" ? account.adminModel : null;
40
+ const publicModel = typeof account.publicModel === "string" ? account.publicModel : null;
41
+ const productName = typeof brand.productName === "string" ? brand.productName : null;
42
+ const shipsPremiumBundles = Array.isArray(brand.shipsPremiumBundles)
43
+ ? brand.shipsPremiumBundles.slice().sort()
44
+ : [];
45
+ const platformSpecialistsDir = resolve(input.platformRoot, "templates", "specialists", "agents");
46
+ const installedPlatformSpecialists = listSpecialistFiles(platformSpecialistsDir);
47
+ const installedPremiumSpecialists = [];
48
+ for (const bundle of shipsPremiumBundles) {
49
+ const bundleAgentsDir = resolve(input.premiumPluginsRoot, bundle, "agents");
50
+ const names = listSpecialistFiles(bundleAgentsDir);
51
+ for (const name of names)
52
+ installedPremiumSpecialists.push({ bundle, name });
53
+ }
54
+ const registeredAgentTypes = listAgentRegistry(resolve(input.claudeConfigDir, "agents"));
55
+ return {
56
+ accountId: input.accountId,
57
+ tier,
58
+ adminModel,
59
+ publicModel,
60
+ enabledPlugins,
61
+ installedPlatformSpecialists,
62
+ installedPremiumSpecialists,
63
+ registeredAgentTypes,
64
+ brand: { productName, shipsPremiumBundles },
65
+ capturedAt: nowIso,
66
+ };
67
+ }
68
+ //# sourceMappingURL=capabilities-here.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities-here.js","sourceRoot":"","sources":["../../src/tools/capabilities-here.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAoC1C,SAAS,mBAAmB,CAAC,GAAW;IACtC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,EAAE,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC;YACrE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAA4B,EAC5B,MAAc;IAEd,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAA4B,CAAC;IAClE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAA4B,CAAC;IAE9D,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC;QAC1D,CAAC,CAAE,OAAO,CAAC,cAA2B,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;QACrD,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,IAAI,GAAG,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,MAAM,UAAU,GAAG,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACtF,MAAM,WAAW,GAAG,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IACzF,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,MAAM,mBAAmB,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC;QAClE,CAAC,CAAE,KAAK,CAAC,mBAAgC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;QACxD,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,sBAAsB,GAAG,OAAO,CACpC,KAAK,CAAC,YAAY,EAClB,WAAW,EACX,aAAa,EACb,QAAQ,CACT,CAAC;IACF,MAAM,4BAA4B,GAAG,mBAAmB,CAAC,sBAAsB,CAAC,CAAC;IAEjF,MAAM,2BAA2B,GAA4C,EAAE,CAAC;IAChF,KAAK,MAAM,MAAM,IAAI,mBAAmB,EAAE,CAAC;QACzC,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC5E,MAAM,KAAK,GAAG,mBAAmB,CAAC,eAAe,CAAC,CAAC;QACnD,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,2BAA2B,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,oBAAoB,GAAG,iBAAiB,CAC5C,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,QAAQ,CAAC,CACzC,CAAC;IAEF,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,IAAI;QACJ,UAAU;QACV,WAAW;QACX,cAAc;QACd,4BAA4B;QAC5B,2BAA2B;QAC3B,oBAAoB;QACpB,KAAK,EAAE,EAAE,WAAW,EAAE,mBAAmB,EAAE;QAC3C,UAAU,EAAE,MAAM;KACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: capabilities-here
3
+ description: Use when grounding any claim about what is installed on the current account — enabled plugins, available specialists, registered Claude Code agent types, brand, tier, models. Call the `capabilities-here` tool; the JSON it returns is the authoritative install snapshot. Never infer install state from the platform-architecture skill (that skill is the install catalogue, not evidence of what is enabled here).
4
+ ---
5
+
6
+ # capabilities-here
7
+
8
+ The `capabilities-here` admin MCP tool returns a single JSON object describing the live install state on the current account. Call it whenever you are about to:
9
+
10
+ - Tell the operator what you can or cannot do here.
11
+ - Decide whether a specialist exists before dispatching it.
12
+ - Say which plugins are available on this account.
13
+ - Compare the install against a catalogue claim from `platform-architecture`.
14
+
15
+ ## How to use
16
+
17
+ Call `mcp__plugin_admin_admin__capabilities-here` with no arguments. The tool reads:
18
+
19
+ - `account.json` for `enabledPlugins`, `tier`, `adminModel`, `publicModel`.
20
+ - `brand.json` for `productName` and `shipsPremiumBundles`.
21
+ - `platform/templates/specialists/agents/` for `installedPlatformSpecialists`.
22
+ - Each enabled premium bundle's `agents/` for `installedPremiumSpecialists`.
23
+ - `$CLAUDE_CONFIG_DIR/agents/` for `registeredAgentTypes` (the same registry the Agent tool resolves against).
24
+
25
+ Returns one JSON object with those fields plus `accountId`, `capturedAt`, and the `brand` block. No LLM in the resolution path; pure disk + JSON reads.
26
+
27
+ ## Boundary against `platform-architecture`
28
+
29
+ `platform-architecture` is the install catalogue — every plugin, skill, and specialist the platform can ship. It is not evidence that any of them is enabled here. For documented surface (how a plugin works, what a role is for) cite `platform-architecture` with the `Source:` URL. For what is actually installed on this account, cite `capabilities-here`.
30
+
31
+ When the operator asks "what can you do" or "what plugins are enabled" or "do you have X", the answer comes from `capabilities-here`, not from this skill or from `platform-architecture`.
@@ -1,14 +1,18 @@
1
1
  ---
2
2
  name: platform-architecture
3
- description: Use when grounding any claim about what Maxy is, how the platform is built, how plugins/skills/specialists work, how install/deploy runs, or any other product-architecture fact. The body of this skill is the only permissible source for such claims. Cite the `Source:` URL inline.
4
- content-hash: sha256:f26ae06dff1c5b82b674893120da0454280946709391d6f9f79051f48e6f40e0
3
+ description: Use when grounding any documented-surface claim about what Maxy ships plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
+ content-hash: sha256:b26da3e924270e5b1492f4c822c2e45e681b29f535c021c2cecb97ed2eabcda6
5
+ brand: maxy-code
6
+ product-name: Maxy
5
7
  ---
6
8
 
7
- # Platform architecture (reference)
9
+ # Maxy platform architecture (catalogue)
8
10
 
9
- This skill is generated. Its body is the concatenated source markdown for every public Maxy docs page, emitted by `docs/scripts/copy-docs.mjs` as `docs/public/llms-full.txt` and assembled here by `maxy-code/scripts/assemble-architecture-skill.mjs`. Do not hand-edit the drift gate in `platform/scripts/check-architecture-skill-no-drift.mjs` will fail CI.
11
+ This skill is the install catalogue — every surface the platform can ship. It is not evidence of what is installed on the current account. Cite for documented surface. For 'what is installed here' / 'what can you do', call `capabilities-here` and read `account-manage`; do not infer install state from this body.
10
12
 
11
- When you load this skill to ground a factual claim, cite the `Source:` URL printed above the block you drew from. The URL is the canonical public docs page; relay it as a markdown link in your reply. Training-data recall is not a permitted source the body below is.
13
+ This skill is generated. Its body is the concatenated source markdown for every public Maxy docs page, emitted by `docs/scripts/copy-docs.mjs` as `docs/public/llms-full.maxy-code.txt` and assembled here by `maxy-code/scripts/assemble-architecture-skill.mjs`. Do not hand-edit the drift gate in `platform/scripts/check-architecture-skill-no-drift.mjs` will fail CI.
14
+
15
+ When you load this skill to ground a factual claim, cite the `Source:` URL printed above the block you drew from. The URL is the canonical public docs page (https://docs.getmaxy.com); relay it as a markdown link in your reply. Training-data recall is not a permitted source — the body below is.
12
16
 
13
17
  ---
14
18
  # Maxy Documentation — full corpus
@@ -145,18 +149,26 @@ Plugins are installed and managed through conversation. You can add marketplace
145
149
 
146
150
  ## Roles
147
151
 
148
- Maxy has six roles it can dispatch for specific tasks — like members of your team. You don't need to configure or manage them — Maxy decides when to use each role and handles everything automatically. You may see activity like "Dispatching personal-assistant..." in the chat timeline when this happens.
152
+ Maxy ships twelve roles it can dispatch for specific tasks — like members of your team. You don't need to configure or manage them — Maxy decides when to use each role and handles everything automatically. You may see activity like "Dispatching personal-assistant..." in the chat timeline when this happens.
153
+
154
+ The catalogue below is what the platform ships. It is not evidence of what is installed on the current account. For the live install set on this account, ask Maxy to call `capabilities-here`.
149
155
 
150
156
  | Role | What it does |
151
157
  |------|-------------|
152
- | Personal Assistant | Scheduling, platform administration, messaging channels, and browser automation |
153
- | Project Manager | Task and project management creating, tracking, and completing work items |
154
- | Research Assistant | Web research, knowledge management, and visual production |
155
- | Content Producer | Image generation, PDF output, and static-site hosting from prepared archives |
156
- | Librarian | Foreground ingester for the memory graph owns document ingestion (PDF / text / web), conversation-transcript ingestion (WhatsApp, Telegram, Signal, iMessage, Slack, Zoom, meeting minutes, LinkedIn DMs), LinkedIn Basic Data Exports, and post-unzip trees the admin agent forwards. Returns a structured outcome to admin per ingest. |
157
- | Database Operator | Background recorder for the memory graph a Stop hook fires this specialist as a hidden PTY after every completed admin turn, and it writes whatever the turn implied into the graph. The admin agent itself never writes; it asks, listens, and acts on what the recorder has already captured. |
158
-
159
- Roles are installed during setup and listed when Maxy introduces itself. Some premium plugins add their own specialists (e.g. the writer-craft plugin adds a manuscript reviewer). Roles installed mid-session become active from the next session.
158
+ | Archive Ingest Operator | Ingests bulk external archives — Obsidian, ICS, X, Notion — and surfaces schema-mapping ambiguity rather than catching all unmapped relations as :MENTIONS. |
159
+ | Citation Auditor | Audits :TimelineEvent rows for missing citations and writes either citations directly or a CitationProposal stub. |
160
+ | Coding Assistant | Runs shell commands, drives git repositories, and reads or edits code on your behalf — the specialist you reach for when the work belongs in a developer terminal. |
161
+ | Compiled Truth Rewriter | Recomputes a node's compiledTruth (and public twin where applicable) from its 90-day timeline plus optional operator hints. |
162
+ | Content Producer | Produces visual output from your graph: generates images, renders pages to PDF, and hosts static websites you upload as a zip. |
163
+ | Database Operator | Executes graph writes on admin's behalf when delegated via the Task tool admin names each write, the specialist runs it. |
164
+ | Librarian | Owns foreground ingest of documents, conversation transcripts, and external archives into the memory graph. |
165
+ | Personal Assistant | Handles the operational tasks you'd give a personal assistant: scheduling meetings, managing your platform settings, connecting messaging channels, and completing browser-based tasks on your behalf. |
166
+ | Project Manager | Manages your tasks, projects, sessions, and workflows: linking work to people and goals, and keeping everything organised. |
167
+ | Public Session Reviewer | Reads a gated public-agent transcript and dispatches database-operator for each per-visitor memory write. |
168
+ | Research Assistant | Researches topics online, manages your knowledge graph, and produces supporting visuals. |
169
+ | Typed Edge Classifier | Reads recently-written prose nodes and writes typed edges from a closed allowlist. |
170
+
171
+ Roles are installed during setup and listed when Maxy introduces itself. Some premium bundles add their own specialists (e.g. the `real-agent` bundle adds a listing curator, negotiator, valuer, compliance officer, and buyer-enquiry public agent). Roles installed mid-session become active from the next session.
160
172
 
161
173
  ## Memory
162
174
 
@@ -230,6 +242,8 @@ Each session row also carries a small muted timestamp crumb under the name showi
230
242
 
231
243
  `verified=true` requires `master-fd=closed` AND `fdDelta>=1` AND `trackerRemoved=true`. `master-fd=close-failed` is logged at error level (`[rc-spawn-error]` prefix) — never swallowed; the next post-archive sweep is the catch-net.
232
244
 
245
+ **Cross-arm `[rc-life]` schema (Task 562).** Both rc-spawn arms (the legacy node-pty one above and a new isolated `script-scope` arm exposed via the dashboard A/B toggle next to "New session") plus the rc-daemon emit a shared log shape so the populations can be compared from `server.log` alone. One spawn's full lifeline is `grep <unitToken>`; one arm's signature is `grep 'source=<arm>'`. The new `script-scope` arm spawns via the daemon's mechanism — `systemdSpawn` + the same `script(1)` wrap — and the manager holds zero fds. Capture files are retained at `~/.<brand>/logs/spawn-<unitToken>.log` for post-mortem. Full schema and operator runbook in [`.docs/rc-life-observability.md`](../../../.docs/rc-life-observability.md).
246
+
233
247
  **Post-archive fd sweep (Task 558).** Independent of spawn/archive request traffic, the manager runs a 60 s sweep that walks both directions of the master-fd invariant:
234
248
 
235
249
  - `[fd-audit] op=orphan-master sessionId=<8> pid=<n> archivedAt=<ms> heldSinceArchiveMs=<n> fd=<n|unknown>` — fires per tracker whose row is archived (the leak).
@@ -305,6 +319,8 @@ A plugin extends what Maxy can do. Each plugin adds a focused capability — con
305
319
 
306
320
  Maxy's own capabilities are plugins too. Marketplace plugins (like Stripe) work the same way — Maxy manages all of them through conversation.
307
321
 
322
+ The tables below are the install catalogue — every plugin the platform can ship. They are not evidence of what is enabled on the current account. For the live install set, ask Maxy to call `capabilities-here`.
323
+
308
324
  ## Plugin Groups
309
325
 
310
326
  ### Core (always active)
@@ -37,18 +37,26 @@ Plugins are installed and managed through conversation. You can add marketplace
37
37
 
38
38
  ## Roles
39
39
 
40
- {{productName}} has six roles it can dispatch for specific tasks — like members of your team. You don't need to configure or manage them — {{productName}} decides when to use each role and handles everything automatically. You may see activity like "Dispatching personal-assistant..." in the chat timeline when this happens.
40
+ {{productName}} ships twelve roles it can dispatch for specific tasks — like members of your team. You don't need to configure or manage them — {{productName}} decides when to use each role and handles everything automatically. You may see activity like "Dispatching personal-assistant..." in the chat timeline when this happens.
41
+
42
+ The catalogue below is what the platform ships. It is not evidence of what is installed on the current account. For the live install set on this account, ask {{productName}} to call `capabilities-here`.
41
43
 
42
44
  | Role | What it does |
43
45
  |------|-------------|
44
- | Personal Assistant | Scheduling, platform administration, messaging channels, and browser automation |
45
- | Project Manager | Task and project management creating, tracking, and completing work items |
46
- | Research Assistant | Web research, knowledge management, and visual production |
47
- | Content Producer | Image generation, PDF output, and static-site hosting from prepared archives |
48
- | Librarian | Foreground ingester for the memory graph owns document ingestion (PDF / text / web), conversation-transcript ingestion (WhatsApp, Telegram, Signal, iMessage, Slack, Zoom, meeting minutes, LinkedIn DMs), LinkedIn Basic Data Exports, and post-unzip trees the admin agent forwards. Returns a structured outcome to admin per ingest. |
49
- | Database Operator | Background recorder for the memory graph a Stop hook fires this specialist as a hidden PTY after every completed admin turn, and it writes whatever the turn implied into the graph. The admin agent itself never writes; it asks, listens, and acts on what the recorder has already captured. |
50
-
51
- Roles are installed during setup and listed when {{productName}} introduces itself. Some premium plugins add their own specialists (e.g. the writer-craft plugin adds a manuscript reviewer). Roles installed mid-session become active from the next session.
46
+ | Archive Ingest Operator | Ingests bulk external archives — Obsidian, ICS, X, Notion — and surfaces schema-mapping ambiguity rather than catching all unmapped relations as :MENTIONS. |
47
+ | Citation Auditor | Audits :TimelineEvent rows for missing citations and writes either citations directly or a CitationProposal stub. |
48
+ | Coding Assistant | Runs shell commands, drives git repositories, and reads or edits code on your behalf — the specialist you reach for when the work belongs in a developer terminal. |
49
+ | Compiled Truth Rewriter | Recomputes a node's compiledTruth (and public twin where applicable) from its 90-day timeline plus optional operator hints. |
50
+ | Content Producer | Produces visual output from your graph: generates images, renders pages to PDF, and hosts static websites you upload as a zip. |
51
+ | Database Operator | Executes graph writes on admin's behalf when delegated via the Task tool admin names each write, the specialist runs it. |
52
+ | Librarian | Owns foreground ingest of documents, conversation transcripts, and external archives into the memory graph. |
53
+ | Personal Assistant | Handles the operational tasks you'd give a personal assistant: scheduling meetings, managing your platform settings, connecting messaging channels, and completing browser-based tasks on your behalf. |
54
+ | Project Manager | Manages your tasks, projects, sessions, and workflows: linking work to people and goals, and keeping everything organised. |
55
+ | Public Session Reviewer | Reads a gated public-agent transcript and dispatches database-operator for each per-visitor memory write. |
56
+ | Research Assistant | Researches topics online, manages your knowledge graph, and produces supporting visuals. |
57
+ | Typed Edge Classifier | Reads recently-written prose nodes and writes typed edges from a closed allowlist. |
58
+
59
+ Roles are installed during setup and listed when {{productName}} introduces itself. Some premium bundles add their own specialists (e.g. the `real-agent` bundle adds a listing curator, negotiator, valuer, compliance officer, and buyer-enquiry public agent). Roles installed mid-session become active from the next session.
52
60
 
53
61
  ## Memory
54
62
 
@@ -122,6 +130,8 @@ Each session row also carries a small muted timestamp crumb under the name showi
122
130
 
123
131
  `verified=true` requires `master-fd=closed` AND `fdDelta>=1` AND `trackerRemoved=true`. `master-fd=close-failed` is logged at error level (`[rc-spawn-error]` prefix) — never swallowed; the next post-archive sweep is the catch-net.
124
132
 
133
+ **Cross-arm `[rc-life]` schema (Task 562).** Both rc-spawn arms (the legacy node-pty one above and a new isolated `script-scope` arm exposed via the dashboard A/B toggle next to "New session") plus the rc-daemon emit a shared log shape so the populations can be compared from `server.log` alone. One spawn's full lifeline is `grep <unitToken>`; one arm's signature is `grep 'source=<arm>'`. The new `script-scope` arm spawns via the daemon's mechanism — `systemdSpawn` + the same `script(1)` wrap — and the manager holds zero fds. Capture files are retained at `~/.<brand>/logs/spawn-<unitToken>.log` for post-mortem. Full schema and operator runbook in [`.docs/rc-life-observability.md`](../../../.docs/rc-life-observability.md).
134
+
125
135
  **Post-archive fd sweep (Task 558).** Independent of spawn/archive request traffic, the manager runs a 60 s sweep that walks both directions of the master-fd invariant:
126
136
 
127
137
  - `[fd-audit] op=orphan-master sessionId=<8> pid=<n> archivedAt=<ms> heldSinceArchiveMs=<n> fd=<n|unknown>` — fires per tracker whose row is archived (the leak).
@@ -6,6 +6,8 @@ A plugin extends what {{productName}} can do. Each plugin adds a focused capabil
6
6
 
7
7
  {{productName}}'s own capabilities are plugins too. Marketplace plugins (like Stripe) work the same way — {{productName}} manages all of them through conversation.
8
8
 
9
+ The tables below are the install catalogue — every plugin the platform can ship. They are not evidence of what is enabled on the current account. For the live install set, ask {{productName}} to call `capabilities-here`.
10
+
9
11
  ## Plugin Groups
10
12
 
11
13
  ### Core (always active)
@@ -1,20 +1,21 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * check-architecture-skill-no-drift.mjs — prepublishOnly gate.
4
- *
5
- * The platform-architecture skill at
6
- * maxy-code/platform/plugins/admin/skills/platform-architecture/SKILL.md
7
- * is a generated artefact. Its body is the verbatim content of
8
- * `docs/public/llms-full.txt`, wrapped in a short header that carries a
9
- * sha256 of the body. The generator lives at
10
- * maxy-code/scripts/assemble-architecture-skill.mjs
11
- *
12
- * This gate runs the generator against the current tree (after first
13
- * regenerating `docs/public/llms-full.txt` via `docs/scripts/copy-docs.mjs`
14
- * so the source is fresh) and asserts the bytes match the checked-in
15
- * SKILL.md. Fails if someone hand-edited the SKILL.md or updated the
16
- * source docs without re-running the release flow.
17
- */
2
+ // check-architecture-skill-no-drift.mjs — prepublishOnly gate.
3
+ //
4
+ // The per-brand platform-architecture skill body is a generated artefact.
5
+ // Source: docs/public/llms-full.<brand>.txt (emitted by copy-docs.mjs).
6
+ // Generator: maxy-code/scripts/assemble-architecture-skill.mjs --brand <brand>.
7
+ //
8
+ // For brand=maxy-code, the gate also asserts byte-equality with the
9
+ // checked-in skill source at platform/plugins/admin/skills/platform-architecture/SKILL.md
10
+ // (back-compat with the existing release flow).
11
+ //
12
+ // For other brands, the gate asserts the assembled cache body exists and
13
+ // contains the brand product name + the catalogue-boundary sentence.
14
+ //
15
+ // Usage:
16
+ // node platform/scripts/check-architecture-skill-no-drift.mjs # all brands
17
+ // node platform/scripts/check-architecture-skill-no-drift.mjs --brand maxy-code
18
+ // node platform/scripts/check-architecture-skill-no-drift.mjs --brand realagent-code
18
19
 
19
20
  import { readFileSync, existsSync } from 'node:fs';
20
21
  import { dirname, resolve } from 'node:path';
@@ -25,43 +26,89 @@ import { assemble } from '../../scripts/assemble-architecture-skill.mjs';
25
26
  const __dirname = dirname(fileURLToPath(import.meta.url));
26
27
  const MAXY_CODE_ROOT = resolve(__dirname, '..', '..');
27
28
  const REPO_ROOT = resolve(MAXY_CODE_ROOT, '..');
28
- const LLMS_FULL = resolve(REPO_ROOT, 'docs/public/llms-full.txt');
29
29
  const COPY_DOCS = resolve(REPO_ROOT, 'docs/scripts/copy-docs.mjs');
30
- const SKILL_PATH = resolve(
31
- MAXY_CODE_ROOT,
32
- 'platform/plugins/admin/skills/platform-architecture/SKILL.md',
33
- );
30
+
31
+ const args = process.argv.slice(2);
32
+ const brandIdx = args.indexOf('--brand');
33
+ const REQUESTED_BRAND = brandIdx === -1 ? null : args[brandIdx + 1];
34
+
35
+ const CATALOGUE_BOUNDARY_SUBSTRING = 'not evidence of what is installed on the current account';
36
+
37
+ const BRANDS = [
38
+ {
39
+ key: 'maxy-code',
40
+ productName: 'Maxy',
41
+ docsUrl: 'https://docs.getmaxy.com',
42
+ sourceSkillPath: resolve(
43
+ MAXY_CODE_ROOT,
44
+ 'platform/plugins/admin/skills/platform-architecture/SKILL.md',
45
+ ),
46
+ },
47
+ {
48
+ key: 'realagent-code',
49
+ productName: 'Real Agent',
50
+ docsUrl: 'https://docs.realagent.network',
51
+ sourceSkillPath: null,
52
+ },
53
+ ];
34
54
 
35
55
  function fail(msg) {
36
56
  console.error(`[check-architecture-skill-no-drift] ERROR: ${msg}`);
37
57
  process.exit(1);
38
58
  }
39
59
 
40
- if (!existsSync(COPY_DOCS)) {
41
- fail(`docs mirror script missing: ${COPY_DOCS}`);
42
- }
60
+ if (!existsSync(COPY_DOCS)) fail(`docs mirror script missing: ${COPY_DOCS}`);
43
61
 
44
- // Regenerate the docs corpus so the comparison runs against current sources.
45
62
  execFileSync('node', [COPY_DOCS], { stdio: 'pipe' });
46
63
 
47
- if (!existsSync(LLMS_FULL)) {
48
- fail(`expected ${LLMS_FULL} after running copy-docs.mjs`);
49
- }
50
- if (!existsSync(SKILL_PATH)) {
51
- fail(`expected ${SKILL_PATH} (run scripts/assemble-architecture-skill.mjs)`);
52
- }
64
+ const ASSEMBLER = resolve(MAXY_CODE_ROOT, 'scripts/assemble-architecture-skill.mjs');
53
65
 
54
- const body = readFileSync(LLMS_FULL, 'utf8');
55
- const expected = assemble(body);
56
- const actual = readFileSync(SKILL_PATH, 'utf8');
66
+ const brandsToCheck = REQUESTED_BRAND
67
+ ? BRANDS.filter((b) => b.key === REQUESTED_BRAND)
68
+ : BRANDS;
57
69
 
58
- if (expected !== actual) {
59
- fail(
60
- `${SKILL_PATH} is out of sync with docs/public/llms-full.txt. ` +
61
- `Run \`node maxy-code/scripts/assemble-architecture-skill.mjs\` and commit the result.`,
62
- );
70
+ if (REQUESTED_BRAND && brandsToCheck.length === 0) {
71
+ fail(`unknown brand: ${REQUESTED_BRAND}`);
63
72
  }
64
73
 
65
- console.log(
66
- `[check-architecture-skill-no-drift] ok (${actual.length} bytes match)`,
67
- );
74
+ for (const brand of brandsToCheck) {
75
+ const corpus = resolve(REPO_ROOT, `docs/public/llms-full.${brand.key}.txt`);
76
+ if (!existsSync(corpus)) fail(`expected ${corpus} after running copy-docs.mjs`);
77
+
78
+ // Re-run assembler for this brand. The assembler writes the cache file;
79
+ // we then compare its body to a fresh in-process assemble() call to catch
80
+ // drift between the assembler module and the cache write.
81
+ execFileSync('node', [ASSEMBLER, '--brand', brand.key], { stdio: 'pipe' });
82
+
83
+ const body = readFileSync(corpus, 'utf8');
84
+ const expected = assemble(body, {
85
+ productName: brand.productName,
86
+ brand: brand.key,
87
+ docsUrl: brand.docsUrl,
88
+ });
89
+
90
+ if (!expected.includes(CATALOGUE_BOUNDARY_SUBSTRING)) {
91
+ fail(`brand=${brand.key}: assembled body missing catalogue-boundary sentence`);
92
+ }
93
+ if (!expected.includes(brand.productName)) {
94
+ fail(`brand=${brand.key}: assembled body missing product name ${brand.productName}`);
95
+ }
96
+
97
+ if (brand.sourceSkillPath) {
98
+ if (!existsSync(brand.sourceSkillPath)) {
99
+ fail(
100
+ `brand=${brand.key}: source skill missing at ${brand.sourceSkillPath}; ` +
101
+ `run \`node maxy-code/scripts/assemble-architecture-skill.mjs\``,
102
+ );
103
+ }
104
+ const actual = readFileSync(brand.sourceSkillPath, 'utf8');
105
+ if (expected !== actual) {
106
+ fail(
107
+ `brand=${brand.key}: ${brand.sourceSkillPath} out of sync. ` +
108
+ `Run \`node maxy-code/scripts/assemble-architecture-skill.mjs\` and commit.`,
109
+ );
110
+ }
111
+ }
112
+
113
+ console.log(`[check-architecture-skill-no-drift] ok brand=${brand.key} (${expected.length} bytes)`);
114
+ }
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ // check-roles-doc-completeness.mjs
3
+ // Asserts every specialist on disk has a row in the authoritative roles doc.
4
+ // Brands: --brand maxy (default) | --brand real-agent
5
+ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
6
+ import { resolve, join, dirname } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ const maxyCodeRoot = resolve(here, '..', '..');
11
+
12
+ const args = process.argv.slice(2);
13
+ const brandIdx = args.indexOf('--brand');
14
+ const brand = brandIdx === -1 ? 'maxy' : args[brandIdx + 1];
15
+
16
+ const config = {
17
+ maxy: {
18
+ diskDir: resolve(maxyCodeRoot, 'platform/templates/specialists/agents'),
19
+ docPath: resolve(maxyCodeRoot, 'platform/plugins/docs/references/platform.md'),
20
+ },
21
+ 'real-agent': {
22
+ diskDir: resolve(maxyCodeRoot, 'premium-plugins/real-agent/agents'),
23
+ docPath: resolve(maxyCodeRoot, 'premium-plugins/real-agent/docs/references/real-agent-roles.md'),
24
+ },
25
+ };
26
+
27
+ if (!config[brand]) {
28
+ console.error(`[check-roles-doc] unknown brand: ${brand}`);
29
+ process.exit(2);
30
+ }
31
+
32
+ const { diskDir, docPath } = config[brand];
33
+ const TABLE_HEADER = '| Role | What it does |';
34
+
35
+ if (!existsSync(diskDir)) {
36
+ console.error(`[check-roles-doc] disk dir missing: ${diskDir}`);
37
+ process.exit(2);
38
+ }
39
+
40
+ function listSpecialists(dir) {
41
+ const out = [];
42
+ for (const entry of readdirSync(dir)) {
43
+ const full = join(dir, entry);
44
+ const st = statSync(full);
45
+ if (st.isFile() && entry.endsWith('.md')) {
46
+ out.push(entry.replace(/\.md$/, ''));
47
+ } else if (st.isDirectory()) {
48
+ if (existsSync(join(full, 'IDENTITY.md'))) out.push(entry);
49
+ }
50
+ }
51
+ return out.sort();
52
+ }
53
+
54
+ const diskSpecialists = listSpecialists(diskDir);
55
+
56
+ if (!existsSync(docPath)) {
57
+ console.error(`[check-roles-doc] doc missing: ${docPath}`);
58
+ console.error(`[check-roles-doc] disk specialists (${diskSpecialists.length}): ${diskSpecialists.join(', ')}`);
59
+ process.exit(1);
60
+ }
61
+
62
+ const docBody = readFileSync(docPath, 'utf8');
63
+
64
+ function extractRoleRows(body, header) {
65
+ const lines = body.split(/\r?\n/);
66
+ const start = lines.findIndex((l) => l.trim() === header.trim());
67
+ if (start === -1) return [];
68
+ const rows = [];
69
+ for (let i = start + 2; i < lines.length; i++) {
70
+ const ln = lines[i];
71
+ if (!ln.startsWith('|')) break;
72
+ const cells = ln.split('|').map((c) => c.trim()).filter(Boolean);
73
+ if (cells.length >= 2) rows.push(cells[0]);
74
+ }
75
+ return rows;
76
+ }
77
+
78
+ const rowLabels = extractRoleRows(docBody, TABLE_HEADER);
79
+
80
+ const slugify = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
81
+ const rowSlugs = new Set(rowLabels.map(slugify));
82
+
83
+ const missing = diskSpecialists.filter((s) => !rowSlugs.has(slugify(s)));
84
+ const stale = rowLabels.filter((r) => !diskSpecialists.some((s) => slugify(s) === slugify(r)));
85
+
86
+ if (missing.length === 0 && stale.length === 0) {
87
+ console.log(`[check-roles-doc] brand=${brand} ok — ${diskSpecialists.length} specialists documented`);
88
+ process.exit(0);
89
+ }
90
+
91
+ console.error(`[check-roles-doc] brand=${brand} FAIL`);
92
+ console.error(` disk specialists (${diskSpecialists.length}): ${diskSpecialists.join(', ')}`);
93
+ console.error(` doc rows (${rowLabels.length}): ${rowLabels.join(', ')}`);
94
+ if (missing.length) console.error(` MISSING from doc: ${missing.join(', ')}`);
95
+ if (stale.length) console.error(` STALE in doc (no template): ${stale.join(', ')}`);
96
+ process.exit(1);
@@ -50,7 +50,7 @@ function rootsFor(maxyCodeRoot) {
50
50
  // Mirrors CC_NATIVE_TOOLS in specialist-drift.ts. Listed exhaustively so a
51
51
  // new CC-native tool forces an explicit ledger update in both surfaces.
52
52
  const CC_NATIVE_TOOLS = new Set([
53
- 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'BashOutput', 'KillShell',
53
+ 'Read', 'Write', 'Edit', 'MultiEdit', 'Glob', 'Grep', 'Bash', 'BashOutput', 'KillShell',
54
54
  'Skill', 'WebSearch', 'WebFetch', 'Task', 'TodoWrite', 'NotebookEdit',
55
55
  'NotebookRead', 'AskUserQuestion',
56
56
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"canonical-tool-names.generated.d.ts","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"AAQA,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAiB7C,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EA+KtD,CAAA"}
1
+ {"version":3,"file":"canonical-tool-names.generated.d.ts","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"AAQA,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAiB7C,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAgLtD,CAAA"}
@@ -39,6 +39,7 @@ export const CANONICAL_MAXY_TOOL_NAMES = [
39
39
  "mcp__plugin_admin_admin__agent-config-read",
40
40
  "mcp__plugin_admin_admin__agent-list",
41
41
  "mcp__plugin_admin_admin__brand-settings",
42
+ "mcp__plugin_admin_admin__capabilities-here",
42
43
  "mcp__plugin_admin_admin__logs-read",
43
44
  "mcp__plugin_admin_admin__plugin-read",
44
45
  "mcp__plugin_admin_admin__public-hostname",