appostle-installer 0.0.18 → 0.0.20
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/appostle.js +141 -134
- package/dist/appostle.js.map +2 -2
- package/dist/schema-templates/animations.md +32 -43
- package/dist/schema-templates/colors.md +12 -0
- package/dist/schema-templates/shadows.md +5 -0
- package/dist/schema-templates/shapes.md +8 -8
- package/dist/schema-templates/typography.md +66 -0
- package/dist/worker.js +142 -134
- package/dist/worker.js.map +2 -2
- package/package.json +1 -1
package/dist/appostle.js
CHANGED
|
@@ -1922,6 +1922,10 @@ function toAgentPayload(agent, options) {
|
|
|
1922
1922
|
title: options?.title ?? null,
|
|
1923
1923
|
labels: agent.labels,
|
|
1924
1924
|
internal: agent.internal,
|
|
1925
|
+
// Forward archivedAt on the live broadcast so paired clients (e.g. mobile)
|
|
1926
|
+
// see the archive land — without this, only the device that ran the
|
|
1927
|
+
// archive mutation knows about it.
|
|
1928
|
+
archivedAt: agent.archivedAt ?? null,
|
|
1925
1929
|
// Surface ownership so the client can render an owner badge / detect
|
|
1926
1930
|
// "shared with me" agents. `sharedWithUserIds` deliberately stays off
|
|
1927
1931
|
// the snapshot — only owners read the full ACL, via the dedicated
|
|
@@ -3203,6 +3207,7 @@ var RoleMoveResponseSchema = z10.object({
|
|
|
3203
3207
|
var BrandScopeSchema = z10.literal("project");
|
|
3204
3208
|
var BrandVariableTypeSchema = z10.enum([
|
|
3205
3209
|
"color",
|
|
3210
|
+
"gradient",
|
|
3206
3211
|
"font",
|
|
3207
3212
|
"asset",
|
|
3208
3213
|
"text",
|
|
@@ -19463,11 +19468,53 @@ var ClaudeAgentSession = class {
|
|
|
19463
19468
|
// sub-agents stop announcing them as suspected prompt injections. See
|
|
19464
19469
|
// getSystemReminderGuidance for the full rationale.
|
|
19465
19470
|
getSystemReminderGuidance(),
|
|
19466
|
-
|
|
19467
|
-
|
|
19471
|
+
//
|
|
19472
|
+
//
|
|
19473
|
+
//
|
|
19474
|
+
//
|
|
19475
|
+
// ╔══════════════════════════════════════════════════════════════════╗
|
|
19476
|
+
// ║ CUSTOM INSTRUCTIONS — add new system prompt lines below here ║
|
|
19477
|
+
// ║ Each line is a quoted string: "your instruction here", ║
|
|
19478
|
+
// ╚══════════════════════════════════════════════════════════════════╝
|
|
19479
|
+
//
|
|
19480
|
+
//
|
|
19481
|
+
//
|
|
19482
|
+
//
|
|
19483
|
+
"Default response shape: open with a short, scannable plain-English read. 2\u20134 short sentences, one idea each, no comma-chained clauses or em-dash pile-ups. When the read covers 3+ discrete points, use bullets instead of prose. Then the technical detail in dense form (paths, line refs, code) without narration. Don\u2019t explain what well-named code already explains. Skip the shape for trivial questions.",
|
|
19484
|
+
//
|
|
19468
19485
|
"When the user sends `>learn`, re-explain your previous message in plain English only \u2014 1 short paragraph that helps them build the mental model. No code, no file references, no technical repeat.",
|
|
19486
|
+
//
|
|
19487
|
+
//
|
|
19488
|
+
//
|
|
19489
|
+
//
|
|
19490
|
+
// ╔══════════════════════════════════════════════════════════════════╗
|
|
19491
|
+
// ║ END CUSTOM INSTRUCTIONS — don't edit below this line ║
|
|
19492
|
+
// ╚══════════════════════════════════════════════════════════════════╝
|
|
19493
|
+
//
|
|
19494
|
+
//
|
|
19495
|
+
//
|
|
19496
|
+
//
|
|
19469
19497
|
this.config.systemPrompt?.trim()
|
|
19470
19498
|
].filter((entry) => typeof entry === "string" && entry.length > 0).join("\n\n");
|
|
19499
|
+
const appostleAgents = {
|
|
19500
|
+
researcher: {
|
|
19501
|
+
description: "Use this agent when you need to explore unfamiliar code, trace data flow across modules, search broadly across the codebase, or read more than 2 files you haven't seen yet. Delegate investigation work here to keep the main conversation context lean.",
|
|
19502
|
+
prompt: "You are a codebase researcher. Your job is to investigate, trace, and report findings \u2014 never edit files. Read code, grep for patterns, follow imports, and build a clear picture. Report back a concise summary of what you found: key files, relevant code paths, and your conclusion. Keep your report under 300 words unless the investigation is complex.",
|
|
19503
|
+
model: "sonnet"
|
|
19504
|
+
},
|
|
19505
|
+
refactorer: {
|
|
19506
|
+
description: "Use this agent for refactors that touch more than 2 files \u2014 renames, migrations, pattern replacements, moving code between modules. Delegate multi-file changes here to isolate the blast radius.",
|
|
19507
|
+
prompt: "You are a refactoring specialist. Make the requested changes across all affected files. Be thorough \u2014 update imports, references, types, and tests. Run typecheck after changes if available. Report what you changed and any issues found."
|
|
19508
|
+
},
|
|
19509
|
+
reviewer: {
|
|
19510
|
+
description: "Use this agent to review code for bugs, security issues, performance problems, or style violations. Use it before committing large changes or when the user asks for a review.",
|
|
19511
|
+
prompt: "You are a code reviewer. Analyze the code for correctness, security vulnerabilities (OWASP top 10), performance issues, and adherence to the project's coding standards. Be specific \u2014 cite file paths and line numbers. Flag severity: critical, warning, or nitpick. Keep the review focused and actionable."
|
|
19512
|
+
},
|
|
19513
|
+
debugger: {
|
|
19514
|
+
description: "Use this agent to investigate bugs \u2014 read logs, trace error paths, check database state, reproduce issues. Delegate debugging here when the root cause isn't obvious from a quick look.",
|
|
19515
|
+
prompt: "You are a debugger. Your job is to find the root cause, not patch symptoms. Check real data first \u2014 logs, network requests, database state. Trace the full lifecycle of the bug. Present your findings: root cause, evidence, and a proposed minimal fix. Do not edit files unless explicitly asked."
|
|
19516
|
+
}
|
|
19517
|
+
};
|
|
19471
19518
|
const claudeBinary = await findExecutable("claude");
|
|
19472
19519
|
this.logger.debug(
|
|
19473
19520
|
{
|
|
@@ -19485,7 +19532,7 @@ var ClaudeAgentSession = class {
|
|
|
19485
19532
|
// bypass launch capability available so later setPermissionMode("bypassPermissions")
|
|
19486
19533
|
// calls do not fail after a model/thinking/rewind-driven restart.
|
|
19487
19534
|
allowDangerouslySkipPermissions: true,
|
|
19488
|
-
agents: this.defaults?.agents,
|
|
19535
|
+
agents: { ...appostleAgents, ...this.defaults?.agents },
|
|
19489
19536
|
canUseTool: this.handlePermissionRequest,
|
|
19490
19537
|
...claudeBinary ? { pathToClaudeCodeExecutable: claudeBinary } : {},
|
|
19491
19538
|
// Use Claude Code preset system prompt and load CLAUDE.md files
|
|
@@ -35111,6 +35158,7 @@ ${original}`;
|
|
|
35111
35158
|
// ../server/src/server/brand/token-generator.ts
|
|
35112
35159
|
import { z as z37 } from "zod";
|
|
35113
35160
|
var HEX6 = /^#[0-9a-f]{6}$/i;
|
|
35161
|
+
var CSS_GRADIENT = /^(linear|radial|conic)-gradient\(/i;
|
|
35114
35162
|
var TokensResponseSchema = z37.object({
|
|
35115
35163
|
tokens: z37.record(z37.string(), z37.string())
|
|
35116
35164
|
});
|
|
@@ -35186,6 +35234,31 @@ function buildPrompt2(args) {
|
|
|
35186
35234
|
}
|
|
35187
35235
|
}
|
|
35188
35236
|
const tokensToFill = args.unlockedTokens.map((v) => `- ${v.key}`).join("\n");
|
|
35237
|
+
const gradientSection = args.unlockedGradients.length > 0 ? [
|
|
35238
|
+
"",
|
|
35239
|
+
"Gradient tokens to fill (values must be valid CSS gradient strings,",
|
|
35240
|
+
"e.g. linear-gradient(135deg, #c52669 0%, #ff6b35 100%)):",
|
|
35241
|
+
...args.unlockedGradients.map((v) => `- ${v.key}`),
|
|
35242
|
+
"",
|
|
35243
|
+
"Gradient key glossary:",
|
|
35244
|
+
"- gradient.primary \u2014 the brand's signature gradient, built from the",
|
|
35245
|
+
" most prominent palette colours. Used for hero backgrounds, feature",
|
|
35246
|
+
" highlights, and primary decorative surfaces.",
|
|
35247
|
+
"- gradient.accent \u2014 a secondary gradient for interactive states,",
|
|
35248
|
+
" hover effects, or accent surfaces. Distinct from primary but",
|
|
35249
|
+
" harmonious with the palette.",
|
|
35250
|
+
"- gradient.subtle \u2014 a very soft, low-contrast gradient for subtle",
|
|
35251
|
+
" background washes \u2014 card backgrounds, section tints. Should feel",
|
|
35252
|
+
" almost invisible; same hue family as bg-base with minimal shift.",
|
|
35253
|
+
"",
|
|
35254
|
+
"Include gradient values in the same JSON under the 'tokens' key,",
|
|
35255
|
+
"alongside the hex tokens. Gradient values are CSS gradient strings."
|
|
35256
|
+
].join("\n") : "";
|
|
35257
|
+
const lockedGradientSection = args.lockedGradients.length > 0 ? [
|
|
35258
|
+
"",
|
|
35259
|
+
"Locked gradients \u2014 DO NOT include these in your output:",
|
|
35260
|
+
...args.lockedGradients.map((v) => `- ${v.key}: ${v.value}`)
|
|
35261
|
+
].join("\n") : "";
|
|
35189
35262
|
const lockedSection = args.lockedTokens.length > 0 ? [
|
|
35190
35263
|
"",
|
|
35191
35264
|
"Locked tokens \u2014 DO NOT include these in your output. The user has",
|
|
@@ -35232,13 +35305,15 @@ function buildPrompt2(args) {
|
|
|
35232
35305
|
"Tokens to fill (every value must be a #RRGGBB lowercase hex string):",
|
|
35233
35306
|
tokensToFill,
|
|
35234
35307
|
lockedSection,
|
|
35308
|
+
gradientSection,
|
|
35309
|
+
lockedGradientSection,
|
|
35235
35310
|
directionSection,
|
|
35236
35311
|
"",
|
|
35237
35312
|
"Return JSON only with the shape:",
|
|
35238
|
-
'{ "tokens": { "<token-key>": "#rrggbb", ... } }',
|
|
35313
|
+
'{ "tokens": { "<token-key>": "#rrggbb or css-gradient-string", ... } }',
|
|
35239
35314
|
"",
|
|
35240
|
-
"Output ONLY the unlocked tokens listed above. Do not include
|
|
35241
|
-
"
|
|
35315
|
+
"Output ONLY the unlocked tokens and gradients listed above. Do not include",
|
|
35316
|
+
"locked entries, do not invent new keys, do not output anything outside the JSON.",
|
|
35242
35317
|
"Never use a hex value from a colour marked OFF."
|
|
35243
35318
|
].filter((line) => line !== "").join("\n");
|
|
35244
35319
|
}
|
|
@@ -35252,9 +35327,12 @@ async function generateAndApplyBrandTokens(options) {
|
|
|
35252
35327
|
const allVars = colorsBrand.variables;
|
|
35253
35328
|
const paletteVars = allVars.filter((v) => v.key.startsWith("color."));
|
|
35254
35329
|
const tokenVars = allVars.filter((v) => v.key.startsWith("token."));
|
|
35330
|
+
const gradientVars = allVars.filter((v) => v.type === "gradient");
|
|
35255
35331
|
const unlockedTokens = tokenVars.filter((v) => !v.locked);
|
|
35256
35332
|
const lockedTokens = tokenVars.filter((v) => v.locked);
|
|
35257
|
-
|
|
35333
|
+
const unlockedGradients = gradientVars.filter((v) => !v.locked);
|
|
35334
|
+
const lockedGradients = gradientVars.filter((v) => v.locked);
|
|
35335
|
+
if (unlockedTokens.length === 0 && unlockedGradients.length === 0) {
|
|
35258
35336
|
logger.info({ brandPath }, "brand-tokens: nothing to generate (all tokens locked)");
|
|
35259
35337
|
return { generatedCount: 0 };
|
|
35260
35338
|
}
|
|
@@ -35265,6 +35343,8 @@ async function generateAndApplyBrandTokens(options) {
|
|
|
35265
35343
|
paletteVars,
|
|
35266
35344
|
unlockedTokens,
|
|
35267
35345
|
lockedTokens,
|
|
35346
|
+
unlockedGradients,
|
|
35347
|
+
lockedGradients,
|
|
35268
35348
|
mode,
|
|
35269
35349
|
userPrompt: prompt
|
|
35270
35350
|
});
|
|
@@ -35290,14 +35370,19 @@ async function generateAndApplyBrandTokens(options) {
|
|
|
35290
35370
|
}
|
|
35291
35371
|
throw error;
|
|
35292
35372
|
}
|
|
35293
|
-
const
|
|
35373
|
+
const unlockedTokenKeySet = new Set(unlockedTokens.map((v) => v.key));
|
|
35374
|
+
const unlockedGradientKeySet = new Set(unlockedGradients.map((v) => v.key));
|
|
35294
35375
|
const acceptedUpdates = /* @__PURE__ */ new Map();
|
|
35295
35376
|
for (const [key, value] of Object.entries(response.tokens)) {
|
|
35296
|
-
if (!unlockedKeySet.has(key)) continue;
|
|
35297
35377
|
if (typeof value !== "string") continue;
|
|
35298
35378
|
const trimmed = value.trim();
|
|
35299
|
-
if (
|
|
35300
|
-
|
|
35379
|
+
if (unlockedTokenKeySet.has(key)) {
|
|
35380
|
+
if (!HEX6.test(trimmed)) continue;
|
|
35381
|
+
acceptedUpdates.set(key, trimmed.toLowerCase());
|
|
35382
|
+
} else if (unlockedGradientKeySet.has(key)) {
|
|
35383
|
+
if (!CSS_GRADIENT.test(trimmed)) continue;
|
|
35384
|
+
acceptedUpdates.set(key, trimmed);
|
|
35385
|
+
}
|
|
35301
35386
|
}
|
|
35302
35387
|
if (acceptedUpdates.size === 0) {
|
|
35303
35388
|
logger.warn(
|
|
@@ -35334,6 +35419,7 @@ import path20 from "node:path";
|
|
|
35334
35419
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
35335
35420
|
import { z as z38 } from "zod";
|
|
35336
35421
|
var QA_FILENAME = "layout-qa.md";
|
|
35422
|
+
var PROMPT_FILENAME = "layout-prompt.md";
|
|
35337
35423
|
var MAX_LOOKUP_LEVELS = 10;
|
|
35338
35424
|
var ROLE_FILE_RELATIVE = ".appostle/brand/assets/role/brand-layout-role.md";
|
|
35339
35425
|
async function findFileUpward(filename) {
|
|
@@ -35442,111 +35528,37 @@ function buildStructuralContext(allBrands) {
|
|
|
35442
35528
|
return lines.length > 0 ? lines.join("\n") : "(No structural context established yet.)";
|
|
35443
35529
|
}
|
|
35444
35530
|
var TARGET_QUESTIONS = 7;
|
|
35445
|
-
|
|
35446
|
-
|
|
35447
|
-
|
|
35448
|
-
|
|
35449
|
-
|
|
35450
|
-
|
|
35451
|
-
|
|
35452
|
-
|
|
35453
|
-
|
|
35454
|
-
|
|
35455
|
-
|
|
35456
|
-
|
|
35457
|
-
|
|
35458
|
-
-
|
|
35459
|
-
|
|
35460
|
-
|
|
35461
|
-
|
|
35462
|
-
- What grid philosophy \u2014 strict 12-column, asymmetric, modular, or freeform?
|
|
35463
|
-
|
|
35464
|
-
## Containers & Cards
|
|
35465
|
-
- What's your card philosophy \u2014 flat, elevated, outlined, or glassmorphic?
|
|
35466
|
-
|
|
35467
|
-
## Image Treatment
|
|
35468
|
-
- What role do images play \u2014 hero-level, supporting, or minimal?
|
|
35469
|
-
|
|
35470
|
-
## CTA Strategy
|
|
35471
|
-
- How many CTAs per page and what's the hierarchy?
|
|
35472
|
-
|
|
35473
|
-
## Mobile Behavior
|
|
35474
|
-
- How should the desktop layout transform on mobile?
|
|
35475
|
-
|
|
35476
|
-
## Prohibitions & Bans
|
|
35477
|
-
- What design patterns are absolutely forbidden?
|
|
35478
|
-
- Any CSS properties or techniques that are banned?`;
|
|
35531
|
+
async function loadSpecFile(filename, logger) {
|
|
35532
|
+
const filePath = await findFileUpward(filename);
|
|
35533
|
+
if (!filePath) {
|
|
35534
|
+
throw new Error(
|
|
35535
|
+
`layout-generator: ${filename} not found by walking up from ${fileURLToPath2(import.meta.url)}. This file is the canonical spec and must exist at the appostle repo root.`
|
|
35536
|
+
);
|
|
35537
|
+
}
|
|
35538
|
+
try {
|
|
35539
|
+
const content = await fs12.readFile(filePath, "utf8");
|
|
35540
|
+
logger.debug({ filePath }, `layout-generator: loaded ${filename} from disk`);
|
|
35541
|
+
return content;
|
|
35542
|
+
} catch (err) {
|
|
35543
|
+
throw new Error(
|
|
35544
|
+
`layout-generator: failed to read ${filename} at ${filePath}: ${err.message}`
|
|
35545
|
+
);
|
|
35546
|
+
}
|
|
35547
|
+
}
|
|
35479
35548
|
async function loadQaQuestions(logger) {
|
|
35480
|
-
|
|
35481
|
-
|
|
35482
|
-
|
|
35483
|
-
|
|
35484
|
-
|
|
35485
|
-
|
|
35486
|
-
|
|
35487
|
-
|
|
35488
|
-
|
|
35489
|
-
"layout-generator: failed to read Q&A file; using embedded fallback"
|
|
35490
|
-
);
|
|
35491
|
-
}
|
|
35549
|
+
return loadSpecFile(QA_FILENAME, logger);
|
|
35550
|
+
}
|
|
35551
|
+
async function loadLayoutPrompt(logger) {
|
|
35552
|
+
return loadSpecFile(PROMPT_FILENAME, logger);
|
|
35553
|
+
}
|
|
35554
|
+
function interpolateTemplate(template, vars) {
|
|
35555
|
+
let out = template;
|
|
35556
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
35557
|
+
out = out.split(`{{${key}}}`).join(value);
|
|
35492
35558
|
}
|
|
35493
|
-
return
|
|
35559
|
+
return out;
|
|
35494
35560
|
}
|
|
35495
|
-
|
|
35496
|
-
|
|
35497
|
-
The output must be a SINGLE cohesive markdown document (NOT JSON, NOT key-value pairs). It reads like a design manifesto \u2014 dense, opinionated, specific enough that two different builders reading it would produce nearly identical structural decisions.
|
|
35498
|
-
|
|
35499
|
-
## Required sections (use these exact headings)
|
|
35500
|
-
|
|
35501
|
-
# Brand Layout Role
|
|
35502
|
-
|
|
35503
|
-
## Compositional Philosophy
|
|
35504
|
-
The north star. 3-5 sentences describing the fundamental spatial personality.
|
|
35505
|
-
|
|
35506
|
-
## Hero Behavior
|
|
35507
|
-
Exact rules for hero sections \u2014 dimensions, content placement, image treatment, scroll behavior, what's forbidden.
|
|
35508
|
-
|
|
35509
|
-
## Section Variation & Flow
|
|
35510
|
-
How consecutive sections differ. Allowed arrangements. How many types cycle. Full-bleed rules.
|
|
35511
|
-
|
|
35512
|
-
## Density Philosophy
|
|
35513
|
-
Where dense, where airy, specific spacing ratios, padding rules.
|
|
35514
|
-
|
|
35515
|
-
## Vertical Rhythm
|
|
35516
|
-
Section height strategy, padding patterns, oscillation rules, breathing room logic.
|
|
35517
|
-
|
|
35518
|
-
## Grid System
|
|
35519
|
-
Column philosophy, alignment rules, breakpoint behavior, visible vs invisible structure.
|
|
35520
|
-
|
|
35521
|
-
## Container & Card Rules
|
|
35522
|
-
Borders, shadows, corners, nesting rules, elevation hierarchy.
|
|
35523
|
-
|
|
35524
|
-
## Image Treatment
|
|
35525
|
-
Aspect ratios, size constraints, cropping rules, filter/overlay rules, frequency.
|
|
35526
|
-
|
|
35527
|
-
## CTA Strategy
|
|
35528
|
-
Frequency per page, hierarchy, styling constraints, placement rules.
|
|
35529
|
-
|
|
35530
|
-
## Mobile Behavior
|
|
35531
|
-
Breakpoint strategy, what collapses vs stacks, density changes, navigation transformation.
|
|
35532
|
-
|
|
35533
|
-
## Bans
|
|
35534
|
-
The most powerful section. At least 20 specific prohibitions at EVERY level:
|
|
35535
|
-
- CSS-level (specific properties, values, patterns)
|
|
35536
|
-
- Component-level (specific UI patterns forbidden)
|
|
35537
|
-
- Layout-level (spatial patterns forbidden)
|
|
35538
|
-
- Content-level (content patterns forbidden)
|
|
35539
|
-
- Interaction-level (motion/animation patterns forbidden)
|
|
35540
|
-
|
|
35541
|
-
Each ban on its own line starting with "\u2022". Be ruthlessly specific \u2014 "no gradients" is vague, "no linear-gradient except single-stop overlays on hero images" is useful.
|
|
35542
|
-
|
|
35543
|
-
## Critical quality bar
|
|
35544
|
-
- Every rule must be specific enough to resolve an ambiguous decision
|
|
35545
|
-
- No generic advice ("keep it clean") \u2014 only actionable constraints
|
|
35546
|
-
- The bans section alone should have 20+ items
|
|
35547
|
-
- Rules should reference specific CSS properties, pixel values, viewport units where applicable
|
|
35548
|
-
- The document should be 50-100 rules total across all sections`;
|
|
35549
|
-
function buildQaSystemPrompt(questions) {
|
|
35561
|
+
function buildQaSystemPrompt(questions, layoutPromptSpec) {
|
|
35550
35562
|
return `You are a chill creative director doing a quick vibe check on someone's layout taste.
|
|
35551
35563
|
|
|
35552
35564
|
CONTEXT:
|
|
@@ -35580,10 +35592,11 @@ When done=false, return:
|
|
|
35580
35592
|
{ "done": false, "question": "Short question?", "options": ["Option A", "Option B", "Option C", "Option D"] }
|
|
35581
35593
|
|
|
35582
35594
|
When done=true, return:
|
|
35583
|
-
{ "done": true, "roleDocument": "# Brand Layout Role\\n\\n##
|
|
35595
|
+
{ "done": true, "roleDocument": "# Brand Layout Role\\n\\n## Enemy\\n..." }
|
|
35584
35596
|
|
|
35585
|
-
The roleDocument must be a complete markdown document following
|
|
35586
|
-
|
|
35597
|
+
The roleDocument must be a complete markdown document following the structure and rules below. Treat the Q&A conversation as the user's intent; there is no current role document yet.
|
|
35598
|
+
|
|
35599
|
+
${layoutPromptSpec}`;
|
|
35587
35600
|
}
|
|
35588
35601
|
async function layoutQaNext(options) {
|
|
35589
35602
|
const { agentManager, workspaceRoot, brandPath, conversation, logger } = options;
|
|
@@ -35592,7 +35605,13 @@ async function layoutQaNext(options) {
|
|
|
35592
35605
|
if (!brand) throw new Error(`Brand file not found at ${brandPath}`);
|
|
35593
35606
|
const structuralContext = buildStructuralContext(brands);
|
|
35594
35607
|
const questions = await loadQaQuestions(logger);
|
|
35595
|
-
const
|
|
35608
|
+
const layoutPromptTemplate = await loadLayoutPrompt(logger);
|
|
35609
|
+
const layoutPromptSpec = interpolateTemplate(layoutPromptTemplate, {
|
|
35610
|
+
structuralContext,
|
|
35611
|
+
currentValues: "(No existing role document \u2014 this is initial generation from a Q&A conversation.)",
|
|
35612
|
+
userPrompt: "(See Q&A conversation below for the user's intent.)"
|
|
35613
|
+
});
|
|
35614
|
+
const qaSystemPrompt = buildQaSystemPrompt(questions, layoutPromptSpec);
|
|
35596
35615
|
const convLines = conversation.map((m) => `${m.role === "assistant" ? "AI" : "User"}: ${m.content}`).join("\n\n");
|
|
35597
35616
|
const userAnswerCount = conversation.filter((m) => m.role === "user").length;
|
|
35598
35617
|
const prompt = [
|
|
@@ -35643,24 +35662,12 @@ async function generateAndApplyLayout(options) {
|
|
|
35643
35662
|
if (!brand) throw new Error(`Brand file not found at ${brandPath}`);
|
|
35644
35663
|
const structuralContext = buildStructuralContext(brands);
|
|
35645
35664
|
const existingRole = await readRoleFile(workspaceRoot);
|
|
35646
|
-
const
|
|
35647
|
-
|
|
35648
|
-
"",
|
|
35649
|
-
"## Structural context (sibling brand files)",
|
|
35665
|
+
const layoutPromptTemplate = await loadLayoutPrompt(logger);
|
|
35666
|
+
const prompt = interpolateTemplate(layoutPromptTemplate, {
|
|
35650
35667
|
structuralContext,
|
|
35651
|
-
"",
|
|
35652
|
-
|
|
35653
|
-
|
|
35654
|
-
${existingRole}` : "(No existing role document \u2014 generate from scratch.)",
|
|
35655
|
-
"",
|
|
35656
|
-
"## User's refinement prompt",
|
|
35657
|
-
userPrompt,
|
|
35658
|
-
"",
|
|
35659
|
-
"This is a REFINEMENT. The user already has a role document (shown above). Their prompt refines, evolves, or redirects \u2014 it does NOT start from scratch unless they explicitly say so.",
|
|
35660
|
-
"",
|
|
35661
|
-
'Return ONLY a JSON object: { "roleDocument": "the complete updated markdown document" }',
|
|
35662
|
-
"No markdown fences. No explanation."
|
|
35663
|
-
].join("\n");
|
|
35668
|
+
currentValues: existingRole ?? "(No existing role document \u2014 generate from scratch.)",
|
|
35669
|
+
userPrompt
|
|
35670
|
+
});
|
|
35664
35671
|
let response;
|
|
35665
35672
|
try {
|
|
35666
35673
|
response = await generateStructuredAgentResponseWithFallback({
|