@rubytech/create-maxy-code 0.1.117 → 0.1.119

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 (78) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/neo4j/schema.cypher +31 -0
  3. package/payload/platform/package-lock.json +141 -125
  4. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  5. package/payload/platform/plugins/admin/PLUGIN.md +1 -0
  6. package/payload/platform/plugins/aeo/.claude-plugin/plugin.json +17 -0
  7. package/payload/platform/plugins/aeo/PLUGIN.md +81 -0
  8. package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts +2 -0
  9. package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts.map +1 -0
  10. package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js +121 -0
  11. package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js.map +1 -0
  12. package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts +2 -0
  13. package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts.map +1 -0
  14. package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js +129 -0
  15. package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js.map +1 -0
  16. package/payload/platform/plugins/aeo/mcp/dist/index.d.ts +2 -0
  17. package/payload/platform/plugins/aeo/mcp/dist/index.d.ts.map +1 -0
  18. package/payload/platform/plugins/aeo/mcp/dist/index.js +171 -0
  19. package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -0
  20. package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts +27 -0
  21. package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts.map +1 -0
  22. package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js +274 -0
  23. package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js.map +1 -0
  24. package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts +5 -0
  25. package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts.map +1 -0
  26. package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js +38 -0
  27. package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js.map +1 -0
  28. package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts +48 -0
  29. package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts.map +1 -0
  30. package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js +254 -0
  31. package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js.map +1 -0
  32. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts +25 -0
  33. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts.map +1 -0
  34. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js +78 -0
  35. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js.map +1 -0
  36. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts +18 -0
  37. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts.map +1 -0
  38. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js +56 -0
  39. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js.map +1 -0
  40. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts +37 -0
  41. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts.map +1 -0
  42. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js +94 -0
  43. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js.map +1 -0
  44. package/payload/platform/plugins/aeo/mcp/package.json +22 -0
  45. package/payload/platform/plugins/aeo/mcp/vitest.config.ts +9 -0
  46. package/payload/platform/plugins/aeo/skills/structured-answer/SKILL.md +53 -0
  47. package/payload/platform/plugins/docs/PLUGIN.md +1 -0
  48. package/payload/platform/plugins/docs/references/admin-session.md +1 -1
  49. package/payload/platform/plugins/docs/references/aeo.md +87 -0
  50. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  51. package/payload/platform/plugins/email/PLUGIN.md +6 -0
  52. package/payload/platform/plugins/email/skills/email-composition/SKILL.md +180 -0
  53. package/payload/platform/plugins/memory/mcp/dist/index.js +5 -0
  54. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.d.ts +2 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.d.ts.map +1 -0
  57. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.js +62 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-origin-gate.test.js.map +1 -0
  59. package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.d.ts +18 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.d.ts.map +1 -0
  61. package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.js +29 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/label-origin-gate.js.map +1 -0
  63. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +2 -0
  64. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +1 -1
  66. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +1 -1
  67. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +3 -0
  68. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
  69. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +9 -1
  70. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  71. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  72. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +8 -0
  73. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  74. package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
  75. package/payload/platform/plugins/memory/references/schema-base.md +5 -1
  76. package/payload/platform/scripts/smoke-boot-services.sh +30 -2
  77. package/payload/platform/templates/specialists/agents/signal-detector.md +1 -1
  78. package/payload/server/server.js +1 -0
