@neuroverseos/governance 0.3.4 → 0.4.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/README.md +280 -405
- package/dist/adapters/autoresearch.cjs +63 -9
- package/dist/adapters/autoresearch.d.cts +1 -1
- package/dist/adapters/autoresearch.d.ts +1 -1
- package/dist/adapters/autoresearch.js +3 -3
- package/dist/adapters/deep-agents.cjs +63 -9
- package/dist/adapters/deep-agents.d.cts +2 -2
- package/dist/adapters/deep-agents.d.ts +2 -2
- package/dist/adapters/deep-agents.js +3 -3
- package/dist/adapters/express.cjs +63 -9
- package/dist/adapters/express.d.cts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +3 -3
- package/dist/adapters/index.cjs +896 -9
- package/dist/adapters/index.d.cts +278 -2
- package/dist/adapters/index.d.ts +278 -2
- package/dist/adapters/index.js +45 -8
- package/dist/adapters/langchain.cjs +63 -9
- package/dist/adapters/langchain.d.cts +2 -2
- package/dist/adapters/langchain.d.ts +2 -2
- package/dist/adapters/langchain.js +3 -3
- package/dist/adapters/openai.cjs +63 -9
- package/dist/adapters/openai.d.cts +2 -2
- package/dist/adapters/openai.d.ts +2 -2
- package/dist/adapters/openai.js +3 -3
- package/dist/adapters/openclaw.cjs +63 -9
- package/dist/adapters/openclaw.d.cts +2 -2
- package/dist/adapters/openclaw.d.ts +2 -2
- package/dist/adapters/openclaw.js +3 -3
- package/dist/{add-ROOZLU62.js → add-LYHDZ5RL.js} +1 -1
- package/dist/{behavioral-MJO34S6Q.js → behavioral-SPWPGYXL.js} +2 -2
- package/dist/{bootstrap-CQRZVOXK.js → bootstrap-IP5QMC3Q.js} +2 -2
- package/dist/{bootstrap-emitter-Q7UIJZ2O.js → bootstrap-emitter-GIMOJFOC.js} +1 -1
- package/dist/{bootstrap-parser-EEF36XDU.js → bootstrap-parser-LBLGVEMU.js} +1 -1
- package/dist/browser.global.js +149 -5
- package/dist/{build-ZHPMX5AZ.js → build-THUEYMVT.js} +3 -3
- package/dist/{chunk-G7DJ6VOD.js → chunk-25XHSTPT.js} +2 -2
- package/dist/{chunk-A7GKPPU7.js → chunk-2VAWP6FI.js} +1 -1
- package/dist/{chunk-EMQDLDAF.js → chunk-3NZMMSOW.js} +80 -2
- package/dist/{chunk-B6OXJLJ5.js → chunk-5JUZ4HL7.js} +2 -2
- package/dist/{chunk-VXHSMA3I.js → chunk-6CV4XG3J.js} +1 -1
- package/dist/{chunk-5TPFNWRU.js → chunk-7D7PZLB7.js} +3 -3
- package/dist/{chunk-ZWI3NIXK.js → chunk-7QIAF377.js} +54 -3
- package/dist/chunk-APU4OZIP.js +828 -0
- package/dist/{chunk-CTZHONLA.js → chunk-BXLTEUS4.js} +2 -2
- package/dist/{chunk-O5ABKEA7.js → chunk-DWHUZUEY.js} +2 -2
- package/dist/{chunk-U6U7EJZL.js → chunk-JKGPSFGH.js} +2 -2
- package/dist/{chunk-3WQLXYTP.js → chunk-MFKHTE5R.js} +2 -2
- package/dist/{chunk-TG6SEF24.js → chunk-OQU65525.js} +1 -1
- package/dist/{chunk-YEKMVDWK.js → chunk-QZ666FCV.js} +2 -2
- package/dist/{chunk-4FLICVVA.js → chunk-TD5GKIHP.js} +2 -2
- package/dist/{chunk-IS4WUH6Y.js → chunk-UTH7OXTM.js} +2 -2
- package/dist/{chunk-BNKJPUPQ.js → chunk-V4FZHJQX.js} +2 -2
- package/dist/{chunk-F66BVUYB.js → chunk-Y6WXAPKY.js} +3 -3
- package/dist/{chunk-QXBFT7NI.js → chunk-YNYCQECH.js} +2 -2
- package/dist/{chunk-PVTQQS3Y.js → chunk-YPCVY4GS.js} +31 -0
- package/dist/{chunk-W7LLXRGY.js → chunk-ZAF6JH23.js} +65 -10
- package/dist/cli/neuroverse.cjs +3031 -182
- package/dist/cli/neuroverse.js +39 -23
- package/dist/cli/plan.cjs +176 -12
- package/dist/cli/plan.js +2 -2
- package/dist/cli/run.cjs +63 -9
- package/dist/cli/run.js +2 -2
- package/dist/configure-world-XU2COHOZ.js +705 -0
- package/dist/{decision-flow-M63D47LO.js → decision-flow-3K4D72G4.js} +2 -2
- package/dist/{demo-G43RLCPK.js → demo-66MMJTEH.js} +3 -3
- package/dist/{derive-LMDUTXDD.js → derive-5LOMN7GO.js} +4 -4
- package/dist/{doctor-6BC6X2VO.js → doctor-WIO4FLA3.js} +2 -1
- package/dist/{equity-penalties-SG5IZQ7I.js → equity-penalties-WWC7UDQD.js} +3 -3
- package/dist/{explain-RHBU2GBR.js → explain-MUSGDT67.js} +1 -1
- package/dist/{guard-AEEJNWLD.js → guard-W3BMQPBJ.js} +3 -3
- package/dist/{guard-contract-B7lplwm9.d.ts → guard-contract-CLBbTGK_.d.cts} +91 -1
- package/dist/{guard-contract-B7lplwm9.d.cts → guard-contract-CLBbTGK_.d.ts} +91 -1
- package/dist/{guard-engine-PNR6MHCM.js → guard-engine-N7TUIUU7.js} +5 -3
- package/dist/{impact-3XVDSCBU.js → impact-WIAM66IH.js} +3 -3
- package/dist/{improve-TQP4ECSY.js → improve-PJDAWW4Q.js} +3 -3
- package/dist/index.cjs +230 -14
- package/dist/index.d.cts +72 -3
- package/dist/index.d.ts +72 -3
- package/dist/index.js +24 -22
- package/dist/{init-FYPV4SST.js → init-TKIJDR7I.js} +5 -1
- package/dist/lens-IP6GIZ2Q.js +1017 -0
- package/dist/{mcp-server-5Y3ZM7TV.js → mcp-server-OG3PPVD2.js} +3 -3
- package/dist/mentraos-YFS7FMJH.js +48 -0
- package/dist/{playground-VZBNPPBO.js → playground-4BK2XQ47.js} +2 -2
- package/dist/{redteam-MZPZD3EF.js → redteam-BRZALBPP.js} +2 -2
- package/dist/{session-JYOARW54.js → session-SGRUT2UH.js} +3 -3
- package/dist/{shared-C_zpdvBm.d.cts → shared-BGzmYP5g.d.cts} +1 -1
- package/dist/{shared-Cf7yxx4-.d.ts → shared-CwGpPheR.d.ts} +1 -1
- package/dist/{simulate-LJXYBC6M.js → simulate-FGXKIH7V.js} +17 -4
- package/dist/{test-BOOR4A5F.js → test-PT44BSYG.js} +2 -2
- package/dist/{trace-PKV4KX56.js → trace-2YDNAXMK.js} +2 -2
- package/dist/{validate-RALX7CZS.js → validate-Q5O5TGLT.js} +1 -1
- package/dist/{world-BIP4GZBZ.js → world-V52ZMH26.js} +1 -1
- package/dist/{world-loader-Y6HMQH2D.js → world-loader-C4D3VPP3.js} +1 -1
- package/dist/worlds/mentraos-smartglasses.nv-world.md +423 -0
- package/dist/worlds/user-rules.nv-world.md +328 -0
- package/package.json +1 -1
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
import "./chunk-QWGCMQQD.js";
|
|
2
|
+
|
|
3
|
+
// src/cli/prompt-utils.ts
|
|
4
|
+
import * as readline from "readline";
|
|
5
|
+
var rl = null;
|
|
6
|
+
function getRL() {
|
|
7
|
+
if (!rl) {
|
|
8
|
+
rl = readline.createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stderr,
|
|
11
|
+
// prompts go to stderr, data to stdout
|
|
12
|
+
terminal: true
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return rl;
|
|
16
|
+
}
|
|
17
|
+
function closePrompts() {
|
|
18
|
+
if (rl) {
|
|
19
|
+
rl.close();
|
|
20
|
+
rl = null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function ask(question, defaultValue) {
|
|
24
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
getRL().question(`
|
|
27
|
+
${question}${suffix}: `, (answer) => {
|
|
28
|
+
const val = answer.trim();
|
|
29
|
+
resolve(val || defaultValue || "");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function confirm(question, defaultYes = true) {
|
|
34
|
+
const hint = defaultYes ? "[Y/n]" : "[y/N]";
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
getRL().question(`
|
|
37
|
+
${question} ${hint}: `, (answer) => {
|
|
38
|
+
const val = answer.trim().toLowerCase();
|
|
39
|
+
if (val === "") resolve(defaultYes);
|
|
40
|
+
else resolve(val === "y" || val === "yes");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function choose(question, options) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const r = getRL();
|
|
47
|
+
r.write(`
|
|
48
|
+
${question}
|
|
49
|
+
`);
|
|
50
|
+
options.forEach((opt, i) => r.write(` ${i + 1}. ${opt}
|
|
51
|
+
`));
|
|
52
|
+
r.question(` Choice [1-${options.length}]: `, (answer) => {
|
|
53
|
+
const idx = parseInt(answer.trim(), 10) - 1;
|
|
54
|
+
if (idx >= 0 && idx < options.length) {
|
|
55
|
+
resolve(options[idx]);
|
|
56
|
+
} else {
|
|
57
|
+
resolve(options[0]);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async function askMany(question, hint) {
|
|
63
|
+
const items = [];
|
|
64
|
+
const hintText = hint ? ` (${hint})` : "";
|
|
65
|
+
process.stderr.write(`
|
|
66
|
+
${question}${hintText}
|
|
67
|
+
`);
|
|
68
|
+
process.stderr.write(" Enter items one at a time. Empty line to finish.\n");
|
|
69
|
+
while (true) {
|
|
70
|
+
const item = await ask(` ${items.length + 1}.`);
|
|
71
|
+
if (!item) break;
|
|
72
|
+
items.push(item);
|
|
73
|
+
}
|
|
74
|
+
return items;
|
|
75
|
+
}
|
|
76
|
+
function heading(text) {
|
|
77
|
+
process.stderr.write(`
|
|
78
|
+
${"\u2500".repeat(60)}
|
|
79
|
+
`);
|
|
80
|
+
process.stderr.write(` ${text}
|
|
81
|
+
`);
|
|
82
|
+
process.stderr.write(`${"\u2500".repeat(60)}
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
function summary(label, items) {
|
|
86
|
+
process.stderr.write(`
|
|
87
|
+
${label}:
|
|
88
|
+
`);
|
|
89
|
+
items.forEach((item) => process.stderr.write(` \u2022 ${item}
|
|
90
|
+
`));
|
|
91
|
+
}
|
|
92
|
+
function info(text) {
|
|
93
|
+
process.stderr.write(` ${text}
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/cli/configure-world.ts
|
|
98
|
+
var DOMAIN_TEMPLATES = {
|
|
99
|
+
"Customer service": {
|
|
100
|
+
label: "Customer service",
|
|
101
|
+
healthMetrics: ["customer_satisfaction", "trust_score", "resolution_rate"],
|
|
102
|
+
negativDrivers: ["complaints", "slow_responses", "escalations"],
|
|
103
|
+
positiveDrivers: ["fast_responses", "positive_feedback", "first_contact_resolution"],
|
|
104
|
+
blockActions: ["share customer PII", "issue unauthorized refunds", "make legal promises"],
|
|
105
|
+
reviewActions: ["escalations", "large refund requests", "account closures"]
|
|
106
|
+
},
|
|
107
|
+
"Trading system": {
|
|
108
|
+
label: "Trading system",
|
|
109
|
+
healthMetrics: ["portfolio_health", "risk_score", "compliance_rate"],
|
|
110
|
+
negativDrivers: ["losses", "risk_violations", "unauthorized_trades"],
|
|
111
|
+
positiveDrivers: ["profitable_trades", "risk_compliance", "diversification"],
|
|
112
|
+
blockActions: ["exceed risk limits", "trade restricted securities", "bypass compliance"],
|
|
113
|
+
reviewActions: ["large positions", "new asset classes", "margin changes"]
|
|
114
|
+
},
|
|
115
|
+
"Content moderation": {
|
|
116
|
+
label: "Content moderation",
|
|
117
|
+
healthMetrics: ["content_quality", "safety_score", "creator_trust"],
|
|
118
|
+
negativDrivers: ["policy_violations", "false_positives", "user_reports"],
|
|
119
|
+
positiveDrivers: ["clean_content", "accurate_moderation", "appeal_resolutions"],
|
|
120
|
+
blockActions: ["approve harmful content", "ban without review", "ignore reports"],
|
|
121
|
+
reviewActions: ["borderline content", "repeat offenders", "appeal requests"]
|
|
122
|
+
},
|
|
123
|
+
"Research agent": {
|
|
124
|
+
label: "Research agent",
|
|
125
|
+
healthMetrics: ["accuracy_score", "source_quality", "output_reliability"],
|
|
126
|
+
negativDrivers: ["hallucinations", "unsourced_claims", "bias_incidents"],
|
|
127
|
+
positiveDrivers: ["verified_findings", "diverse_sources", "peer_validation"],
|
|
128
|
+
blockActions: ["fabricate citations", "present opinion as fact", "ignore contradicting evidence"],
|
|
129
|
+
reviewActions: ["novel conclusions", "controversial topics", "policy recommendations"]
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
function metricToStateVariable(metric) {
|
|
133
|
+
const id = metric.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
134
|
+
return {
|
|
135
|
+
id,
|
|
136
|
+
variable: {
|
|
137
|
+
type: "number",
|
|
138
|
+
min: 0,
|
|
139
|
+
max: 100,
|
|
140
|
+
step: 5,
|
|
141
|
+
default: 70,
|
|
142
|
+
mutable: true,
|
|
143
|
+
label: metric.replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
144
|
+
description: `Measures ${metric.toLowerCase()} on a 0-100 scale`,
|
|
145
|
+
display_as: "integer"
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function negativeDriverToRule(driver, healthMetrics, ruleIndex) {
|
|
150
|
+
const driverId = driver.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
151
|
+
const primaryTarget = healthMetrics[0]?.id || "system_health";
|
|
152
|
+
const eventRuleId = `rule-${String(ruleIndex).padStart(3, "0")}`;
|
|
153
|
+
const stateRuleId = `rule-${String(ruleIndex).padStart(3, "0")}-threshold`;
|
|
154
|
+
const counterId = `${driverId}_count`;
|
|
155
|
+
const stateVar = {
|
|
156
|
+
type: "number",
|
|
157
|
+
min: 0,
|
|
158
|
+
max: 100,
|
|
159
|
+
step: 1,
|
|
160
|
+
default: 0,
|
|
161
|
+
mutable: true,
|
|
162
|
+
label: `${driver.replace(/\b\w/g, (c) => c.toUpperCase())} Count`,
|
|
163
|
+
description: `Number of ${driver.toLowerCase()} events (0 = none)`,
|
|
164
|
+
display_as: "integer"
|
|
165
|
+
};
|
|
166
|
+
const eventRule = {
|
|
167
|
+
id: eventRuleId,
|
|
168
|
+
severity: "degradation",
|
|
169
|
+
label: `${driver} event degrades ${primaryTarget.replace(/_/g, " ")}`,
|
|
170
|
+
description: `Each ${driver.toLowerCase()} event reduces ${primaryTarget.replace(/_/g, " ")} by 5 and increments the counter.`,
|
|
171
|
+
order: ruleIndex,
|
|
172
|
+
triggers: [{
|
|
173
|
+
field: "event",
|
|
174
|
+
operator: "==",
|
|
175
|
+
value: driverId,
|
|
176
|
+
source: "state"
|
|
177
|
+
}],
|
|
178
|
+
effects: [
|
|
179
|
+
{ target: primaryTarget, operation: "subtract", value: 5 },
|
|
180
|
+
{ target: counterId, operation: "add", value: 1 }
|
|
181
|
+
],
|
|
182
|
+
causal_translation: {
|
|
183
|
+
trigger_text: `A ${driver.toLowerCase()} event occurs`,
|
|
184
|
+
rule_text: `Each ${driver.toLowerCase()} chips away at system health`,
|
|
185
|
+
shift_text: `${primaryTarget.replace(/_/g, " ")} decreases incrementally`,
|
|
186
|
+
effect_text: `${primaryTarget.replace(/_/g, " ")} reduced by 5 points per event`
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const stateRule = {
|
|
190
|
+
id: stateRuleId,
|
|
191
|
+
severity: "degradation",
|
|
192
|
+
label: `${driver} accumulation compounds damage`,
|
|
193
|
+
description: `When ${driver.toLowerCase()} count exceeds threshold, ${primaryTarget.replace(/_/g, " ")} suffers compounding loss.`,
|
|
194
|
+
order: ruleIndex + 100,
|
|
195
|
+
triggers: [{
|
|
196
|
+
field: counterId,
|
|
197
|
+
operator: ">",
|
|
198
|
+
value: 30,
|
|
199
|
+
source: "state"
|
|
200
|
+
}],
|
|
201
|
+
effects: [
|
|
202
|
+
{ target: primaryTarget, operation: "multiply", value: 0.7 }
|
|
203
|
+
],
|
|
204
|
+
causal_translation: {
|
|
205
|
+
trigger_text: `${driver} count exceeds safe threshold (30)`,
|
|
206
|
+
rule_text: `Accumulated ${driver.toLowerCase()} creates compounding pressure`,
|
|
207
|
+
shift_text: `${primaryTarget.replace(/_/g, " ")} begins accelerating decline`,
|
|
208
|
+
effect_text: `${primaryTarget.replace(/_/g, " ")} multiplied by 0.7 (30% loss)`
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
return { id: eventRuleId, rules: [eventRule, stateRule], stateVar: { id: counterId, variable: stateVar } };
|
|
212
|
+
}
|
|
213
|
+
function positiveDriverToRule(driver, healthMetrics, ruleIndex) {
|
|
214
|
+
const driverId = driver.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
215
|
+
const primaryTarget = healthMetrics[0]?.id || "system_health";
|
|
216
|
+
const eventRuleId = `rule-${String(ruleIndex).padStart(3, "0")}`;
|
|
217
|
+
const stateRuleId = `rule-${String(ruleIndex).padStart(3, "0")}-threshold`;
|
|
218
|
+
const counterId = `${driverId}_count`;
|
|
219
|
+
const stateVar = {
|
|
220
|
+
type: "number",
|
|
221
|
+
min: 0,
|
|
222
|
+
max: 100,
|
|
223
|
+
step: 1,
|
|
224
|
+
default: 0,
|
|
225
|
+
mutable: true,
|
|
226
|
+
label: `${driver.replace(/\b\w/g, (c) => c.toUpperCase())} Count`,
|
|
227
|
+
description: `Number of ${driver.toLowerCase()} events (0 = none)`,
|
|
228
|
+
display_as: "integer"
|
|
229
|
+
};
|
|
230
|
+
const eventRule = {
|
|
231
|
+
id: eventRuleId,
|
|
232
|
+
severity: "advantage",
|
|
233
|
+
label: `${driver} event improves ${primaryTarget.replace(/_/g, " ")}`,
|
|
234
|
+
description: `Each ${driver.toLowerCase()} event increases ${primaryTarget.replace(/_/g, " ")} by 3 and increments the counter.`,
|
|
235
|
+
order: ruleIndex,
|
|
236
|
+
triggers: [{
|
|
237
|
+
field: "event",
|
|
238
|
+
operator: "==",
|
|
239
|
+
value: driverId,
|
|
240
|
+
source: "state"
|
|
241
|
+
}],
|
|
242
|
+
effects: [
|
|
243
|
+
{ target: primaryTarget, operation: "add", value: 3 },
|
|
244
|
+
{ target: counterId, operation: "add", value: 1 }
|
|
245
|
+
],
|
|
246
|
+
causal_translation: {
|
|
247
|
+
trigger_text: `A ${driver.toLowerCase()} event occurs`,
|
|
248
|
+
rule_text: `Each ${driver.toLowerCase()} reinforces system health`,
|
|
249
|
+
shift_text: `${primaryTarget.replace(/_/g, " ")} improves incrementally`,
|
|
250
|
+
effect_text: `${primaryTarget.replace(/_/g, " ")} increased by 3 points per event`
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
const stateRule = {
|
|
254
|
+
id: stateRuleId,
|
|
255
|
+
severity: "advantage",
|
|
256
|
+
label: `${driver} momentum amplifies improvement`,
|
|
257
|
+
description: `When ${driver.toLowerCase()} count exceeds threshold, ${primaryTarget.replace(/_/g, " ")} gets a compounding boost.`,
|
|
258
|
+
order: ruleIndex + 100,
|
|
259
|
+
triggers: [{
|
|
260
|
+
field: counterId,
|
|
261
|
+
operator: ">",
|
|
262
|
+
value: 20,
|
|
263
|
+
source: "state"
|
|
264
|
+
}],
|
|
265
|
+
effects: [
|
|
266
|
+
{ target: primaryTarget, operation: "multiply", value: 1.15 }
|
|
267
|
+
],
|
|
268
|
+
causal_translation: {
|
|
269
|
+
trigger_text: `${driver} count exceeds momentum threshold (20)`,
|
|
270
|
+
rule_text: `Sustained ${driver.toLowerCase()} creates compounding improvement`,
|
|
271
|
+
shift_text: `${primaryTarget.replace(/_/g, " ")} begins accelerating growth`,
|
|
272
|
+
effect_text: `${primaryTarget.replace(/_/g, " ")} multiplied by 1.15 (15% boost)`
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
return { id: eventRuleId, rules: [eventRule, stateRule], stateVar: { id: counterId, variable: stateVar } };
|
|
276
|
+
}
|
|
277
|
+
function blockActionToGuard(action, index) {
|
|
278
|
+
const id = `guard_block_${String(index).padStart(3, "0")}`;
|
|
279
|
+
const words = action.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 2);
|
|
280
|
+
const pattern = `*${words.join("*")}*`;
|
|
281
|
+
return {
|
|
282
|
+
id,
|
|
283
|
+
label: `Block: ${action}`,
|
|
284
|
+
description: `Prevents the system from attempting to ${action.toLowerCase()}.`,
|
|
285
|
+
category: "structural",
|
|
286
|
+
enforcement: "block",
|
|
287
|
+
immutable: false,
|
|
288
|
+
intent_patterns: [pattern]
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function reviewActionToGuard(action, index) {
|
|
292
|
+
const id = `guard_pause_${String(index).padStart(3, "0")}`;
|
|
293
|
+
const words = action.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 2);
|
|
294
|
+
const pattern = `*${words.join("*")}*`;
|
|
295
|
+
return {
|
|
296
|
+
id,
|
|
297
|
+
label: `Review: ${action}`,
|
|
298
|
+
description: `Requires human review before the system can ${action.toLowerCase()}.`,
|
|
299
|
+
category: "operational",
|
|
300
|
+
enforcement: "pause",
|
|
301
|
+
immutable: false,
|
|
302
|
+
intent_patterns: [pattern]
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function generateGates(primaryMetricId) {
|
|
306
|
+
const gates = [
|
|
307
|
+
{ status: "THRIVING", field: primaryMetricId, operator: ">=", value: 80, color: "#22c55e", icon: "\u25C6" },
|
|
308
|
+
{ status: "STABLE", field: primaryMetricId, operator: ">=", value: 60, color: "#3b82f6", icon: "\u25CF" },
|
|
309
|
+
{ status: "COMPRESSED", field: primaryMetricId, operator: ">=", value: 40, color: "#f59e0b", icon: "\u25B2" },
|
|
310
|
+
{ status: "CRITICAL", field: primaryMetricId, operator: ">=", value: 20, color: "#ef4444", icon: "\u2726" },
|
|
311
|
+
{ status: "MODEL_COLLAPSES", field: primaryMetricId, operator: "<", value: 20, color: "#7f1d1d", icon: "\u2715" }
|
|
312
|
+
];
|
|
313
|
+
return {
|
|
314
|
+
viability_classification: gates,
|
|
315
|
+
structural_override: {
|
|
316
|
+
description: "System collapse when primary health metric falls below critical threshold",
|
|
317
|
+
enforcement: "mandatory"
|
|
318
|
+
},
|
|
319
|
+
sustainability_threshold: 40,
|
|
320
|
+
collapse_visual: {
|
|
321
|
+
background: "#7f1d1d",
|
|
322
|
+
text: "#fecaca",
|
|
323
|
+
border: "#ef4444",
|
|
324
|
+
label: "SYSTEM COLLAPSED"
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
async function phaseContext() {
|
|
329
|
+
heading("Let's define your system");
|
|
330
|
+
info("We'll do two things:");
|
|
331
|
+
info(" 1. Control what actions are allowed");
|
|
332
|
+
info(" 2. Model what happens over time");
|
|
333
|
+
const domain = await choose("What are you building?", [
|
|
334
|
+
...Object.keys(DOMAIN_TEMPLATES),
|
|
335
|
+
"Something else"
|
|
336
|
+
]);
|
|
337
|
+
const worldName = await ask("Give your world a name", domain === "Something else" ? "My System" : `${domain} Governance`);
|
|
338
|
+
const worldId = worldName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
339
|
+
const thesis = await ask(
|
|
340
|
+
"In one sentence, what does this system govern?",
|
|
341
|
+
domain !== "Something else" ? `Governance model for ${domain.toLowerCase()} operations` : void 0
|
|
342
|
+
);
|
|
343
|
+
return { worldName, worldId, thesis, domain };
|
|
344
|
+
}
|
|
345
|
+
async function phaseGuard(domain) {
|
|
346
|
+
heading("Layer 1: Action Control");
|
|
347
|
+
info("What should this system NOT do?");
|
|
348
|
+
const template = DOMAIN_TEMPLATES[domain];
|
|
349
|
+
let blockActions;
|
|
350
|
+
let reviewActions;
|
|
351
|
+
if (template) {
|
|
352
|
+
info(`
|
|
353
|
+
Suggested for ${domain}:`);
|
|
354
|
+
template.blockActions.forEach((a) => info(` BLOCK: ${a}`));
|
|
355
|
+
const useSuggested = await confirm("Use these suggestions?");
|
|
356
|
+
if (useSuggested) {
|
|
357
|
+
blockActions = [...template.blockActions];
|
|
358
|
+
} else {
|
|
359
|
+
blockActions = await askMany("What should be BLOCKED?", "actions the system must never do");
|
|
360
|
+
}
|
|
361
|
+
info("\n What should require human review?");
|
|
362
|
+
if (useSuggested) {
|
|
363
|
+
template.reviewActions.forEach((a) => info(` REVIEW: ${a}`));
|
|
364
|
+
const useReviewSuggested = await confirm("Use these suggestions?");
|
|
365
|
+
reviewActions = useReviewSuggested ? [...template.reviewActions] : await askMany("What needs human REVIEW?");
|
|
366
|
+
} else {
|
|
367
|
+
reviewActions = await askMany("What needs human REVIEW?", "actions that need approval");
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
blockActions = await askMany("What should be BLOCKED?", "actions the system must never do");
|
|
371
|
+
reviewActions = await askMany("What needs human REVIEW?", "actions that need approval");
|
|
372
|
+
}
|
|
373
|
+
if (blockActions.length > 0 || reviewActions.length > 0) {
|
|
374
|
+
summary("Actions configured", [
|
|
375
|
+
...blockActions.map((a) => `BLOCK: ${a}`),
|
|
376
|
+
...reviewActions.map((a) => `REVIEW: ${a}`)
|
|
377
|
+
]);
|
|
378
|
+
}
|
|
379
|
+
return { blockActions, reviewActions };
|
|
380
|
+
}
|
|
381
|
+
async function phaseSystem(domain) {
|
|
382
|
+
heading("Layer 2: System Dynamics");
|
|
383
|
+
info("Now let's define what a healthy system looks like over time.\n");
|
|
384
|
+
const template = DOMAIN_TEMPLATES[domain];
|
|
385
|
+
let healthMetrics;
|
|
386
|
+
let negativeDrivers;
|
|
387
|
+
let positiveDrivers;
|
|
388
|
+
info("What are you trying to protect or optimize?");
|
|
389
|
+
if (template) {
|
|
390
|
+
info(" Examples:");
|
|
391
|
+
template.healthMetrics.forEach((m) => info(` \u2022 ${m.replace(/_/g, " ")}`));
|
|
392
|
+
const useSuggested = await confirm("Use these suggestions?");
|
|
393
|
+
if (useSuggested) {
|
|
394
|
+
healthMetrics = [...template.healthMetrics];
|
|
395
|
+
} else {
|
|
396
|
+
healthMetrics = await askMany("What metrics define system health?", "e.g., customer satisfaction, trust, revenue");
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
healthMetrics = await askMany("What metrics define system health?", "e.g., customer satisfaction, trust, revenue");
|
|
400
|
+
}
|
|
401
|
+
if (healthMetrics.length === 0) {
|
|
402
|
+
healthMetrics = ["system_health"];
|
|
403
|
+
info(' Defaulting to "system_health" as primary metric.');
|
|
404
|
+
}
|
|
405
|
+
info("\n What makes this worse?");
|
|
406
|
+
if (template) {
|
|
407
|
+
info(" Examples:");
|
|
408
|
+
template.negativDrivers.forEach((d) => info(` \u2022 ${d.replace(/_/g, " ")}`));
|
|
409
|
+
const useSuggested = await confirm("Use these suggestions?");
|
|
410
|
+
negativeDrivers = useSuggested ? [...template.negativDrivers] : await askMany("What degrades your system?");
|
|
411
|
+
} else {
|
|
412
|
+
negativeDrivers = await askMany("What degrades your system?", "e.g., complaints, errors, delays");
|
|
413
|
+
}
|
|
414
|
+
info("\n What makes this better?");
|
|
415
|
+
if (template) {
|
|
416
|
+
info(" Examples:");
|
|
417
|
+
template.positiveDrivers.forEach((d) => info(` \u2022 ${d.replace(/_/g, " ")}`));
|
|
418
|
+
const useSuggested = await confirm("Use these suggestions?");
|
|
419
|
+
positiveDrivers = useSuggested ? [...template.positiveDrivers] : await askMany("What improves your system?");
|
|
420
|
+
} else {
|
|
421
|
+
positiveDrivers = await askMany("What improves your system?", "e.g., fast responses, positive feedback");
|
|
422
|
+
}
|
|
423
|
+
summary("System dynamics", [
|
|
424
|
+
`Health: ${healthMetrics.join(", ")}`,
|
|
425
|
+
`Degrades from: ${negativeDrivers.join(", ") || "(none)"}`,
|
|
426
|
+
`Improves from: ${positiveDrivers.join(", ") || "(none)"}`
|
|
427
|
+
]);
|
|
428
|
+
return { healthMetrics, negativeDrivers, positiveDrivers };
|
|
429
|
+
}
|
|
430
|
+
function generateWorld(state) {
|
|
431
|
+
const worldJson = {
|
|
432
|
+
world_id: state.worldId,
|
|
433
|
+
name: state.worldName,
|
|
434
|
+
thesis: state.thesis,
|
|
435
|
+
version: "1.0.0",
|
|
436
|
+
runtime_mode: "SIMULATION",
|
|
437
|
+
default_assumption_profile: "baseline",
|
|
438
|
+
default_alternative_profile: "stress",
|
|
439
|
+
modules: [],
|
|
440
|
+
players: { thinking_space: true, experience_space: true, action_space: true }
|
|
441
|
+
};
|
|
442
|
+
const variables = {};
|
|
443
|
+
const metricIds = [];
|
|
444
|
+
for (const metric of state.healthMetrics) {
|
|
445
|
+
const { id, variable } = metricToStateVariable(metric);
|
|
446
|
+
variables[id] = variable;
|
|
447
|
+
metricIds.push({ id });
|
|
448
|
+
}
|
|
449
|
+
const rules = [];
|
|
450
|
+
let ruleIdx = 1;
|
|
451
|
+
for (const driver of state.negativeDrivers) {
|
|
452
|
+
const result = negativeDriverToRule(driver, metricIds, ruleIdx++);
|
|
453
|
+
rules.push(...result.rules);
|
|
454
|
+
if (!variables[result.stateVar.id]) {
|
|
455
|
+
variables[result.stateVar.id] = result.stateVar.variable;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
for (const driver of state.positiveDrivers) {
|
|
459
|
+
const result = positiveDriverToRule(driver, metricIds, ruleIdx++);
|
|
460
|
+
rules.push(...result.rules);
|
|
461
|
+
if (!variables[result.stateVar.id]) {
|
|
462
|
+
variables[result.stateVar.id] = result.stateVar.variable;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const stateSchema = {
|
|
466
|
+
variables,
|
|
467
|
+
presets: {
|
|
468
|
+
"Healthy": {
|
|
469
|
+
description: "System operating normally",
|
|
470
|
+
values: Object.fromEntries(
|
|
471
|
+
Object.entries(variables).map(([id, v]) => [id, v.default])
|
|
472
|
+
)
|
|
473
|
+
},
|
|
474
|
+
"Stressed": {
|
|
475
|
+
description: "System under pressure",
|
|
476
|
+
values: Object.fromEntries(
|
|
477
|
+
Object.entries(variables).map(([id, v]) => {
|
|
478
|
+
if (id.endsWith("_count") && state.negativeDrivers.some((d) => id.startsWith(d.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "")))) {
|
|
479
|
+
return [id, 40];
|
|
480
|
+
}
|
|
481
|
+
if (state.healthMetrics.some((m) => m.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") === id)) {
|
|
482
|
+
return [id, 40];
|
|
483
|
+
}
|
|
484
|
+
return [id, v.default];
|
|
485
|
+
})
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
const guards = [];
|
|
491
|
+
state.blockActions.forEach((action, i) => {
|
|
492
|
+
guards.push(blockActionToGuard(action, i + 1));
|
|
493
|
+
});
|
|
494
|
+
state.reviewActions.forEach((action, i) => {
|
|
495
|
+
guards.push(reviewActionToGuard(action, i + 1));
|
|
496
|
+
});
|
|
497
|
+
const guardsJson = {
|
|
498
|
+
guards,
|
|
499
|
+
intent_vocabulary: {}
|
|
500
|
+
};
|
|
501
|
+
const primaryMetricId = metricIds[0]?.id || "system_health";
|
|
502
|
+
const gatesJson = generateGates(primaryMetricId);
|
|
503
|
+
const invariants = [
|
|
504
|
+
{
|
|
505
|
+
id: "system_must_remain_governable",
|
|
506
|
+
label: "System must remain under governance at all times",
|
|
507
|
+
enforcement: "structural",
|
|
508
|
+
mutable: false
|
|
509
|
+
}
|
|
510
|
+
];
|
|
511
|
+
const primaryOutcome = {
|
|
512
|
+
id: primaryMetricId,
|
|
513
|
+
type: "number",
|
|
514
|
+
range: [0, 100],
|
|
515
|
+
display_as: "integer",
|
|
516
|
+
label: variables[primaryMetricId]?.label || "System Health",
|
|
517
|
+
primary: true,
|
|
518
|
+
show_in_comparison: true
|
|
519
|
+
};
|
|
520
|
+
const outcomes = {
|
|
521
|
+
computed_outcomes: [
|
|
522
|
+
primaryOutcome,
|
|
523
|
+
...metricIds.slice(1).map((m) => ({
|
|
524
|
+
id: m.id,
|
|
525
|
+
type: "number",
|
|
526
|
+
range: [0, 100],
|
|
527
|
+
display_as: "integer",
|
|
528
|
+
label: variables[m.id]?.label || m.id,
|
|
529
|
+
primary: false,
|
|
530
|
+
show_in_comparison: true
|
|
531
|
+
}))
|
|
532
|
+
],
|
|
533
|
+
comparison_layout: {
|
|
534
|
+
primary_card: primaryMetricId,
|
|
535
|
+
status_badge: primaryMetricId,
|
|
536
|
+
structural_indicators: metricIds.map((m) => m.id)
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
const metadata = {
|
|
540
|
+
format_version: "1.0.0",
|
|
541
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
542
|
+
last_modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
543
|
+
authoring_method: "configurator-ai"
|
|
544
|
+
};
|
|
545
|
+
return { worldJson, stateSchema, guardsJson, rules, gatesJson, invariants, outcomes, metadata };
|
|
546
|
+
}
|
|
547
|
+
async function writeWorld(outputDir, world) {
|
|
548
|
+
const { mkdirSync, existsSync } = await import("fs");
|
|
549
|
+
const { writeFile } = await import("fs/promises");
|
|
550
|
+
const { join } = await import("path");
|
|
551
|
+
const files = [];
|
|
552
|
+
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
|
|
553
|
+
const rulesDir = join(outputDir, "rules");
|
|
554
|
+
if (!existsSync(rulesDir)) mkdirSync(rulesDir, { recursive: true });
|
|
555
|
+
const writeJson = async (name, data) => {
|
|
556
|
+
const path = join(outputDir, name);
|
|
557
|
+
await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
558
|
+
files.push(path);
|
|
559
|
+
};
|
|
560
|
+
await writeJson("world.json", world.worldJson);
|
|
561
|
+
await writeJson("state-schema.json", world.stateSchema);
|
|
562
|
+
await writeJson("guards.json", world.guardsJson);
|
|
563
|
+
await writeJson("gates.json", world.gatesJson);
|
|
564
|
+
await writeJson("invariants.json", world.invariants);
|
|
565
|
+
await writeJson("outcomes.json", world.outcomes);
|
|
566
|
+
await writeJson("metadata.json", world.metadata);
|
|
567
|
+
for (const rule of world.rules) {
|
|
568
|
+
const rulePath = join(rulesDir, `${rule.id}.json`);
|
|
569
|
+
await writeFile(rulePath, JSON.stringify(rule, null, 2) + "\n", "utf-8");
|
|
570
|
+
files.push(rulePath);
|
|
571
|
+
}
|
|
572
|
+
return files;
|
|
573
|
+
}
|
|
574
|
+
function parseArgs(argv) {
|
|
575
|
+
let outputDir = "./world/";
|
|
576
|
+
for (let i = 0; i < argv.length; i++) {
|
|
577
|
+
const arg = argv[i];
|
|
578
|
+
if ((arg === "--output" || arg === "-o") && i + 1 < argv.length) {
|
|
579
|
+
outputDir = argv[++i];
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return { outputDir };
|
|
583
|
+
}
|
|
584
|
+
async function main(argv = process.argv.slice(2)) {
|
|
585
|
+
try {
|
|
586
|
+
const args = parseArgs(argv);
|
|
587
|
+
const context = await phaseContext();
|
|
588
|
+
const guardState = await phaseGuard(context.domain);
|
|
589
|
+
const systemState = await phaseSystem(context.domain);
|
|
590
|
+
const wizardState = {
|
|
591
|
+
...context,
|
|
592
|
+
...guardState,
|
|
593
|
+
...systemState
|
|
594
|
+
};
|
|
595
|
+
heading("Summary");
|
|
596
|
+
info(`World: ${wizardState.worldName}`);
|
|
597
|
+
info(`Thesis: ${wizardState.thesis}`);
|
|
598
|
+
summary("Guard rules", [
|
|
599
|
+
...wizardState.blockActions.map((a) => `BLOCK: ${a}`),
|
|
600
|
+
...wizardState.reviewActions.map((a) => `REVIEW: ${a}`)
|
|
601
|
+
]);
|
|
602
|
+
summary("System dynamics", [
|
|
603
|
+
`Health metrics: ${wizardState.healthMetrics.join(", ")}`,
|
|
604
|
+
`Degrades from: ${wizardState.negativeDrivers.join(", ") || "(none)"}`,
|
|
605
|
+
`Improves from: ${wizardState.positiveDrivers.join(", ") || "(none)"}`
|
|
606
|
+
]);
|
|
607
|
+
const proceed = await confirm("\nCreate world?");
|
|
608
|
+
if (!proceed) {
|
|
609
|
+
info("Aborted.");
|
|
610
|
+
closePrompts();
|
|
611
|
+
process.exit(0);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
info("\nGenerating world...");
|
|
615
|
+
const world = generateWorld(wizardState);
|
|
616
|
+
const files = await writeWorld(args.outputDir, world);
|
|
617
|
+
heading("World created");
|
|
618
|
+
info(`Output: ${args.outputDir}`);
|
|
619
|
+
info(`Files: ${files.length}`);
|
|
620
|
+
summary("Generated", [
|
|
621
|
+
`${Object.keys(world.stateSchema.variables).length} state variables`,
|
|
622
|
+
`${world.rules.length} rules (${world.rules.filter((r) => r.severity === "degradation").length} degradation, ${world.rules.filter((r) => r.severity === "advantage").length} advantage)`,
|
|
623
|
+
`${world.guardsJson.guards.length} guards (${world.guardsJson.guards.filter((g) => g.enforcement === "block").length} block, ${world.guardsJson.guards.filter((g) => g.enforcement === "pause").length} pause)`,
|
|
624
|
+
`5 viability gates (THRIVING \u2192 MODEL_COLLAPSES)`
|
|
625
|
+
]);
|
|
626
|
+
info("\nNext steps:");
|
|
627
|
+
info(` neuroverse validate --world ${args.outputDir}`);
|
|
628
|
+
info(` neuroverse simulate ${args.outputDir} --steps 5`);
|
|
629
|
+
info(` neuroverse explain ${args.outputDir}`);
|
|
630
|
+
const refine = await confirm("\nWant to refine thresholds and collapse rules?", false);
|
|
631
|
+
if (refine) {
|
|
632
|
+
await phaseRefine(args.outputDir, world);
|
|
633
|
+
}
|
|
634
|
+
closePrompts();
|
|
635
|
+
const result = {
|
|
636
|
+
created: args.outputDir,
|
|
637
|
+
worldName: wizardState.worldName,
|
|
638
|
+
files: files.length,
|
|
639
|
+
stateVariables: Object.keys(world.stateSchema.variables).length,
|
|
640
|
+
rules: world.rules.length,
|
|
641
|
+
guards: world.guardsJson.guards.length,
|
|
642
|
+
gates: 5
|
|
643
|
+
};
|
|
644
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
645
|
+
process.exit(0);
|
|
646
|
+
} catch (e) {
|
|
647
|
+
closePrompts();
|
|
648
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : String(e)}
|
|
649
|
+
`);
|
|
650
|
+
process.exit(3);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
async function phaseRefine(outputDir, world) {
|
|
654
|
+
const { writeFile } = await import("fs/promises");
|
|
655
|
+
const { join } = await import("path");
|
|
656
|
+
heading("Refinement: Thresholds & Collapse");
|
|
657
|
+
const primaryMetric = world.gatesJson.viability_classification[0]?.field || "system_health";
|
|
658
|
+
info(`
|
|
659
|
+
Primary health metric: ${primaryMetric}`);
|
|
660
|
+
info(" Current gate thresholds:");
|
|
661
|
+
for (const gate of world.gatesJson.viability_classification) {
|
|
662
|
+
info(` ${gate.status}: ${gate.field} ${gate.operator} ${gate.value}`);
|
|
663
|
+
}
|
|
664
|
+
const changeGates = await confirm("Adjust gate thresholds?", false);
|
|
665
|
+
if (changeGates) {
|
|
666
|
+
for (const gate of world.gatesJson.viability_classification) {
|
|
667
|
+
const newVal = await ask(` ${gate.status} threshold (${gate.operator})`, String(gate.value));
|
|
668
|
+
const parsed = parseInt(newVal, 10);
|
|
669
|
+
if (!isNaN(parsed)) gate.value = parsed;
|
|
670
|
+
}
|
|
671
|
+
await writeFile(
|
|
672
|
+
join(outputDir, "gates.json"),
|
|
673
|
+
JSON.stringify(world.gatesJson, null, 2) + "\n",
|
|
674
|
+
"utf-8"
|
|
675
|
+
);
|
|
676
|
+
info(" Gates updated.");
|
|
677
|
+
}
|
|
678
|
+
const addCollapse = await confirm("Add collapse conditions to degradation rules?", false);
|
|
679
|
+
if (addCollapse) {
|
|
680
|
+
for (const rule of world.rules.filter((r) => r.severity === "degradation")) {
|
|
681
|
+
info(`
|
|
682
|
+
Rule: ${rule.label}`);
|
|
683
|
+
const target = rule.effects?.[0]?.target || primaryMetric;
|
|
684
|
+
const collapseVal = await ask(` ${target} collapses below what value?`, "10");
|
|
685
|
+
const parsed = parseInt(collapseVal, 10);
|
|
686
|
+
if (!isNaN(parsed)) {
|
|
687
|
+
rule.collapse_check = {
|
|
688
|
+
field: target,
|
|
689
|
+
operator: "<",
|
|
690
|
+
value: parsed,
|
|
691
|
+
result: "MODEL_COLLAPSES"
|
|
692
|
+
};
|
|
693
|
+
await writeFile(
|
|
694
|
+
join(outputDir, "rules", `${rule.id}.json`),
|
|
695
|
+
JSON.stringify(rule, null, 2) + "\n",
|
|
696
|
+
"utf-8"
|
|
697
|
+
);
|
|
698
|
+
info(` Collapse condition added: ${target} < ${parsed}`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
export {
|
|
704
|
+
main
|
|
705
|
+
};
|