@mindrian_os/install 1.13.0-beta.14 → 1.13.0-beta.17
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +15 -0
- package/commands/file-meeting.md +2 -0
- package/commands/grade.md +2 -0
- package/commands/mva-brief.md +56 -0
- package/commands/mva-option.md +89 -0
- package/commands/new-project.md +2 -0
- package/commands/onboard.md +2 -0
- package/hooks/hooks.json +9 -0
- package/lib/agents/mva/brain-classic-traps.cjs +77 -0
- package/lib/agents/mva/brain-cross-domain.cjs +79 -0
- package/lib/agents/mva/brain-similar-ventures.cjs +93 -0
- package/lib/agents/mva/dashboard-graph-neighborhood.cjs +72 -0
- package/lib/agents/mva/index.cjs +42 -0
- package/lib/agents/mva/six-hats-red-black.cjs +137 -0
- package/lib/agents/mva/tavily-funding-scan.cjs +147 -0
- package/lib/agents/mva/test-all-six-agents.cjs +467 -0
- package/lib/conversation/operator.cjs +64 -0
- package/lib/conversation/operator.test.cjs +160 -0
- package/lib/core/cache-prune.cjs +114 -8
- package/lib/core/install-state.cjs +242 -0
- package/lib/core/mva-agent-contract.cjs +170 -0
- package/lib/core/mva-agent-contract.test.cjs +169 -0
- package/lib/core/mva-budget.cjs +75 -0
- package/lib/core/mva-budget.test.cjs +68 -0
- package/lib/core/mva-classifier.cjs +370 -0
- package/lib/core/mva-classifier.test.cjs +248 -0
- package/lib/core/mva-deck-builder.cjs +452 -0
- package/lib/core/mva-deck-builder.test.cjs +287 -0
- package/lib/core/mva-detect.smoke.test.cjs +197 -0
- package/lib/core/mva-dispatcher.cjs +110 -0
- package/lib/core/mva-dispatcher.test.cjs +216 -0
- package/lib/core/mva-option-router.cjs +292 -0
- package/lib/core/mva-option-router.test.cjs +483 -0
- package/lib/core/mva-orchestrator.cjs +324 -0
- package/lib/core/mva-orchestrator.test.cjs +908 -0
- package/lib/core/mva-progressive-renderer.cjs +194 -0
- package/lib/core/mva-progressive-renderer.test.cjs +157 -0
- package/lib/core/mva-rule-linter.cjs +213 -0
- package/lib/core/mva-rule-linter.test.cjs +336 -0
- package/lib/core/mva-state.cjs +159 -0
- package/lib/core/mva-telemetry.cjs +170 -0
- package/lib/core/mva-telemetry.test.cjs +196 -0
- package/lib/core/mva-vercel-deploy.cjs +168 -0
- package/lib/core/mva-vercel-deploy.test.cjs +239 -0
- package/lib/core/navigation/dashboard-helpers.cjs +145 -0
- package/lib/core/navigation.cjs +11 -0
- package/lib/core/resolve-vercel-key.cjs +107 -0
- package/lib/core/resolve-vercel-key.test.cjs +137 -0
- package/lib/memory/run-feynman-tests.cjs +27 -0
- package/package.json +1 -1
- package/skills/mva-pipeline/SKILL.md +129 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
3
|
+
*
|
|
4
|
+
* Phase 118-04 Plan 04 Task 2 -- mva-deck-builder.
|
|
5
|
+
*
|
|
6
|
+
* Pure function: buildDeck(orchestratorOutcome) -> string (full HTML document).
|
|
7
|
+
*
|
|
8
|
+
* De Stijl theme, INLINE styles ONLY (no <style> blocks anywhere). Mondrian
|
|
9
|
+
* palette mirrored from the shipped De Stijl source-of-truth (lib/wiki/
|
|
10
|
+
* wiki-layout.cjs CSS vars + lib/core/visual-ops.cjs DS_HEX). System fonts
|
|
11
|
+
* (Impact, Helvetica, Courier). 5-7 slides depending on agent return count.
|
|
12
|
+
*
|
|
13
|
+
* Canon Part 8 invariants:
|
|
14
|
+
* - Deck contains the rendered summary_lines + structured deck_data
|
|
15
|
+
* (already sanitized by Plan 118-02 agents).
|
|
16
|
+
* - Raw sentence is NEVER serialized; only the sha8 marker appears.
|
|
17
|
+
* - No raw error strings (Plan 118-02 agents already redact).
|
|
18
|
+
*
|
|
19
|
+
* Em-dash discipline (per feedback_no_emdashes.md): all emitted text uses
|
|
20
|
+
* `--` and `-`. The 3-option footer uses `--` not `—`. Verified by Test 2.
|
|
21
|
+
*
|
|
22
|
+
* No real-names discipline (per feedback_no_real_names_in_repo.md): the
|
|
23
|
+
* template + builder never spell tester/advisor/partner names. Verified by
|
|
24
|
+
* Test 13.
|
|
25
|
+
*
|
|
26
|
+
* Per OQ13 lean: footer attributes "Generated by MindrianOS" with link to
|
|
27
|
+
* the install minisite.
|
|
28
|
+
*
|
|
29
|
+
* Per OQ15 lean: footer shows the 3-option static text + a copy hint for
|
|
30
|
+
* /mos:new-project --from-brief <sha8>. Real routing happens in the terminal
|
|
31
|
+
* (Plan 118-05), not in the deployed HTML.
|
|
32
|
+
*
|
|
33
|
+
* NIT-3 palette parity: DECK_PALETTE matches wiki-layout.cjs CSS variables
|
|
34
|
+
* and visual-ops.cjs DS_HEX values to maintain product coherence between the
|
|
35
|
+
* MVA deck and the existing De Stijl wiki surface.
|
|
36
|
+
*
|
|
37
|
+
* Pure CJS. fs.readFileSync at module load only (for the template). No I/O
|
|
38
|
+
* elsewhere.
|
|
39
|
+
*/
|
|
40
|
+
'use strict';
|
|
41
|
+
|
|
42
|
+
const fs = require('node:fs');
|
|
43
|
+
const path = require('node:path');
|
|
44
|
+
|
|
45
|
+
// ---------- Module-level constants ----------
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* DECK_PALETTE -- frozen De Stijl Mondrian colors.
|
|
49
|
+
*
|
|
50
|
+
* Mirrors the shipped source-of-truth:
|
|
51
|
+
* - lib/core/visual-ops.cjs::DS_HEX (used by Mermaid diagrams)
|
|
52
|
+
* - lib/wiki/wiki-layout.cjs CSS variables (--ds-red, --ds-blue, etc.)
|
|
53
|
+
*
|
|
54
|
+
* NOTE on palette divergence: the plan's <interfaces> section documented an
|
|
55
|
+
* aspirational classic-Mondrian palette (#E0162B/#F8D43E/#0F52BA/#7A4FA0).
|
|
56
|
+
* Canon Part 7 (Reuse Before Build) + the NIT-3 palette-parity test override:
|
|
57
|
+
* the DECK_PALETTE here uses the EXISTING De Stijl values so the wiki surface
|
|
58
|
+
* and the MVA deck feel like one coherent product. This divergence is
|
|
59
|
+
* documented in 118-04-feynman-deck-vercel-SUMMARY.md.
|
|
60
|
+
*/
|
|
61
|
+
const DECK_PALETTE = Object.freeze({
|
|
62
|
+
red: '#A63D2F', // ds-red
|
|
63
|
+
blue: '#1E3A6E', // ds-blue
|
|
64
|
+
yellow: '#C8A43C', // ds-yellow
|
|
65
|
+
green: '#2D6B4A', // ds-green
|
|
66
|
+
amethyst: '#6B4E8B', // ds-amethyst
|
|
67
|
+
sienna: '#B5602A', // ds-sienna (for accent rails)
|
|
68
|
+
cream: '#F5F0E8', // ds-cream (canvas)
|
|
69
|
+
black: '#0D0D0D', // ds-bg (text)
|
|
70
|
+
muted: '#A09A90', // ds-muted
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Load the template at module-load time (cached). Throws on missing file --
|
|
74
|
+
// that's a build-time failure, not a runtime one.
|
|
75
|
+
const TEMPLATE_PATH = path.join(__dirname, '..', '..', 'data', 'mva-deck-template.html');
|
|
76
|
+
const TEMPLATE = fs.readFileSync(TEMPLATE_PATH, 'utf8');
|
|
77
|
+
|
|
78
|
+
// Per-agent slide accent color (deterministic rotation through palette)
|
|
79
|
+
const AGENT_ACCENT = Object.freeze({
|
|
80
|
+
brain_similar: DECK_PALETTE.red,
|
|
81
|
+
brain_cross_domain: DECK_PALETTE.yellow,
|
|
82
|
+
brain_classic_traps: DECK_PALETTE.blue,
|
|
83
|
+
tavily_funding: DECK_PALETTE.green,
|
|
84
|
+
six_hats_red_black: DECK_PALETTE.amethyst,
|
|
85
|
+
dashboard_graph: DECK_PALETTE.sienna,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const AGENT_LABEL = Object.freeze({
|
|
89
|
+
brain_similar: 'Similar Ventures',
|
|
90
|
+
brain_cross_domain: 'Cross-Domain Analogy',
|
|
91
|
+
brain_classic_traps: 'Classic Traps',
|
|
92
|
+
tavily_funding: 'Live Funding Scan',
|
|
93
|
+
six_hats_red_black: 'Worth Chewing On',
|
|
94
|
+
dashboard_graph: 'Your Room',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const INSTALL_SITE_URL = 'https://mindrianos-install-site.vercel.app';
|
|
98
|
+
|
|
99
|
+
// Sharp-question fallback (verbatim from Plan 118-03 renderer; em-dash-free)
|
|
100
|
+
const SHARP_QUESTION_LINES = [
|
|
101
|
+
"I didn't find precedents for this in 30 seconds.",
|
|
102
|
+
"That's either a gap in my data or a signal that you're in a genuinely unexplored space.",
|
|
103
|
+
"Which do you think it is?",
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
// Hebrew refusal (defensive -- per LD1 the orchestrator short-circuits before
|
|
107
|
+
// invoking the deck builder on locale=he, but we mirror the safety here too)
|
|
108
|
+
const HEBREW_REFUSAL_LINES = [
|
|
109
|
+
'MindrianOS does not yet support Hebrew in v1.13.0. Please try in English.',
|
|
110
|
+
'MindrianOS לא תומך בעברית ב-v1.13.0; אנא נסה באנגלית.',
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
// ---------- Helpers ----------
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Escape HTML in untrusted text. Used for every dynamic insertion so that
|
|
117
|
+
* agent summary lines (or any future user-derived content -- there is none
|
|
118
|
+
* today per Canon Part 8) cannot inject markup.
|
|
119
|
+
*/
|
|
120
|
+
function _esc(s) {
|
|
121
|
+
if (s === null || s === undefined) return '';
|
|
122
|
+
return String(s)
|
|
123
|
+
.replace(/&/g, '&')
|
|
124
|
+
.replace(/</g, '<')
|
|
125
|
+
.replace(/>/g, '>')
|
|
126
|
+
.replace(/"/g, '"')
|
|
127
|
+
.replace(/'/g, ''');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _summaryLine(payload) {
|
|
131
|
+
if (!payload || typeof payload !== 'object') return '';
|
|
132
|
+
if (typeof payload.summary_line === 'string') return payload.summary_line;
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function _agentLabelFor(agent_id) {
|
|
137
|
+
return AGENT_LABEL[agent_id] || agent_id;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function _accentFor(agent_id) {
|
|
141
|
+
return AGENT_ACCENT[agent_id] || DECK_PALETTE.muted;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Render the agent-specific deck_data payload as a small structured block.
|
|
146
|
+
* The deck_data shapes vary per agent (Plan 118-02 contract). We render
|
|
147
|
+
* defensively: any unknown / missing shape falls back to a single line.
|
|
148
|
+
*/
|
|
149
|
+
function _renderDeckData(agent_id, payload) {
|
|
150
|
+
if (!payload || typeof payload !== 'object' || !payload.deck_data) return '';
|
|
151
|
+
const d = payload.deck_data;
|
|
152
|
+
const items = [];
|
|
153
|
+
|
|
154
|
+
// brain_similar: ventures list
|
|
155
|
+
if (Array.isArray(d.ventures)) {
|
|
156
|
+
for (const v of d.ventures) {
|
|
157
|
+
items.push(_esc(v.name) + (v.stage ? ' (' + _esc(v.stage) + ')' : ''));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// brain_cross_domain / brain_classic_traps: framework/trap + signature
|
|
161
|
+
if (d.framework || d.trap) {
|
|
162
|
+
items.push(_esc(d.framework || d.trap) +
|
|
163
|
+
(d.signature ? ': ' + _esc(d.signature) : ''));
|
|
164
|
+
}
|
|
165
|
+
// tavily_funding: title + deadline
|
|
166
|
+
if (d.title) {
|
|
167
|
+
items.push(_esc(d.title) + (d.deadline ? ' (deadline ' + _esc(d.deadline) + ')' : ''));
|
|
168
|
+
}
|
|
169
|
+
// six_hats_red_black: question
|
|
170
|
+
if (d.question) {
|
|
171
|
+
items.push(_esc(d.question));
|
|
172
|
+
}
|
|
173
|
+
// dashboard_graph: node list
|
|
174
|
+
if (Array.isArray(d.nodes)) {
|
|
175
|
+
for (const n of d.nodes) {
|
|
176
|
+
items.push(_esc(n));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (items.length === 0) return '';
|
|
181
|
+
const liStyle = 'padding:4px 0;border-bottom:1px solid ' + DECK_PALETTE.muted + ';font-family:Courier,"Courier New",monospace;font-size:13px;color:' + DECK_PALETTE.black + ';';
|
|
182
|
+
return '<ul style="list-style:none;margin:12px 0 0 0;padding:0;">' +
|
|
183
|
+
items.map(function (it) {
|
|
184
|
+
return '<li style="' + liStyle + '">' + it + '</li>';
|
|
185
|
+
}).join('') +
|
|
186
|
+
'</ul>';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ---------- Slide rendering ----------
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* buildSlide(result) -> string. Render one <article> per agent result.
|
|
193
|
+
*
|
|
194
|
+
* Status-aware:
|
|
195
|
+
* ok -> label + summary_line + deck_data list
|
|
196
|
+
* empty -> placeholder (tavily_unavailable has special config-hint text)
|
|
197
|
+
* timeout -> "(still in progress at 45s)"
|
|
198
|
+
* error -> "(skipped)"
|
|
199
|
+
*
|
|
200
|
+
* Each article has a 4px top accent rail in the agent's palette color.
|
|
201
|
+
*/
|
|
202
|
+
function buildSlide(result) {
|
|
203
|
+
if (!result || typeof result !== 'object') return '';
|
|
204
|
+
const agent_id = result.agent_id || 'agent';
|
|
205
|
+
const label = _agentLabelFor(agent_id);
|
|
206
|
+
const accent = _accentFor(agent_id);
|
|
207
|
+
const status = result.status;
|
|
208
|
+
|
|
209
|
+
let body;
|
|
210
|
+
if (status === 'ok') {
|
|
211
|
+
const summary = _summaryLine(result.payload);
|
|
212
|
+
const deckBlock = _renderDeckData(agent_id, result.payload);
|
|
213
|
+
body =
|
|
214
|
+
'<p style="margin:0;font-size:16px;color:' + DECK_PALETTE.black + ';line-height:1.55;">' +
|
|
215
|
+
_esc(summary || '(no summary returned)') +
|
|
216
|
+
'</p>' +
|
|
217
|
+
deckBlock;
|
|
218
|
+
} else if (status === 'empty') {
|
|
219
|
+
const reason = result.payload && typeof result.payload === 'object' ? result.payload.reason : null;
|
|
220
|
+
if (agent_id === 'tavily_funding' && reason === 'tavily_unavailable') {
|
|
221
|
+
body = '<p style="margin:0;font-style:italic;color:' + DECK_PALETTE.muted + ';font-size:15px;">' +
|
|
222
|
+
'Live funding scan: not configured (add TAVILY_API_KEY to ~/.mindrian.env)' +
|
|
223
|
+
'</p>';
|
|
224
|
+
} else {
|
|
225
|
+
body = '<p style="margin:0;font-style:italic;color:' + DECK_PALETTE.muted + ';font-size:15px;">' +
|
|
226
|
+
'(no findings this pass)' +
|
|
227
|
+
'</p>';
|
|
228
|
+
}
|
|
229
|
+
} else if (status === 'timeout') {
|
|
230
|
+
body = '<p style="margin:0;font-style:italic;color:' + DECK_PALETTE.muted + ';font-size:15px;">' +
|
|
231
|
+
'(still in progress at 45s)' +
|
|
232
|
+
'</p>';
|
|
233
|
+
} else {
|
|
234
|
+
// error / unknown
|
|
235
|
+
body = '<p style="margin:0;font-style:italic;color:' + DECK_PALETTE.muted + ';font-size:15px;">' +
|
|
236
|
+
'(skipped)' +
|
|
237
|
+
'</p>';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const articleStyle = [
|
|
241
|
+
'margin:0 0 24px 0',
|
|
242
|
+
'padding:20px 24px',
|
|
243
|
+
'background:#FFFFFF',
|
|
244
|
+
'border-top:4px solid ' + accent,
|
|
245
|
+
'border-left:1px solid ' + DECK_PALETTE.muted,
|
|
246
|
+
'border-right:1px solid ' + DECK_PALETTE.muted,
|
|
247
|
+
'border-bottom:1px solid ' + DECK_PALETTE.muted,
|
|
248
|
+
].join(';');
|
|
249
|
+
|
|
250
|
+
const labelStyle = [
|
|
251
|
+
'margin:0 0 12px 0',
|
|
252
|
+
'font-family:Impact,"Arial Black",sans-serif',
|
|
253
|
+
'font-size:18px',
|
|
254
|
+
'letter-spacing:1px',
|
|
255
|
+
'text-transform:uppercase',
|
|
256
|
+
'color:' + accent,
|
|
257
|
+
].join(';');
|
|
258
|
+
|
|
259
|
+
return '<article style="' + articleStyle + '">' +
|
|
260
|
+
'<h2 style="' + labelStyle + '">' + _esc(label) + '</h2>' +
|
|
261
|
+
body +
|
|
262
|
+
'</article>';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ---------- Header / footer ----------
|
|
266
|
+
|
|
267
|
+
function _buildHeader(sha8) {
|
|
268
|
+
// 5-rectangle Mondrian color bar across the top
|
|
269
|
+
const barStyle = 'display:flex;height:8px;width:100%;';
|
|
270
|
+
const cells = [
|
|
271
|
+
DECK_PALETTE.red, DECK_PALETTE.yellow, DECK_PALETTE.blue,
|
|
272
|
+
DECK_PALETTE.green, DECK_PALETTE.amethyst,
|
|
273
|
+
].map(function (c) {
|
|
274
|
+
return '<div style="flex:1;background:' + c + ';"></div>';
|
|
275
|
+
}).join('');
|
|
276
|
+
|
|
277
|
+
const headerStyle = [
|
|
278
|
+
'padding:24px 32px 16px 32px',
|
|
279
|
+
'background:' + DECK_PALETTE.black,
|
|
280
|
+
'color:' + DECK_PALETTE.cream,
|
|
281
|
+
'border-bottom:2px solid ' + DECK_PALETTE.cream,
|
|
282
|
+
].join(';');
|
|
283
|
+
|
|
284
|
+
const wordmarkStyle = [
|
|
285
|
+
'margin:0',
|
|
286
|
+
'font-family:Impact,"Arial Black",sans-serif',
|
|
287
|
+
'font-size:32px',
|
|
288
|
+
'letter-spacing:3px',
|
|
289
|
+
].join(';');
|
|
290
|
+
|
|
291
|
+
const taglineStyle = [
|
|
292
|
+
'margin:8px 0 0 0',
|
|
293
|
+
'font-size:13px',
|
|
294
|
+
'color:' + DECK_PALETTE.muted,
|
|
295
|
+
'font-family:Courier,"Courier New",monospace',
|
|
296
|
+
].join(';');
|
|
297
|
+
|
|
298
|
+
return '<div style="' + barStyle + '">' + cells + '</div>' +
|
|
299
|
+
'<header style="' + headerStyle + '">' +
|
|
300
|
+
'<h1 style="' + wordmarkStyle + '">MINDRIAN</h1>' +
|
|
301
|
+
'<p style="' + taglineStyle + '">30-Second Brief // sentence ' + _esc(sha8) + '</p>' +
|
|
302
|
+
'</header>';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function _buildFooter(sha8) {
|
|
306
|
+
// 3-option static text per OQ15 lean (no clickable router; the deck is
|
|
307
|
+
// public HTML and can't invoke a local CLI; the user pastes the copy hint
|
|
308
|
+
// into their terminal).
|
|
309
|
+
const footerStyle = [
|
|
310
|
+
'margin-top:32px',
|
|
311
|
+
'padding:24px 32px',
|
|
312
|
+
'background:' + DECK_PALETTE.black,
|
|
313
|
+
'color:' + DECK_PALETTE.cream,
|
|
314
|
+
'border-top:2px solid ' + DECK_PALETTE.cream,
|
|
315
|
+
].join(';');
|
|
316
|
+
|
|
317
|
+
const promptStyle = 'margin:0 0 16px 0;font-family:Impact,"Arial Black",sans-serif;font-size:20px;color:' + DECK_PALETTE.yellow + ';';
|
|
318
|
+
const optionStyle = 'margin:6px 0;font-family:Courier,"Courier New",monospace;font-size:14px;color:' + DECK_PALETTE.cream + ';';
|
|
319
|
+
const copyStyle = [
|
|
320
|
+
'margin-top:20px',
|
|
321
|
+
'padding:12px 16px',
|
|
322
|
+
'background:' + DECK_PALETTE.cream,
|
|
323
|
+
'color:' + DECK_PALETTE.black,
|
|
324
|
+
'border:1px solid ' + DECK_PALETTE.yellow,
|
|
325
|
+
'font-family:Courier,"Courier New",monospace',
|
|
326
|
+
'font-size:13px',
|
|
327
|
+
].join(';');
|
|
328
|
+
|
|
329
|
+
const attribStyle = [
|
|
330
|
+
'margin:24px 0 0 0',
|
|
331
|
+
'padding-top:16px',
|
|
332
|
+
'border-top:1px solid ' + DECK_PALETTE.muted,
|
|
333
|
+
'font-size:11px',
|
|
334
|
+
'color:' + DECK_PALETTE.muted,
|
|
335
|
+
'font-family:Courier,"Courier New",monospace',
|
|
336
|
+
].join(';');
|
|
337
|
+
|
|
338
|
+
// Bottom Mondrian bar
|
|
339
|
+
const bottomBarStyle = 'display:flex;height:8px;width:100%;';
|
|
340
|
+
const bottomCells = [
|
|
341
|
+
DECK_PALETTE.amethyst, DECK_PALETTE.green, DECK_PALETTE.blue,
|
|
342
|
+
DECK_PALETTE.yellow, DECK_PALETTE.red,
|
|
343
|
+
].map(function (c) {
|
|
344
|
+
return '<div style="flex:1;background:' + c + ';"></div>';
|
|
345
|
+
}).join('');
|
|
346
|
+
|
|
347
|
+
return '<footer style="' + footerStyle + '">' +
|
|
348
|
+
'<p style="' + promptStyle + '">What now?</p>' +
|
|
349
|
+
'<p style="' + optionStyle + '">[1] Just tell me what's new (stay in "tell me" mode)</p>' +
|
|
350
|
+
'<p style="' + optionStyle + '">[2] Build a room around this (invest)</p>' +
|
|
351
|
+
'<p style="' + optionStyle + '">[3] Challenge me -- Devil's Advocate (go deeper cognitively)</p>' +
|
|
352
|
+
'<div style="' + copyStyle + '">' +
|
|
353
|
+
'Paste in your terminal: <strong>/mos:new-project --from-brief ' + _esc(sha8) + '</strong>' +
|
|
354
|
+
'</div>' +
|
|
355
|
+
'<p style="' + attribStyle + '">' +
|
|
356
|
+
'Generated by MindrianOS -- ' +
|
|
357
|
+
'<a href="' + INSTALL_SITE_URL + '" style="color:' + DECK_PALETTE.yellow + ';text-decoration:none;">' +
|
|
358
|
+
INSTALL_SITE_URL +
|
|
359
|
+
'</a>' +
|
|
360
|
+
' // brief ' + _esc(sha8) +
|
|
361
|
+
'</p>' +
|
|
362
|
+
'</footer>' +
|
|
363
|
+
'<div style="' + bottomBarStyle + '">' + bottomCells + '</div>';
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ---------- Special-case decks ----------
|
|
367
|
+
|
|
368
|
+
function _buildHebrewRefusalDeck(sha8) {
|
|
369
|
+
const articleStyle = 'margin:0;padding:32px;background:#FFFFFF;border-top:4px solid ' + DECK_PALETTE.red + ';border:1px solid ' + DECK_PALETTE.muted + ';';
|
|
370
|
+
const body = HEBREW_REFUSAL_LINES.map(function (line) {
|
|
371
|
+
return '<p style="margin:8px 0;font-size:16px;color:' + DECK_PALETTE.black + ';">' + _esc(line) + '</p>';
|
|
372
|
+
}).join('');
|
|
373
|
+
const slides = '<article style="' + articleStyle + '">' + body + '</article>';
|
|
374
|
+
return TEMPLATE
|
|
375
|
+
.replace('{{HEADER}}', _buildHeader(sha8))
|
|
376
|
+
.replace('{{SLIDES}}', slides)
|
|
377
|
+
.replace('{{FOOTER}}', _buildFooter(sha8));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function _buildSharpQuestionDeck(sha8) {
|
|
381
|
+
const articleStyle = [
|
|
382
|
+
'margin:0',
|
|
383
|
+
'padding:40px 32px',
|
|
384
|
+
'background:#FFFFFF',
|
|
385
|
+
'border-top:4px solid ' + DECK_PALETTE.amethyst,
|
|
386
|
+
'border:1px solid ' + DECK_PALETTE.muted,
|
|
387
|
+
].join(';');
|
|
388
|
+
const labelStyle = 'margin:0 0 16px 0;font-family:Impact,"Arial Black",sans-serif;font-size:20px;letter-spacing:1px;text-transform:uppercase;color:' + DECK_PALETTE.amethyst + ';';
|
|
389
|
+
// SHARP_QUESTION_LINES are module-level constants (verbatim from source
|
|
390
|
+
// spec; no user content). We render them WITHOUT escape so the apostrophes
|
|
391
|
+
// stay as readable ASCII characters in the HTML, not as ' entities.
|
|
392
|
+
// This is safe-by-construction: no user data flows through this branch.
|
|
393
|
+
const body = SHARP_QUESTION_LINES.map(function (line) {
|
|
394
|
+
return '<p style="margin:12px 0;font-size:17px;color:' + DECK_PALETTE.black + ';line-height:1.55;">' + line + '</p>';
|
|
395
|
+
}).join('');
|
|
396
|
+
const slides = '<article style="' + articleStyle + '">' +
|
|
397
|
+
'<h2 style="' + labelStyle + '">A Sharper Question</h2>' +
|
|
398
|
+
body +
|
|
399
|
+
'</article>';
|
|
400
|
+
return TEMPLATE
|
|
401
|
+
.replace('{{HEADER}}', _buildHeader(sha8))
|
|
402
|
+
.replace('{{SLIDES}}', slides)
|
|
403
|
+
.replace('{{FOOTER}}', _buildFooter(sha8));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ---------- Public buildDeck ----------
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* buildDeck(outcome) -> full HTML document string.
|
|
410
|
+
*
|
|
411
|
+
* @param {{
|
|
412
|
+
* results: Array<{agent_id: string, status: string, payload?: object, error?: string}>,
|
|
413
|
+
* rendered?: string,
|
|
414
|
+
* footer_data: { ok: number, failed: number, sha256: string, hebrew_refusal?: boolean } | null
|
|
415
|
+
* }} outcome
|
|
416
|
+
* @returns {string}
|
|
417
|
+
*/
|
|
418
|
+
function buildDeck(outcome) {
|
|
419
|
+
if (!outcome || typeof outcome !== 'object') {
|
|
420
|
+
return '';
|
|
421
|
+
}
|
|
422
|
+
const fd = outcome.footer_data || {};
|
|
423
|
+
const sha256 = typeof fd.sha256 === 'string' ? fd.sha256 : '';
|
|
424
|
+
const sha8 = sha256.slice(0, 8) || 'unknown';
|
|
425
|
+
|
|
426
|
+
// Hebrew refusal short-circuit (defensive -- Plan 118-03 should not call us here)
|
|
427
|
+
if (fd.hebrew_refusal) {
|
|
428
|
+
return _buildHebrewRefusalDeck(sha8);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const results = Array.isArray(outcome.results) ? outcome.results : [];
|
|
432
|
+
const okCount = results.filter(function (r) { return r && r.status === 'ok'; }).length;
|
|
433
|
+
|
|
434
|
+
// All-fail -> sharp-question single slide
|
|
435
|
+
if (okCount === 0 && results.length > 0) {
|
|
436
|
+
return _buildSharpQuestionDeck(sha8);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Normal path: one slide per result (placeholder slides for empty/error)
|
|
440
|
+
const slides = results.map(buildSlide).join('\n');
|
|
441
|
+
|
|
442
|
+
return TEMPLATE
|
|
443
|
+
.replace('{{HEADER}}', _buildHeader(sha8))
|
|
444
|
+
.replace('{{SLIDES}}', slides)
|
|
445
|
+
.replace('{{FOOTER}}', _buildFooter(sha8));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
module.exports = {
|
|
449
|
+
buildDeck,
|
|
450
|
+
buildSlide,
|
|
451
|
+
DECK_PALETTE,
|
|
452
|
+
};
|