@@ -0,0 +1,25 @@
1
+ import { type AuditResult } from "../lib/audit-heuristics.js";
2
+ export interface AuditPageInput {
3
+ accountId: string;
4
+ /** Provide one of `url` or `html`. */
5
+ url?: string;
6
+ html?: string;
7
+ /**
8
+ * When true, the audit result is persisted as an `:AEOAudit` node
9
+ * with `HAS_AEO_AUDIT` edge from `targetKnowledgeDocumentId` (if
10
+ * given) or the synthetic `targetUrl` audit history.
11
+ */
12
+ persist?: boolean;
13
+ targetKnowledgeDocumentId?: string;
14
+ agentSlug?: string;
15
+ sessionId?: string;
16
+ }
17
+ export interface AuditPageResult extends AuditResult {
18
+ target: string;
19
+ audit: {
20
+ elementId?: string;
21
+ runAt: string;
22
+ };
23
+ }
24
+ export declare function auditPage(input: AuditPageInput): Promise<AuditPageResult>;
25
+ //# sourceMappingURL=aeo-audit-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-audit-page.d.ts","sourceRoot":"","sources":["../../src/tools/aeo-audit-page.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAGzE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QACL,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAeD,wBAAsB,SAAS,CAC7B,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,eAAe,CAAC,CA2E1B"}
@@ -0,0 +1,78 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { auditHtml } from "../lib/audit-heuristics.js";
3
+ import { getSession } from "../lib/neo4j.js";
4
+ async function fetchHtml(url) {
5
+ const res = await fetch(url, {
6
+ redirect: "follow",
7
+ headers: { "User-Agent": "maxy-aeo-audit/0.1" },
8
+ });
9
+ if (!res.ok) {
10
+ throw new Error(`aeo-audit-page: GET ${url} returned HTTP ${res.status}`);
11
+ }
12
+ return await res.text();
13
+ }
14
+ export async function auditPage(input) {
15
+ if (!input.url && !input.html) {
16
+ throw new Error("aeo-audit-page requires one of url or html");
17
+ }
18
+ if (input.url && input.html) {
19
+ throw new Error("aeo-audit-page: pass exactly one of url or html, not both");
20
+ }
21
+ const html = input.html ?? (await fetchHtml(input.url));
22
+ const target = input.url ?? "(inline html)";
23
+ const result = auditHtml(html);
24
+ const runAt = new Date().toISOString();
25
+ const fails = result.heuristics
26
+ .filter((h) => h.status === "fail")
27
+ .map((h) => h.name);
28
+ const warns = result.heuristics
29
+ .filter((h) => h.status === "warn")
30
+ .map((h) => h.name);
31
+ process.stderr.write(`[aeo-audit] target=${target} score=${result.score} fails=${fails.join(",") || "-"} warns=${warns.join(",") || "-"}\n`);
32
+ let elementId;
33
+ if (input.persist) {
34
+ const auditId = randomUUID();
35
+ const session = getSession();
36
+ try {
37
+ const r = await session.run(`CREATE (a:AEOAudit {
38
+ accountId: $accountId,
39
+ auditId: $auditId,
40
+ target: $target,
41
+ score: $score,
42
+ heuristicsJson: $heuristicsJson,
43
+ runAt: $runAt,
44
+ createdByAgent: $agent,
45
+ createdBySession: $session,
46
+ createdAt: $runAt
47
+ })
48
+ WITH a
49
+ OPTIONAL MATCH (d:KnowledgeDocument)
50
+ WHERE elementId(d) = $kdocId AND d.accountId = $accountId
51
+ FOREACH (_ IN CASE WHEN d IS NULL THEN [] ELSE [1] END |
52
+ MERGE (d)-[:HAS_AEO_AUDIT]->(a)
53
+ )
54
+ RETURN elementId(a) AS id`, {
55
+ accountId: input.accountId,
56
+ auditId,
57
+ target,
58
+ score: result.score,
59
+ heuristicsJson: JSON.stringify(result.heuristics),
60
+ runAt,
61
+ agent: input.agentSlug ?? "system",
62
+ session: input.sessionId ?? null,
63
+ kdocId: input.targetKnowledgeDocumentId ?? null,
64
+ });
65
+ elementId = r.records[0]?.get("id");
66
+ process.stderr.write(`[aeo-audit] persisted auditId=${auditId} elementId=${elementId ?? "-"}\n`);
67
+ }
68
+ finally {
69
+ await session.close();
70
+ }
71
+ }
72
+ return {
73
+ ...result,
74
+ target,
75
+ audit: { elementId, runAt },
76
+ };
77
+ }
78
+ //# sourceMappingURL=aeo-audit-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-audit-page.js","sourceRoot":"","sources":["../../src/tools/aeo-audit-page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { MissingPropertyError, type JsonLdObject } from "../lib/schema-mapping.js";
2
+ export interface EmitJsonLdInput {
3
+ accountId: string;
4
+ /** Pull entity from the graph by Neo4j elementId. */
5
+ entityId?: string;
6
+ /** Or render a JSON-LD block from an inline label + property bag. */
7
+ label?: string;
8
+ properties?: Record<string, unknown>;
9
+ }
10
+ export interface EmitJsonLdResult {
11
+ jsonLd: JsonLdObject;
12
+ scriptBlock: string;
13
+ schemaType: string;
14
+ source: "graph" | "inline";
15
+ }
16
+ export declare function emitJsonLd(input: EmitJsonLdInput): Promise<EmitJsonLdResult>;
17
+ export { MissingPropertyError };
18
+ //# sourceMappingURL=aeo-emit-jsonld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-emit-jsonld.d.ts","sourceRoot":"","sources":["../../src/tools/aeo-emit-jsonld.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,oBAAoB,EACpB,KAAK,YAAY,EAClB,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC5B;AAMD,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA8D3B;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { getSession } from "../lib/neo4j.js";
2
+ import { buildJsonLd, renderJsonLdScript, MissingPropertyError, } from "../lib/schema-mapping.js";
3
+ function isPropertyBag(v) {
4
+ return typeof v === "object" && v !== null && !Array.isArray(v);
5
+ }
6
+ export async function emitJsonLd(input) {
7
+ if (!input.entityId && !input.label) {
8
+ throw new Error("aeo-emit-jsonld requires either entityId or label+properties");
9
+ }
10
+ if (input.entityId) {
11
+ const session = getSession();
12
+ try {
13
+ const r = await session.run(`MATCH (n) WHERE elementId(n) = $id AND n.accountId = $accountId
14
+ RETURN labels(n) AS labels, properties(n) AS props, elementId(n) AS id LIMIT 1`, { id: input.entityId, accountId: input.accountId });
15
+ if (r.records.length === 0) {
16
+ throw new Error(`aeo-emit-jsonld: no node with elementId=${input.entityId} in account=${input.accountId}`);
17
+ }
18
+ const rec = r.records[0];
19
+ const labels = rec.get("labels");
20
+ const props = rec.get("props");
21
+ const id = rec.get("id");
22
+ const primaryLabel = labels.find((l) => l !== "Resource") ?? labels[0];
23
+ const obj = buildJsonLd({
24
+ label: primaryLabel,
25
+ elementId: id,
26
+ properties: props,
27
+ });
28
+ process.stderr.write(`[aeo-emit-jsonld] entityId=${id} schemaType=${obj["@type"]} source=graph\n`);
29
+ return {
30
+ jsonLd: obj,
31
+ scriptBlock: renderJsonLdScript(obj),
32
+ schemaType: obj["@type"],
33
+ source: "graph",
34
+ };
35
+ }
36
+ finally {
37
+ await session.close();
38
+ }
39
+ }
40
+ if (!input.label || !isPropertyBag(input.properties)) {
41
+ throw new Error("aeo-emit-jsonld: label requires a properties object");
42
+ }
43
+ const obj = buildJsonLd({
44
+ label: input.label,
45
+ properties: input.properties,
46
+ });
47
+ process.stderr.write(`[aeo-emit-jsonld] label=${input.label} schemaType=${obj["@type"]} source=inline\n`);
48
+ return {
49
+ jsonLd: obj,
50
+ scriptBlock: renderJsonLdScript(obj),
51
+ schemaType: obj["@type"],
52
+ source: "inline",
53
+ };
54
+ }
55
+ export { MissingPropertyError };
56
+ //# sourceMappingURL=aeo-emit-jsonld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-emit-jsonld.js","sourceRoot":"","sources":["../../src/tools/aeo-emit-jsonld.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Generates the `llms.txt` and `llms-full.txt` pair for an account's
3
+ * published content. Format follows the current llmstxt.org draft:
4
+ *
5
+ * # <Site name>
6
+ * > <Short description>
7
+ *
8
+ * ## <Section name>
9
+ * - [<Title>](<URL>): <Summary>
10
+ *
11
+ * `llms-full.txt` concatenates the full text of every listed page in
12
+ * the same order, separated by a blank line and the page's title as an
13
+ * H1.
14
+ *
15
+ * Source: `KnowledgeDocument` nodes for the account that have a `url`
16
+ * property. `summary` and `body` properties feed the index and the
17
+ * concatenated dump respectively. Pages without a `url` are skipped
18
+ * (they aren't reachable from a public site), and the count of skipped
19
+ * pages is returned so the operator can audit completeness.
20
+ */
21
+ export interface WriteLlmsTxtInput {
22
+ accountId: string;
23
+ siteName: string;
24
+ siteDescription?: string;
25
+ }
26
+ export interface WriteLlmsTxtResult {
27
+ llmsTxt: string;
28
+ llmsFullTxt: string;
29
+ pageCount: number;
30
+ skippedNoUrl: number;
31
+ sizeBytes: {
32
+ index: number;
33
+ full: number;
34
+ };
35
+ }
36
+ export declare function writeLlmsTxt(input: WriteLlmsTxtInput): Promise<WriteLlmsTxtResult>;
37
+ //# sourceMappingURL=aeo-write-llms-txt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-write-llms-txt.d.ts","sourceRoot":"","sources":["../../src/tools/aeo-write-llms-txt.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C;AAqBD,wBAAsB,YAAY,CAChC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CA4F7B"}
@@ -0,0 +1,94 @@
1
+ import { getSession } from "../lib/neo4j.js";
2
+ const HEADER_RX = /^#\s/m;
3
+ function escapeMarkdown(text) {
4
+ return text.replace(/[\[\]\(\)\n\r]/g, " ").trim();
5
+ }
6
+ function summariseBody(body) {
7
+ const flat = body.replace(/\s+/g, " ").trim();
8
+ if (flat.length <= 200)
9
+ return flat;
10
+ return flat.slice(0, 197).trimEnd() + "…";
11
+ }
12
+ export async function writeLlmsTxt(input) {
13
+ const session = getSession();
14
+ let rows = [];
15
+ let skippedNoUrl = 0;
16
+ try {
17
+ const r = await session.run(`MATCH (d:KnowledgeDocument {accountId: $accountId})
18
+ RETURN d.title AS title, d.url AS url, d.summary AS summary,
19
+ coalesce(d.body, d.abstract, '') AS body
20
+ ORDER BY d.title ASC`, { accountId: input.accountId });
21
+ for (const rec of r.records) {
22
+ const title = rec.get("title") ?? "";
23
+ const url = rec.get("url") ?? "";
24
+ const summary = rec.get("summary") ?? "";
25
+ const body = rec.get("body") ?? "";
26
+ if (!url || !title) {
27
+ skippedNoUrl += 1;
28
+ continue;
29
+ }
30
+ rows.push({
31
+ title: escapeMarkdown(title),
32
+ url: url.trim(),
33
+ summary: escapeMarkdown(summary) || summariseBody(body) || "(no summary)",
34
+ body: body.trim(),
35
+ });
36
+ }
37
+ }
38
+ finally {
39
+ await session.close();
40
+ }
41
+ const indexLines = [];
42
+ indexLines.push(`# ${escapeMarkdown(input.siteName)}`);
43
+ indexLines.push("");
44
+ if (input.siteDescription) {
45
+ indexLines.push(`> ${escapeMarkdown(input.siteDescription)}`);
46
+ indexLines.push("");
47
+ }
48
+ indexLines.push("## Pages");
49
+ indexLines.push("");
50
+ for (const row of rows) {
51
+ indexLines.push(`- [${row.title}](${row.url}): ${row.summary}`);
52
+ }
53
+ indexLines.push("");
54
+ indexLines.push("<!-- generated by @maxy/aeo aeo-write-llms-txt; spec: https://llmstxt.org/ -->");
55
+ const llmsTxt = indexLines.join("\n");
56
+ const fullLines = [];
57
+ fullLines.push(`# ${escapeMarkdown(input.siteName)}`);
58
+ fullLines.push("");
59
+ if (input.siteDescription) {
60
+ fullLines.push(`> ${escapeMarkdown(input.siteDescription)}`);
61
+ fullLines.push("");
62
+ }
63
+ for (const row of rows) {
64
+ fullLines.push(`# ${row.title}`);
65
+ fullLines.push("");
66
+ fullLines.push(`Source: ${row.url}`);
67
+ fullLines.push("");
68
+ if (row.body) {
69
+ // Demote any in-body H1s to H2 so a single H1 hierarchy holds.
70
+ const safeBody = row.body.replace(HEADER_RX, "## ");
71
+ fullLines.push(safeBody);
72
+ }
73
+ else {
74
+ fullLines.push(row.summary);
75
+ }
76
+ fullLines.push("");
77
+ fullLines.push("---");
78
+ fullLines.push("");
79
+ }
80
+ const llmsFullTxt = fullLines.join("\n");
81
+ const sizeBytes = {
82
+ index: Buffer.byteLength(llmsTxt, "utf-8"),
83
+ full: Buffer.byteLength(llmsFullTxt, "utf-8"),
84
+ };
85
+ process.stderr.write(`[aeo-llms-txt] site=${input.siteName} pages=${rows.length} skippedNoUrl=${skippedNoUrl} indexBytes=${sizeBytes.index} fullBytes=${sizeBytes.full}\n`);
86
+ return {
87
+ llmsTxt,
88
+ llmsFullTxt,
89
+ pageCount: rows.length,
90
+ skippedNoUrl,
91
+ sizeBytes,
92
+ };
93
+ }
94
+ //# sourceMappingURL=aeo-write-llms-txt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aeo-write-llms-txt.js","sourceRoot":"","sources":["../../src/tools/aeo-write-llms-txt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@maxy/aeo",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "start": "node dist/index.js",
10
+ "test": "vitest run"
11
+ },
12
+ "dependencies": {
13
+ "@modelcontextprotocol/sdk": "^1.12.1",
14
+ "neo4j-driver": "^5.28.1",
15
+ "zod": "^3.24.0"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.7.0",
19
+ "@types/node": "^22.0.0",
20
+ "vitest": "^4.1.2"
21
+ }
22
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ include: ["src/**/__tests__/**/*.test.ts"],
7
+ testTimeout: 10_000,
8
+ },
9
+ });
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: structured-answer
3
+ description: Use when authoring or auditing a page whose primary purpose is to answer a single question — service pages, FAQ entries, "what is X" pages, comparison pages. Defines the page shape that answer engines lift verbatim. Triggers include "make this AEO-friendly", "structure this for Claude / ChatGPT", "audit this page for answer-engine pickup".
4
+ ---
5
+
6
+ # Structured-answer page template
7
+
8
+ Answer engines (Claude, ChatGPT, Perplexity, Google AI Overviews, Bing Copilot) lift a single short span when they cite a page. That span is almost always the first paragraph after the H1, when it is short and direct. Every other element on the page either supports the lift or gets ignored.
9
+
10
+ ## The shape
11
+
12
+ ```
13
+ <head>
14
+ <title>{question or topic} — {site name}</title>
15
+ <meta name="description" content="{80–160 char summary of the answer}">
16
+ <link rel="canonical" href="{this page's URL}">
17
+ <meta property="og:title" content="{question or topic}">
18
+ <meta property="og:description" content="{same as meta description}">
19
+ <script type="application/ld+json">{ emit via aeo-emit-jsonld }</script>
20
+ </head>
21
+
22
+ <body>
23
+ <h1>{question or topic}</h1>
24
+ <p>{≤280 characters. The answer. One sentence preferred. Names the entity, states the answer, ends on a fact.}</p>
25
+
26
+ <h2>{follow-up section}</h2>
27
+ <p>{details, examples, links}</p>
28
+
29
+ <h2>Frequently asked questions</h2>
30
+ <!-- emit FAQPage JSON-LD with 3–5 Question / Answer pairs -->
31
+ </body>
32
+ ```
33
+
34
+ ## Why this works
35
+
36
+ - The H1 names the question; the first `<p>` is the answer. Engines treat that pair as a structured Q/A.
37
+ - The `<p>` is short enough to be lifted whole. Longer paragraphs get truncated mid-sentence.
38
+ - The JSON-LD repeats the structured data in a parser-friendly form so engines that index pre-render don't have to scrape the DOM.
39
+ - The FAQ section adds a second class of lift target — engines often cite an FAQ answer when the page's main question is too broad.
40
+
41
+ ## What breaks the lift
42
+
43
+ - A multi-sentence first paragraph. The engine truncates and the citation reads like a fragment.
44
+ - Marketing prose before the answer ("In today's fast-moving world…"). The engine lifts the marketing prose, not the answer.
45
+ - The H1 stating the brand instead of the question. "Acme Plumbing" is not a question; "How much does a boiler service cost?" is.
46
+ - Missing JSON-LD. Engines that pre-render the page still index the DOM, but they down-weight pages without structured data.
47
+ - Skipped heading levels. Engines use heading hierarchy to decide which content is the answer and which is supporting detail.
48
+
49
+ ## How to audit
50
+
51
+ Run `aeo-audit-page` against the URL or HTML. Eight heuristics return pass / warn / fail. Treat any `fail` as a blocker — the rewrite suggestion in the response is concrete. Treat `warn` as a polish target.
52
+
53
+ To record the audit on the document for trend analysis, pass `persist: true` with the document's `elementId` as `targetKnowledgeDocumentId`. The plugin writes an `:AEOAudit` node linked via `HAS_AEO_AUDIT`.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: docs
3
+ surface: platform
3
4
  description: "Comprehensive user guide and platform documentation for Maxy. Load references when users ask how-to, setup, or troubleshooting questions. Also contains platform architecture references for admin use."
