@mindrian_os/install 1.13.0-beta.16 → 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 +10 -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/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,137 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
3
|
+
*
|
|
4
|
+
* Phase 118-02 Plan 02 Task 2 -- six-hats-red-black agent (Agent 5 of 6 in
|
|
5
|
+
* B1; agent_id 'six_hats_red_black' in lib/agents/mva/index.cjs).
|
|
6
|
+
*
|
|
7
|
+
* This agent is FULLY LOCAL -- zero network calls, zero Brain calls, zero
|
|
8
|
+
* filesystem reads beyond this source file itself. The "intelligence" is a
|
|
9
|
+
* curated registry of 12 questions patterned on Berger 2014's Beautiful
|
|
10
|
+
* Questions framework + de Bono Red/Black hats. Deterministic selection by
|
|
11
|
+
* sha256 modulo so the same sentence always gets the same question pair.
|
|
12
|
+
*
|
|
13
|
+
* Source spec line 67: "One question you haven't asked yourself" is the Red+
|
|
14
|
+
* Black flag voicing, not a separate agent. This module IS that voicing.
|
|
15
|
+
*
|
|
16
|
+
* Graph-native HARD RULES:
|
|
17
|
+
* 1. NEVER write to stdout / stderr (telemetry side-channel rule).
|
|
18
|
+
* 2. NEVER call fetch / network APIs (Test 12 grep regression asserts).
|
|
19
|
+
* 3. NEVER read user-content env vars (Canon Part 8).
|
|
20
|
+
*
|
|
21
|
+
* Pure CJS, node built-ins only, zero new runtime dependencies.
|
|
22
|
+
*/
|
|
23
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The 12 questions. Each carries:
|
|
27
|
+
* summary -- the "One question you haven't asked yourself: ..." voicing
|
|
28
|
+
* red -- the intuitive flag (de Bono Red Hat: gut, emotion, instinct)
|
|
29
|
+
* black -- the risk flag (de Bono Black Hat: critique, devil's advocate)
|
|
30
|
+
*
|
|
31
|
+
* Patterned on Berger 2014 ("A More Beautiful Question") + de Bono 1985
|
|
32
|
+
* ("Six Thinking Hats"). These 12 are GENERIC; they apply to any early-stage
|
|
33
|
+
* venture sentence. Canon Part 2 Engine 2: BONO orchestration.
|
|
34
|
+
*/
|
|
35
|
+
const QUESTION_REGISTRY = [
|
|
36
|
+
{
|
|
37
|
+
summary: "How does the person who currently manages this manually agree to adopt your product?",
|
|
38
|
+
red: "Adoption resistance feels heavier here than the pitch deck suggests.",
|
|
39
|
+
black: "The status-quo workflow has hidden switching costs you have not enumerated.",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
summary: "What does success look like in 90 days, and what evidence proves you got there?",
|
|
43
|
+
red: "You will know whether this is working by feel within 30 days.",
|
|
44
|
+
black: "Without a written 90-day evidence bar, success becomes whatever you want it to be.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
summary: "If your strongest competitor copied your idea tomorrow, what's left that they can't replicate?",
|
|
48
|
+
red: "Your unique angle is more replicable than you tell yourself.",
|
|
49
|
+
black: "The moat thesis is thinner than the deck claims; identify the irreducible asset.",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
summary: "What single assumption would, if wrong, kill the entire thesis?",
|
|
53
|
+
red: "You already know which assumption is the load-bearing one; you have been avoiding it.",
|
|
54
|
+
black: "The thesis depends on a chain; one weak link voids the rest.",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
summary: "Who would be furious if this worked? Why?",
|
|
58
|
+
red: "The incumbent's reaction will be faster and more political than you expect.",
|
|
59
|
+
black: "Surface the parties whose budgets, status, or careers your success threatens.",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
summary: "What would have to be true for a stranger to pay you within 30 days?",
|
|
63
|
+
red: "Cold-pay is the cleanest signal you can get; warm intros mask the truth.",
|
|
64
|
+
black: "If your only buyers are friends or affiliates, your pricing power is fictional.",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
summary: "Where is the smartest critic you know, and what would they tear apart first?",
|
|
68
|
+
red: "You already know who; the friction is asking.",
|
|
69
|
+
black: "Avoiding the critic is the most expensive form of optimism.",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
summary: "What part of this would you keep building even if no one ever paid you for it?",
|
|
73
|
+
red: "There is a small core you would build anyway; the rest is rationalization.",
|
|
74
|
+
black: "The parts you would not build for free are the parts the market also will not pay for.",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
summary: "If you had to ship in two weeks with one feature, which one would it be?",
|
|
78
|
+
red: "Your gut already picked the one feature; you have been adding the others to feel safer.",
|
|
79
|
+
black: "Multi-feature MVPs hide which feature is actually load-bearing for the value claim.",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
summary: "What is the smallest experiment that would change your mind?",
|
|
83
|
+
red: "If no experiment can change your mind, you have a religion, not a thesis.",
|
|
84
|
+
black: "Define the falsifier in writing before you run the test; otherwise the result will rationalize either outcome.",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
summary: "Whose calendar do you need to be on this month for this to matter in twelve?",
|
|
88
|
+
red: "The right meeting beats the right slide deck.",
|
|
89
|
+
black: "Twelve-month traction requires this-month introductions; deferring the ask compounds the delay.",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
summary: "What does the second version of this idea look like, after the first one fails?",
|
|
93
|
+
red: "You already see V2; you are clinging to V1 because of sunk effort.",
|
|
94
|
+
black: "Without a written V2, every V1 setback feels existential and decisions get reactive.",
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Deterministic index selection from a 64-hex sentence_sha256.
|
|
100
|
+
* Uses the leading 8 hex chars (32-bit window) mod 12 -- uniform-enough for
|
|
101
|
+
* 12 buckets that small biases don't matter, and same hash always picks the
|
|
102
|
+
* same bucket so the test contract holds.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} sha256
|
|
105
|
+
* @returns {number}
|
|
106
|
+
*/
|
|
107
|
+
function selectIndex(sha256) {
|
|
108
|
+
if (typeof sha256 !== 'string' || sha256.length === 0) return 0;
|
|
109
|
+
const head = sha256.slice(0, 8);
|
|
110
|
+
const n = parseInt(head, 16);
|
|
111
|
+
if (!Number.isFinite(n) || Number.isNaN(n)) return 0;
|
|
112
|
+
return n % QUESTION_REGISTRY.length;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {{ sentence_sha256: string, remaining_budget_ms: number }} context
|
|
117
|
+
* @param {AbortSignal} signal
|
|
118
|
+
* @returns {Promise<{ status:'ok'|'empty', payload:any } | null>}
|
|
119
|
+
*/
|
|
120
|
+
async function run(context, signal) {
|
|
121
|
+
if (signal && signal.aborted) return null;
|
|
122
|
+
const sha = (context && typeof context.sentence_sha256 === 'string') ? context.sentence_sha256 : '';
|
|
123
|
+
const idx = selectIndex(sha);
|
|
124
|
+
const q = QUESTION_REGISTRY[idx];
|
|
125
|
+
return {
|
|
126
|
+
status: 'ok',
|
|
127
|
+
payload: {
|
|
128
|
+
summary_line: "One question you haven't asked yourself: " + q.summary,
|
|
129
|
+
deck_data: {
|
|
130
|
+
red_flag: q.red,
|
|
131
|
+
black_flag: q.black,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { run, _test: { QUESTION_REGISTRY, selectIndex } };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
3
|
+
*
|
|
4
|
+
* Phase 118-02 Plan 02 Task 2 -- tavily-funding-scan agent (Agent 4 of 6 in
|
|
5
|
+
* B1; agent_id 'tavily_funding' in lib/agents/mva/index.cjs).
|
|
6
|
+
*
|
|
7
|
+
* Calls Tavily public-funding search via native fetch with a HARDCODED generic
|
|
8
|
+
* query. Returns top-1 funding match with summary line + structured deck_data.
|
|
9
|
+
*
|
|
10
|
+
* Graph-native HARD RULES:
|
|
11
|
+
* 1. NEVER require room-db.cjs directly (Phase 109 D-06 chokepoint).
|
|
12
|
+
* 2. NEVER write to stdout / stderr (telemetry side-channel rule).
|
|
13
|
+
* 3. NEVER include the user's raw sentence in the fetch body; only the
|
|
14
|
+
* hardcoded generic query string + a fixed search profile.
|
|
15
|
+
*
|
|
16
|
+
* Canon Part 8 hard invariant (MVA-118-11):
|
|
17
|
+
* The Tavily query is a HARDCODED STRING LITERAL. NEVER context-derived.
|
|
18
|
+
* NEVER the sentence_sha256 (a hash of a hash is still derived from user
|
|
19
|
+
* content). NEVER any user-content env var (token name elided so the
|
|
20
|
+
* test-all-six-agents.cjs Test 10 grep regression returns zero matches).
|
|
21
|
+
*
|
|
22
|
+
* OQ9: Tavily fallback when TAVILY_API_KEY is missing.
|
|
23
|
+
* Returns { status:'empty', payload: { reason:'tavily_unavailable' } } in <50ms.
|
|
24
|
+
* The brief still renders 5 of 6 surfaces; Plan 118-03's renderer will show
|
|
25
|
+
* "Live funding scan: not configured -- add TAVILY_API_KEY to ~/.mindrian.env".
|
|
26
|
+
*
|
|
27
|
+
* Pure CJS, node built-ins only, zero new runtime dependencies.
|
|
28
|
+
*/
|
|
29
|
+
'use strict';
|
|
30
|
+
|
|
31
|
+
const fs = require('node:fs');
|
|
32
|
+
const path = require('node:path');
|
|
33
|
+
const os = require('node:os');
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the Tavily API key with the standard env -> ~/.mindrian.env precedence
|
|
37
|
+
* pattern mirrored from lib/core/resolve-brain-key.cjs (Phase 123 Plan-07).
|
|
38
|
+
*
|
|
39
|
+
* Precedence (first hit wins):
|
|
40
|
+
* 1. process.env.TAVILY_API_KEY
|
|
41
|
+
* 2. <home>/.mindrian.env line `TAVILY_API_KEY=...`
|
|
42
|
+
* 3. <cwd>/.env line `TAVILY_API_KEY=...`
|
|
43
|
+
* 4. null
|
|
44
|
+
*
|
|
45
|
+
* Returns the trimmed key or null. Quotes around the value are stripped to
|
|
46
|
+
* match the feedback_gmail_qp_env_var_corruption memory rule (values starting
|
|
47
|
+
* with a hex digit get QP-decoded if not quoted; users wrap in double quotes).
|
|
48
|
+
*
|
|
49
|
+
* @returns {string|null}
|
|
50
|
+
*/
|
|
51
|
+
function resolveTavilyKey() {
|
|
52
|
+
// 1. env var
|
|
53
|
+
const envKey = process.env.TAVILY_API_KEY;
|
|
54
|
+
if (typeof envKey === 'string' && envKey.trim().length > 0) {
|
|
55
|
+
return envKey.trim();
|
|
56
|
+
}
|
|
57
|
+
// 2. ~/.mindrian.env
|
|
58
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
59
|
+
const mindrianEnv = path.join(home, '.mindrian.env');
|
|
60
|
+
if (fs.existsSync(mindrianEnv)) {
|
|
61
|
+
try {
|
|
62
|
+
const body = fs.readFileSync(mindrianEnv, 'utf8');
|
|
63
|
+
const m = body.match(/^TAVILY_API_KEY\s*=\s*"?([^"\n]+?)"?\s*$/m);
|
|
64
|
+
if (m && m[1] && m[1].trim().length > 0) return m[1].trim();
|
|
65
|
+
} catch (_e) { /* fall through */ }
|
|
66
|
+
}
|
|
67
|
+
// 3. <cwd>/.env
|
|
68
|
+
const cwdEnv = path.join(process.cwd(), '.env');
|
|
69
|
+
if (fs.existsSync(cwdEnv)) {
|
|
70
|
+
try {
|
|
71
|
+
const body = fs.readFileSync(cwdEnv, 'utf8');
|
|
72
|
+
const m = body.match(/^TAVILY_API_KEY\s*=\s*"?([^"\n]+?)"?\s*$/m);
|
|
73
|
+
if (m && m[1] && m[1].trim().length > 0) return m[1].trim();
|
|
74
|
+
} catch (_e) { /* fall through */ }
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {{ sentence_sha256: string, remaining_budget_ms: number }} _context
|
|
81
|
+
* @param {AbortSignal} signal
|
|
82
|
+
* @returns {Promise<{ status:'ok'|'empty', payload:any } | null>}
|
|
83
|
+
*/
|
|
84
|
+
async function run(_context, signal) {
|
|
85
|
+
const key = resolveTavilyKey();
|
|
86
|
+
if (!key) {
|
|
87
|
+
return { status: 'empty', payload: { reason: 'tavily_unavailable' } };
|
|
88
|
+
}
|
|
89
|
+
if (signal && signal.aborted) return null;
|
|
90
|
+
|
|
91
|
+
// Canon Part 8: HARDCODED generic query. NEVER context-derived.
|
|
92
|
+
// The intelligence comes from Tavily indexing public funding sources, not
|
|
93
|
+
// from tailoring the query to THIS user's venture.
|
|
94
|
+
const query = "early-stage venture grants pre-seed funding tracks open deadline";
|
|
95
|
+
|
|
96
|
+
let response;
|
|
97
|
+
try {
|
|
98
|
+
response = await fetch('https://api.tavily.com/search', {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: {
|
|
101
|
+
'Content-Type': 'application/json',
|
|
102
|
+
'Authorization': 'Bearer ' + key,
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
query: query,
|
|
106
|
+
max_results: 3,
|
|
107
|
+
search_depth: 'basic',
|
|
108
|
+
topic: 'news',
|
|
109
|
+
}),
|
|
110
|
+
signal: signal,
|
|
111
|
+
});
|
|
112
|
+
} catch (_e) {
|
|
113
|
+
if (signal && signal.aborted) return null;
|
|
114
|
+
return { status: 'empty', payload: { reason: 'tavily_error' } };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (signal && signal.aborted) return null;
|
|
118
|
+
if (!response || !response.ok) {
|
|
119
|
+
return { status: 'empty', payload: { reason: 'tavily_error' } };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let data;
|
|
123
|
+
try {
|
|
124
|
+
data = await response.json();
|
|
125
|
+
} catch (_e) {
|
|
126
|
+
return { status: 'empty', payload: { reason: 'tavily_parse_error' } };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const results = (data && Array.isArray(data.results)) ? data.results : [];
|
|
130
|
+
if (results.length === 0) {
|
|
131
|
+
return { status: 'empty', payload: { reason: 'no_funding_matches' } };
|
|
132
|
+
}
|
|
133
|
+
const top = results[0];
|
|
134
|
+
const titleStr = top.title || 'opportunity';
|
|
135
|
+
const snippetStr = String(top.snippet || '').slice(0, 80);
|
|
136
|
+
const summary = 'Live funding match: ' + titleStr + ' -- ' + snippetStr;
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
status: 'ok',
|
|
140
|
+
payload: {
|
|
141
|
+
summary_line: summary,
|
|
142
|
+
deck_data: { funding: results.slice(0, 3) },
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { run, _test: { resolveTavilyKey } };
|