@weactive8/8contentpro 0.1.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.
Files changed (66) hide show
  1. package/README.md +33 -0
  2. package/dist/agent.js +115 -0
  3. package/dist/agent.js.map +1 -0
  4. package/dist/attach.js +144 -0
  5. package/dist/attach.js.map +1 -0
  6. package/dist/auth.js +49 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/brand.js +110 -0
  9. package/dist/brand.js.map +1 -0
  10. package/dist/brandgen.js +76 -0
  11. package/dist/brandgen.js.map +1 -0
  12. package/dist/build-info.js +3 -0
  13. package/dist/build-info.js.map +1 -0
  14. package/dist/detect.js +163 -0
  15. package/dist/detect.js.map +1 -0
  16. package/dist/index.js +334 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/ink/App.js +733 -0
  19. package/dist/ink/App.js.map +1 -0
  20. package/dist/ink/components.js +214 -0
  21. package/dist/ink/components.js.map +1 -0
  22. package/dist/ink/run.js +9 -0
  23. package/dist/ink/run.js.map +1 -0
  24. package/dist/intel.js +110 -0
  25. package/dist/intel.js.map +1 -0
  26. package/dist/login.js +99 -0
  27. package/dist/login.js.map +1 -0
  28. package/dist/metering.js +53 -0
  29. package/dist/metering.js.map +1 -0
  30. package/dist/methodology/loader.js +68 -0
  31. package/dist/methodology/loader.js.map +1 -0
  32. package/dist/models.js +24 -0
  33. package/dist/models.js.map +1 -0
  34. package/dist/onboarding/onboard.js +102 -0
  35. package/dist/onboarding/onboard.js.map +1 -0
  36. package/dist/onboarding/schema.js +25 -0
  37. package/dist/onboarding/schema.js.map +1 -0
  38. package/dist/onboarding/setup.js +324 -0
  39. package/dist/onboarding/setup.js.map +1 -0
  40. package/dist/output/callouts.js +81 -0
  41. package/dist/output/callouts.js.map +1 -0
  42. package/dist/output/docx.js +155 -0
  43. package/dist/output/docx.js.map +1 -0
  44. package/dist/output/pdf.js +195 -0
  45. package/dist/output/pdf.js.map +1 -0
  46. package/dist/pipeline.js +140 -0
  47. package/dist/pipeline.js.map +1 -0
  48. package/dist/plan/targeting.js +165 -0
  49. package/dist/plan/targeting.js.map +1 -0
  50. package/dist/shell.js +286 -0
  51. package/dist/shell.js.map +1 -0
  52. package/dist/site/resources.js +88 -0
  53. package/dist/site/resources.js.map +1 -0
  54. package/dist/site/sitemap.js +92 -0
  55. package/dist/site/sitemap.js.map +1 -0
  56. package/dist/store.js +110 -0
  57. package/dist/store.js.map +1 -0
  58. package/dist/topics.js +50 -0
  59. package/dist/topics.js.map +1 -0
  60. package/dist/ui/spinner.js +111 -0
  61. package/dist/ui/spinner.js.map +1 -0
  62. package/dist/ui/statusbar.js +90 -0
  63. package/dist/ui/statusbar.js.map +1 -0
  64. package/dist/update.js +45 -0
  65. package/dist/update.js.map +1 -0
  66. package/package.json +47 -0