4
5
  metadata: {"platform":{"always":true,"embed":["public","admin"],"pluginKey":"docs"}}
5
6
  ---
@@ -79,7 +79,7 @@ PreToolUse hook `platform/plugins/admin/hooks/askuserquestion-investigate-gate.s
79
79
 
80
80
  ## Spawn-with-`initialMessage` wrapper
81
81
 
82
- The Hono route `POST /api/admin/claude-sessions` at [`platform/ui/server/routes/admin/claude-sessions.ts`](../../../ui/server/routes/admin/claude-sessions.ts) is the single canonical entry point for any caller — UI click handler or programmatic — that needs to open an admin PTY session with a first user prompt. Direct calls to the session-manager's `/spawn` are not allowed; the wrapper owns the per-spawn enrichment (`aboutOwner`, `dormantPlugins`, `activePlugins`, `specialistDomains`, `tunnelUrl`) and the `senderId` resolution from the admin session cookie. The session manager binds to `127.0.0.1` only so the wrapper is the sole authorised caller; any ad-hoc spawn that bypasses it duplicates this contract and is a bug.
82
+ The Hono route `POST /api/admin/claude-sessions` at [`platform/ui/server/routes/admin/claude-sessions.ts`](../../../ui/server/routes/admin/claude-sessions.ts) is the single canonical entry point for any caller — UI click handler or programmatic — that needs to open an admin PTY session with a first user prompt. Direct calls to the session-manager's `/spawn` are not allowed; the wrapper owns the per-spawn enrichment (`aboutOwner`, `dormantPlugins`, `activePlugins`, `specialistDomains`, `tunnelUrl`) and the `senderId` resolution from the admin session cookie. The session manager binds to `127.0.0.1` only so the wrapper is the sole authorised caller; any ad-hoc spawn that bypasses it duplicates this contract and is a bug. `dormantPlugins` is `installed − enabledPlugins` with one exclusion: PLUGIN.md frontmatter `surface: platform` (admin, docs) marks platform-shell plugins that ship with every install and are never opt-in features, so they never surface in the `<dormant-plugins>` nudge.
83
83
 
84
84
  **Body schema.** `{channel?, permissionMode?, initialMessage?, specialist?, model?}` for the cookie path (Sidebar — `senderId` resolved from the admin session cookie, `hidden: false`). `permissionMode` accepts one of five values matching Claude Code's CLI: `'default'`, `'acceptEdits'`, `'plan'`, `'auto'`, `'bypassPermissions'`. The Stop-hook loopback caller adds `adminSessionId` and uses three overrides: `specialist: 'database-operator'`, `model: 'haiku'`, plus `initialMessage`. The wrapper rejects nothing structurally on the cookie path — string fields are coerced or defaulted (`channel` → `'browser'`, `permissionMode` → undefined, `initialMessage` → null if empty/whitespace). The loopback path refuses with `400 admin-session-id-required` if `adminSessionId` is missing or non-UUID, and `404 admin-session-not-found` if the manager has no StoredSession for it.
85
85
 
@@ -0,0 +1,87 @@
1
+ # AEO (Answer Engine Optimisation) — user guide
2
+
3
+ The AEO plugin shapes the site for answer engines (Claude, ChatGPT, Perplexity, Google AI Overviews, Bing Copilot). It does three things: emits schema.org JSON-LD from typed graph entities, generates `/llms.txt` and `/llms-full.txt`, and audits any page against eight heuristics.
4
+
5
+ ## When to use it
6
+
7
+ - A new page is being authored and you want it to be cited when customers ask an answer engine about your service.
8
+ - An existing page is not being cited. Audit it to find the structural reasons.
9
+ - You're standing up a new site and want the `llms.txt` pair generated so answer engines can index your content directly.
10
+
11
+ ## The tools
12
+
13
+ ### `aeo-emit-jsonld`
14
+
15
+ Generates a `<script type="application/ld+json">` block for a typed entity. Two modes:
16
+
17
+ - **From the graph.** Pass `entityId` (Neo4j elementId of an `Organization`, `Person`, `Service`, `Product`, `CreativeWork`, `FAQPage`, `RealEstateListing`, or `Event`). The tool resolves the entity, maps the label to its schema.org type, and emits the block.
18
+ - **Inline.** Pass `label` (one of the supported page types) plus a `properties` object. Use this when the page isn't backed by a stored entity (yet) but you want the JSON-LD shape.
19
+
20
+ Returns the parsed JSON-LD object and a ready-to-inline script block string. Inline the script in your page `<head>`.
21
+
22
+ ### `aeo-write-llms-txt`
23
+
24
+ Generates the `llms.txt` / `llms-full.txt` pair for the account. Source is every `KnowledgeDocument` for the account that has a `url` property. Returns both files as strings plus a count of pages skipped because they had no URL.
25
+
26
+ Wire the output to your site host. Convention: `/llms.txt` (index) and `/llms-full.txt` (concatenated content), served as `text/plain`. Format follows the current draft at `https://llmstxt.org/`.
27
+
28
+ ### `aeo-audit-page`
29
+
30
+ Runs the eight-heuristic audit. Pass either `url` (the tool fetches) or `html` (you supply the rendered content). Returns:
31
+
32
+ ```
33
+ {
34
+ "score": 0–100,
35
+ "heuristics": [
36
+ {
37
+ "name": "structured-answer",
38
+ "status": "pass" | "warn" | "fail",
39
+ "detail": "first <p> after <h1> is 142 chars",
40
+ "suggestion": "Add one ≤280-character <p> immediately after the <h1>…"
41
+ },
42
+
43
+ ],
44
+ "target": "<url or '(inline html)'>",
45
+ "audit": { "runAt": "<iso>", "elementId": "<set when persisted>" }
46
+ }
47
+ ```
48
+
49
+ Pass `persist: true` plus `targetKnowledgeDocumentId` to write the result as an `:AEOAudit` node linked to the document.
50
+
51
+ ## The eight heuristics
52
+
53
+ | Heuristic | What it checks |
54
+ |---|---|
55
+ | `h1-present` | exactly one `<h1>` |
56
+ | `jsonld-present` | at least one parseable JSON-LD block |
57
+ | `structured-answer` | first `<p>` after `<h1>` is ≤280 chars |
58
+ | `faq-section` | `FAQPage` JSON-LD on the page |
59
+ | `meta-description` | 80–160 char `<meta name="description">` |
60
+ | `canonical-url` | `<link rel="canonical">` present |
61
+ | `og-tags` | `og:title` and `og:description` both set |
62
+ | `heading-hierarchy` | no level skips (h1→h3 etc.) |
63
+
64
+ `structured-answer` is the highest-impact: that one paragraph is what gets lifted into the engine's answer. A page can fail every other heuristic and still be cited if this one passes.
65
+
66
+ ## Observability
67
+
68
+ Every tool emits a single log line per invocation:
69
+
70
+ - `[aeo-emit-jsonld] entityId=… schemaType=… source=graph|inline`
71
+ - `[aeo-llms-txt] site=… pages=… skippedNoUrl=… indexBytes=… fullBytes=…`
72
+ - `[aeo-audit] target=… score=… fails=… warns=…`
73
+
74
+ Diagnostic path: `grep -E '^\[aeo-' platform-logs/*.log | grep <urlOrEntityId>`.
75
+
76
+ ## What this plugin does not do
77
+
78
+ - **No citation monitor — out of scope.** Tracking whether your brand is cited by Claude / ChatGPT / Perplexity / Gemini would require multi-engine answer harvesting that doesn't fit maxy-code's no-API-key architecture. Archived without sprinting (Task 363). Check citation manually when needed.
79
+ - **No auto-emission on page render.** `aeo-emit-jsonld` is callable on demand. Wiring it into the platform's page-generator render path is per-renderer work, filed as a follow-up.
80
+ - **No publish-hook regeneration of `llms.txt`.** The tool runs on demand. Hooking it into the publish event is a follow-up.
81
+
82
+ ## See also
83
+
84
+ - Plugin manifest: `platform/plugins/aeo/PLUGIN.md`
85
+ - Structured-answer template: `platform/plugins/aeo/skills/structured-answer/SKILL.md`
86
+ - Schema declaration: `platform/neo4j/schema.cypher` (search `AEOAudit`)
87
+ - Spec source: `https://llmstxt.org/`
@@ -85,7 +85,7 @@ Every PTY spawn injects an `--append-system-prompt` block composed of these sent
85
85
  | `<about-owner>` | upstream `loadUserProfile` + `formatProfileSummary` (UI process) | sentinel renders the prose body with `NOTHING (operator-data source unavailable: \`<reason>\`)` on line one; spawn proceeds |
