@rubytech/create-sitedesk-code 0.1.402 → 0.1.405
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/__tests__/launchd-plist.test.js +13 -1
- package/dist/__tests__/neo4j-darwin-dedicated.test.js +52 -0
- package/dist/__tests__/neo4j-datadir.test.js +8 -7
- package/dist/__tests__/neo4j-teardown.test.js +17 -1
- package/dist/index.js +183 -9
- package/dist/launchd-plist.js +21 -0
- package/dist/neo4j-darwin-dedicated.js +39 -0
- package/dist/neo4j-datadir.js +9 -5
- package/dist/neo4j-teardown.js +15 -0
- package/dist/uninstall.js +34 -8
- package/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
- package/payload/platform/plugins/web-designer/skills/web-design/references/placeholders.md +19 -2
- package/payload/platform/plugins/web-designer/skills/web-design/templates/index.html +12 -12
- package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
- package/payload/premium-plugins/sitedesk/BUNDLE.md +6 -4
- package/payload/premium-plugins/sitedesk/PLUGIN.md +2 -2
- package/payload/premium-plugins/sitedesk/agents/sitedesk--media-producer.md +18 -7
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/.claude-plugin/plugin.json +1 -1
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/PLUGIN.md +30 -26
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/README.md +26 -36
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/commands/make-media.md +2 -2
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/SKILL.md +52 -75
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/SKILL.md +52 -120
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/references/sources.md +17 -52
- package/payload/server/public/assets/{AdminLoginScreens-rlk263u9.js → AdminLoginScreens-D36zz_SY.js} +1 -1
- package/payload/server/public/assets/AdminShell-DH6Lcj-d.js +1 -0
- package/payload/server/public/assets/{Checkbox-Chz6bfy-.js → Checkbox-54KtArF3.js} +1 -1
- package/payload/server/public/assets/{Transcript-Bsx-bGgY.js → Transcript-BCpiXt4J.js} +1 -1
- package/payload/server/public/assets/{admin-BnZH758N.js → admin-C0FCHcJj.js} +1 -1
- package/payload/server/public/assets/{browser-CIrfyZWe.js → browser-bKeZ5iLx.js} +1 -1
- package/payload/server/public/assets/{calendar-D6Z8Kmrn.js → calendar-BDPBArFA.js} +1 -1
- package/payload/server/public/assets/chat-Cp_1ebzU.js +1 -0
- package/payload/server/public/assets/chevron-left-S7Nk-se6.js +1 -0
- package/payload/server/public/assets/data-BshlTgIW.js +1 -0
- package/payload/server/public/assets/{graph-XZJZHzvv.js → graph-Dvru9CW9.js} +1 -1
- package/payload/server/public/assets/{graph-labels-Cabqmf35.js → graph-labels-DLhR6Rit.js} +1 -1
- package/payload/server/public/assets/{maximize-2-DPe-Noig.js → maximize-2-CyPKq7HZ.js} +1 -1
- package/payload/server/public/assets/{operator-BM_CDyCb.js → operator-Ql5iQXB-.js} +1 -1
- package/payload/server/public/assets/page-ClJOW2fk.js +32 -0
- package/payload/server/public/assets/{page-E7I5sEVt.js → page-YsTtZsn6.js} +1 -1
- package/payload/server/public/assets/{public-CrdWCSk2.js → public-BlDxwcmS.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-Dwtj0NJ7.js → rotate-ccw-DSynJPZX.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CJelLQK-.css +1 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +5 -5
- package/payload/server/public/chat.html +9 -9
- package/payload/server/public/data.html +8 -8
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +11 -11
- package/payload/server/public/operator.html +11 -11
- package/payload/server/public/public.html +9 -9
- package/payload/server/server.js +34 -32
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/brand-extract/SKILL.md +0 -86
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/make-media/SKILL.md +0 -75
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/make-media/references/intake-and-questions.md +0 -74
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/references/placeholders.md +0 -96
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/references/sections.md +0 -48
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/templates/index.html +0 -1004
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/microsite/templates/llms.txt +0 -32
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/references/pii-strip.md +0 -64
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/og.html +0 -92
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/social-posts.md +0 -47
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/socials/templates/socials.html +0 -87
- package/payload/server/public/assets/AdminShell-C5vWTy2n.js +0 -1
- package/payload/server/public/assets/chat-h7SytZHx.js +0 -1
- package/payload/server/public/assets/chevron-left-DSTeS1_g.js +0 -1
- package/payload/server/public/assets/data-CL7CyX1C.js +0 -1
- package/payload/server/public/assets/page-CMIbXzSg.js +0 -32
- package/payload/server/public/assets/useSubAccountSwitcher-BH-YjP0H.css +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-BVCMAnWK.js → useSubAccountSwitcher-DGKQxXDD.js} +0 -0
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>SiteDesk</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/admin-
|
|
8
|
+
<script type="module" crossorigin src="/assets/admin-C0FCHcJj.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/graph-labels-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminShell-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-DGKQxXDD.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/graph-labels-DLhR6Rit.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminShell-DH6Lcj-d.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/admin-types-hioowVct.js">
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/Checkbox-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/Checkbox-54KtArF3.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-D36zz_SY.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-CvXuAEud.js">
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/maximize-2-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/Transcript-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/assets/chevron-left-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/assets/page-
|
|
21
|
-
<link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/maximize-2-CyPKq7HZ.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/Transcript-BCpiXt4J.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/chevron-left-S7Nk-se6.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/page-YsTtZsn6.js">
|
|
21
|
+
<link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-CJelLQK-.css">
|
|
22
22
|
<link rel="stylesheet" crossorigin href="/assets/AdminLoginScreens-CWMpccrR.css">
|
|
23
23
|
<link rel="stylesheet" href="/brand-defaults.css">
|
|
24
24
|
</head>
|
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Operator — Maxy</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/operator-
|
|
8
|
+
<script type="module" crossorigin src="/assets/operator-Ql5iQXB-.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminShell-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-DGKQxXDD.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminShell-DH6Lcj-d.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/admin-types-hioowVct.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/Checkbox-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/Checkbox-54KtArF3.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminLoginScreens-D36zz_SY.js">
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-CvXuAEud.js">
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/maximize-2-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/Transcript-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/chevron-left-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/assets/rotate-ccw-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/assets/page-
|
|
21
|
-
<link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/maximize-2-CyPKq7HZ.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/Transcript-BCpiXt4J.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/chevron-left-S7Nk-se6.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/rotate-ccw-DSynJPZX.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/page-ClJOW2fk.js">
|
|
21
|
+
<link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-CJelLQK-.css">
|
|
22
22
|
<link rel="stylesheet" crossorigin href="/assets/AdminLoginScreens-CWMpccrR.css">
|
|
23
23
|
<link rel="stylesheet" href="/brand-defaults.css">
|
|
24
24
|
</head>
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>SiteDesk</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/public-
|
|
8
|
+
<script type="module" crossorigin src="/assets/public-BlDxwcmS.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-CAM3fms7.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/AdminShell-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/useSubAccountSwitcher-DGKQxXDD.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/AdminShell-DH6Lcj-d.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/admin-types-hioowVct.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-CvXuAEud.js">
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/maximize-2-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/Transcript-
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/chevron-left-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/rotate-ccw-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/page-
|
|
19
|
-
<link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/maximize-2-CyPKq7HZ.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/Transcript-BCpiXt4J.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/chevron-left-S7Nk-se6.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/rotate-ccw-DSynJPZX.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/page-ClJOW2fk.js">
|
|
19
|
+
<link rel="stylesheet" crossorigin href="/assets/useSubAccountSwitcher-CJelLQK-.css">
|
|
20
20
|
<link rel="stylesheet" href="/brand-defaults.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body class="public-surface">
|
package/payload/server/server.js
CHANGED
|
@@ -1417,7 +1417,7 @@ function webchatChannelDescriptor(key) {
|
|
|
1417
1417
|
}
|
|
1418
1418
|
|
|
1419
1419
|
// app/lib/claude-auth.ts
|
|
1420
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
1420
|
+
import { readFileSync, writeFileSync, renameSync, unlinkSync } from "fs";
|
|
1421
1421
|
import { sep } from "path";
|
|
1422
1422
|
import { lock } from "proper-lockfile";
|
|
1423
1423
|
if (!CLAUDE_CREDENTIALS_FILE.startsWith(MAXY_DIR + sep)) {
|
|
@@ -1473,7 +1473,17 @@ function writeOAuthCredentials(tokens) {
|
|
|
1473
1473
|
...tokens.refreshToken != null ? { refreshToken: tokens.refreshToken } : {},
|
|
1474
1474
|
expiresAt: newExpiresAt
|
|
1475
1475
|
};
|
|
1476
|
-
|
|
1476
|
+
const tmpPath = `${CLAUDE_CREDENTIALS_FILE}.${process.pid}.tmp`;
|
|
1477
|
+
try {
|
|
1478
|
+
writeFileSync(tmpPath, JSON.stringify(fileData, null, 2), "utf-8");
|
|
1479
|
+
renameSync(tmpPath, CLAUDE_CREDENTIALS_FILE);
|
|
1480
|
+
} catch (writeErr) {
|
|
1481
|
+
try {
|
|
1482
|
+
unlinkSync(tmpPath);
|
|
1483
|
+
} catch {
|
|
1484
|
+
}
|
|
1485
|
+
throw writeErr;
|
|
1486
|
+
}
|
|
1477
1487
|
return newExpiresAt;
|
|
1478
1488
|
} catch (err) {
|
|
1479
1489
|
console.error(`[claude-auth] credential write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -8315,6 +8325,8 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
8315
8325
|
if (!isScheduleSource(text)) {
|
|
8316
8326
|
renderOrSuppressChannelInbound(unwrapChannel(text), ts, out, queuedPendingSuppress);
|
|
8317
8327
|
}
|
|
8328
|
+
} else if (row.isMeta === true) {
|
|
8329
|
+
out.push({ kind: "harness-internal", text, ts });
|
|
8318
8330
|
} else {
|
|
8319
8331
|
out.push({ kind: "operator-typed", text, ts });
|
|
8320
8332
|
}
|
|
@@ -9259,10 +9271,10 @@ var public_reader_default = app5;
|
|
|
9259
9271
|
// server/routes/webchat.ts
|
|
9260
9272
|
import { basename as basename7, dirname as dirname6 } from "path";
|
|
9261
9273
|
import { join as join18 } from "path";
|
|
9262
|
-
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync19, renameSync as
|
|
9274
|
+
import { existsSync as existsSync10, readdirSync as readdirSync11, readFileSync as readFileSync19, renameSync as renameSync4, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
9263
9275
|
|
|
9264
9276
|
// server/canonical-webchat-override.ts
|
|
9265
|
-
import { readFileSync as readFileSync17, writeFileSync as writeFileSync5, renameSync } from "fs";
|
|
9277
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync5, renameSync as renameSync2 } from "fs";
|
|
9266
9278
|
import { join as join16 } from "path";
|
|
9267
9279
|
var FILE = "canonical-webchat-session.json";
|
|
9268
9280
|
var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
@@ -9300,7 +9312,7 @@ function writeCanonicalOverrideId(accountDir, userId, sessionId) {
|
|
|
9300
9312
|
const path2 = canonicalOverridePath(accountDir);
|
|
9301
9313
|
const tmp = `${path2}.${process.pid}.tmp`;
|
|
9302
9314
|
writeFileSync5(tmp, JSON.stringify({ byAdmin }, null, 2), "utf8");
|
|
9303
|
-
|
|
9315
|
+
renameSync2(tmp, path2);
|
|
9304
9316
|
}
|
|
9305
9317
|
|
|
9306
9318
|
// ../lib/models/src/index.ts
|
|
@@ -9346,7 +9358,7 @@ function isNewSessionEffortLevel(value) {
|
|
|
9346
9358
|
}
|
|
9347
9359
|
|
|
9348
9360
|
// ../services/claude-session-manager/src/sidecar-store.ts
|
|
9349
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync as readdirSync10, readFileSync as readFileSync18, renameSync as
|
|
9361
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync as readdirSync10, readFileSync as readFileSync18, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
9350
9362
|
import { join as join17 } from "path";
|
|
9351
9363
|
function escapeRegExp(s) {
|
|
9352
9364
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -9363,11 +9375,11 @@ function createSidecarStore(config) {
|
|
|
9363
9375
|
const tmp = `${path2}.tmp.${process.pid}`;
|
|
9364
9376
|
try {
|
|
9365
9377
|
writeFileSync6(tmp, JSON.stringify(record), "utf8");
|
|
9366
|
-
|
|
9378
|
+
renameSync3(tmp, path2);
|
|
9367
9379
|
return { ok: true };
|
|
9368
9380
|
} catch (error) {
|
|
9369
9381
|
try {
|
|
9370
|
-
if (existsSync9(tmp))
|
|
9382
|
+
if (existsSync9(tmp)) unlinkSync2(tmp);
|
|
9371
9383
|
} catch {
|
|
9372
9384
|
}
|
|
9373
9385
|
return { ok: false, error };
|
|
@@ -9424,7 +9436,7 @@ function createSidecarStore(config) {
|
|
|
9424
9436
|
const path2 = pathFor(sessionsDir, id);
|
|
9425
9437
|
try {
|
|
9426
9438
|
if (existsSync9(path2)) {
|
|
9427
|
-
|
|
9439
|
+
unlinkSync2(path2);
|
|
9428
9440
|
return { ok: true, removed: true };
|
|
9429
9441
|
}
|
|
9430
9442
|
return { ok: true, removed: false };
|
|
@@ -9974,7 +9986,7 @@ ${note}` : note;
|
|
|
9974
9986
|
else parsed.adminPermissionMode = value;
|
|
9975
9987
|
const tmp = `${accountJsonPath}.${process.pid}.tmp`;
|
|
9976
9988
|
writeFileSync7(tmp, JSON.stringify(parsed, null, 2), "utf8");
|
|
9977
|
-
|
|
9989
|
+
renameSync4(tmp, accountJsonPath);
|
|
9978
9990
|
} catch (err) {
|
|
9979
9991
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9980
9992
|
console.error(`[webchat:settings] op=${op} outcome=refused value=${value} reason=write-failed detail=${detail}`);
|
|
@@ -10373,7 +10385,7 @@ var telegram_default = app7;
|
|
|
10373
10385
|
|
|
10374
10386
|
// server/routes/quickbooks.ts
|
|
10375
10387
|
import { join as join21 } from "path";
|
|
10376
|
-
import { existsSync as existsSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4, rmSync, renameSync as
|
|
10388
|
+
import { existsSync as existsSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4, rmSync, renameSync as renameSync5 } from "fs";
|
|
10377
10389
|
var TAG24 = "[quickbooks]";
|
|
10378
10390
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
10379
10391
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
@@ -10392,7 +10404,7 @@ async function completeConsent(args) {
|
|
|
10392
10404
|
}
|
|
10393
10405
|
const claimFile = `${pendingPath(accountDir, state)}.claimed`;
|
|
10394
10406
|
try {
|
|
10395
|
-
|
|
10407
|
+
renameSync5(pendingPath(accountDir, state), claimFile);
|
|
10396
10408
|
} catch {
|
|
10397
10409
|
console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
10398
10410
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
@@ -10481,20 +10493,10 @@ var quickbooks_default = app8;
|
|
|
10481
10493
|
|
|
10482
10494
|
// server/routes/onboarding.ts
|
|
10483
10495
|
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync3 } from "child_process";
|
|
10484
|
-
import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync15, readFileSync as readFileSync24, unlinkSync as
|
|
10496
|
+
import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync15, readFileSync as readFileSync24, unlinkSync as unlinkSync3, renameSync as renameSync7 } from "fs";
|
|
10485
10497
|
import { createHash as createHash3, randomUUID as randomUUID7 } from "crypto";
|
|
10486
10498
|
|
|
10487
10499
|
// app/lib/claude-spawn-env.ts
|
|
10488
|
-
var HOST_AUTH_REFRESH_TIMEOUT_MS = "30000";
|
|
10489
|
-
function hostCredsEnv(platform, credsFile) {
|
|
10490
|
-
if (platform === "darwin" && credsFile) {
|
|
10491
|
-
return {
|
|
10492
|
-
CLAUDE_CODE_HOST_CREDS_FILE: credsFile,
|
|
10493
|
-
CLAUDE_CODE_HOST_AUTH_REFRESH_TIMEOUT_MS: HOST_AUTH_REFRESH_TIMEOUT_MS
|
|
10494
|
-
};
|
|
10495
|
-
}
|
|
10496
|
-
return {};
|
|
10497
|
-
}
|
|
10498
10500
|
function claudeBin() {
|
|
10499
10501
|
return process.env.CLAUDE_BIN ?? "claude";
|
|
10500
10502
|
}
|
|
@@ -10586,7 +10588,7 @@ function readHostCredsBlob(service) {
|
|
|
10586
10588
|
}
|
|
10587
10589
|
|
|
10588
10590
|
// ../lib/admins-write/src/index.ts
|
|
10589
|
-
import { existsSync as existsSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync9, renameSync as
|
|
10591
|
+
import { existsSync as existsSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync9, renameSync as renameSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync13, statSync as statSync8, appendFileSync as appendFileSync2 } from "fs";
|
|
10590
10592
|
import { dirname as dirname7, join as join22 } from "path";
|
|
10591
10593
|
function id8(value) {
|
|
10592
10594
|
return value.slice(0, 8);
|
|
@@ -10621,7 +10623,7 @@ function writeFileAtomic(filePath, contents, mode) {
|
|
|
10621
10623
|
mkdirSync5(dirname7(filePath), { recursive: true });
|
|
10622
10624
|
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
10623
10625
|
writeFileSync9(tempPath, contents, mode !== void 0 ? { mode } : void 0);
|
|
10624
|
-
|
|
10626
|
+
renameSync6(tempPath, filePath);
|
|
10625
10627
|
}
|
|
10626
10628
|
function writeAdminEntry(input) {
|
|
10627
10629
|
const result = { usersJsonResult: "noop", accountJsonResult: "noop" };
|
|
@@ -10818,7 +10820,7 @@ app9.post("/claude-auth", async (c) => {
|
|
|
10818
10820
|
let logoutError = null;
|
|
10819
10821
|
try {
|
|
10820
10822
|
execFileSync3(claudeBin(), ["auth", "logout"], {
|
|
10821
|
-
env: { ...process.env
|
|
10823
|
+
env: { ...process.env },
|
|
10822
10824
|
encoding: "utf-8",
|
|
10823
10825
|
timeout: 8e3
|
|
10824
10826
|
});
|
|
@@ -10866,7 +10868,7 @@ app9.post("/claude-auth", async (c) => {
|
|
|
10866
10868
|
vncLog("claude-auth", { action: "start", transport: "native", platform: "darwin" });
|
|
10867
10869
|
darwinCredsBefore = listHostCredsMdats();
|
|
10868
10870
|
const claudeProc2 = spawn(claudeBin(), ["auth", "login"], {
|
|
10869
|
-
env: { ...process.env
|
|
10871
|
+
env: { ...process.env },
|
|
10870
10872
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10871
10873
|
});
|
|
10872
10874
|
claudeProc2.unref();
|
|
@@ -10885,7 +10887,7 @@ app9.post("/claude-auth", async (c) => {
|
|
|
10885
10887
|
writeFile: (blob) => {
|
|
10886
10888
|
const tmp = `${CLAUDE_CREDENTIALS_FILE}.${process.pid}.tmp`;
|
|
10887
10889
|
writeFileSync10(tmp, blob, { mode: 384 });
|
|
10888
|
-
|
|
10890
|
+
renameSync7(tmp, CLAUDE_CREDENTIALS_FILE);
|
|
10889
10891
|
},
|
|
10890
10892
|
log: (line) => console.log(line)
|
|
10891
10893
|
});
|
|
@@ -11049,7 +11051,7 @@ app9.delete("/set-pin", requireAdminSession, async (c) => {
|
|
|
11049
11051
|
}
|
|
11050
11052
|
const remaining = users.filter((u) => u.userId !== sessionUserId);
|
|
11051
11053
|
if (remaining.length === 0) {
|
|
11052
|
-
|
|
11054
|
+
unlinkSync3(USERS_FILE);
|
|
11053
11055
|
console.log(`[set-pin] cleared users.json (last entry removed): userId=${sessionUserId.slice(0, 8)}\u2026`);
|
|
11054
11056
|
} else {
|
|
11055
11057
|
writeFileSync10(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
|
|
@@ -11151,7 +11153,7 @@ ${body.newPin}
|
|
|
11151
11153
|
var onboarding_default = app9;
|
|
11152
11154
|
|
|
11153
11155
|
// server/routes/client-error.ts
|
|
11154
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as
|
|
11156
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync8, statSync as statSync9 } from "fs";
|
|
11155
11157
|
import { join as join23 } from "path";
|
|
11156
11158
|
var CLIENT_ERRORS_LOG = join23(LOG_DIR, "client-errors.log");
|
|
11157
11159
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -11204,7 +11206,7 @@ function rotateIfNeeded() {
|
|
|
11204
11206
|
if (!existsSync16(CLIENT_ERRORS_LOG)) return;
|
|
11205
11207
|
const stats = statSync9(CLIENT_ERRORS_LOG);
|
|
11206
11208
|
if (stats.size < MAX_LOG_SIZE) return;
|
|
11207
|
-
|
|
11209
|
+
renameSync8(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
|
|
11208
11210
|
} catch (err) {
|
|
11209
11211
|
console.error(`[client-error] log rotation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
11210
11212
|
}
|
|
@@ -11684,7 +11686,7 @@ var logs_default = app13;
|
|
|
11684
11686
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
11685
11687
|
var app14 = new Hono();
|
|
11686
11688
|
app14.get("/", (c) => {
|
|
11687
|
-
const claudeEnv = { ...process.env
|
|
11689
|
+
const claudeEnv = { ...process.env };
|
|
11688
11690
|
let version = "unknown";
|
|
11689
11691
|
try {
|
|
11690
11692
|
const raw = execFileSync4(claudeBin(), ["--version"], { env: claudeEnv, encoding: "utf-8", timeout: 5e3 });
|
package/payload/premium-plugins/sitedesk/plugins/sitedesk-media/skills/brand-extract/SKILL.md
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: brand-extract
|
|
3
|
-
description: Turn a business's existing website into a brand pack for the micro-site — a tokenized palette (ink/paper/rule/accent plus a deepened accent for legible text), the logo and icon saved as image files, and a brand.md recording the company facts the site exposes (name, contact, services, about copy, socials) together with an explicit list of what is still unknown. Use as the first step of make-media, or on its own when someone wants design tokens + logo + company facts pulled from a URL. Trigger phrases include "extract the brand from <url>", "get tokens and logo from <site>", "pull the company details from their website".
|
|
4
|
-
allowed-tools:
|
|
5
|
-
- Bash
|
|
6
|
-
- Read
|
|
7
|
-
- Write
|
|
8
|
-
- WebFetch
|
|
9
|
-
- Grep
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# brand-extract
|
|
13
|
-
|
|
14
|
-
Reads an existing website and produces what the `microsite` and `socials`
|
|
15
|
-
templates need to be branded: a **palette**, the **logo** and **icon** as files,
|
|
16
|
-
and a **brand.md** of company facts with an honest "still unknown" list.
|
|
17
|
-
|
|
18
|
-
## Outputs
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
<output>/site/images/logo.png # the wordmark/lockup, on a white or transparent ground
|
|
22
|
-
<output>/site/images/icon.png # the square mark / monogram (favicon source)
|
|
23
|
-
<output>/site/favicon.png # 64px icon
|
|
24
|
-
<output>/brand.md # tokens + facts found + gaps
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Palette — the token contract
|
|
28
|
-
|
|
29
|
-
The templates resolve every colour through ten CSS custom properties. Fill their
|
|
30
|
-
values from the site; do not introduce new token names.
|
|
31
|
-
|
|
32
|
-
| Token | Role | How to choose |
|
|
33
|
-
|---|---|---|
|
|
34
|
-
| `INK_900` | primary text, headings, dark surfaces | the site's darkest near-black text colour |
|
|
35
|
-
| `INK_700` | secondary text | a step lighter than INK_900 |
|
|
36
|
-
| `INK_400` | muted / captions | a mid grey |
|
|
37
|
-
| `PAPER_100` | white surface | the lightest surface (usually `#FFFFFF`) |
|
|
38
|
-
| `PAPER_200` | page-bg tint / bands | a faint off-white for alternating bands |
|
|
39
|
-
| `RULE_100` | borders / dividers | a pale neutral hairline |
|
|
40
|
-
| `ACCENT` | the brand accent (separators, hairlines, CTA hover) | the logo's accent colour, used verbatim |
|
|
41
|
-
| `ACCENT_TEXT` | accent used as small text (eyebrows, links) | the accent **deepened** until it is legible at ~10px on white (aim for ≥ 3:1 contrast). This is the one token you compute rather than copy — a light brand accent is unreadable as tracked-caps text, so darken it while keeping the same hue. |
|
|
42
|
-
| `ACCENT_SOFT` | soft tint behind accent text | a pale wash of the accent |
|
|
43
|
-
| `BRAND_GREY` | logo/wordmark grey | the logo's neutral grey |
|
|
44
|
-
|
|
45
|
-
If the operator supplied design tokens directly, use them verbatim and skip
|
|
46
|
-
derivation. The three-font editorial system (Cormorant Garamond display, Lora
|
|
47
|
-
body, Inter UI) is the template default and stays unless the operator asks to
|
|
48
|
-
change it — brand identity lives in the logo, the accent, and the copy.
|
|
49
|
-
|
|
50
|
-
## Logo — make it usable on dark and light
|
|
51
|
-
|
|
52
|
-
Save the site's primary logo as `images/logo.png`. Note its background: a logo on
|
|
53
|
-
a **white ground** sits cleanly on a light nav but cannot be dropped onto a dark
|
|
54
|
-
photo. The `socials` tiles place the logo on a white chip precisely so a
|
|
55
|
-
white-ground logo still reads over any hero. Record in brand.md whether a
|
|
56
|
-
transparent or light-art variant exists. Save the square mark as `images/icon.png`
|
|
57
|
-
and a 64px `favicon.png`.
|
|
58
|
-
|
|
59
|
-
Right-size the logo (a multi-megabyte original is overkill for a 42–84px display);
|
|
60
|
-
keep it crisp but reasonable.
|
|
61
|
-
|
|
62
|
-
## Facts — record, and mark the gaps
|
|
63
|
-
|
|
64
|
-
Write `brand.md` with everything the site exposes: trading name, legal name,
|
|
65
|
-
tagline, sector, established year, phone, email, address, region, services,
|
|
66
|
-
"about" copy, social links. For every field the site does **not** expose, write
|
|
67
|
-
it under a `## Still unknown` heading. That list is exactly what `make-media`
|
|
68
|
-
turns into questions for the operator.
|
|
69
|
-
|
|
70
|
-
Do not promote a marketing line to a hard fact. "Over 35 years" is a claim about
|
|
71
|
-
experience, not a company age; "since 1987" is a trading date, not necessarily an
|
|
72
|
-
incorporation date. Keep claims as claims.
|
|
73
|
-
|
|
74
|
-
## The 2000px image rule
|
|
75
|
-
|
|
76
|
-
Never `Read` an image whose longest edge exceeds 2000px — it can break the
|
|
77
|
-
session. Measure first (`sips -g pixelWidth -g pixelHeight`), and if larger,
|
|
78
|
-
downscale a preview into `/tmp`, read that, and delete it.
|
|
79
|
-
|
|
80
|
-
## Completion criteria
|
|
81
|
-
|
|
82
|
-
- `images/logo.png`, `images/icon.png`, `favicon.png` exist and are right-sized.
|
|
83
|
-
- `brand.md` lists the ten palette tokens with values, the company facts found,
|
|
84
|
-
and an explicit "Still unknown" list.
|
|
85
|
-
- No colour was invented beyond deriving `ACCENT_TEXT` from `ACCENT`; no marketing
|
|
86
|
-
claim was recorded as a registration fact.
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: make-media
|
|
3
|
-
description: Single-entry orchestrator that turns an operator's raw inputs — a folder or zip of photos, an existing website URL, social-media links, and any free-text notes about the business — into a finished marketing micro-site plus a set of share-ready social tiles. Use whenever the deliverable is "a website and social images for this business" from mixed source material. Routes to brand-extract → microsite → socials, and asks the operator targeted questions to fill every field the sources do not supply. Trigger phrases include "make a site for this business", "build a microsite from these photos and their website", "turn this into a landing page and social tiles", "sitedesk media for <business>".
|
|
4
|
-
allowed-tools:
|
|
5
|
-
- Bash
|
|
6
|
-
- Read
|
|
7
|
-
- Write
|
|
8
|
-
- Edit
|
|
9
|
-
- Glob
|
|
10
|
-
- Grep
|
|
11
|
-
- WebFetch
|
|
12
|
-
- Skill
|
|
13
|
-
- AskUserQuestion
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
# make-media
|
|
17
|
-
|
|
18
|
-
Orchestrates the `brand-extract → microsite → socials` pipeline. The deliverable is a self-contained `site/` directory (a scrollable single-page micro-site, an `llms.txt`, and a `socials/` folder of Open Graph + social tiles with a gallery and captions) plus a zipped copy, built from whatever the operator provides and whatever the orchestrator has to ask for.
|
|
19
|
-
|
|
20
|
-
This skill is a playbook, not a programmatic dispatcher. It tells you the routing and the gap-filling contract; you execute each step under the underlying skills' own contracts.
|
|
21
|
-
|
|
22
|
-
## What the operator gives, and what you do with it
|
|
23
|
-
|
|
24
|
-
| Operator supplies | Used for | If missing |
|
|
25
|
-
|---|---|---|
|
|
26
|
-
| **Images** (folder or zip of photos) | hero rotator, photo grids, founder portrait, testimonial, social tiles | required — the site is image-led. If none, stop and ask. |
|
|
27
|
-
| **Website URL** | brand tokens (palette, accent), logo, company name, contact, services, "about" copy | run `brand-extract`; anything it cannot read becomes a question |
|
|
28
|
-
| **Social links** (Facebook / Instagram / TikTok / LinkedIn) | footer + drawer links, social handles, extra copy/ideas in the operator's own voice | login-walled platforms usually cannot be scraped — say so plainly and ask the operator to paste any posts they want used |
|
|
29
|
-
| **Free-text notes / pasted posts / quotes** | founder section, testimonials, "from the site" updates, service descriptions, taglines | use verbatim where the operator marks a quote; never reword a real client testimonial into marketing prose |
|
|
30
|
-
| **Design tokens** (if supplied directly) | the palette `:root` — use them verbatim, do not re-pick | if absent, `brand-extract` derives them from the site |
|
|
31
|
-
|
|
32
|
-
## The core rule: ask, never invent
|
|
33
|
-
|
|
34
|
-
Every `{{ PLACEHOLDER }}` in the templates is a fact about a real business. The brand and the operator's notes fill most of them. **For every placeholder still unfilled after `brand-extract`, ask the operator — do not synthesise a plausible value.** A fabricated phone number, a guessed founding year, an invented testimonial, or a made-up company number is a defect, not a convenience. The reference list of questions is in [`references/intake-and-questions.md`](references/intake-and-questions.md); ask only for what is genuinely missing, grouped so the operator answers in as few turns as possible.
|
|
35
|
-
|
|
36
|
-
Two facts are load-bearing and frequently wrong if guessed: the **founding/established year** (a business that "trades since 1987" may have incorporated much later — keep the two distinct), and any **company-registration detail** (number, registered office) which must come from the register or the operator, never from the website's marketing line.
|
|
37
|
-
|
|
38
|
-
## Process — high level
|
|
39
|
-
|
|
40
|
-
1. **Resolve the workspace.** Default output root is the images directory's parent, or the operator's stated path. The deliverable lands in `<output>/site/`.
|
|
41
|
-
2. **Run `brand-extract`** against the website URL: produces the brand tokens (palette + accent), the logo (placed as `site/images/logo.png`) and icon, the company facts it can read, and a `brand.md` recording what was found and what is still unknown. If the operator supplied design tokens directly, use those verbatim instead of re-deriving.
|
|
42
|
-
3. **Curate the images.** Verify each photo's *content* by eye (never trust filenames), measuring first and previewing any image whose longest edge exceeds 2000px (downscale to `/tmp`, read, delete) so a large photo never breaks the session. Pick the strongest, most varied shots for the hero rotator and the strongest finished result for the testimonial; map the rest to the photo grids. A person's portrait is for the founder section and a portrait-only social tile, never a landscape crop.
|
|
43
|
-
4. **Fill the gaps.** Compare the placeholder inventory ([`microsite/references/placeholders.md`](../microsite/references/placeholders.md)) against what brand-extract found and the operator's notes. Ask the operator for everything still missing, in grouped questions. Wait for answers. Do not proceed with blanks.
|
|
44
|
-
5. **Run `microsite`** to build `site/index.html` + `site/llms.txt` from the templates, substituting every placeholder. No `{{ }}` may survive into the output.
|
|
45
|
-
6. **Run `socials` in `microsite` mode** to render the Open Graph + social tiles into `site/socials/`, plus `site/socials.html` (gallery) and `site/social-posts.md` (captions), and to wire the site's `og:image` to the landscape tile.
|
|
46
|
-
7. **Verify and report.** Confirm no placeholder survived, every referenced asset resolves over a local HTTP server, the site renders at desktop and mobile, and the social tiles are at exact dimensions. Report the path to `site/` and the zip, and list any value you had to ask for.
|
|
47
|
-
|
|
48
|
-
## Outcome contract
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
<output>/site/
|
|
52
|
-
index.html # the scrollable micro-site
|
|
53
|
-
llms.txt # answer-engine summary
|
|
54
|
-
favicon.png
|
|
55
|
-
images/ # curated, optimised webp + logo.png + icon.png
|
|
56
|
-
socials/ # og-{landscape,square,portrait}-{main,interior,build}.png + og-portrait-founder.png
|
|
57
|
-
socials.html # tile gallery with download links + OG snippet
|
|
58
|
-
social-posts.md # captions per tile and platform
|
|
59
|
-
og.html # tile render page (kept for re-runs)
|
|
60
|
-
<output>/<brand-slug>-microsite.zip
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Completion criteria
|
|
64
|
-
|
|
65
|
-
- `grep "{{" site/index.html site/llms.txt site/socials.html` returns nothing — every placeholder substituted.
|
|
66
|
-
- No invented facts: every value traces to the website, the operator's notes, or an answer the operator gave. Anything still genuinely unknown is shown as a clear gap, not a guess.
|
|
67
|
-
- The site renders at desktop and a 390px mobile viewport with no horizontal overflow and no broken images; the hero rotates; the back-to-top works.
|
|
68
|
-
- Social tiles exist at exact sizes (1200×628 / 1200×1200 / 1080×1350); any person tile is portrait only.
|
|
69
|
-
- The operator-facing report names the site path, the zip path, and every field that had to be asked for.
|
|
70
|
-
|
|
71
|
-
## When NOT to use this skill
|
|
72
|
-
|
|
73
|
-
- The operator only wants the micro-site, or only the social tiles → call `microsite` or `socials` directly.
|
|
74
|
-
- The operator only wants brand tokens and a logo from a URL → call `brand-extract` directly.
|
|
75
|
-
- There are no usable photos of the business → the site is image-led; stop and ask for photos before anything else.
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# Intake & gap-filling questions
|
|
2
|
-
|
|
3
|
-
Ask only for what the website and the operator's notes did not already supply.
|
|
4
|
-
Group the questions so the operator answers in as few turns as possible, and
|
|
5
|
-
present sensible candidates from the source material where you have them ("Your
|
|
6
|
-
site says you've traded since 1994 — is that the year to show?").
|
|
7
|
-
|
|
8
|
-
Never invent a value to avoid asking. A guessed phone number or founding year is
|
|
9
|
-
a defect. If the operator does not know a field (e.g. company number), leave that
|
|
10
|
-
slot out of the output rather than filling it with a placeholder or a guess.
|
|
11
|
-
|
|
12
|
-
## 1. Identity
|
|
13
|
-
|
|
14
|
-
- Trading name and full legal name (e.g. "Acme Joinery" / "Acme Joinery Ltd").
|
|
15
|
-
- Short monogram / initials, if any (used as a small brand mark).
|
|
16
|
-
- Tagline / strapline, in their own words.
|
|
17
|
-
- Sector label for the nav and tiles (e.g. "Builders", "Joiners", "Florist").
|
|
18
|
-
- Established year. **Keep "trading since" separate from "company incorporated".**
|
|
19
|
-
- Company registration number + jurisdiction, and registered office — only if the
|
|
20
|
-
operator wants legal detail in the footer. Take these from the register or the
|
|
21
|
-
operator, never from a marketing line.
|
|
22
|
-
|
|
23
|
-
## 2. Contact & place
|
|
24
|
-
|
|
25
|
-
- Phone (display + dialable), mobile if separate.
|
|
26
|
-
- Email.
|
|
27
|
-
- Website domain and full URL (for canonical + Open Graph).
|
|
28
|
-
- Address: street, post town, county, postcode (only the parts they want shown).
|
|
29
|
-
- Service area / region phrase (e.g. "the South East" or "Greater Manchester").
|
|
30
|
-
- Social profiles: Facebook, Instagram, TikTok, LinkedIn (URLs + handles).
|
|
31
|
-
|
|
32
|
-
## 3. The story (copy, in their voice)
|
|
33
|
-
|
|
34
|
-
- One or two paragraphs on who they are and how long they've been going.
|
|
35
|
-
- What they're proudest of / the standard they hold to.
|
|
36
|
-
- A headline sentence that sums the business up.
|
|
37
|
-
- The three or four "by the numbers" facts (established, experience, ownership,
|
|
38
|
-
coverage, specialism, approach, promise).
|
|
39
|
-
|
|
40
|
-
## 4. What they do (services)
|
|
41
|
-
|
|
42
|
-
- Up to six services, each a short name and a one-line description.
|
|
43
|
-
- A one-line lede for the services section.
|
|
44
|
-
|
|
45
|
-
## 5. The founder / person on site
|
|
46
|
-
|
|
47
|
-
- Name and role of the person who fronts the business.
|
|
48
|
-
- A short intro in their voice (what they do, how hands-on they are).
|
|
49
|
-
- A quote from them, ideally about a specific job or their approach.
|
|
50
|
-
- 1–3 recent "from the site" updates in their own words (the social-post voice
|
|
51
|
-
is good here — keep it, just drop the emoji per the plain register).
|
|
52
|
-
- A portrait photo of them (used in the founder section and a portrait social
|
|
53
|
-
tile — a person is never cropped to landscape).
|
|
54
|
-
|
|
55
|
-
## 6. Proof (testimonial)
|
|
56
|
-
|
|
57
|
-
- A real client quote, used verbatim. Note the one phrase to highlight.
|
|
58
|
-
- Who it's from and the context ("on their new kitchen").
|
|
59
|
-
- Which finished-job photo best illustrates it.
|
|
60
|
-
- Any thank-you artefact (a card, a gift photo) that adds a personal touch.
|
|
61
|
-
|
|
62
|
-
## 7. FAQ (answer-engine targets)
|
|
63
|
-
|
|
64
|
-
- 3–5 plain questions a customer actually asks, each with a one or two sentence
|
|
65
|
-
answer. Default set: what they do, how long they've been going, where they're
|
|
66
|
-
based / areas covered, plus two specific to the trade.
|
|
67
|
-
|
|
68
|
-
## 8. Images (you curate, but confirm)
|
|
69
|
-
|
|
70
|
-
- Confirm which photo is the strongest "arrive here" shot (hero / main tile).
|
|
71
|
-
- Confirm the best finished-result shot (testimonial + interior tile).
|
|
72
|
-
- Confirm a strong in-progress / scale shot (build tile, full-bleed).
|
|
73
|
-
- Flag if the photo set is mostly work-in-progress so expectations are set, and
|
|
74
|
-
ask for any finished-result photos that would strengthen the hero.
|