@@ -0,0 +1,68 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, join } from "node:path";
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ /**
6
+ * The Blog Protocol IS the product. It is loaded verbatim as the agent's
7
+ * operating spec so a client gets the exact rigor WeActive8 gets — the quality
8
+ * is the methodology, not a per-tenant approximation of it.
9
+ *
10
+ * Ships in the package `methodology/` dir (see package.json "files").
11
+ */
12
+ /** A sentinel the gateway replaces with the real methodology for MANAGED customers. */
13
+ export const SENTINEL = "\u27E68CP-ENGINE-METHODOLOGY\u27E7";
14
+ /**
15
+ * The engine secret: Blog Protocol + operating procedure. Read from the methodology/ files,
16
+ * which are EXCLUDED from the published npm package (see package.json "files") — so a
17
+ * customer's installed CLI never contains the recipe. Present in the repo (local/dev and
18
+ * WeActive8's own build) and on the gateway (which injects it for managed customers via the
19
+ * SENTINEL). Returns null when absent -> caller emits the SENTINEL instead.
20
+ */
21
+ export function engineSecret() {
22
+ try {
23
+ const dir = join(__dirname, "..", "..", "methodology");
24
+ return readFileSync(join(dir, "BLOG-PROTOCOL.md"), "utf8") + "\n\n" + readFileSync(join(dir, "OPERATING.md"), "utf8");
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ /**
31
+ * Build the full system prompt: the Blog Protocol methodology, re-pointed from
32
+ * WeActive8 to the client's own ICP. The methodology's rigor is unchanged; only
33
+ * the brand context is theirs. When no profile is supplied (WeActive8's own
34
+ * instance), the protocol runs as-authored.
35
+ */
36
+ export function buildSystemPrompt(profile) {
37
+ const head = engineSecret() ?? SENTINEL; // secret when present (local/dev/gateway); sentinel in the published CLI
38
+ const operational = [];
39
+ if (profile) {
40
+ const personas = profile.personas
41
+ .map((p) => ` - ${p.name} (${p.role}) — pains: ${p.topPains.join(", ")}; triggers: ${p.buyingTriggers.join(", ")}`)
42
+ .join("\n");
43
+ const services = profile.services
44
+ .map((s) => ` - ${s.name}: ${s.description}${s.targetUrls?.length ? ` [links: ${s.targetUrls.join(", ")}]` : ""}`)
45
+ .join("\n");
46
+ operational.push("", "## THIS OPERATOR'S ICP (write for THIS brand, not WeActive8)", "", `- Brand: ${profile.brand}${profile.site ? ` (${profile.site})` : ""}`, `- Business: ${profile.summary}`, "- Personas (reference by name; every piece must serve one):", personas, "- Services (map internal links to these target URLs):", services, profile.voice ? `- Voice / copy rules: ${profile.voice}` : "", "", "Every keyword, angle, and internal link must serve THIS brand's buyers.");
47
+ if (profile.competitors?.length) {
48
+ const pol = profile.competitorPolicy ?? { link: "never", cite: "cite-data" };
49
+ operational.push("", "## COMPETITORS — name, don't promote (strict)", `This brand's competitors: ${profile.competitors.join(", ")}.`, "Rules when any of them comes up:", "- NEVER praise, recommend, or rave about a competitor. You may name them for an honest comparison,", " but the framing must favor THIS brand on the merits (never dishonestly).", pol.link === "never"
50
+ ? "- NEVER link to a competitor's website. Name them in plain text only. No hyperlink to their domain, ever."
51
+ : "- Do NOT link to a competitor's website unless it is genuinely unavoidable for credibility (rare). Default to naming them in plain text with no link.", pol.cite === "cite-data"
52
+ ? "- If you cite a competitor's published data/report, name the source in text but link the citation to a NEUTRAL third party (news, a study, an association) rather than the competitor's own site. Never send the reader to a competitor."
53
+ : "- Do NOT cite or reference a competitor's own material. Name them only for comparison; source any claims from neutral third parties.", "- Lead readers toward THIS brand's offering, not a competitor's.");
54
+ }
55
+ if (profile.sitePages?.length) {
56
+ operational.push("", "## INTERNAL LINKING — use these REAL pages (learned from the sitemap)", "Insert 2-4 contextual internal links to the MOST relevant pages below, using the exact URLs.", "Never invent an internal URL. Pick pages whose topic genuinely fits where you link.", ...profile.sitePages.slice(0, 60).map((p) => ` ${p.url}`));
57
+ }
58
+ if (profile.assetLibrary === "has" && profile.assets?.length) {
59
+ const norm = (u) => (u ? (/^https?:\/\//i.test(u) ? u : `https://${u}`) : "");
60
+ operational.push("", "## GATED ASSETS — offer the most relevant REAL one as a lead-magnet callout", "This operator has a resource library (real assets below, each with its [type] + download URL).", "Pick the SINGLE asset most relevant to THIS article and place ONE `> [!LEADMAGNET]` callout at", "the strongest conversion point. Structure it as EXACTLY three lines, each on its own line:", " 1. HEADLINE — the asset's name in BOLD, NOT a link: `**Title**`. Nothing else on this line.", " 2. PITCH — one catchy sentence that connects to the point in the paragraph right before/after it", " (contextual, not generic). Make the reader feel it answers exactly what they just read.", " 3. CTA BUTTON — this is the CLICKABLE button (the link goes HERE, not on the title). Write it as a", " SHORT punchy label (2 to 5 words) that is a markdown LINK to the asset URL, on its own line,", " prefixed with `→ `: `→ [Get the billing playbook](url)`. It's a button, not a sentence: no period,", " imperative, benefit-forward. VARY it every time — specific to this asset, this reader, this moment;", " never a boilerplate phrase. Match the verb to the asset type: Download/Get for a guide/ebook/", " checklist/report, View for an infographic, Watch for a webinar. Good labels: `Get the billing playbook`,", " `Download the AFA checklist`, `Watch the 12-minute walkthrough`. Bad (never use these generic ones):", " `Download now`, `Learn more`, `Click here`, `Get my guide`.", "Never invent an asset. Use one of these real ones (type in brackets), and put its URL on the CTA link:", ...profile.assets.map((a) => ` - [${a.topic || "guide"}] ${a.name}${a.url ? ` — ${norm(a.url)}` : ""}`), "Example (title plain-bold; the arrow-marked LINK button on the last line is what's clickable):", " > [!LEADMAGNET] **Master Your Billing Process**", " > The exact step-by-step system for closing the measurement gap this section describes.", " > → [Get the billing playbook](https://go.timesolv.com/guides/master-your-billing-process)");
61
+ }
62
+ else {
63
+ operational.push("", "## GATED-ASSET LEAD MAGNET (they have no asset for this topic yet — propose one)", "Where a downloadable lead magnet would naturally convert, insert ONE `> [!LEADMAGNET]` callout", "proposing a NEW downloadable that fits THIS post's exact topic. Structure it as EXACTLY three lines:", " 1. HEADLINE — the proposed asset's title in bold: `**The AFA Margin Checklist**` (no link — it doesn't exist yet).", " 2. PITCH — one catchy sentence tying it to what the reader just read.", " 3. CTA BUTTON — a SHORT punchy button label, 2-5 words, on its own line, prefixed with `→ `. Imperative,", " benefit-forward, no period, VARIED (never `→ Download now` / `→ Get my guide` / `→ Learn more`).", "Example:", " > [!LEADMAGNET] **The AFA Margin Checklist**", " > The 9 checks that catch a flat-fee matter bleeding money before it closes.", " > → Get the margin checklist", "Never fabricate a real download link (there is none yet). Place it once, at the strongest conversion point.");
64
+ }
65
+ }
66
+ return `${head}\n\n${operational.filter(Boolean).join("\n")}`;
67
+ }
68
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/methodology/loader.ts"],"names":[],"mappings":"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"}
package/dist/models.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Cheapest model that does each job WELL. The article is the paid output, so it
3
+ * gets the strong tier; everything around it runs on the cheapest capable model.
4
+ * Sitemap learning needs no model at all (pure code) and isn't listed here.
5
+ *
6
+ * Article default = Sonnet 5 (the value tier). Enterprise/premium runs pass
7
+ * `--model claude-opus-4-8` (measured cheaper AND better on our workload: fewer,
8
+ * more decisive turns). We stay on Claude across the board: the Agent SDK is
9
+ * Claude-native, and Claude's human-voice long-form is the whole differentiator
10
+ * (it's what passes our AI-detection panel). Gemini/GPT would weaken that edge.
11
+ */
12
+ export const MODELS = {
13
+ /** The paid output — value tier. Enterprise overrides to claude-opus-4-8. */
14
+ article: "claude-sonnet-5",
15
+ /** Editing an existing draft. */
16
+ revise: "claude-sonnet-5",
17
+ /** Onboarding interview — reasoning + reading files; persona quality matters. */
18
+ onboard: "claude-sonnet-5",
19
+ /** Ideation — research + creative angles. */
20
+ ideate: "claude-sonnet-5",
21
+ /** AI-detection panel — short classification; the cheapest capable model. */
22
+ detect: "claude-haiku-4-5",
23
+ };
24
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,6EAA6E;IAC7E,OAAO,EAAE,iBAAiB;IAC1B,iCAAiC;IACjC,MAAM,EAAE,iBAAiB;IACzB,iFAAiF;IACjF,OAAO,EAAE,iBAAiB;IAC1B,6CAA6C;IAC7C,MAAM,EAAE,iBAAiB;IACzB,6EAA6E;IAC7E,MAAM,EAAE,kBAAkB;CAClB,CAAC"}
@@ -0,0 +1,102 @@
1
+ import { query } from "@anthropic-ai/claude-agent-sdk";
2
+ import { createInterface } from "node:readline/promises";
3
+ import { stdin, stdout } from "node:process";
4
+ import { resolveModelAccess } from "../auth.js";
5
+ import { MODELS } from "../models.js";
6
+ import { isProfileComplete } from "./schema.js";
7
+ import { learnSitemap } from "../site/sitemap.js";
8
+ const INTERVIEWER = `You are the 8ContentPro onboarding interviewer. Your job is to
9
+ learn a business's ICP deeply enough to write content as them: their brand, what
10
+ they do and for whom, their buyer personas, their services, and their voice.
11
+
12
+ Conduct a natural interview:
13
+ - Ask ONE focused question at a time. Build on each answer; probe where it's thin.
14
+ - The operator may reference local files (a pitch deck PDF, brand guide, screenshots,
15
+ their website copy). When they do, use the Read tool to read that file and fold
16
+ what you learn into the profile — don't make them retype it.
17
+ - RESOURCE LIBRARY: ask whether they have downloadable gated assets (ebooks, guides,
18
+ checklists, templates). Capture each you learn (name, URL if known, topic). Set
19
+ assetLibrary to "has" if they have some, "none" if they explicitly don't (we'll offer
20
+ to generate them), or "unknown". If they don't know where they live, that's fine — "unknown".
21
+ - Keep going until you can confidently fill: brand, a one-paragraph business summary,
22
+ buyer personas (name, role, top pains, buying triggers, objections), at least 1
23
+ service (with target landing-page URL if known), and the brand voice / hard copy rules.
24
+ - PERSONAS: never settle for one. Draw out at LEAST 2 distinct personas; push for 4
25
+ for best results (e.g. the economic buyer, the champion, the skeptic, the end user).
26
+ Real firms serve more than one kind of buyer — surface them.
27
+
28
+ Output protocol — EVERY turn, respond with EXACTLY ONE of:
29
+ 1. Your next interview question as plain text (no preamble, no JSON), OR
30
+ 2. When and only when you have enough for a complete profile, output a single
31
+ fenced code block \`\`\`json ... \`\`\` containing the ClientProfile object and
32
+ nothing else. Shape:
33
+ {"version":1,"brand":"","site":"","summary":"","personas":[{"name":"","role":"",
34
+ "topPains":[],"buyingTriggers":[],"objections":[]}],"services":[{"name":"",
35
+ "description":"","targetUrls":[]}],"voice":"","sources":[],
36
+ "assetLibrary":"has|none|unknown","assets":[{"name":"","url":"","topic":""}]}
37
+
38
+ Do not output the JSON until the interview is genuinely sufficient.`;
39
+ const FENCE = /```json\s*([\s\S]*?)```/;
40
+ /** Run the conversational onboarding interview and return a complete profile. */
41
+ export async function onboard(model) {
42
+ resolveModelAccess(); // validates license / sets model env; throws with guidance
43
+ const rl = createInterface({ input: stdin, output: stdout });
44
+ const transcript = [];
45
+ stdout.write("\nLet's set up your content engine. Answer naturally — you can also\n");
46
+ stdout.write("point me at files (e.g. \"read ./pitch-deck.pdf\"). Type 'quit' to stop.\n\n");
47
+ try {
48
+ for (let turn = 0; turn < 40; turn++) {
49
+ const convo = transcript.map((m) => `${m.role.toUpperCase()}: ${m.text}`).join("\n\n");
50
+ const prompt = turn === 0
51
+ ? "Begin the onboarding interview. Greet briefly, then ask your first question."
52
+ : `${convo}\n\nContinue the interview: ask your next single question, or output the final profile JSON if you now have enough.`;
53
+ let out = "";
54
+ for await (const message of query({
55
+ prompt,
56
+ options: {
57
+ systemPrompt: INTERVIEWER,
58
+ model: model ?? MODELS.onboard,
59
+ allowedTools: ["Read", "Glob", "Grep"],
60
+ permissionMode: "acceptEdits",
61
+ },
62
+ })) {
63
+ if (message.type === "result" && message.subtype === "success")
64
+ out = message.result;
65
+ }
66
+ const fenced = out.match(FENCE);
67
+ if (fenced) {
68
+ const profile = JSON.parse(fenced[1]);
69
+ if (!isProfileComplete(profile)) {
70
+ transcript.push({ role: "assistant", text: out });
71
+ transcript.push({ role: "operator", text: "That profile looks incomplete — keep asking until it's solid." });
72
+ continue;
73
+ }
74
+ stdout.write("\nGot everything I need. Building your profile.\n");
75
+ if (profile.site) {
76
+ stdout.write("Learning your website from its sitemap for internal linking...\n");
77
+ try {
78
+ const { sitemapUrl, pages } = await learnSitemap(profile.site);
79
+ profile.sitemapUrl = sitemapUrl;
80
+ profile.sitePages = pages;
81
+ stdout.write(`Learned ${pages.length} pages.\n`);
82
+ }
83
+ catch (e) {
84
+ stdout.write(`(No sitemap read: ${e.message}. Internal linking will be limited until 'tune sitemap'.)\n`);
85
+ }
86
+ }
87
+ return profile;
88
+ }
89
+ stdout.write(`\n${out.trim()}\n`);
90
+ const answer = await rl.question("\n> ");
91
+ if (answer.trim().toLowerCase() === "quit")
92
+ throw new Error("Onboarding cancelled.");
93
+ transcript.push({ role: "assistant", text: out });
94
+ transcript.push({ role: "operator", text: answer });
95
+ }
96
+ throw new Error("Onboarding ran long without converging. Try again with more detail up front.");
97
+ }
98
+ finally {
99
+ rl.close();
100
+ }
101
+ }
102
+ //# sourceMappingURL=onboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../src/onboarding/onboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The ClientProfile is the client's identity in 8ContentPro — their ICP,
3
+ * personas, services, and voice. Built once by the AI onboarding interview,
4
+ * stored server-side on AX41 (8cp-postgres), and fetched by license on every
5
+ * run so the client's content is written for THEIR buyers, not generic ones.
6
+ *
7
+ * Mirrors what 8intel already collects (workspace_business_profiles /
8
+ * workspace_personas / workspace_services) so the two products share a shape.
9
+ */
10
+ /**
11
+ * Minimum bar before we consider a profile good enough to write from.
12
+ * RULE: never operate on a single persona. 2 is the floor; 4 is best-results.
13
+ */
14
+ export const PERSONA_MIN = 2;
15
+ export const PERSONA_BEST = 4;
16
+ export function isProfileComplete(p) {
17
+ return Boolean(p.version === 1 &&
18
+ p.brand &&
19
+ p.summary &&
20
+ p.personas &&
21
+ p.personas.length >= PERSONA_MIN &&
22
+ p.services &&
23
+ p.services.length >= 1);
24
+ }
25
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/onboarding/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkEH;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAC7B,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAE9B,MAAM,UAAU,iBAAiB,CAAC,CAAyB;IACzD,OAAO,OAAO,CACZ,CAAC,CAAC,OAAO,KAAK,CAAC;QACb,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,WAAW;QAChC,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,CACzB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,324 @@
1
+ import { stdout } from "node:process";
2
+ import { input } from "@inquirer/prompts";
3
+ import { query } from "@anthropic-ai/claude-agent-sdk";
4
+ import { resolveModelAccess } from "../auth.js";
5
+ import { MODELS } from "../models.js";
6
+ import { learnSitemap } from "../site/sitemap.js";
7
+ import { crawlResourceLibrary } from "../site/resources.js";
8
+ import { step, splash, clearScreen } from "../brand.js";
9
+ import { attachFile } from "../attach.js";
10
+ import { startSpinner, stopSpinner } from "../ui/spinner.js";
11
+ import { isProfileComplete } from "./schema.js";
12
+ const IMG_MEDIA = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp" };
13
+ /**
14
+ * Read an uploaded file with Claude's REAL vision/document capability and summarize it.
15
+ * WebFetch (the old approach) is for web PAGES — it never passes an image to the vision
16
+ * model or a PDF to the document reader, so it wrongly reported "no OCR" / "corrupted".
17
+ * We fetch the bytes from our upload server and pass them as an image/document content
18
+ * block through the Agent SDK (which holds the Max credential in local mode).
19
+ */
20
+ export async function summarizeAttachment(url) {
21
+ resolveModelAccess();
22
+ const ext = (url.split("?")[0].split(".").pop() || "").toLowerCase();
23
+ const isImg = ext in IMG_MEDIA;
24
+ const isPdf = ext === "pdf";
25
+ const isText = ["txt", "md", "csv", "json"].includes(ext);
26
+ let block;
27
+ try {
28
+ if (isImg || isPdf) {
29
+ const data = Buffer.from(await (await fetch(url)).arrayBuffer()).toString("base64");
30
+ block = isImg
31
+ ? { type: "image", source: { type: "base64", media_type: IMG_MEDIA[ext], data } }
32
+ : { type: "document", source: { type: "base64", media_type: "application/pdf", data } };
33
+ }
34
+ else if (isText) {
35
+ const txt = (await (await fetch(url)).text()).slice(0, 6000);
36
+ block = { type: "text", text: `File contents:\n${txt}` };
37
+ }
38
+ else {
39
+ return "(stored for context; this file type can't be previewed inline)";
40
+ }
41
+ }
42
+ catch (e) {
43
+ return `(couldn't fetch it: ${e.message})`;
44
+ }
45
+ const ask = "Look at this uploaded file and tell me, in one or two sentences, exactly what it contains. For an image: describe what you see and read any visible text. For a PDF or text: summarize the actual content. Be specific and concrete.";
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ const prompt = (async function* () {
48
+ yield { type: "user", message: { role: "user", content: [{ type: "text", text: ask }, block] }, parent_tool_use_id: null };
49
+ })();
50
+ let out = "";
51
+ for await (const m of query({
52
+ prompt,
53
+ options: { systemPrompt: "You briefly and accurately summarize an uploaded file or image so the user knows it was read.", model: MODELS.detect, allowedTools: [], permissionMode: "acceptEdits", maxTurns: 1 },
54
+ })) {
55
+ if (m.type === "result" && m.subtype === "success")
56
+ out = m.result;
57
+ }
58
+ return out.trim().replace(/\s+/g, " ").slice(0, 340) || "(couldn't read it)";
59
+ }
60
+ /** Looks like a local file path the user expects us to read (we never touch the disk). */
61
+ function looksLikeLocalPath(v) {
62
+ if (/^https?:\/\//i.test(v) || v.includes(" "))
63
+ return false;
64
+ return /^(~|\/|[A-Za-z]:\\)/.test(v) || /\.(png|jpe?g|gif|webp|pdf|docx?|txt|csv|pptx?)$/i.test(v);
65
+ }
66
+ /** One of OUR upload links (the browser-upload page), not a real answer. */
67
+ function isUploadUrl(v) {
68
+ try {
69
+ const host = new URL(v).host;
70
+ const intelHost = new URL(process.env.CONTENTPRO_INTEL_URL || "https://8cp-upload.wa8.us").host;
71
+ return /(^|\.)8cp-upload\.wa8\.us$/i.test(host) || host === intelHost;
72
+ }
73
+ catch {
74
+ return false;
75
+ }
76
+ }
77
+ const QUESTIONS = [
78
+ { key: "brand", q: "What's your brand or company name?", required: true },
79
+ {
80
+ key: "site",
81
+ q: "Your website URL? (so I can learn your pages for internal linking)",
82
+ required: true,
83
+ hint: "e.g. https://yourcompany.com",
84
+ validate: (v) => (/[a-z0-9-]+\.[a-z]{2,}/i.test(v) ? null : "That doesn't look like a website. Include a domain, e.g. https://yourcompany.com"),
85
+ },
86
+ { key: "summary", q: "In a sentence or two: what do you do, and for whom?", required: true },
87
+ { key: "buyers", q: "Who are your buyers? Name the different types (decision-maker, champion, end user) and what each cares about.", required: true },
88
+ { key: "services", q: "What are your main services or products?", required: true },
89
+ { key: "voice", q: "Any brand voice or hard copy rules? (e.g. no em-dashes, formal vs casual). Press enter to skip.", required: false },
90
+ {
91
+ key: "assets",
92
+ q: "Downloadable gated assets (ebooks, guides, checklists)? Name them or paste URLs.",
93
+ required: true,
94
+ hint: "type 'none' if you don't have any",
95
+ },
96
+ {
97
+ key: "competitors",
98
+ q: "Who are your main competitors? I'll name them for comparison but never praise or link to them.",
99
+ required: false,
100
+ hint: "comma-separated, or 'none'",
101
+ },
102
+ ];
103
+ const SYNTH_SYSTEM = `You turn raw onboarding answers into a structured ClientProfile JSON.
104
+ Derive 2-4 DISTINCT buyer personas from the "buyers" answer (name, role, topPains[],
105
+ buyingTriggers[], objections[]) — never fewer than 2. Structure the services. Map the
106
+ assets answer to assetLibrary ("has" if any named, "none" if they said none, else
107
+ "unknown") and an assets[] list. Keep the client's own voice rules verbatim.
108
+ Output ONLY a fenced code block: \`\`\`json {the ClientProfile} \`\`\` and nothing else.`;
109
+ const FENCE = /```json\s*([\s\S]*?)```/;
110
+ /**
111
+ * Run the scripted interview, synthesize once (cheap), learn the sitemap.
112
+ * Takes the shared readline interface — never creates or closes its own (doing so
113
+ * would tear down the shell's input stream, causing double-echo / use-after-close).
114
+ */
115
+ export async function runSetup(onLog) {
116
+ resolveModelAccess();
117
+ const answers = {};
118
+ const attachments = [];
119
+ const files = []; // attached this session, referenceable by URL
120
+ stdout.write(step("Type 'attach' to upload a file/image for context, 'back' to fix the last answer, 'restart' to start over, 'quit' to cancel.") + "\n");
121
+ let i = 0;
122
+ while (i < QUESTIONS.length) {
123
+ const q = QUESTIONS[i];
124
+ const raw = (await input({ message: `[${i + 1}/${QUESTIONS.length}] ${q.q}`, default: answers[q.key] })).trim();
125
+ const low = raw.toLowerCase();
126
+ if (low === "back" || low === "b") {
127
+ if (i > 0)
128
+ i--;
129
+ else
130
+ stdout.write(step("You're on the first question already.") + "\n");
131
+ continue;
132
+ }
133
+ if (low === "restart") {
134
+ for (const k of Object.keys(answers))
135
+ delete answers[k];
136
+ i = 0;
137
+ clearScreen();
138
+ stdout.write(splash());
139
+ stdout.write(step("Starting fresh.") + "\n");
140
+ continue;
141
+ }
142
+ if (low === "quit" || low === "exit")
143
+ throw new Error("Setup cancelled.");
144
+ // Upload a file/image for context — Claude reads it and acknowledges what it saw.
145
+ if (low === "attach" || low === "upload") {
146
+ const atts = await attachFile((l) => stdout.write(step(l) + "\n"));
147
+ for (const att of atts) {
148
+ startSpinner(`Reading "${att.filename}"…`);
149
+ const summary = await summarizeAttachment(att.fileUrl);
150
+ stopSpinner();
151
+ stdout.write(step(`Got it — I read "${att.filename}": ${summary}`) + "\n");
152
+ stdout.write(step(` URL (paste into any answer to reference it): ${att.fileUrl}`) + "\n");
153
+ attachments.push(`${att.filename}: ${summary}`);
154
+ files.push({ filename: att.filename, url: att.fileUrl });
155
+ }
156
+ if (files.length)
157
+ stdout.write(step("Type 'files' any time to list every attached file and its URL.") + "\n");
158
+ continue; // stay on the same question
159
+ }
160
+ // List attached files + URLs so they can be referenced (now or in a later step).
161
+ if (low === "files") {
162
+ if (!files.length)
163
+ stdout.write(step("No files attached yet — type 'attach' to add one.") + "\n");
164
+ else {
165
+ stdout.write(step("Attached files (paste a URL into any answer, and add your own text around it):") + "\n");
166
+ files.forEach((f, i) => stdout.write(step(` [${i + 1}] ${f.filename} → ${f.url}`) + "\n"));
167
+ }
168
+ continue;
169
+ }
170
+ // Pasted one of our upload links as an answer? Don't store the raw URL — it would
171
+ // pollute the field. The uploaded file is read via 'attach', not by pasting a link.
172
+ if (isUploadUrl(raw)) {
173
+ stdout.write(step(attachments.length
174
+ ? "That's just the upload link on its own — the file is already read and included. To REFERENCE it inside an answer, add your own text around the URL, e.g. 'For pricing, use this: <url>'. Type 'files' to see URLs."
175
+ : "That's an upload link, not the file itself. Type 'attach' to upload it and I'll read it — pasting the link alone won't work.") + "\n");
176
+ continue; // a bare URL alone isn't a useful answer; a URL WITH text passes through
177
+ }
178
+ // Pasted a local file path? We never read the disk — point them to 'attach'.
179
+ if (looksLikeLocalPath(raw)) {
180
+ stdout.write(step("I can't read files from your computer — I never access your machine. Type 'attach' to upload it securely and I'll read it.") + "\n");
181
+ continue;
182
+ }
183
+ // Empty on a required question → re-ask, don't advance.
184
+ if (!raw && q.required) {
185
+ stdout.write(step(`This one's needed${q.hint ? ` — ${q.hint}` : ""}. (Or type 'back' to change a previous answer.)`) + "\n");
186
+ continue;
187
+ }
188
+ // Format check (e.g. the URL).
189
+ if (raw && q.validate) {
190
+ const err = q.validate(raw);
191
+ if (err) {
192
+ stdout.write(step(err) + "\n");
193
+ continue;
194
+ }
195
+ }
196
+ answers[q.key] = raw;
197
+ i++;
198
+ }
199
+ return synthesizeProfile(answers, attachments, onLog);
200
+ }
201
+ /** The scripted questions, exported so any front-end (readline or Ink) can drive them. */
202
+ export { QUESTIONS };
203
+ /**
204
+ * Turn collected raw answers (+ read attachments) into a structured ClientProfile:
205
+ * one cheap Haiku synthesis, then learn the sitemap (free). Front-end-agnostic — the
206
+ * readline loop AND the Ink app both call this once they've gathered answers.
207
+ */
208
+ export async function synthesizeProfile(answers, attachments, onLog) {
209
+ resolveModelAccess();
210
+ onLog("Building your profile (one quick AI pass)...");
211
+ const prompt = "Raw answers:\n" +
212
+ Object.entries(answers)
213
+ .map(([k, v]) => `${k}: ${v}`)
214
+ .join("\n") +
215
+ (attachments.length ? "\n\nContext from uploaded files (fold into the profile):\n" + attachments.join("\n") : "") +
216
+ '\n\nShape: {"version":1,"brand":"","site":"","summary":"","personas":[{"name":"","role":"","topPains":[],"buyingTriggers":[],"objections":[]}],"services":[{"name":"","description":"","targetUrls":[]}],"voice":"","assetLibrary":"has|none|unknown","assets":[{"name":"","url":"","topic":""}]}';
217
+ let out = "";
218
+ for await (const m of query({
219
+ prompt,
220
+ options: { systemPrompt: SYNTH_SYSTEM, model: MODELS.detect, allowedTools: [], permissionMode: "acceptEdits" },
221
+ })) {
222
+ if (m.type === "result" && m.subtype === "success")
223
+ out = m.result;
224
+ }
225
+ const fenced = out.match(FENCE);
226
+ if (!fenced)
227
+ throw new Error("Could not build your profile — please try again.");
228
+ const profile = JSON.parse(fenced[1]);
229
+ if (!isProfileComplete(profile))
230
+ throw new Error("Profile came back incomplete — please run setup again with a bit more detail.");
231
+ if (profile.site) {
232
+ onLog("Learning your website from its sitemap for internal linking (no AI, free)...");
233
+ try {
234
+ const { sitemapUrl, pages } = await learnSitemap(profile.site);
235
+ profile.sitemapUrl = sitemapUrl;
236
+ profile.sitePages = pages;
237
+ onLog(`Learned ${pages.length} pages.`);
238
+ }
239
+ catch (e) {
240
+ onLog(`(No sitemap read: ${e.message}. Internal linking limited until 'tune sitemap'.)`);
241
+ }
242
+ }
243
+ // If they pointed us at a resource-library page, crawl it for the INDIVIDUAL guides
244
+ // (the real lead-magnets + their download links) — not just the category page.
245
+ const libUrl = (answers.assets || "").match(/\b(?:https?:\/\/)?[a-z0-9-]+(?:\.[a-z0-9-]+)+\/\S*/i)?.[0];
246
+ if (libUrl && !/^none$/i.test((answers.assets || "").trim())) {
247
+ onLog("Crawling your resource library for downloadable guides...");
248
+ try {
249
+ const guides = await crawlResourceLibrary(libUrl);
250
+ if (guides.length) {
251
+ profile.assets = guides;
252
+ profile.assetLibrary = "has";
253
+ onLog(`Found ${guides.length} downloadable guides to link as lead magnets: ${guides.slice(0, 3).map((g) => g.name).join(" · ")}${guides.length > 3 ? " …" : ""}`);
254
+ }
255
+ }
256
+ catch {
257
+ /* keep whatever the synthesis produced */
258
+ }
259
+ }
260
+ // Competitors — parsed deterministically (never AI-guessed), with the safe default policy:
261
+ // name them for comparison, never link to their site, cite their public data by name.
262
+ const compRaw = (answers.competitors || "").trim();
263
+ if (compRaw && !/^none$/i.test(compRaw)) {
264
+ profile.competitors = compRaw
265
+ .split(/[,\n;]+/)
266
+ .map((c) => c.trim())
267
+ .filter(Boolean)
268
+ .slice(0, 12);
269
+ profile.competitorPolicy = profile.competitorPolicy ?? { link: "never", cite: "cite-data" };
270
+ if (profile.competitors.length)
271
+ onLog(`Noted ${profile.competitors.length} competitors — I'll name them for comparison, never praise or link to them.`);
272
+ }
273
+ onLog(`Built ${profile.personas.length} personas: ${profile.personas.map((p) => `${p.name} (${p.role})`).join(" · ")}`);
274
+ onLog("Refine any single piece later from the menu — no need to redo everything.");
275
+ return profile;
276
+ }
277
+ /** One targeted cheap synthesis (Haiku) for a single facet. */
278
+ async function synth(system, prompt) {
279
+ let out = "";
280
+ for await (const m of query({ prompt, options: { systemPrompt: system, model: MODELS.detect, allowedTools: [], permissionMode: "acceptEdits" } })) {
281
+ if (m.type === "result" && m.subtype === "success")
282
+ out = m.result;
283
+ }
284
+ const f = out.match(FENCE);
285
+ if (!f)
286
+ throw new Error("Couldn't process that — please try again.");
287
+ return JSON.parse(f[1]);
288
+ }
289
+ /** Re-crawl the sitemap only. No re-interview, no AI. */
290
+ export async function tuneSitemap(profile, onLog) {
291
+ const src = profile.sitemapUrl ?? profile.site;
292
+ if (!src)
293
+ throw new Error("No website on file — do a full setup first.");
294
+ onLog(`Re-crawling ${src} for internal-link pages...`);
295
+ const { sitemapUrl, pages } = await learnSitemap(src);
296
+ onLog(`Learned ${pages.length} pages.`);
297
+ return { ...profile, sitemapUrl, sitePages: pages };
298
+ }
299
+ /** Redo personas only (one question + one cheap pass). */
300
+ export async function tunePersonas(profile, onLog) {
301
+ const buyers = (await input({ message: "Who are your buyers? Name the different types and what each one cares about." })).trim();
302
+ if (!buyers)
303
+ throw new Error("Nothing entered.");
304
+ onLog("Rebuilding personas...");
305
+ const r = await synth('Turn this into 2-4 DISTINCT buyer personas. Never fewer than 2. Output ONLY ```json {"personas":[{"name":"","role":"","topPains":[],"buyingTriggers":[],"objections":[]}]} ```', "Buyers: " + buyers);
306
+ onLog(`Built ${r.personas.length} personas: ${r.personas.map((p) => p.name).join(", ")}`);
307
+ return { ...profile, personas: r.personas };
308
+ }
309
+ /** Redo services only. */
310
+ export async function tuneServices(profile, onLog) {
311
+ const s = (await input({ message: "What are your main services or products?" })).trim();
312
+ if (!s)
313
+ throw new Error("Nothing entered.");
314
+ onLog("Updating services...");
315
+ const r = await synth('Turn this into a services list. Output ONLY ```json {"services":[{"name":"","description":"","targetUrls":[]}]} ```', "Services: " + s);
316
+ onLog(`Updated ${r.services.length} services.`);
317
+ return { ...profile, services: r.services };
318
+ }
319
+ /** Update brand voice only (no AI). */
320
+ export async function tuneVoice(profile) {
321
+ const v = (await input({ message: "Brand voice / hard copy rules? (e.g. no em-dashes, formal vs casual)", default: profile.voice })).trim();
322
+ return { ...profile, voice: v || profile.voice };
323
+ }
324
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/onboarding/setup.ts"],"names":[],"mappings":"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"}