86
86
  | `<specialist-domains>` | `<accountDir>/specialists/agents/*.md` frontmatter (UI process) | sentinel omitted when set is empty |
87
87
  | `<plugin-manifest>` | enabled plugins' PLUGIN.md frontmatter + skill SKILL.md frontmatter (UI process) | sentinel omitted when set is empty |
88
- | `<dormant-plugins>` | installed-minus-enabled set from `platform/plugins/` and `account.json` | sentinel omitted when set is empty |
88
+ | `<dormant-plugins>` | installed-minus-enabled set from `platform/plugins/` and `account.json`, excluding plugins whose PLUGIN.md frontmatter sets `surface: platform` (platform-shell — ships with every install, never opt-in). Absent `surface` field defaults to `feature` (dormant-eligible). | sentinel omitted when set is empty |
89
89
 
90
90
  Diagnostic line per spawn (`~/.<brand>/logs/server.log`):
91
91
 
@@ -70,6 +70,12 @@ Manages the agent's own dedicated email account — IMAP for reading, SMTP for s
70
70
  - **Graph history** (`email-graph-query`) — stored Email nodes in Neo4j. Use for recall, semantic search, email history questions.
71
71
  - **General knowledge** (`memory-search`) — cross-type knowledge graph. Use when the question spans all memory, not just emails.
72
72
 
73
+ ## Skills
74
+
75
+ | Skill | Purpose |
76
+ |-------|---------|
77
+ | [email-composition](skills/email-composition/SKILL.md) | Conversational wrapper for drafting replies, new emails, and inbox triage. Orchestrates the five `email-*` tools with human-in-the-loop approval on every send. |
78
+
73
79
  ## References
74
80
 
75
81
  | Reference | Topics |