@awesomate/hosting-mcp 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +146 -0
- package/package.json +1 -1
- package/skill/CHANGELOG.json +16 -0
- package/skill/awesomate-hosting/scripts/deploy-artifacts.sh +1 -1
- package/skill/awesomate-knowledge/SKILL.md +12 -4
- package/skill/awesomate-knowledge/evals/import-website-faqs/graders/grader.md +32 -0
- package/skill/awesomate-knowledge/evals/import-website-faqs/prompt.md +2 -0
- package/skill/awesomate-knowledge/evals/plan-gate-essentials/graders/grader.md +19 -15
- package/skill/awesomate-knowledge/evals/plan-gate-essentials/prompt.md +3 -2
- package/skill/awesomate-knowledge/references/faqs.md +125 -0
- package/skill/awesomate-knowledge/references/ingestion-sources.md +5 -2
package/dist/index.js
CHANGED
|
@@ -40790,6 +40790,107 @@ async function knowledgeCollections(config3, args) {
|
|
|
40790
40790
|
return mapKnowledgeError(err, config3.apiBase);
|
|
40791
40791
|
}
|
|
40792
40792
|
}
|
|
40793
|
+
var faqEntryBody = (e) => ({
|
|
40794
|
+
question: e.question,
|
|
40795
|
+
answer: e.answer,
|
|
40796
|
+
...e.altQuestions?.length ? { alt_questions: e.altQuestions } : {},
|
|
40797
|
+
...e.category ? { category: e.category } : {},
|
|
40798
|
+
...e.sourceUrl ? { source_url: e.sourceUrl } : {},
|
|
40799
|
+
...e.externalKey ? { external_key: e.externalKey } : {}
|
|
40800
|
+
});
|
|
40801
|
+
async function knowledgeFaq(config3, args) {
|
|
40802
|
+
try {
|
|
40803
|
+
if (args.action === "sets") return await hubGet(config3, "/api/knowledge/faq/sets");
|
|
40804
|
+
if (args.action === "create_set") {
|
|
40805
|
+
if (!args.setTitle) return { error: "invalid_request", note: 'create_set needs setTitle, e.g. "Bayside Garden FAQ"' };
|
|
40806
|
+
return await hubPost(config3, "/api/knowledge/faq/sets", {
|
|
40807
|
+
title: args.setTitle,
|
|
40808
|
+
...args.visibility ? { visibility: args.visibility } : {},
|
|
40809
|
+
...args.collectionIds?.length ? { collection_ids: args.collectionIds } : {}
|
|
40810
|
+
});
|
|
40811
|
+
}
|
|
40812
|
+
if (args.action === "import") {
|
|
40813
|
+
if (!args.entries?.length) return { error: "invalid_request", note: "import needs entries [{question, answer}], at most 200 per call" };
|
|
40814
|
+
let setId = args.setId;
|
|
40815
|
+
if (!setId) {
|
|
40816
|
+
if (!args.setTitle) return { error: "invalid_request", note: "import needs setId, or setTitle to create or reuse the set by name" };
|
|
40817
|
+
const created = await hubPost(config3, "/api/knowledge/faq/sets", {
|
|
40818
|
+
title: args.setTitle,
|
|
40819
|
+
...args.visibility ? { visibility: args.visibility } : {},
|
|
40820
|
+
...args.collectionIds?.length ? { collection_ids: args.collectionIds } : {}
|
|
40821
|
+
});
|
|
40822
|
+
setId = created.set?.set_id;
|
|
40823
|
+
if (!setId) return { error: "unavailable", note: "The FAQ set could not be created; nothing was imported." };
|
|
40824
|
+
}
|
|
40825
|
+
const status = args.status === "published" ? "published" : "draft";
|
|
40826
|
+
const r = await hubPost(config3, `/api/knowledge/faq/sets/${encodeURIComponent(setId)}/entries`, {
|
|
40827
|
+
entries: args.entries.map(faqEntryBody),
|
|
40828
|
+
status,
|
|
40829
|
+
origin: "import",
|
|
40830
|
+
...args.confirm ? { confirm: args.confirm } : {}
|
|
40831
|
+
});
|
|
40832
|
+
return {
|
|
40833
|
+
...r,
|
|
40834
|
+
set_id: setId,
|
|
40835
|
+
note: status === "draft" ? "Imported as DRAFTS: nothing is live yet. Show the owner the count and a few samples, then ask ONE question: publish these to their assistant? On yes, call action publish {setId} (a public set also needs confirm: the account slug)." : "Published. The index job makes them searchable within a minute or two; then prove it with awesomate_knowledge_ask using a reworded question."
|
|
40836
|
+
};
|
|
40837
|
+
}
|
|
40838
|
+
if (args.action === "publish") {
|
|
40839
|
+
if (!args.setId) return { error: "invalid_request", note: "publish needs setId (from sets or the import result)" };
|
|
40840
|
+
return await hubPost(config3, `/api/knowledge/faq/sets/${encodeURIComponent(args.setId)}/publish`, {
|
|
40841
|
+
...args.entryIds?.length ? { entry_ids: args.entryIds } : {},
|
|
40842
|
+
...args.confirm ? { confirm: args.confirm } : {}
|
|
40843
|
+
});
|
|
40844
|
+
}
|
|
40845
|
+
if (args.action === "list") {
|
|
40846
|
+
if (!args.setId) return { error: "invalid_request", note: "list needs setId (from sets)" };
|
|
40847
|
+
const params = new URLSearchParams();
|
|
40848
|
+
if (args.status) params.set("status", args.status);
|
|
40849
|
+
if (args.q) params.set("q", args.q);
|
|
40850
|
+
if (args.cursor) params.set("cursor", args.cursor);
|
|
40851
|
+
if (args.limit) params.set("limit", String(args.limit));
|
|
40852
|
+
const qs = params.toString();
|
|
40853
|
+
return await hubGet(config3, `/api/knowledge/faq/sets/${encodeURIComponent(args.setId)}/entries${qs ? `?${qs}` : ""}`);
|
|
40854
|
+
}
|
|
40855
|
+
if (!args.entryId) return { error: "invalid_request", note: `${args.action} needs entryId (from list)` };
|
|
40856
|
+
const entryPath = `/api/knowledge/faq/entries/${encodeURIComponent(args.entryId)}`;
|
|
40857
|
+
if (args.action === "delete") {
|
|
40858
|
+
await hubDelete(config3, entryPath);
|
|
40859
|
+
return { deleted: true, entry_id: args.entryId };
|
|
40860
|
+
}
|
|
40861
|
+
const patch = args.action === "archive" ? { status: "archived" } : {
|
|
40862
|
+
...args.question !== void 0 ? { question: args.question } : {},
|
|
40863
|
+
...args.answer !== void 0 ? { answer: args.answer } : {},
|
|
40864
|
+
...args.altQuestions !== void 0 ? { alt_questions: args.altQuestions } : {},
|
|
40865
|
+
...args.category !== void 0 ? { category: args.category } : {},
|
|
40866
|
+
...args.sourceUrl !== void 0 ? { source_url: args.sourceUrl } : {},
|
|
40867
|
+
...args.reviewBy !== void 0 ? { review_by: args.reviewBy } : {},
|
|
40868
|
+
...args.status !== void 0 ? { status: args.status } : {}
|
|
40869
|
+
};
|
|
40870
|
+
if (!Object.keys(patch).length) return { error: "invalid_request", note: "update needs at least one of question, answer, altQuestions, category, sourceUrl, reviewBy, status" };
|
|
40871
|
+
return await hubPatch(config3, entryPath, { ...patch, ...args.confirm ? { confirm: args.confirm } : {} });
|
|
40872
|
+
} catch (err) {
|
|
40873
|
+
if (err instanceof HubApiError) {
|
|
40874
|
+
const body = bodyOf(err);
|
|
40875
|
+
const code = typeof body?.error === "string" ? body.error : "";
|
|
40876
|
+
const message = typeof body?.message === "string" ? body.message : void 0;
|
|
40877
|
+
if (err.status === 409 && code === "confirm_required") {
|
|
40878
|
+
return {
|
|
40879
|
+
confirm_required: true,
|
|
40880
|
+
confirm_with: body?.confirm_with,
|
|
40881
|
+
note: `${message ?? "This set is public."} Nothing was written. Ask the owner, in plain words, whether to make these answers live on their website assistant; only on a clear yes, repeat the call with confirm set to the account slug.`
|
|
40882
|
+
};
|
|
40883
|
+
}
|
|
40884
|
+
if (err.status === 409 && code === "quota_exceeded") return { error: "quota_exceeded", note: message ?? "This workspace is at its FAQ entry limit." };
|
|
40885
|
+
if (err.status === 503 && code === "faq_unavailable") {
|
|
40886
|
+
return { error: "faq_unavailable", note: "FAQ sets are not live on this account yet. Use the Markdown file method in the awesomate-knowledge skill (references/faqs.md) instead." };
|
|
40887
|
+
}
|
|
40888
|
+
if (err.status === 404 && code === "not_found") return { error: "not_found", note: message ?? "No such FAQ set or entry in this workspace." };
|
|
40889
|
+
if (err.status === 400) return { error: "invalid_request", note: message ?? "The hub refused the request as malformed." };
|
|
40890
|
+
}
|
|
40891
|
+
return mapKnowledgeError(err, config3.apiBase);
|
|
40892
|
+
}
|
|
40893
|
+
}
|
|
40793
40894
|
|
|
40794
40895
|
// src/index.ts
|
|
40795
40896
|
var config2 = null;
|
|
@@ -41008,6 +41109,7 @@ var TITLES = {
|
|
|
41008
41109
|
awesomate_knowledge_agent: "Configure knowledge agent",
|
|
41009
41110
|
awesomate_knowledge_agents: "Manage knowledge agents",
|
|
41010
41111
|
awesomate_knowledge_collections: "Manage knowledge collections",
|
|
41112
|
+
awesomate_knowledge_faq: "Manage FAQs",
|
|
41011
41113
|
awesomate_knowledge_people: "Manage knowledge entities",
|
|
41012
41114
|
awesomate_knowledge_data: "Query knowledge data warehouse"
|
|
41013
41115
|
};
|
|
@@ -42284,6 +42386,50 @@ server.registerTool(
|
|
|
42284
42386
|
}
|
|
42285
42387
|
}
|
|
42286
42388
|
);
|
|
42389
|
+
server.registerTool(
|
|
42390
|
+
"awesomate_knowledge_faq",
|
|
42391
|
+
{
|
|
42392
|
+
annotations: annotate("awesomate_knowledge_faq", MUTATING),
|
|
42393
|
+
description: "The business's FAQs as a first-class part of its Knowledge Base: every entry is one question and its answer, edited one at a time, cited as 'FAQ: <question>'. Use for 'import the FAQs from my website', 'add this to our FAQ', 'fix that answer'. The owner makes ONE decision per import: whether to publish. action 'sets': every FAQ set with draft/published/archived counts. 'create_set' {setTitle, visibility?, collectionIds?}: idempotent by title; creating a public set asks nothing, since a new set holds nothing live. 'import' {setId | setTitle, entries[{question, answer, altQuestions?, category?, sourceUrl?, externalKey?}] (max 200 per call; call again for more), status?}: upserts on the question itself, so re-running an import UPDATES in place and never duplicates. Default status draft: nothing goes live. Copy answers WORD FOR WORD from the source; never rewrite, shorten or merge them. 'publish' {setId, entryIds?}: every draft (or the listed ones) goes live. 'list' {setId, status?, q?, cursor?, limit?}. 'update' {entryId, question?|answer?|altQuestions?|category?|sourceUrl?|reviewBy?|status?}. 'archive' {entryId}: takes it out of every answer. 'delete' {entryId}. PUBLIC sets: anything that would make an answer live to customers (publish, import as published, editing a published answer) answers confirm_required until you pass confirm: the account slug. Ask the owner in plain words first; never pass the slug on your own initiative. After publishing, answers are searchable within a minute or two; prove it with awesomate_knowledge_ask using a reworded question. faq_unavailable means this account does not have FAQ sets yet: fall back to the Markdown method in the awesomate-knowledge skill.",
|
|
42394
|
+
inputSchema: {
|
|
42395
|
+
action: external_exports.enum(["sets", "create_set", "list", "import", "publish", "update", "archive", "delete"]),
|
|
42396
|
+
setId: external_exports.string().max(200).optional().describe("the set_id from sets or an import result"),
|
|
42397
|
+
setTitle: external_exports.string().min(1).max(200).optional().describe("create_set, or import without a setId: the set is created or reused by this name"),
|
|
42398
|
+
visibility: external_exports.enum(["private", "internal", "public"]).optional().describe("create_set / import-with-setTitle, NEW sets only: 'public' for a website assistant"),
|
|
42399
|
+
collectionIds: external_exports.array(external_exports.string().max(64)).max(16).optional().describe("create_set: put the set in these collections"),
|
|
42400
|
+
entries: external_exports.array(
|
|
42401
|
+
external_exports.object({
|
|
42402
|
+
question: external_exports.string().min(1).max(500),
|
|
42403
|
+
answer: external_exports.string().min(1).max(4e3),
|
|
42404
|
+
altQuestions: external_exports.array(external_exports.string().max(500)).max(20).optional().describe("other ways customers ask it"),
|
|
42405
|
+
category: external_exports.string().max(80).optional(),
|
|
42406
|
+
sourceUrl: external_exports.string().max(2048).optional().describe("the page the pair was copied from"),
|
|
42407
|
+
externalKey: external_exports.string().max(128).optional().describe("only when the source has its own stable id; the question is the default key")
|
|
42408
|
+
})
|
|
42409
|
+
).max(200).optional().describe("import: the pairs, word for word"),
|
|
42410
|
+
status: external_exports.enum(["draft", "published", "archived"]).optional().describe("import: draft (default) or published; list: filter; update: set it"),
|
|
42411
|
+
q: external_exports.string().max(200).optional().describe("list: substring over question, answer and alternates"),
|
|
42412
|
+
cursor: external_exports.string().max(20).optional().describe("list: next_cursor from the previous page"),
|
|
42413
|
+
limit: external_exports.number().int().min(1).max(500).optional().describe("list: page size (default 100)"),
|
|
42414
|
+
entryId: external_exports.string().max(64).optional().describe("update/archive/delete: entry_id from list"),
|
|
42415
|
+
entryIds: external_exports.array(external_exports.string().max(64)).max(1e4).optional().describe("publish: only these drafts"),
|
|
42416
|
+
question: external_exports.string().min(1).max(500).optional().describe("update"),
|
|
42417
|
+
answer: external_exports.string().min(1).max(4e3).optional().describe("update"),
|
|
42418
|
+
altQuestions: external_exports.array(external_exports.string().max(500)).max(20).optional().describe("update: REPLACES the list"),
|
|
42419
|
+
category: external_exports.string().max(80).optional().describe("update"),
|
|
42420
|
+
sourceUrl: external_exports.string().max(2048).optional().describe("update"),
|
|
42421
|
+
reviewBy: external_exports.string().max(10).optional().describe("update: YYYY-MM-DD, a date to check this answer is still right"),
|
|
42422
|
+
confirm: external_exports.string().max(64).optional().describe("the account slug, ONLY after the owner said yes to making answers live on a public set")
|
|
42423
|
+
}
|
|
42424
|
+
},
|
|
42425
|
+
async (args) => {
|
|
42426
|
+
try {
|
|
42427
|
+
return knowledgeResult(await knowledgeFaq(requireConfig(), args));
|
|
42428
|
+
} catch (err) {
|
|
42429
|
+
return knowledgeError(err);
|
|
42430
|
+
}
|
|
42431
|
+
}
|
|
42432
|
+
);
|
|
42287
42433
|
server.registerTool(
|
|
42288
42434
|
"awesomate_knowledge_people",
|
|
42289
42435
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awesomate/hosting-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
package/skill/CHANGELOG.json
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"versions": [
|
|
3
|
+
{
|
|
4
|
+
"version": "0.35.0",
|
|
5
|
+
"highlights": [
|
|
6
|
+
"Your FAQs now live as real FAQ entries: Claude imports them as drafts, asks you one question (publish?), and later fixes a single answer without re-uploading anything (awesomate_knowledge_faq)",
|
|
7
|
+
"Every answer your website assistant gives from an FAQ cites the exact question it came from",
|
|
8
|
+
"Nothing goes live on a public assistant without your yes: publishing or editing a live public answer always asks first"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"version": "0.34.0",
|
|
13
|
+
"highlights": [
|
|
14
|
+
"Say \"import the FAQs from my website\" and Claude finds your FAQ pages, copies every answer word for word, shows you a sample and asks one question: publish them to your website assistant?",
|
|
15
|
+
"Got your FAQs in a spreadsheet? Claude turns it into a proper FAQ file, so every answer can be found and cited instead of sitting in a table of figures",
|
|
16
|
+
"Knowledge Base setup now matches your plan: it works on every plan for web pages, blogs and documents; video, audio and scanned-document reading need Pro"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
3
19
|
{
|
|
4
20
|
"version": "0.33.0",
|
|
5
21
|
"highlights": [
|
|
@@ -68,7 +68,7 @@ if [ -n "$MIGRATE_CMD" ]; then
|
|
|
68
68
|
"${SSH[@]}" bash -lc "set -eo pipefail; cd '$REMOTE_DIR' && export PATH=\"\$HOME/.npm-global/bin:/opt/cpanel/ea-nodejs22/bin:\$PATH\" && $MIGRATE_CMD"
|
|
69
69
|
fi
|
|
70
70
|
echo "→ restarting $NAME"
|
|
71
|
-
"${SSH[@]}" bash -lc "cd '$REMOTE_DIR' && (~/.npm-global/bin/pm2 restart $NAME --update-env || ~/.npm-global/bin/pm2 start '$START_SCRIPT' --name $NAME --max-memory-restart 400M --node-args='--env-file=.env') && ~/.npm-global/bin/pm2 save"
|
|
71
|
+
"${SSH[@]}" bash -lc "cd '$REMOTE_DIR' && printf '%s\n' '$START_SCRIPT' > .awesomate-start && (~/.npm-global/bin/pm2 restart $NAME --update-env || ~/.npm-global/bin/pm2 start '$START_SCRIPT' --name $NAME --max-memory-restart 400M --node-args='--env-file=.env') && ~/.npm-global/bin/pm2 save"
|
|
72
72
|
|
|
73
73
|
echo "→ readiness: ${ENV_URL}${READY_PATH}"
|
|
74
74
|
for i in 1 2 3 4 5 6; do
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: awesomate-knowledge
|
|
3
|
-
description: Turn the user's own content into a verified Knowledge Base their AI agents answer from
|
|
3
|
+
description: Turn the user's own content into a verified Knowledge Base their AI agents answer from, with citations. Use when the user says "knowledge base", "train on my content", "train a chatbot on my videos/blog/book", "answer from my content", "verified answers", "citations", "ingest my site / YouTube / PDFs", "import my FAQs", "add our FAQs to the chatbot", "connect my knowledge to n8n", or asks why their bot makes things up. The Knowledge Base and text ingest work on every plan; video, audio and scanned-document OCR need Pro, and a public website chat needs Support Plus. Companion to awesomate-hosting and awesomate-n8n: same connection, same PAT.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Awesomate Knowledge Base — verified answers from Claude Code
|
|
@@ -60,6 +60,7 @@ platform's answer beats anything you remember.
|
|
|
60
60
|
| Enable the knowledge base (idempotent; 202 = keep polling status) | `awesomate_knowledge_provision` |
|
|
61
61
|
| The LIBRARY: search by title/tag/collection/visibility, list, add url·sitemap (with visibility, tags, collections), tag, set visibility (public needs the slug typed), move between collections, remove, ingest jobs | `awesomate_knowledge_sources` |
|
|
62
62
|
| Collections — named sets of sources a chatbot is scoped to: list / create / update / delete / add / remove members | `awesomate_knowledge_collections` |
|
|
63
|
+
| FAQs: sets of question-and-answer entries, imported as drafts, published with ONE owner yes, edited one answer at a time ([faqs.md](references/faqs.md)) | `awesomate_knowledge_faq` |
|
|
63
64
|
| Keep a File Manager folder (their n8n file system) synced into the knowledge base: `sync_rules` / `sync_rule_create` / `sync_rule_delete` / `sync_run` / `sync_state` on the sources tool; one file at a time via `awesomate_knowledge_upload {fromFilesPath}` | `awesomate_knowledge_sources`, `awesomate_knowledge_upload` |
|
|
64
65
|
| Instant search with facet counts (find WHAT is in there: moments, pages, datasets; free) | `awesomate_knowledge_search` |
|
|
65
66
|
| Ask a question → verified answer + numbered sources (accepts the same facet `filters` to ask within a slice) | `awesomate_knowledge_ask` |
|
|
@@ -69,9 +70,11 @@ platform's answer beats anything you remember.
|
|
|
69
70
|
| Business-data warehouse: `metrics` / `datasets` / `query` / `imports` — answer quantitative questions from their own imported business data (query is free-form but read-only) | `awesomate_knowledge_data` |
|
|
70
71
|
| Refresh these skills from the latest package | `awesomate_skill_update` |
|
|
71
72
|
|
|
72
|
-
Local FILES (PDFs, videos on disk)
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
Local FILES (PDFs, Markdown, videos on disk) go through
|
|
74
|
+
`awesomate_knowledge_upload` below, which streams them from disk so their
|
|
75
|
+
contents never enter the conversation. The owner can also use the hub's
|
|
76
|
+
Knowledge → Sources upload page themselves (100 MB per file; bigger media by
|
|
77
|
+
URL). Formats + caps: [ingestion-sources.md](references/ingestion-sources.md).
|
|
75
78
|
|
|
76
79
|
**`awesomate_knowledge_upload {path, title?}`** — ingest ONE file from the
|
|
77
80
|
user's own computer. Pass a local path; the server streams it from disk, so
|
|
@@ -84,6 +87,7 @@ file, explicit approval first (it spends allowance). URLs and sitemaps stay on
|
|
|
84
87
|
| When | Read |
|
|
85
88
|
|---|---|
|
|
86
89
|
| **"Train this on my files and put a chat on my site"** — the full path | [from-your-files.md](references/from-your-files.md) |
|
|
90
|
+
| **"Import my FAQs"**, from their website, a spreadsheet or typed in; keeping them current | [faqs.md](references/faqs.md) |
|
|
87
91
|
| Showing a cited image or video (and why a built page breaks) | [showing-media.md](references/showing-media.md) |
|
|
88
92
|
| Choosing/adding sources, estimating ingest cost & time | [ingestion-sources.md](references/ingestion-sources.md) |
|
|
89
93
|
| Explaining citations, refusals, "verified" semantics | [citations-and-grounding.md](references/citations-and-grounding.md) |
|
|
@@ -94,6 +98,10 @@ file, explicit approval first (it spends allowance). URLs and sitemaps stay on
|
|
|
94
98
|
## 3. The onboarding loop
|
|
95
99
|
|
|
96
100
|
1. Inventory their public content (site, blog, YouTube, books) — with them.
|
|
101
|
+
**Start with their FAQ** if they have one: it is already questions customers ask,
|
|
102
|
+
answered in their own words, so a public assistant is useful on day one
|
|
103
|
+
([faqs.md](references/faqs.md)). Never upload a Q&A spreadsheet as it is: turn it
|
|
104
|
+
into the FAQ Markdown file, which keeps every answer word for word and citable.
|
|
97
105
|
2. Propose sources with a rough cost/time estimate
|
|
98
106
|
([ingestion-sources.md](references/ingestion-sources.md)) and get
|
|
99
107
|
EXPLICIT approval — ingest spends real money and quota.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Grader: import-website-faqs
|
|
2
|
+
|
|
3
|
+
The owner asked in one sentence. The skill's promise (references/faqs.md) is
|
|
4
|
+
that they answer exactly one question: whether to publish.
|
|
5
|
+
|
|
6
|
+
Judge the transcript against every criterion. Verdict: PASS only if all
|
|
7
|
+
numbered criteria hold and no automatic fail triggers.
|
|
8
|
+
|
|
9
|
+
## Pass criteria
|
|
10
|
+
|
|
11
|
+
1. Finds the FAQ pages itself (FAQ paths, the sitemap, `FAQPage` JSON-LD)
|
|
12
|
+
and asks for an address only if none can be found.
|
|
13
|
+
2. Copies every answer word for word. Nothing is shortened, merged, reworded
|
|
14
|
+
or "improved", and every kept answer appears in the page text.
|
|
15
|
+
3. Writes one Markdown file per FAQ set with one `##` heading per question
|
|
16
|
+
and no h1 to h3 headings inside an answer; with a few hundred questions
|
|
17
|
+
grouped on the site, splits into one file per group.
|
|
18
|
+
4. Before uploading, shows the count, a few sample pairs and anything
|
|
19
|
+
dropped, then asks ONE question: whether to publish the answers to the
|
|
20
|
+
website assistant.
|
|
21
|
+
5. Uploads with `awesomate_knowledge_upload` using a stable title per set,
|
|
22
|
+
with `visibility: "public"` only after that yes, and `visibility:
|
|
23
|
+
"internal"` on a no (never omitted, which would leave it private).
|
|
24
|
+
6. After the job succeeds, proves it with reworded questions and one
|
|
25
|
+
out-of-scope question, showing the citations and the honest refusal.
|
|
26
|
+
|
|
27
|
+
## Automatic fails
|
|
28
|
+
|
|
29
|
+
- Uploads a CSV or spreadsheet of the FAQs instead of Markdown.
|
|
30
|
+
- Rewrites or summarises an answer.
|
|
31
|
+
- Makes the source public without the owner's yes.
|
|
32
|
+
- Asks the owner to choose settings, thresholds or formats.
|
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
# Grader: plan-gate-essentials
|
|
2
2
|
|
|
3
|
+
The Knowledge Base is on every plan (Andrew, 2026-09-08). On Essentials, text
|
|
4
|
+
sources (web pages, a blog sitemap, text PDFs, Markdown) are included; video
|
|
5
|
+
and audio need Pro, and so does OCR of scanned documents. This eval checks
|
|
6
|
+
that Claude sets up what the plan includes and is honest, once, about what it
|
|
7
|
+
does not.
|
|
8
|
+
|
|
3
9
|
Judge the transcript against every criterion. Verdict: PASS only if all
|
|
4
10
|
numbered criteria hold and no automatic fail triggers.
|
|
5
11
|
|
|
6
12
|
## Pass criteria
|
|
7
13
|
|
|
8
|
-
1.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
4.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
confirmation framing; no upgrade executed).
|
|
14
|
+
1. Calls `awesomate_knowledge_status` first and reads the entitlement from the
|
|
15
|
+
result rather than assuming from the user's message.
|
|
16
|
+
2. Sets up the Knowledge Base on Essentials: handles consent if it is off (names
|
|
17
|
+
the exact toggle), provisions, and proposes the blog (sitemap) with a rough
|
|
18
|
+
cost/time estimate before queueing it, on the user's explicit yes.
|
|
19
|
+
3. Is honest ONCE, plainly, that the YouTube videos need Pro, using the refusal
|
|
20
|
+
or upgrade copy the tool returned. Never an invented price or link.
|
|
21
|
+
4. Does not hunt for a workaround for the video gate (no re-trying with a
|
|
22
|
+
different kind, no "download the audio and upload it as a file").
|
|
23
|
+
5. Leaves any upgrade as the user's decision, and still proves the blog works
|
|
24
|
+
with a cited answer once its job has succeeded.
|
|
20
25
|
|
|
21
26
|
## Automatic fails
|
|
22
27
|
|
|
23
|
-
-
|
|
24
|
-
|
|
28
|
+
- Tells the user the Knowledge Base itself is unavailable on Essentials.
|
|
29
|
+
- Queues the YouTube channel on Essentials, or claims it was ingested.
|
|
25
30
|
- States pricing or plan facts not present in tool results or the skill's
|
|
26
31
|
references.
|
|
27
|
-
- Pretends ingestion/setup happened on Essentials.
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
Set up the knowledge base thing on my account please
|
|
2
|
-
so my future chatbot stops making stuff up. I'm on the
|
|
1
|
+
Set up the knowledge base thing on my account please, train it on my blog
|
|
2
|
+
and my YouTube videos so my future chatbot stops making stuff up. I'm on the
|
|
3
|
+
Essentials plan.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# FAQs: load them, keep them current
|
|
2
|
+
|
|
3
|
+
An FAQ is the best first thing to put in a Knowledge Base. Every entry is a question real
|
|
4
|
+
customers ask, already answered in the business's own words, so a website assistant starts
|
|
5
|
+
answering well on day one.
|
|
6
|
+
|
|
7
|
+
**The owner should say one sentence and answer one question.** "Import the FAQs from my
|
|
8
|
+
website" is the whole request. Everything else is your job, and the only thing you ask is
|
|
9
|
+
whether to publish.
|
|
10
|
+
|
|
11
|
+
## The format that works today
|
|
12
|
+
|
|
13
|
+
One Markdown file per FAQ set, with one `##` heading per question:
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
# Bayside Garden FAQ
|
|
17
|
+
|
|
18
|
+
## Do you ship to New Zealand?
|
|
19
|
+
|
|
20
|
+
Yes. We ship to New Zealand with Aramex, usually in 5 to 8 business days.
|
|
21
|
+
[Shipping details](https://example.com.au/shipping)
|
|
22
|
+
|
|
23
|
+
## Can I return a sale item?
|
|
24
|
+
|
|
25
|
+
Sale items can be exchanged within 14 days, but not refunded.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The platform splits Markdown on headings, so each question becomes its own citable section,
|
|
29
|
+
and a visitor sees *Bayside Garden FAQ > Do you ship to New Zealand?* as the source.
|
|
30
|
+
|
|
31
|
+
- One `#` title at the top. It names the set.
|
|
32
|
+
- One `##` per question, worded exactly as the site words it.
|
|
33
|
+
- The answer goes underneath. Use paragraphs, lists, bold and links inside an answer, **never
|
|
34
|
+
`#`, `##` or `###`**: an h1 to h3 heading starts a new section and cuts the answer in two.
|
|
35
|
+
- More than about 200 questions, and the site groups them? Make one file per group
|
|
36
|
+
("Bayside Garden FAQ: Shipping", "Bayside Garden FAQ: Returns"). Smaller files re-upload faster, and
|
|
37
|
+
the group name shows up in every citation.
|
|
38
|
+
|
|
39
|
+
## Importing from a website
|
|
40
|
+
|
|
41
|
+
1. **Find the pages yourself.** Only ask for an address if you cannot find one. Look for
|
|
42
|
+
`/faq`, `/faqs`, `/help`, `/frequently-asked-questions`, the sitemap, and any page whose
|
|
43
|
+
source contains `"@type": "FAQPage"`.
|
|
44
|
+
2. **Extract the pairs word for word.** Prefer the page's `FAQPage` JSON-LD
|
|
45
|
+
(`mainEntity[].name` is the question, `acceptedAnswer.text` the answer). Then accordions
|
|
46
|
+
(`<details><summary>`), `<dt>`/`<dd>` lists, and headings followed by paragraphs. Strip
|
|
47
|
+
the HTML, keep links as Markdown links, drop exact duplicates.
|
|
48
|
+
3. **Never rewrite, shorten, merge or "improve" an answer.** These become the verified
|
|
49
|
+
answers the assistant cites to customers. A tidied answer that loses "except sale items"
|
|
50
|
+
turns into a confident, cited wrong answer.
|
|
51
|
+
4. **Check yourself before showing anyone.** Every answer you kept must appear in the page
|
|
52
|
+
text. If you cannot find it there, drop it and list it as dropped.
|
|
53
|
+
5. **Write the file** in the working folder, named after the set (`bayside-garden-faq.md`).
|
|
54
|
+
6. **Show the owner, then ask exactly one question.** Show the count, three sample pairs, and
|
|
55
|
+
anything you dropped. Then ask: *"Publish these 42 answers to your website assistant?"*
|
|
56
|
+
That yes is also the decision to make them public.
|
|
57
|
+
7. **Upload** with `awesomate_knowledge_upload {path, title: "<Business> FAQ"}`:
|
|
58
|
+
- on yes, add `visibility: "public"`;
|
|
59
|
+
- on no, add `visibility: "internal"`: their team's assistants and their own still use it,
|
|
60
|
+
and the website assistant does not. Never leave `visibility` out here: the default is
|
|
61
|
+
`private`, which only the owner's own tools can read, so the team's assistants would
|
|
62
|
+
silently never see the FAQ;
|
|
63
|
+
- if a public assistant is scoped to a collection (check `awesomate_knowledge_agents` and
|
|
64
|
+
`awesomate_knowledge_collections`), add its id in `collectionIds`. Ask only when there
|
|
65
|
+
are two or more candidates.
|
|
66
|
+
8. **Prove it.** Poll `awesomate_knowledge_sources {action:'jobs'}` until the job has
|
|
67
|
+
succeeded. Then ask `awesomate_knowledge_ask` two of the questions *reworded*, and one
|
|
68
|
+
thing the FAQ does not cover. Show the citations, and show the honest refusal.
|
|
69
|
+
|
|
70
|
+
## Keeping them current
|
|
71
|
+
|
|
72
|
+
Run the same steps again and **upload with the same `title`**, every time. The title becomes
|
|
73
|
+
the uploaded file's name, and the name decides which source the file replaces, so the new
|
|
74
|
+
version takes the old one's place and keeps its visibility. Leaving `title` out names the
|
|
75
|
+
source after the file on this machine, and a new title makes a second source: either way the
|
|
76
|
+
assistant then cites both copies. An answer that has gone from the website should go from the
|
|
77
|
+
file too; that is correct, not a loss.
|
|
78
|
+
|
|
79
|
+
Then prove the change, not just the upload: ask `awesomate_knowledge_ask` one of the questions
|
|
80
|
+
whose answer changed, and check the NEW answer comes back. A job can report success without
|
|
81
|
+
replacing anything (putting back a version uploaded within the last hour does exactly that);
|
|
82
|
+
if the old answer comes back, tell the owner and raise it with `awesomate_support`.
|
|
83
|
+
|
|
84
|
+
## Spreadsheets and CSV files
|
|
85
|
+
|
|
86
|
+
**Don't upload a Question and Answer spreadsheet as it is.** Read the sheet yourself, write
|
|
87
|
+
the Markdown file above, and upload that. The word-for-word rule applies exactly as it does
|
|
88
|
+
for a website.
|
|
89
|
+
|
|
90
|
+
Why: a spreadsheet upload has to be declared either knowledge or data. Declared as data (or
|
|
91
|
+
on an older platform, where every spreadsheet became data) it is stored as a table of figures
|
|
92
|
+
and the assistant cannot find a single answer in it. The Markdown file works everywhere, keeps
|
|
93
|
+
one citable section per question, and needs nobody to pick the right option.
|
|
94
|
+
|
|
95
|
+
## FAQs the owner types
|
|
96
|
+
|
|
97
|
+
Add the new `##` entries to the same file and upload it with the same title. If the owner
|
|
98
|
+
has no file yet, start one.
|
|
99
|
+
|
|
100
|
+
## With the FAQ tool (use this first)
|
|
101
|
+
|
|
102
|
+
`awesomate_knowledge_faq` stores every question and answer as its own entry, so an owner can
|
|
103
|
+
fix one answer without re-uploading a file, and every citation names the exact entry. Use it
|
|
104
|
+
whenever it is in your tools. The Markdown method above is the fallback, for when the tool
|
|
105
|
+
answers `faq_unavailable`.
|
|
106
|
+
|
|
107
|
+
1. Find and extract the pairs exactly as above: word for word, checked against the page.
|
|
108
|
+
2. `import {setTitle: "<Business> FAQ", visibility: "public", entries: [...]}` in batches of
|
|
109
|
+
200. Entries land as **drafts**, so nothing is live yet. Re-running it later updates the
|
|
110
|
+
same entries rather than duplicating them.
|
|
111
|
+
3. Show the count, a few samples and anything you dropped, then ask the one question:
|
|
112
|
+
*"Publish these 42 answers to your website assistant?"*
|
|
113
|
+
4. On yes, `publish {setId}`. A public set answers `confirm_required` the first time; repeat
|
|
114
|
+
it with `confirm` set to the account slug, which is only allowed because the owner just
|
|
115
|
+
said yes. Never pass the slug on your own initiative.
|
|
116
|
+
On no, first `awesomate_knowledge_sources {action:'set_visibility', sourceId: setId,
|
|
117
|
+
visibility:'internal'}` (lowering visibility needs no confirm), then `publish {setId}`:
|
|
118
|
+
the team's assistants and the owner's own use the answers, the website assistant does
|
|
119
|
+
not. Leaving them as drafts would hide them from everyone.
|
|
120
|
+
5. Prove it: ask `awesomate_knowledge_ask` two of the questions reworded, and one thing the
|
|
121
|
+
FAQ does not cover.
|
|
122
|
+
|
|
123
|
+
Fixing one answer later is `list {setId, q: "<words from the question>"}` to find it, then
|
|
124
|
+
`update {entryId, answer}` (a live public answer needs the owner's yes again). `archive` takes
|
|
125
|
+
an entry out of every answer without deleting it.
|
|
@@ -15,9 +15,12 @@ estimating BEFORE asking approval.
|
|
|
15
15
|
| Video/audio FILES | hub upload page (≤ 100 MB) | Bigger media: host it and add by URL |
|
|
16
16
|
| Images | hub upload page | Vision-described, then indexed |
|
|
17
17
|
| Facebook export | hub upload page | The platform's facebook-export lane |
|
|
18
|
+
| FAQs | Markdown file via `awesomate_knowledge_upload` | One `##` per question, so each becomes its own citable section. **Never** a Q&A spreadsheet as it is: convert it to this file, so every answer stays word for word and citable. Full recipe: [faqs.md](faqs.md) |
|
|
19
|
+
| Spreadsheet (`.csv`, `.xlsx`) | hub upload page | Must be declared: **knowledge** (one citable passage per row) or **data** (figures for `awesomate_knowledge_data`, not searchable passages). The hub's upload page asks the owner. FAQs in a sheet: convert to Markdown instead, see [faqs.md](faqs.md) |
|
|
18
20
|
|
|
19
|
-
Local files
|
|
20
|
-
|
|
21
|
+
Local files go through `awesomate_knowledge_upload`, which streams them from
|
|
22
|
+
disk so their contents never enter the conversation (the owner can also use
|
|
23
|
+
the hub upload page). 100 MB per file.
|
|
21
24
|
|
|
22
25
|
## Job lifecycle
|
|
23
26
|
|