Fix CSS layout width and add more user friendly messages
This commit is contained in:
@@ -128,7 +128,7 @@ Return a JSON object with EXACTLY these fields:
|
||||
- "repo_relative_path": a STRING — the path of the target section file from the manifest (e.g. "content/sections/hero.json"). NEVER null or empty.
|
||||
- "needs_clarification": a boolean — true only if the request is genuinely ambiguous between multiple sections
|
||||
- "reason": a short string explaining the routing decision
|
||||
- "clarification_message": a string (only when needs_clarification is true) — a question to ask the user
|
||||
- "clarification_message": a string (only when needs_clarification is true) — a friendly question to ask the user, referring to sections by their display names (e.g. "Our Story", "What We Offer") not filenames
|
||||
|
||||
Rules:
|
||||
- "repo_relative_path" MUST be one of the paths listed in the MANIFEST. Copy it exactly.
|
||||
@@ -208,12 +208,16 @@ export async function generateInfoResponse(params: GenerateInfoResponseParams, c
|
||||
const messages = [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are a helpful assistant for a website owner. They asked a question about their website's content.
|
||||
content: `You are a friendly assistant helping a small business owner manage their website via text message.
|
||||
|
||||
Answer their question based on the MANIFEST of sections below. Keep your response under 300 characters (it will be sent via SMS). Be concise and specific. No markdown, just plain text.
|
||||
They asked a question about their website. Answer based on the MANIFEST below.
|
||||
|
||||
If they ask what's on their site, list the visible sections briefly.
|
||||
If they ask about a specific section, describe its content briefly.`,
|
||||
Rules:
|
||||
- Keep your response under 300 characters (this is an SMS).
|
||||
- Use plain, conversational language — like you're texting a friend.
|
||||
- Refer to sections by their display names (e.g. "your About section", "the hero banner"), never by filenames or technical terms.
|
||||
- If a section is hidden (visible: false), mention it's currently hidden from visitors.
|
||||
- Be warm and helpful.`,
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
@@ -228,11 +232,11 @@ If they ask about a specific section, describe its content briefly.`,
|
||||
// Fallback: generate a basic listing from the manifest
|
||||
const visibleSections = params.manifest.filter(m => m.visible);
|
||||
const sectionNames = visibleSections.map(m => m.title || m.headline || m.heading || m.id).join(', ');
|
||||
return `Your site has these sections: ${sectionNames}`;
|
||||
return `Your site currently shows: ${sectionNames}. Want to change anything?`;
|
||||
}
|
||||
}
|
||||
|
||||
/** Simple summary generation (no schema validation needed) */
|
||||
/** Friendly summary generation for SMS confirmations */
|
||||
export async function generateSummary(params: {
|
||||
before: unknown;
|
||||
after: unknown;
|
||||
@@ -244,11 +248,29 @@ export async function generateSummary(params: {
|
||||
const messages = [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You summarize content changes for a website owner. Keep summaries under 140 characters, plain text, no markdown. Be specific about what changed. Format: "Change X from A to B" or "Add/remove X".`,
|
||||
content: `You write short, friendly summaries of website changes for a small business owner who manages their site via text message.
|
||||
|
||||
Rules:
|
||||
- Keep it under 140 characters.
|
||||
- Describe what visitors will SEE on the website, not what changed in the data.
|
||||
- Use plain, everyday language. No technical terms, no field names, no file paths, no JSON jargon.
|
||||
- Write in a warm, conversational tone — like texting a friend.
|
||||
|
||||
Good examples:
|
||||
- "Show the promo banner on your site"
|
||||
- "Update your main headline to 'Welcome Home'"
|
||||
- "Hide the testimonials section from visitors"
|
||||
- "Add a new event: Wine Tasting on June 15"
|
||||
- "Change the About section text"
|
||||
|
||||
Bad examples (too technical — never do this):
|
||||
- "Change promo-banner visible from false to true"
|
||||
- "Update hero.headline field"
|
||||
- "Modify content/sections/about.json"`,
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `File: ${params.repoRelativePath}\nRequest: "${params.userMessage}"\n\nBefore:\n${JSON.stringify(params.before, null, 2)}\n\nAfter:\n${JSON.stringify(params.after, null, 2)}\n\nSummarize the change in under 140 chars:`,
|
||||
content: `The owner asked: "${params.userMessage}"\n\nBefore:\n${JSON.stringify(params.before, null, 2)}\n\nAfter:\n${JSON.stringify(params.after, null, 2)}\n\nWrite a friendly summary of what this change does to their website:`,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -256,7 +278,7 @@ export async function generateSummary(params: {
|
||||
const result = await chat(messages, PRIMARY_MODEL);
|
||||
return result.replace(/["'`]/g, '').trim().slice(0, 280);
|
||||
} catch {
|
||||
// Fallback: generate a basic diff summary
|
||||
return `Update ${params.repoRelativePath} as requested: "${params.userMessage.slice(0, 80)}"`;
|
||||
// Fallback: echo back the user's original request
|
||||
return `${params.userMessage.charAt(0).toUpperCase() + params.userMessage.slice(1, 100)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,23 @@ import { schemaForRepoRelativePath } from '@dynamic-sites/shared';
|
||||
import { createProposal, getProposal, updateProposalStatus } from '../db.js';
|
||||
import { writeContentFile } from '../io/write-content.js';
|
||||
import { generateEditedJson, routeEditIntent, generateSummary, classifyMessageIntent, generateInfoResponse } from '../llm/client.js';
|
||||
import { buildSectionManifest } from './manifest.js';
|
||||
import { buildSectionManifest, type ManifestEntry } from './manifest.js';
|
||||
import { sendSms } from '../sms/reply.js';
|
||||
import { SMS_TEMPLATES } from '../sms/templates.js';
|
||||
import { logger } from '../logger.js';
|
||||
|
||||
const REPO_ROOT = process.env.REPO_ROOT || '.';
|
||||
|
||||
/** Get a friendly display name for a manifest entry. */
|
||||
function sectionDisplayName(m: ManifestEntry): string {
|
||||
return m.title || m.headline || m.heading || m.id;
|
||||
}
|
||||
|
||||
/** Get a comma-separated list of friendly section names from a manifest. */
|
||||
function sectionNameList(manifest: ManifestEntry[]): string {
|
||||
return manifest.map(sectionDisplayName).join(', ');
|
||||
}
|
||||
|
||||
/**
|
||||
* In-memory map from job ID → proposal ID.
|
||||
* Used by the HTTP API to let the editor poll for a proposal created by a queued job.
|
||||
@@ -80,7 +90,7 @@ async function handlePropose(job: Extract<EditJobPayload, { kind: 'propose' }>)
|
||||
log.info({ event: 'routing.ambiguous' }, 'Routing ambiguous');
|
||||
if (job.smsReplyMeta) {
|
||||
await sendSms(job.smsReplyMeta.from, job.smsReplyMeta.to,
|
||||
routing.clarification_message || SMS_TEMPLATES.ROUTING_AMBIGUOUS(manifest.map(m => m.id).join(', '))
|
||||
routing.clarification_message || SMS_TEMPLATES.ROUTING_AMBIGUOUS(sectionNameList(manifest))
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -96,7 +106,8 @@ async function handlePropose(job: Extract<EditJobPayload, { kind: 'propose' }>)
|
||||
if (!fs.existsSync(absPath)) {
|
||||
log.error({ event: 'propose.file_not_found', path: repoRelativePath }, 'Target file not found');
|
||||
if (job.smsReplyMeta) {
|
||||
await sendSms(job.smsReplyMeta.from, job.smsReplyMeta.to, SMS_TEMPLATES.ROUTING_NO_MATCH(repoRelativePath));
|
||||
const manifest = buildSectionManifest();
|
||||
await sendSms(job.smsReplyMeta.from, job.smsReplyMeta.to, SMS_TEMPLATES.ROUTING_NO_MATCH(sectionNameList(manifest)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
export const SMS_TEMPLATES = {
|
||||
PROPOSAL_SUMMARY: (summary: string, _proposalId: string) =>
|
||||
`Proposed change: ${summary}\n\nReply YES to apply or NO to cancel.`,
|
||||
`I'd like to: ${summary}\n\nReply YES to apply or NO to cancel.`,
|
||||
|
||||
APPLIED: (summary: string) =>
|
||||
`Done! ${summary} Your site will update shortly.`,
|
||||
`Done! ${summary} Your site will update in a moment.`,
|
||||
|
||||
REJECTED: () =>
|
||||
`Got it — change cancelled. Send a new message anytime.`,
|
||||
`No problem — I cancelled that change. Just text me whenever you'd like to make an edit!`,
|
||||
|
||||
LLM_UNAVAILABLE: () =>
|
||||
`Sorry, I couldn't process that right now. Please try again in a few minutes.`,
|
||||
`Sorry, I'm having trouble processing that right now. Could you try again in a few minutes?`,
|
||||
|
||||
ROUTING_AMBIGUOUS: (options: string) =>
|
||||
`I'm not sure which section you mean. Did you mean: ${options}? Reply with the number or name.`,
|
||||
`I'm not sure which part of your site you mean. Could you be more specific? Your site has: ${options}`,
|
||||
|
||||
ROUTING_NO_MATCH: (list: string) =>
|
||||
`I couldn't find a section matching that request. Your current sections are: ${list}. Try again?`,
|
||||
`I couldn't find a matching section on your site. You currently have: ${list}. Want to try again?`,
|
||||
|
||||
PROPOSAL_EXPIRED: () =>
|
||||
`That change request has expired. Please send your edit again to start over.`,
|
||||
`That change request has expired. Just send your edit again and I'll set it up!`,
|
||||
|
||||
PROPOSAL_ALREADY_APPLIED: () =>
|
||||
`That change was already applied.`,
|
||||
`That change was already made to your site!`,
|
||||
|
||||
INVALID_CONFIRM: () =>
|
||||
`Reply YES to apply or NO to cancel.`,
|
||||
`Just reply YES to apply or NO to cancel.`,
|
||||
|
||||
RATE_LIMITED: () =>
|
||||
`You've sent several requests recently. Please wait a few minutes before trying again.`,
|
||||
`You've been busy! Give me a few minutes to catch up, then try again.`,
|
||||
|
||||
MMS_NOT_SUPPORTED: () =>
|
||||
`Image uploads aren't supported yet. Please describe your change in text.`,
|
||||
`I can't handle images yet — just describe what you'd like to change in a text message!`,
|
||||
|
||||
HELP: () =>
|
||||
`I can help you edit your website via text! Just tell me what to change. Examples:\n- "Change the hero headline to Welcome Home"\n- "Hide the testimonials section"\n- "Add an event: Wine Tasting, June 15, 7pm"\n- "What does my about section say?"\n\nAfter each edit, I'll show you the change and you reply YES to apply or NO to cancel.`,
|
||||
`I can help you update your website right from here! Just text me what you'd like to change. For example:\n\n- "Change the headline to Welcome Home"\n- "Hide the testimonials"\n- "Add an event: Wine Tasting, June 15, 7pm"\n- "What's on my site right now?"\n\nI'll show you the change first, and you reply YES to make it live or NO to cancel.`,
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user