@mindrian_os/cli 2.0.0-beta.31 → 2.0.0-beta.35
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 +86 -0
- package/bin/mindrian-brain-mcp-client.cjs +15 -1
- package/commands/doctor.md +2 -2
- package/data/brain-census.generated.json +2827 -1523
- package/lib/core/brain-client.cjs +320 -20
- package/lib/core/brain-prewarm.cjs +164 -0
- package/lib/core/directive-envelope.cjs +13 -0
- package/lib/core/doctor/class-m-brain-smoke.cjs +160 -26
- package/lib/core/doctor/class-m-brain-smoke.test.cjs +441 -82
- package/lib/core/integration-registry.cjs +96 -20
- package/lib/core/intel-pipeline.cjs +28 -7
- package/lib/core/part8-egress-guard.cjs +27 -0
- package/lib/mcp/brain-composition-census.cjs +22 -6
- package/lib/mcp/brain-route-bound.cjs +66 -0
- package/lib/mcp/brain-router.cjs +47 -11
- package/lib/mcp/tool-router.cjs +2 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/references/brain/room-hierarchy-schema.md +11 -5
- package/scripts/build-brain-census.cjs +321 -4
- package/scripts/collect-cold-install-evidence.cjs +411 -0
- package/scripts/doctor.cjs +22 -7
- package/scripts/room-registry +4 -4
- package/scripts/session-start +25 -2
- package/scripts/whitespace-to-brain.cjs +2 -1
- package/skills/doctor/SKILL.md +2 -2
- package/skills/larry-personality/SKILL.md +4 -3
- package/scripts/sync-rooms-brain +0 -438
package/scripts/sync-rooms-brain
DELETED
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* sync-rooms-brain -- Sync room hierarchy to Neo4j Brain (remote graph)
|
|
4
|
-
*
|
|
5
|
-
* Creates/updates Room and RoomGroup nodes in Neo4j Brain via the Brain HTTP API.
|
|
6
|
-
* Creates semantic edges: AT_STAGE, USES_FRAMEWORK, SHARES_THEME, HAS_SECTION.
|
|
7
|
-
* Wires 13 orphaned DataRoomSection nodes to parent Room nodes via HAS_SECTION.
|
|
8
|
-
*
|
|
9
|
-
* Idempotent: uses MERGE semantics throughout.
|
|
10
|
-
* Fire-and-forget: failure never blocks any operation (D-16).
|
|
11
|
-
* Additive only: NEVER writes to filesystem or registry.json (D-15).
|
|
12
|
-
* Only runs when Brain API key is available.
|
|
13
|
-
*
|
|
14
|
-
* Usage: node sync-rooms-brain [roomsHome]
|
|
15
|
-
* roomsHome - Override ~/MindrianRooms (default: $MINDRIAN_ROOMS_HOME or ~/MindrianRooms)
|
|
16
|
-
*
|
|
17
|
-
* Exit codes:
|
|
18
|
-
* 0 - Always (fire-and-forget, D-16)
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
'use strict';
|
|
22
|
-
|
|
23
|
-
const fs = require('fs');
|
|
24
|
-
const path = require('path');
|
|
25
|
-
const os = require('os');
|
|
26
|
-
|
|
27
|
-
const ROOMS_HOME = process.argv[2] || process.env.MINDRIAN_ROOMS_HOME || path.join(os.homedir(), 'MindrianRooms');
|
|
28
|
-
const REGISTRY_PATH = path.join(ROOMS_HOME, '.rooms', 'registry.json');
|
|
29
|
-
const SCRIPT_DIR = __dirname;
|
|
30
|
-
const PLUGIN_ROOT = path.resolve(SCRIPT_DIR, '..');
|
|
31
|
-
|
|
32
|
-
let brain;
|
|
33
|
-
try {
|
|
34
|
-
brain = require(path.join(PLUGIN_ROOT, 'lib', 'core', 'brain-client.cjs'));
|
|
35
|
-
} catch (_) {
|
|
36
|
-
process.stderr.write('sync-rooms-brain: brain-client not found\n');
|
|
37
|
-
process.exit(0);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Execute a Cypher write query via Brain API.
|
|
42
|
-
* Returns true on success, false on failure.
|
|
43
|
-
* @param {string} cypher
|
|
44
|
-
* @returns {Promise<boolean>}
|
|
45
|
-
*/
|
|
46
|
-
async function brainWrite(cypher) {
|
|
47
|
-
try {
|
|
48
|
-
const result = await brain.write(cypher);
|
|
49
|
-
return result !== null;
|
|
50
|
-
} catch (_) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Execute a Cypher read query via Brain API.
|
|
57
|
-
* @param {string} cypher
|
|
58
|
-
* @returns {Promise<object|null>}
|
|
59
|
-
*/
|
|
60
|
-
async function brainRead(cypher) {
|
|
61
|
-
try {
|
|
62
|
-
return await brain.query(cypher);
|
|
63
|
-
} catch (_) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Create a RoomRoot node (the ~/MindrianRooms/ root).
|
|
70
|
-
*/
|
|
71
|
-
async function syncRoomRoot() {
|
|
72
|
-
return brainWrite(`
|
|
73
|
-
MERGE (root:RoomRoot {name: 'MindrianRooms'})
|
|
74
|
-
ON CREATE SET root.created = datetime(), root.path = '~/MindrianRooms/'
|
|
75
|
-
`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Sync Room nodes to Brain.
|
|
80
|
-
* @param {object} rooms - rooms object from registry.json
|
|
81
|
-
*/
|
|
82
|
-
async function syncRoomNodes(rooms) {
|
|
83
|
-
for (const [name, room] of Object.entries(rooms)) {
|
|
84
|
-
if (room.status === 'archived') continue;
|
|
85
|
-
|
|
86
|
-
const stage = (room.venture_stage || '').replace(/'/g, "\\'");
|
|
87
|
-
const vname = (room.venture_name || '').replace(/'/g, "\\'");
|
|
88
|
-
const domain = (room.domain || '').replace(/'/g, "\\'");
|
|
89
|
-
const status = (room.status || 'unknown').replace(/'/g, "\\'");
|
|
90
|
-
const created = room.created || '';
|
|
91
|
-
const roomPath = (room.path || name).replace(/'/g, "\\'");
|
|
92
|
-
|
|
93
|
-
await brainWrite(`
|
|
94
|
-
MERGE (r:Room {name: '${name}'})
|
|
95
|
-
ON CREATE SET r.venture_name = '${vname}', r.venture_stage = '${stage}',
|
|
96
|
-
r.domain = '${domain}', r.status = '${status}',
|
|
97
|
-
r.created = '${created}', r.path = '${roomPath}'
|
|
98
|
-
ON MATCH SET r.venture_name = '${vname}', r.venture_stage = '${stage}',
|
|
99
|
-
r.domain = '${domain}', r.status = '${status}', r.path = '${roomPath}'
|
|
100
|
-
`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Detect group directories and sync RoomGroup nodes.
|
|
106
|
-
* @param {object} rooms - rooms object from registry.json
|
|
107
|
-
* @returns {Map<string, string[]>} groupName -> [roomNames]
|
|
108
|
-
*/
|
|
109
|
-
async function syncRoomGroups(rooms) {
|
|
110
|
-
const groups = new Map();
|
|
111
|
-
|
|
112
|
-
for (const [name, room] of Object.entries(rooms)) {
|
|
113
|
-
if (room.status === 'archived') continue;
|
|
114
|
-
const roomPath = room.path || name;
|
|
115
|
-
const parts = roomPath.split('/').filter(Boolean);
|
|
116
|
-
|
|
117
|
-
if (parts.length >= 2) {
|
|
118
|
-
const groupName = parts[0];
|
|
119
|
-
if (!groups.has(groupName)) {
|
|
120
|
-
groups.set(groupName, []);
|
|
121
|
-
}
|
|
122
|
-
groups.get(groupName).push(name);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
for (const [groupName, roomNames] of groups) {
|
|
127
|
-
// Create RoomGroup node
|
|
128
|
-
await brainWrite(`
|
|
129
|
-
MERGE (g:RoomGroup {name: '${groupName}'})
|
|
130
|
-
ON CREATE SET g.icm_layer = 'group', g.created = datetime()
|
|
131
|
-
`);
|
|
132
|
-
|
|
133
|
-
// CONTAINS: RoomRoot -> RoomGroup
|
|
134
|
-
await brainWrite(`
|
|
135
|
-
MATCH (root:RoomRoot {name: 'MindrianRooms'}), (g:RoomGroup {name: '${groupName}'})
|
|
136
|
-
MERGE (root)-[:CONTAINS]->(g)
|
|
137
|
-
`);
|
|
138
|
-
|
|
139
|
-
// CONTAINS: RoomGroup -> Room
|
|
140
|
-
for (const roomName of roomNames) {
|
|
141
|
-
await brainWrite(`
|
|
142
|
-
MATCH (g:RoomGroup {name: '${groupName}'}), (r:Room {name: '${roomName}'})
|
|
143
|
-
MERGE (g)-[:CONTAINS]->(r)
|
|
144
|
-
`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Rooms not in any group: RoomRoot -> Room directly
|
|
149
|
-
for (const [name, room] of Object.entries(rooms)) {
|
|
150
|
-
if (room.status === 'archived') continue;
|
|
151
|
-
const roomPath = room.path || name;
|
|
152
|
-
const parts = roomPath.split('/').filter(Boolean);
|
|
153
|
-
|
|
154
|
-
if (parts.length < 2) {
|
|
155
|
-
await brainWrite(`
|
|
156
|
-
MATCH (root:RoomRoot {name: 'MindrianRooms'}), (r:Room {name: '${name}'})
|
|
157
|
-
MERGE (root)-[:CONTAINS]->(r)
|
|
158
|
-
`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return groups;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Create AT_STAGE edges connecting Room nodes to existing VentureStage taxonomy.
|
|
167
|
-
* @param {object} rooms - rooms object from registry.json
|
|
168
|
-
*/
|
|
169
|
-
async function syncAtStageEdges(rooms) {
|
|
170
|
-
for (const [name, room] of Object.entries(rooms)) {
|
|
171
|
-
if (room.status === 'archived' || !room.venture_stage) continue;
|
|
172
|
-
|
|
173
|
-
const stage = room.venture_stage.replace(/'/g, "\\'");
|
|
174
|
-
|
|
175
|
-
// Remove existing AT_STAGE edges (room can change stage)
|
|
176
|
-
await brainWrite(`
|
|
177
|
-
MATCH (r:Room {name: '${name}'})-[e:AT_STAGE]->(:VentureStage)
|
|
178
|
-
DELETE e
|
|
179
|
-
`);
|
|
180
|
-
|
|
181
|
-
// Create new AT_STAGE edge
|
|
182
|
-
await brainWrite(`
|
|
183
|
-
MATCH (r:Room {name: '${name}'}), (s:VentureStage {name: '${stage}'})
|
|
184
|
-
MERGE (r)-[:AT_STAGE]->(s)
|
|
185
|
-
`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Create USES_FRAMEWORK edges from room analytics data.
|
|
191
|
-
* Reads each room's .analytics.json for command usage that maps to frameworks.
|
|
192
|
-
* @param {object} rooms - rooms object from registry.json
|
|
193
|
-
*/
|
|
194
|
-
async function syncUsesFrameworkEdges(rooms) {
|
|
195
|
-
// Command-to-framework mapping (from methodology index)
|
|
196
|
-
const cmdToFramework = {
|
|
197
|
-
'jtbd': 'Jobs-to-Be-Done',
|
|
198
|
-
'analyze-needs': 'Jobs-to-Be-Done',
|
|
199
|
-
'dominant-designs': 'Blue Ocean Strategy',
|
|
200
|
-
'think-hats': 'Six Thinking Hats',
|
|
201
|
-
'persona': 'Design Thinking',
|
|
202
|
-
'swot': 'SWOT Analysis',
|
|
203
|
-
'bmc': 'Business Model Canvas',
|
|
204
|
-
'lean-canvas': 'Lean Canvas',
|
|
205
|
-
'pestel': 'PESTEL Analysis',
|
|
206
|
-
'porters': "Porter's Five Forces",
|
|
207
|
-
'value-chain': 'Value Chain Analysis',
|
|
208
|
-
'design-thinking': 'Design Thinking',
|
|
209
|
-
'systems-thinking': 'Systems Thinking',
|
|
210
|
-
'cynefin': 'Cynefin Framework',
|
|
211
|
-
'root-cause': 'Root Cause Analysis',
|
|
212
|
-
'find-analogies': 'Design-by-Analogy',
|
|
213
|
-
'stakeholder-map': 'Stakeholder Mapping',
|
|
214
|
-
'risk-matrix': 'Risk Matrix',
|
|
215
|
-
'scenario-planning': 'Scenario Planning',
|
|
216
|
-
'triple-validation': 'PWS Triple Validation Compass',
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
for (const [name, room] of Object.entries(rooms)) {
|
|
220
|
-
if (room.status === 'archived') continue;
|
|
221
|
-
|
|
222
|
-
const roomPath = path.join(ROOMS_HOME, room.path || name);
|
|
223
|
-
const analyticsPath = path.join(roomPath, '.analytics.json');
|
|
224
|
-
|
|
225
|
-
if (!fs.existsSync(analyticsPath)) continue;
|
|
226
|
-
|
|
227
|
-
let analytics;
|
|
228
|
-
try {
|
|
229
|
-
analytics = JSON.parse(fs.readFileSync(analyticsPath, 'utf8'));
|
|
230
|
-
} catch (_) {
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const commands = analytics.commands || {};
|
|
235
|
-
const frameworksUsed = new Set();
|
|
236
|
-
|
|
237
|
-
for (const [cmd, count] of Object.entries(commands)) {
|
|
238
|
-
// Strip /mos: prefix if present
|
|
239
|
-
const cleanCmd = cmd.replace(/^\/mos:/, '').replace(/^mos:/, '');
|
|
240
|
-
const framework = cmdToFramework[cleanCmd];
|
|
241
|
-
if (framework && !frameworksUsed.has(framework)) {
|
|
242
|
-
frameworksUsed.add(framework);
|
|
243
|
-
const escapedFw = framework.replace(/'/g, "\\'");
|
|
244
|
-
await brainWrite(`
|
|
245
|
-
MATCH (r:Room {name: '${name}'}), (f:Framework {name: '${escapedFw}'})
|
|
246
|
-
MERGE (r)-[e:USES_FRAMEWORK]->(f)
|
|
247
|
-
ON CREATE SET e.first_used = datetime(), e.usage_count = ${count || 1}
|
|
248
|
-
ON MATCH SET e.usage_count = ${count || 1}
|
|
249
|
-
`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Wire orphaned DataRoomSection nodes to parent Room nodes via HAS_SECTION.
|
|
257
|
-
* DataRoomSection nodes already exist in Brain (13 orphans detected during context gathering).
|
|
258
|
-
* @param {object} rooms - rooms object from registry.json
|
|
259
|
-
*/
|
|
260
|
-
async function wireOrphanedSections(rooms) {
|
|
261
|
-
// Standard room sections (matching DataRoomSection names in Brain)
|
|
262
|
-
const sectionNames = [
|
|
263
|
-
'Problem Definition', 'Market Analysis', 'Solution Design',
|
|
264
|
-
'Business Model', 'Competitive Analysis', 'Team & Execution',
|
|
265
|
-
'Legal & IP', 'Financial Model', 'Opportunity Bank',
|
|
266
|
-
'Funding', 'Product', 'Decisions', 'Beta Testing'
|
|
267
|
-
];
|
|
268
|
-
|
|
269
|
-
for (const [name, room] of Object.entries(rooms)) {
|
|
270
|
-
if (room.status === 'archived') continue;
|
|
271
|
-
|
|
272
|
-
const roomPath = path.join(ROOMS_HOME, room.path || name);
|
|
273
|
-
|
|
274
|
-
// Check which sections actually exist in this room's filesystem
|
|
275
|
-
for (const sectionName of sectionNames) {
|
|
276
|
-
const slug = sectionName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+$/, '');
|
|
277
|
-
const sectionDir = path.join(roomPath, slug);
|
|
278
|
-
|
|
279
|
-
if (fs.existsSync(sectionDir)) {
|
|
280
|
-
const escapedSection = sectionName.replace(/'/g, "\\'");
|
|
281
|
-
await brainWrite(`
|
|
282
|
-
MATCH (r:Room {name: '${name}'}), (s:DataRoomSection {name: '${escapedSection}'})
|
|
283
|
-
MERGE (r)-[:HAS_SECTION]->(s)
|
|
284
|
-
`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Detect SHARES_THEME edges between rooms based on CO_OCCURS pattern.
|
|
292
|
-
* Reads problem-definition content from each room and finds shared domain keywords.
|
|
293
|
-
* @param {object} rooms - rooms object from registry.json
|
|
294
|
-
*/
|
|
295
|
-
async function detectSharedThemes(rooms) {
|
|
296
|
-
const roomKeywords = new Map();
|
|
297
|
-
|
|
298
|
-
// Extract keywords from each room's problem-definition
|
|
299
|
-
for (const [name, room] of Object.entries(rooms)) {
|
|
300
|
-
if (room.status === 'archived') continue;
|
|
301
|
-
|
|
302
|
-
const roomPath = path.join(ROOMS_HOME, room.path || name);
|
|
303
|
-
const probDefDir = path.join(roomPath, 'problem-definition');
|
|
304
|
-
|
|
305
|
-
if (!fs.existsSync(probDefDir)) continue;
|
|
306
|
-
|
|
307
|
-
const keywords = new Set();
|
|
308
|
-
|
|
309
|
-
try {
|
|
310
|
-
const files = fs.readdirSync(probDefDir).filter(f => f.endsWith('.md'));
|
|
311
|
-
for (const file of files.slice(0, 5)) { // Limit to 5 files per room
|
|
312
|
-
const content = fs.readFileSync(path.join(probDefDir, file), 'utf8');
|
|
313
|
-
// Extract meaningful words (5+ chars, not common noise)
|
|
314
|
-
const noise = new Set([
|
|
315
|
-
'about', 'their', 'these', 'those', 'which', 'where', 'through',
|
|
316
|
-
'between', 'using', 'based', 'should', 'would', 'could', 'other',
|
|
317
|
-
'being', 'there', 'every', 'after', 'before', 'while', 'since'
|
|
318
|
-
]);
|
|
319
|
-
const words = content.toLowerCase().match(/\b[a-z]{5,}\b/g) || [];
|
|
320
|
-
for (const word of words) {
|
|
321
|
-
if (!noise.has(word)) keywords.add(word);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
} catch (_) {
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (keywords.size > 0) {
|
|
329
|
-
roomKeywords.set(name, keywords);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Find room pairs with significant keyword overlap
|
|
334
|
-
const roomNames = [...roomKeywords.keys()];
|
|
335
|
-
for (let i = 0; i < roomNames.length; i++) {
|
|
336
|
-
for (let j = i + 1; j < roomNames.length; j++) {
|
|
337
|
-
const nameA = roomNames[i];
|
|
338
|
-
const nameB = roomNames[j];
|
|
339
|
-
const kwA = roomKeywords.get(nameA);
|
|
340
|
-
const kwB = roomKeywords.get(nameB);
|
|
341
|
-
|
|
342
|
-
// Count intersection
|
|
343
|
-
let shared = 0;
|
|
344
|
-
const sharedTerms = [];
|
|
345
|
-
for (const kw of kwA) {
|
|
346
|
-
if (kwB.has(kw)) {
|
|
347
|
-
shared++;
|
|
348
|
-
if (sharedTerms.length < 10) sharedTerms.push(kw);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Require significant overlap (10+ shared domain words)
|
|
353
|
-
if (shared >= 10) {
|
|
354
|
-
const themes = sharedTerms.slice(0, 5).join(', ').replace(/'/g, "\\'");
|
|
355
|
-
await brainWrite(`
|
|
356
|
-
MATCH (r1:Room {name: '${nameA}'}), (r2:Room {name: '${nameB}'})
|
|
357
|
-
MERGE (r1)-[e:SHARES_THEME]->(r2)
|
|
358
|
-
ON CREATE SET e.shared_terms = '${themes}', e.term_count = ${shared}, e.detected = datetime()
|
|
359
|
-
ON MATCH SET e.shared_terms = '${themes}', e.term_count = ${shared}
|
|
360
|
-
`);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
async function main() {
|
|
367
|
-
// Check Brain availability
|
|
368
|
-
if (!brain.isAvailable()) {
|
|
369
|
-
process.stderr.write('sync-rooms-brain: Brain API not available (no key), skipping\n');
|
|
370
|
-
process.exit(0);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Check registry exists
|
|
374
|
-
if (!fs.existsSync(REGISTRY_PATH)) {
|
|
375
|
-
process.exit(0);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Load registry
|
|
379
|
-
let registry;
|
|
380
|
-
try {
|
|
381
|
-
registry = JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf8'));
|
|
382
|
-
} catch (_) {
|
|
383
|
-
process.stderr.write('sync-rooms-brain: invalid registry.json\n');
|
|
384
|
-
process.exit(0);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const rooms = registry.rooms || {};
|
|
388
|
-
if (Object.keys(rooms).length === 0) {
|
|
389
|
-
process.exit(0);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const results = {
|
|
393
|
-
root: false,
|
|
394
|
-
rooms: 0,
|
|
395
|
-
groups: 0,
|
|
396
|
-
atStage: 0,
|
|
397
|
-
usesFramework: 0,
|
|
398
|
-
hasSections: 0,
|
|
399
|
-
sharesTheme: 0,
|
|
400
|
-
errors: []
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
try {
|
|
404
|
-
// 1. Create RoomRoot
|
|
405
|
-
results.root = await syncRoomRoot();
|
|
406
|
-
|
|
407
|
-
// 2. Sync Room nodes
|
|
408
|
-
await syncRoomNodes(rooms);
|
|
409
|
-
results.rooms = Object.keys(rooms).filter(n => rooms[n].status !== 'archived').length;
|
|
410
|
-
|
|
411
|
-
// 3. Sync RoomGroup nodes and CONTAINS edges
|
|
412
|
-
const groups = await syncRoomGroups(rooms);
|
|
413
|
-
results.groups = groups.size;
|
|
414
|
-
|
|
415
|
-
// 4. AT_STAGE edges
|
|
416
|
-
await syncAtStageEdges(rooms);
|
|
417
|
-
results.atStage = Object.values(rooms).filter(r => r.venture_stage && r.status !== 'archived').length;
|
|
418
|
-
|
|
419
|
-
// 5. USES_FRAMEWORK edges
|
|
420
|
-
await syncUsesFrameworkEdges(rooms);
|
|
421
|
-
|
|
422
|
-
// 6. Wire orphaned DataRoomSection nodes
|
|
423
|
-
await wireOrphanedSections(rooms);
|
|
424
|
-
|
|
425
|
-
// 7. SHARES_THEME detection
|
|
426
|
-
await detectSharedThemes(rooms);
|
|
427
|
-
|
|
428
|
-
} catch (err) {
|
|
429
|
-
results.errors.push(err.message);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
console.log(JSON.stringify(results));
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
main().catch(err => {
|
|
436
|
-
process.stderr.write(`sync-rooms-brain: ${err.message}\n`);
|
|
437
|
-
process.exit(0);
|
|
438
|
-
});
|