@maestroagora/agora 1.0.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/.agents/plugins/marketplace.json +21 -0
- package/.claude-plugin/marketplace.json +15 -0
- package/.claude-plugin/plugin.json +22 -0
- package/.codex-plugin/plugin.json +39 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/assets/agora-orbit.svg +158 -0
- package/assets/icon.png +0 -0
- package/assets/maestro-agora-banner.png +0 -0
- package/package.json +56 -0
- package/scripts/install.mjs +400 -0
- package/skills/agora/SKILL.md +57 -0
- package/skills/agora/agents/openai.yaml +4 -0
- package/skills/agora/references/agora-marketing.md +894 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import {
|
|
5
|
+
access,
|
|
6
|
+
cp,
|
|
7
|
+
mkdir,
|
|
8
|
+
readFile,
|
|
9
|
+
readdir,
|
|
10
|
+
rename,
|
|
11
|
+
rm,
|
|
12
|
+
stat,
|
|
13
|
+
} from "node:fs/promises";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
|
|
18
|
+
const SKILL_NAME = "agora";
|
|
19
|
+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const PACKAGE_ROOT = resolve(SCRIPT_DIR, "..");
|
|
21
|
+
const SOURCE_DIR = join(PACKAGE_ROOT, "skills", SKILL_NAME);
|
|
22
|
+
|
|
23
|
+
const TARGETS = {
|
|
24
|
+
shared: {
|
|
25
|
+
description: "Shared Agent Skills path for Codex, Cursor, Gemini CLI, GitHub Copilot, and Windsurf",
|
|
26
|
+
user: [".agents", "skills"],
|
|
27
|
+
project: [".agents", "skills"],
|
|
28
|
+
},
|
|
29
|
+
codex: {
|
|
30
|
+
description: "Codex CLI, IDE, and desktop app",
|
|
31
|
+
user: [".agents", "skills"],
|
|
32
|
+
project: [".agents", "skills"],
|
|
33
|
+
},
|
|
34
|
+
claude: {
|
|
35
|
+
description: "Claude Code",
|
|
36
|
+
user: [".claude", "skills"],
|
|
37
|
+
project: [".claude", "skills"],
|
|
38
|
+
},
|
|
39
|
+
cursor: {
|
|
40
|
+
description: "Cursor",
|
|
41
|
+
user: [".cursor", "skills"],
|
|
42
|
+
project: [".cursor", "skills"],
|
|
43
|
+
},
|
|
44
|
+
gemini: {
|
|
45
|
+
description: "Gemini CLI",
|
|
46
|
+
user: [".gemini", "skills"],
|
|
47
|
+
project: [".gemini", "skills"],
|
|
48
|
+
},
|
|
49
|
+
copilot: {
|
|
50
|
+
description: "GitHub Copilot CLI, coding agent, and VS Code agent mode",
|
|
51
|
+
user: [".copilot", "skills"],
|
|
52
|
+
project: [".github", "skills"],
|
|
53
|
+
},
|
|
54
|
+
windsurf: {
|
|
55
|
+
description: "Windsurf Cascade",
|
|
56
|
+
user: [".codeium", "windsurf", "skills"],
|
|
57
|
+
project: [".windsurf", "skills"],
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const UNIVERSAL_TARGETS = ["shared", "claude"];
|
|
62
|
+
|
|
63
|
+
function usage() {
|
|
64
|
+
return `Install ${SKILL_NAME} into documented Agent Skills locations.
|
|
65
|
+
|
|
66
|
+
Usage:
|
|
67
|
+
agora [options]
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--target <name> universal (default), shared, codex, claude, cursor,
|
|
71
|
+
gemini, copilot, or windsurf. Accepts comma-separated names.
|
|
72
|
+
--scope <scope> user (default) or project.
|
|
73
|
+
--project <path> Project root for project scope. Defaults to the current directory.
|
|
74
|
+
--home <path> Home directory override. Useful for CI and isolated installs.
|
|
75
|
+
--force Replace a different existing copy at the exact skill destination.
|
|
76
|
+
--dry-run Print destinations without changing files.
|
|
77
|
+
--list-targets Show native target paths.
|
|
78
|
+
-h, --help Show this help.
|
|
79
|
+
|
|
80
|
+
Examples:
|
|
81
|
+
agora --target universal
|
|
82
|
+
agora --target universal --scope project --project .
|
|
83
|
+
agora --target claude,cursor --force`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function fail(message) {
|
|
87
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function takeValue(argv, index, option) {
|
|
92
|
+
const value = argv[index + 1];
|
|
93
|
+
if (!value || value.startsWith("--")) {
|
|
94
|
+
throw new Error(`${option} requires a value`);
|
|
95
|
+
}
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function parseArgs(argv) {
|
|
100
|
+
const options = {
|
|
101
|
+
dryRun: false,
|
|
102
|
+
force: false,
|
|
103
|
+
help: false,
|
|
104
|
+
home: homedir(),
|
|
105
|
+
listTargets: false,
|
|
106
|
+
project: process.cwd(),
|
|
107
|
+
scope: "user",
|
|
108
|
+
targets: [],
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
112
|
+
const arg = argv[index];
|
|
113
|
+
if (arg === "--") {
|
|
114
|
+
continue;
|
|
115
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
116
|
+
options.help = true;
|
|
117
|
+
} else if (arg === "--dry-run") {
|
|
118
|
+
options.dryRun = true;
|
|
119
|
+
} else if (arg === "--force") {
|
|
120
|
+
options.force = true;
|
|
121
|
+
} else if (arg === "--list-targets") {
|
|
122
|
+
options.listTargets = true;
|
|
123
|
+
} else if (arg === "--target") {
|
|
124
|
+
options.targets.push(...takeValue(argv, index, arg).split(","));
|
|
125
|
+
index += 1;
|
|
126
|
+
} else if (arg.startsWith("--target=")) {
|
|
127
|
+
options.targets.push(...arg.slice("--target=".length).split(","));
|
|
128
|
+
} else if (arg === "--scope") {
|
|
129
|
+
options.scope = takeValue(argv, index, arg);
|
|
130
|
+
index += 1;
|
|
131
|
+
} else if (arg.startsWith("--scope=")) {
|
|
132
|
+
options.scope = arg.slice("--scope=".length);
|
|
133
|
+
} else if (arg === "--project") {
|
|
134
|
+
options.project = takeValue(argv, index, arg);
|
|
135
|
+
index += 1;
|
|
136
|
+
} else if (arg.startsWith("--project=")) {
|
|
137
|
+
options.project = arg.slice("--project=".length);
|
|
138
|
+
} else if (arg === "--home") {
|
|
139
|
+
options.home = takeValue(argv, index, arg);
|
|
140
|
+
index += 1;
|
|
141
|
+
} else if (arg.startsWith("--home=")) {
|
|
142
|
+
options.home = arg.slice("--home=".length);
|
|
143
|
+
} else {
|
|
144
|
+
throw new Error(`unknown option: ${arg}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
options.targets = options.targets
|
|
149
|
+
.map((target) => target.trim().toLowerCase())
|
|
150
|
+
.filter(Boolean);
|
|
151
|
+
if (options.targets.length === 0) options.targets = ["universal"];
|
|
152
|
+
if (!new Set(["user", "project"]).has(options.scope)) {
|
|
153
|
+
throw new Error("--scope must be user or project");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const knownTargets = new Set(["universal", ...Object.keys(TARGETS)]);
|
|
157
|
+
for (const target of options.targets) {
|
|
158
|
+
if (!knownTargets.has(target)) {
|
|
159
|
+
throw new Error(`unknown target '${target}'`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
options.home = resolve(options.home);
|
|
164
|
+
options.project = resolve(options.project);
|
|
165
|
+
return options;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function printTargets() {
|
|
169
|
+
process.stdout.write("Target User path Project path\n");
|
|
170
|
+
process.stdout.write("--------- -------------------------------- -----------------------\n");
|
|
171
|
+
process.stdout.write("universal ~/.agents/skills + ~/.claude/skills .agents/skills + .claude/skills\n");
|
|
172
|
+
for (const [name, target] of Object.entries(TARGETS)) {
|
|
173
|
+
process.stdout.write(
|
|
174
|
+
`${name.padEnd(9)} ~/${target.user.join("/").padEnd(31)} ${target.project.join("/")}\n`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function exists(path) {
|
|
180
|
+
try {
|
|
181
|
+
await access(path, constants.F_OK);
|
|
182
|
+
return true;
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function listFiles(root, base = root) {
|
|
189
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
190
|
+
const files = [];
|
|
191
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
192
|
+
const path = join(root, entry.name);
|
|
193
|
+
if (entry.isDirectory()) {
|
|
194
|
+
files.push(...(await listFiles(path, base)));
|
|
195
|
+
} else if (entry.isFile()) {
|
|
196
|
+
files.push(relative(base, path).replaceAll("\\", "/"));
|
|
197
|
+
} else {
|
|
198
|
+
throw new Error(`unsupported filesystem entry in skill: ${path}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return files;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function sameTree(left, right) {
|
|
205
|
+
if (!(await exists(left)) || !(await exists(right))) return false;
|
|
206
|
+
const [leftStat, rightStat] = await Promise.all([stat(left), stat(right)]);
|
|
207
|
+
if (!leftStat.isDirectory() || !rightStat.isDirectory()) return false;
|
|
208
|
+
|
|
209
|
+
const [leftFiles, rightFiles] = await Promise.all([listFiles(left), listFiles(right)]);
|
|
210
|
+
if (leftFiles.length !== rightFiles.length) return false;
|
|
211
|
+
if (leftFiles.some((file, index) => file !== rightFiles[index])) return false;
|
|
212
|
+
|
|
213
|
+
for (const file of leftFiles) {
|
|
214
|
+
const [leftContent, rightContent] = await Promise.all([
|
|
215
|
+
readFile(join(left, file)),
|
|
216
|
+
readFile(join(right, file)),
|
|
217
|
+
]);
|
|
218
|
+
if (!leftContent.equals(rightContent)) return false;
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function expandedTargets(targets) {
|
|
224
|
+
const expanded = targets.flatMap((target) =>
|
|
225
|
+
target === "universal" ? UNIVERSAL_TARGETS : [target],
|
|
226
|
+
);
|
|
227
|
+
return [...new Set(expanded)];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function destinations(options) {
|
|
231
|
+
const base = options.scope === "user" ? options.home : options.project;
|
|
232
|
+
const seen = new Set();
|
|
233
|
+
const output = [];
|
|
234
|
+
|
|
235
|
+
for (const targetName of expandedTargets(options.targets)) {
|
|
236
|
+
const target = TARGETS[targetName];
|
|
237
|
+
const destination = join(base, ...target[options.scope], SKILL_NAME);
|
|
238
|
+
const key = destination.toLowerCase();
|
|
239
|
+
if (seen.has(key)) continue;
|
|
240
|
+
seen.add(key);
|
|
241
|
+
output.push({ destination, targetName });
|
|
242
|
+
}
|
|
243
|
+
return output;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function verifySource() {
|
|
247
|
+
const required = [
|
|
248
|
+
"SKILL.md",
|
|
249
|
+
"agents/openai.yaml",
|
|
250
|
+
"references/agora-marketing.md",
|
|
251
|
+
];
|
|
252
|
+
for (const file of required) {
|
|
253
|
+
if (!(await exists(join(SOURCE_DIR, file)))) {
|
|
254
|
+
throw new Error(`package is missing skills/${SKILL_NAME}/${file}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function buildPlan(requestedDestinations, options) {
|
|
260
|
+
const plan = [];
|
|
261
|
+
for (const requested of requestedDestinations) {
|
|
262
|
+
const current = await sameTree(SOURCE_DIR, requested.destination);
|
|
263
|
+
const destinationExists = await exists(requested.destination);
|
|
264
|
+
plan.push({ ...requested, current, destinationExists });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const conflicts = plan.filter((item) => item.destinationExists && !item.current);
|
|
268
|
+
if (conflicts.length > 0 && !options.force && !options.dryRun) {
|
|
269
|
+
const paths = conflicts.map((item) => item.destination).join(", ");
|
|
270
|
+
throw new Error(
|
|
271
|
+
`${paths} already exists and differs; inspect ${conflicts.length === 1 ? "it" : "them"}, then rerun with --force to replace only those skill folders`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
return plan;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function planResults(plan, options) {
|
|
278
|
+
return plan.map((item) => ({
|
|
279
|
+
action: item.current
|
|
280
|
+
? "current"
|
|
281
|
+
: item.destinationExists
|
|
282
|
+
? options.force
|
|
283
|
+
? "would replace"
|
|
284
|
+
: "needs --force"
|
|
285
|
+
: "would install",
|
|
286
|
+
destination: item.destination,
|
|
287
|
+
targetName: item.targetName,
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function cleanPrepared(items) {
|
|
292
|
+
for (const item of items) {
|
|
293
|
+
if (item.temporary && (await exists(item.temporary))) {
|
|
294
|
+
await rm(item.temporary, { force: true, recursive: true });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function applyPlan(plan) {
|
|
300
|
+
const changes = plan.filter((item) => !item.current);
|
|
301
|
+
const token = `${process.pid}-${Date.now()}`;
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
for (let index = 0; index < changes.length; index += 1) {
|
|
305
|
+
const item = changes[index];
|
|
306
|
+
const parent = dirname(item.destination);
|
|
307
|
+
await mkdir(parent, { recursive: true });
|
|
308
|
+
const stagingRoot = dirname(parent);
|
|
309
|
+
await mkdir(stagingRoot, { recursive: true });
|
|
310
|
+
item.temporary = join(stagingRoot, `.${SKILL_NAME}.install-${token}-${index}`);
|
|
311
|
+
item.backup = join(stagingRoot, `.${SKILL_NAME}.backup-${token}-${index}`);
|
|
312
|
+
await cp(SOURCE_DIR, item.temporary, { errorOnExist: true, recursive: true });
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
await cleanPrepared(changes);
|
|
316
|
+
throw new Error(`could not prepare every destination; no installed copy was changed: ${error.message}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
for (const item of changes) {
|
|
321
|
+
if (item.destinationExists) {
|
|
322
|
+
await rename(item.destination, item.backup);
|
|
323
|
+
item.backedUp = true;
|
|
324
|
+
}
|
|
325
|
+
await rename(item.temporary, item.destination);
|
|
326
|
+
item.swapped = true;
|
|
327
|
+
}
|
|
328
|
+
} catch (error) {
|
|
329
|
+
const rollbackErrors = [];
|
|
330
|
+
for (const item of [...changes].reverse()) {
|
|
331
|
+
try {
|
|
332
|
+
if (item.swapped && (await exists(item.destination))) {
|
|
333
|
+
await rm(item.destination, { force: true, recursive: true });
|
|
334
|
+
}
|
|
335
|
+
if (item.backedUp && (await exists(item.backup))) {
|
|
336
|
+
await rename(item.backup, item.destination);
|
|
337
|
+
}
|
|
338
|
+
if (item.temporary && (await exists(item.temporary))) {
|
|
339
|
+
await rm(item.temporary, { force: true, recursive: true });
|
|
340
|
+
}
|
|
341
|
+
} catch (rollbackError) {
|
|
342
|
+
rollbackErrors.push(`${item.destination}: ${rollbackError.message}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (rollbackErrors.length > 0) {
|
|
346
|
+
throw new Error(
|
|
347
|
+
`install failed and rollback needs attention (${rollbackErrors.join("; ")}): ${error.message}`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
throw new Error(`install failed; every destination was rolled back: ${error.message}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
for (const item of changes) {
|
|
354
|
+
if (item.backedUp && (await exists(item.backup))) {
|
|
355
|
+
await rm(item.backup, { force: true, recursive: true });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return plan.map((item) => ({
|
|
360
|
+
action: item.current ? "current" : item.destinationExists ? "replaced" : "installed",
|
|
361
|
+
destination: item.destination,
|
|
362
|
+
targetName: item.targetName,
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function main() {
|
|
367
|
+
let options;
|
|
368
|
+
try {
|
|
369
|
+
options = parseArgs(process.argv.slice(2));
|
|
370
|
+
} catch (error) {
|
|
371
|
+
fail(error.message);
|
|
372
|
+
process.stderr.write(`\n${usage()}\n`);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (options.help) {
|
|
377
|
+
process.stdout.write(`${usage()}\n`);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (options.listTargets) {
|
|
381
|
+
printTargets();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
await verifySource();
|
|
387
|
+
const plan = await buildPlan(destinations(options), options);
|
|
388
|
+
const results = options.dryRun ? planResults(plan, options) : await applyPlan(plan);
|
|
389
|
+
process.stdout.write(`${options.dryRun ? "Install plan" : "Install result"}:\n`);
|
|
390
|
+
for (const result of results) {
|
|
391
|
+
process.stdout.write(
|
|
392
|
+
` ${result.action.padEnd(13)} ${result.destination} (${result.targetName})\n`,
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
} catch (error) {
|
|
396
|
+
fail(error.message);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
await main();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agora
|
|
3
|
+
description: Write, rewrite, shorten, critique, or plan evidence-led marketing and sales content using the Agora argument-first method. Use when invoked as `/agora` and for marketing and sales copy; CTAs and microcopy; landing, product, and comparison pages; email and direct outreach; mobile-app onboarding, upgrade, and paywall screens; ads and social posts; editorial content; and spoken audio/video scripts plus written derivatives such as titles, descriptions, transcripts, captions, show notes, and companion pages.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Maestro: Agora
|
|
7
|
+
|
|
8
|
+
## Accept direct invocation
|
|
9
|
+
|
|
10
|
+
Treat `/agora` as explicit activation. Use all text after the command as the task. If no task follows, ask for the asset or source copy.
|
|
11
|
+
|
|
12
|
+
## Load the authority
|
|
13
|
+
|
|
14
|
+
Read [references/agora-marketing.md](references/agora-marketing.md) before producing or reviewing copy. Treat it as the canonical marketing-method reference. Preserve its evidence grades, limits, ethical controls, AI-writing-tell gate, and GEO/AEO boundaries.
|
|
15
|
+
|
|
16
|
+
Treat current user-supplied facts, product documentation, live behavior, legal constraints, brand guidance, and approved claim sources as product truth. Never treat an example or marketing principle in the reference as authorization for a product claim.
|
|
17
|
+
|
|
18
|
+
Apply CiteSurge-specific controls only when the request, named product, or active repository is CiteSurge. Ignore those controls for every other brand or app; use that project's verified rules instead. When giving engine-specific technical advice, recheck current official documentation because crawler and product behavior can change.
|
|
19
|
+
|
|
20
|
+
## Route the asset
|
|
21
|
+
|
|
22
|
+
Classify silently before working:
|
|
23
|
+
|
|
24
|
+
| Class | Apply |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `INDEXABLE_PUBLIC` | Agora argument, proof and public-claim review, human-voice gate, full written GEO/AEO gate, and technical publication handoff. |
|
|
27
|
+
| `PUBLIC_NON_INDEXABLE_WRITTEN` | Agora argument, proof and public-claim review, human-voice gate, and written semantic/evidence rules. Skip crawl, canonical, schema, sitemap, and indexability checks. |
|
|
28
|
+
| `WRITTEN_PRIVATE` | Agora argument, proof fidelity, human-voice gate, concrete entities, and self-contained claims. Skip public technical checks. |
|
|
29
|
+
| `SPOKEN_ONLY` | Agora argument, proof gate, human cadence, breath, timing, and listener comprehension. Skip GEO/AEO formatting. |
|
|
30
|
+
| `HYBRID` | Classify each surface separately. Treat spoken delivery as spoken-only; treat every published title, description, transcript, caption, show note, and companion page as written. |
|
|
31
|
+
|
|
32
|
+
## Follow the workflow
|
|
33
|
+
|
|
34
|
+
1. Capture the channel, primary audience, decision stage, problem or desire, current belief, one defensible destination belief, offer, one next action, verified proof, claim limits, voice, and format constraints. Infer only low-risk context. Ask only when missing information would materially change the audience, offer, claim, or action.
|
|
35
|
+
2. Prioritize truth and safety, verified facts and brand constraints, buyer decision, clarity and citability, persuasion, brevity, then polish.
|
|
36
|
+
3. Build the minimum complete argument. Select only needed moves: recognizable reality, real stakes, broken assumption, new criterion, reason to believe, difference and proof, tangible after-state, offer, and action. Keep one primary audience, destination belief, and CTA intent.
|
|
37
|
+
4. Create emotion from a true situation, concrete consequence, and available agency. Preserve trade-offs and material conditions. Apply narrative, curiosity, fear, loss framing, guilt, scarcity, and social proof only within the reference's conditional controls.
|
|
38
|
+
5. Attach proof to each material premise. Distinguish fact, inference, opinion, and promise. Keep source, date, scope, and limitation with the claim. Narrow or remove any claim whose proof is missing.
|
|
39
|
+
6. Draft for the channel. Use the shortest complete decision path suited to device, intent, offer complexity, and risk. Keep one main job per block and one main claim per sentence. Do not force a long sales structure onto simple high-intent or transactional copy.
|
|
40
|
+
7. Make the CTA the logical next step. Name one real action, predict the next screen or commitment, match intent, and add no new claim. Verify the destination or action exists.
|
|
41
|
+
8. Apply written GEO/AEO and citability rules to every written asset: answer early when appropriate, name entities and scope, create self-contained evidence blocks, keep proof beside claims, add genuine information value, structure for reader intent, expose provenance, link contextually, and keep visible and machine-readable facts consistent. Add technical eligibility checks only for indexable public work. Never promise retrieval, selection, quotation, citation, ranking, recommendation, referral, conversion, or revenue.
|
|
42
|
+
9. Skip GEO/AEO formatting for spoken-only delivery. Optimize spoken copy for natural cadence, breath, timing, emphasis, and comprehension. Apply written rules separately to any published derivative.
|
|
43
|
+
10. Run the competitor-swap, read-aloud, defense, continuity, and density tests. Remove vague hype, stock openings, prompt acknowledgements, repeated templates, decorative recaps, generic conclusions, fabricated human texture, and detector-evasion claims.
|
|
44
|
+
11. Compress in the reference's order. Preserve evidence, qualifications, legal terms, accessibility, and next-step clarity. Do not convert `HOUSE`, `C`, `EVIDENCE-INFORMED`, or `TEST` guidance into universal performance claims.
|
|
45
|
+
12. Verify every final factual statement and CTA against supplied sources and current project truth. For CiteSurge work only, apply its claim-ledger, methodology, AI-writing scanner, GEO/AEO positioning, and release controls from the reference.
|
|
46
|
+
|
|
47
|
+
## Enforce the claim gate
|
|
48
|
+
|
|
49
|
+
Never invent or imply unsupported claims, features, prices, routes, evidence, deadlines, urgency, scarcity, testimonials, results, motives, pain, exclusivity, comparisons, or guarantees. Never conceal price, fit, risk, evidence, or material conditions to create curiosity.
|
|
50
|
+
|
|
51
|
+
When required truth is unavailable, omit or narrow the claim. If omission would make the asset unusable, put a concise verification need after the ready-to-use copy; do not fill the gap with a guess.
|
|
52
|
+
|
|
53
|
+
## Return the result
|
|
54
|
+
|
|
55
|
+
Return ready-to-use copy first. Do not expose chain-of-thought, internal classification, planning scaffolds, or rule-by-rule reasoning. Add assumptions, source gaps, rationale, or tests only when requested or materially necessary.
|
|
56
|
+
|
|
57
|
+
Default to one shortest complete output suited to the channel. Do not provide multiple near-duplicate variants unless requested. For critiques, lead with the revised copy when rewriting is authorized; otherwise lead with the most consequential actionable findings.
|