ventureos 1.0.9 → 1.0.11
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 +114 -105
- package/install.js +497 -292
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,161 +1,170 @@
|
|
|
1
1
|
# VentureOS
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Your AI co-founder. From raw idea to investor-ready pitch — step by step.**
|
|
4
4
|
|
|
5
|
-
VentureOS
|
|
5
|
+
VentureOS gives you a team of AI specialists that guide you through building a startup: researching the market, finding real customer problems, designing your solution, stress-testing your business model, and preparing your pitch. It follows a proven 12-week process so nothing gets skipped.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## What you need before starting
|
|
10
10
|
|
|
11
|
-
**
|
|
11
|
+
**1. Node.js** (a small program that runs VentureOS)
|
|
12
|
+
- Check if you already have it: open your terminal and type `node --version`
|
|
13
|
+
- If you see a number like `v20.x.x` you're good
|
|
14
|
+
- If not, download it free at [nodejs.org](https://nodejs.org) → click the big green "LTS" button → install it like any app
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
**2. An AI tool** — pick whichever you have:
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
| You have this | You need |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Cursor, Windsurf, or Antigravity | Nothing else — they include AI |
|
|
21
|
+
| Claude Code | Nothing else — it includes AI |
|
|
22
|
+
| None of the above | An API key (explained below) |
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
mkdir my-venture && cd my-venture
|
|
19
|
-
npx ventureos install
|
|
20
|
-
```
|
|
24
|
+
> **Don't have any of these?** Antigravity is free and takes 2 minutes to install: [antigravity.dev](https://antigravity.dev)
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
---
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
## Get started
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
Open your terminal, create a folder for your project, and run one command:
|
|
27
31
|
|
|
28
32
|
```bash
|
|
29
|
-
|
|
33
|
+
mkdir my-venture
|
|
34
|
+
cd my-venture
|
|
35
|
+
npx ventureos
|
|
30
36
|
```
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
That's it. VentureOS will detect what AI tools you have, ask you a few quick questions, and tell you exactly how to start.
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
> **What's a terminal?**
|
|
41
|
+
> On Mac: press `Cmd + Space`, type "Terminal", press Enter.
|
|
42
|
+
> On Windows: press the Windows key, type "cmd", press Enter.
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## What happens when you run it
|
|
47
|
+
|
|
48
|
+
**VentureOS scans your computer** and finds any AI tools you already have.
|
|
49
|
+
|
|
50
|
+
**Then it routes you to the best experience:**
|
|
51
|
+
|
|
52
|
+
### If you have Cursor, Windsurf, Antigravity, or Claude Code
|
|
53
|
+
|
|
54
|
+
VentureOS installs itself and gives you one line to type in your AI tool:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
@ventureOS/venture-master.md
|
|
40
58
|
```
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
Type that in your AI chat panel and Victor — your AI co-founder — introduces himself and takes it from there. No API keys. No extra setup.
|
|
61
|
+
|
|
62
|
+
### If you have an API key
|
|
63
|
+
|
|
64
|
+
VentureOS asks which service (Claude, ChatGPT, or Gemini), walks you through entering your key, and launches the chat for you directly in the terminal.
|
|
65
|
+
|
|
66
|
+
**No key yet?** VentureOS will show you a link to get one. If you get stuck, it never just crashes — it always gives you options.
|
|
43
67
|
|
|
44
68
|
---
|
|
45
69
|
|
|
46
|
-
##
|
|
70
|
+
## Changing your settings later
|
|
47
71
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
```bash
|
|
73
|
+
npx ventureos config
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Change your name, research depth, execution mode, or AI provider anytime — without starting over.
|
|
53
77
|
|
|
54
78
|
---
|
|
55
79
|
|
|
56
|
-
##
|
|
80
|
+
## Meet Victor and your AI team
|
|
81
|
+
|
|
82
|
+
When you start a session, **Victor** greets you. He's the orchestrator — think of him as your main point of contact. He knows where you are in the journey, what's been done, and what comes next.
|
|
57
83
|
|
|
58
|
-
|
|
84
|
+
When you need deep specialist work, Victor brings in the right expert:
|
|
59
85
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
| Specialist | What they do |
|
|
87
|
+
|---|---|
|
|
88
|
+
| **Diana** | Researches your market — competitors, trends, opportunities |
|
|
89
|
+
| **Clara** | Helps you find and validate real customer pain |
|
|
90
|
+
| **Paulo** | Designs your product wedge and early solution |
|
|
91
|
+
| **Bernard** | Builds your business model and revenue logic |
|
|
92
|
+
| **Fiona** | Sizes the market and builds your financial model |
|
|
93
|
+
| **Eva** | Plays the tough investor — stress-tests your venture |
|
|
94
|
+
| **Petra** | Creates your pitch deck and investor narrative |
|
|
95
|
+
| **Grace** | Builds your go-to-market and growth strategy |
|
|
96
|
+
| **Olivia** | Sets up your team structure and operations |
|
|
63
97
|
|
|
64
|
-
|
|
98
|
+
You talk to Victor. He calls in the right specialist at the right time.
|
|
65
99
|
|
|
66
100
|
---
|
|
67
101
|
|
|
68
|
-
## The
|
|
102
|
+
## The journey (12 weeks)
|
|
103
|
+
|
|
104
|
+
VentureOS follows a structured process so you always know where you are and what's next.
|
|
69
105
|
|
|
70
106
|
```
|
|
71
|
-
Week 1 Weeks 2
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
CHECK-IN PITCH FINAL PITCH
|
|
82
|
-
to Venture Board to Venture Board
|
|
83
|
-
(Go/Pivot/Kill) (Go/Pivot/Kill)
|
|
107
|
+
Week 1 Weeks 2–3 Weeks 4–7 Weeks 8–12
|
|
108
|
+
────────── ───────────── ───────────── ─────────────
|
|
109
|
+
Set up Understand Design your Build the
|
|
110
|
+
your team → the market → solution → business
|
|
111
|
+
Find customer
|
|
112
|
+
problems
|
|
113
|
+
↓ ↓
|
|
114
|
+
CHECK-IN FINAL PITCH
|
|
115
|
+
with board to investors
|
|
116
|
+
(Go/Pivot/Kill) (Go/Pivot/Kill)
|
|
84
117
|
```
|
|
85
118
|
|
|
86
|
-
|
|
119
|
+
At each phase, VentureOS generates documents for you — market maps, interview guides, business model canvases, pitch decks — so you never start from a blank page.
|
|
87
120
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
| Agent | Name | Role |
|
|
91
|
-
|-------|------|------|
|
|
92
|
-
| `venture-master.md` | Victor | Orchestrator — load this first |
|
|
93
|
-
| `agents/domain-explorer.md` | Diana | Market & domain research |
|
|
94
|
-
| `agents/customer-discovery.md` | Clara | Customer pain & interviews |
|
|
95
|
-
| `agents/product-strategist.md` | Paulo | Wedge design & prototyping |
|
|
96
|
-
| `agents/business-architect.md` | Bernard | Business model & revenue |
|
|
97
|
-
| `agents/financial-analyst.md` | Fiona | Market sizing & financial modeling |
|
|
98
|
-
| `agents/venture-evaluator.md` | Eva | Venture Board gate evaluation |
|
|
99
|
-
| `agents/pitch-master.md` | Petra | Pitch deck creation |
|
|
100
|
-
| `agents/growth-strategist.md` | Grace | GTM & market experiments |
|
|
101
|
-
| `agents/venture-ops.md` | Olivia | Team setup & parent org alignment |
|
|
121
|
+
Everything is saved automatically. Come back tomorrow and Victor picks up exactly where you left off.
|
|
102
122
|
|
|
103
123
|
---
|
|
104
124
|
|
|
105
|
-
##
|
|
125
|
+
## Two ways to work
|
|
106
126
|
|
|
107
|
-
|
|
108
|
-
ventureOS/
|
|
109
|
-
├── venture-master.md ← START HERE — load this file first
|
|
110
|
-
├── workflow-engine.md ← Workflow execution engine
|
|
111
|
-
├── config.yaml ← Your configuration (edit before first use)
|
|
112
|
-
├── README.md ← This file
|
|
113
|
-
├── SETUP.md ← Installation guide
|
|
114
|
-
├── agents/ ← Specialist agents (loaded on demand)
|
|
115
|
-
│ ├── domain-explorer.md
|
|
116
|
-
│ ├── customer-discovery.md
|
|
117
|
-
│ ├── product-strategist.md
|
|
118
|
-
│ ├── business-architect.md
|
|
119
|
-
│ ├── financial-analyst.md
|
|
120
|
-
│ ├── venture-evaluator.md
|
|
121
|
-
│ ├── pitch-master.md
|
|
122
|
-
│ ├── growth-strategist.md
|
|
123
|
-
│ └── venture-ops.md
|
|
124
|
-
├── workflows/ ← Phase-specific workflow definitions
|
|
125
|
-
│ ├── 0-explore/
|
|
126
|
-
│ ├── 1-setup-team/
|
|
127
|
-
│ ├── 2-understand-market/
|
|
128
|
-
│ ├── 3-find-pain/
|
|
129
|
-
│ ├── 4-define-solution/
|
|
130
|
-
│ ├── 5-business-case/
|
|
131
|
-
│ ├── 6-design-business/
|
|
132
|
-
│ └── venture-status/
|
|
133
|
-
├── templates/ ← Output document templates (30+)
|
|
134
|
-
├── scoring/ ← Gate rubric, pain scoring, pivot triggers
|
|
135
|
-
├── techniques/ ← Brainstorming techniques, synthetic tools
|
|
136
|
-
└── _memory/
|
|
137
|
-
└── venture-state.yaml ← Active venture state (auto-managed)
|
|
138
|
-
```
|
|
127
|
+
**Guided mode** (default) — Victor pauses after each step, shows you the output, and asks for your approval before moving on. Best for real ventures where decisions matter.
|
|
139
128
|
|
|
140
|
-
|
|
129
|
+
**Auto mode** — Victor runs the full phase on its own and shows you everything at the end. Best when you want to quickly screen an idea.
|
|
141
130
|
|
|
142
131
|
---
|
|
143
132
|
|
|
144
|
-
##
|
|
133
|
+
## Your outputs
|
|
145
134
|
|
|
146
|
-
|
|
135
|
+
Everything VentureOS creates is saved as readable files in `_ventures/your-venture-name/`:
|
|
147
136
|
|
|
148
|
-
|
|
137
|
+
- Market research and competitive analysis
|
|
138
|
+
- Customer interview scripts and synthesis
|
|
139
|
+
- Problem and solution definition documents
|
|
140
|
+
- Business model canvas
|
|
141
|
+
- Financial model
|
|
142
|
+
- Go-to-market plan
|
|
143
|
+
- Check-in pitch and final investor pitch deck
|
|
149
144
|
|
|
150
|
-
|
|
145
|
+
If you pivot or decide to stop, VentureOS archives everything and documents the decision with full rationale — so the work is never lost.
|
|
151
146
|
|
|
152
147
|
---
|
|
153
148
|
|
|
154
|
-
##
|
|
149
|
+
## Common questions
|
|
150
|
+
|
|
151
|
+
**Do I need to know how to code?**
|
|
152
|
+
No. You type in plain English. VentureOS handles everything else.
|
|
153
|
+
|
|
154
|
+
**What's an API key?**
|
|
155
|
+
It's like a password that gives you access to an AI service (like ChatGPT or Claude). If you use Cursor, Windsurf, or Antigravity, you don't need one — they include AI access in their apps.
|
|
156
|
+
|
|
157
|
+
**Does it save my work?**
|
|
158
|
+
Yes. Every session is saved automatically. Start and stop whenever you like.
|
|
155
159
|
|
|
156
|
-
|
|
160
|
+
**Can I use it for multiple ideas?**
|
|
161
|
+
Yes. Run `npx ventureos config` to switch ventures or start a new one.
|
|
162
|
+
|
|
163
|
+
**What if something goes wrong?**
|
|
164
|
+
VentureOS never just crashes. If there's a problem (wrong key, no connection, etc.), it shows you your options and waits for you to decide what to do next.
|
|
165
|
+
|
|
166
|
+
---
|
|
157
167
|
|
|
158
|
-
|
|
159
|
-
- **Kill:** Documents learnings, archives all artifacts, marks venture as killed with full rationale
|
|
168
|
+
## Need help?
|
|
160
169
|
|
|
161
|
-
|
|
170
|
+
Open an issue on GitHub: [github.com/ABTB93/ventureos/issues](https://github.com/ABTB93/ventureos/issues)
|
package/install.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* VentureOS CLI
|
|
4
4
|
* Usage:
|
|
5
|
-
* npx ventureos
|
|
6
|
-
* npx ventureos
|
|
7
|
-
* npx ventureos
|
|
5
|
+
* npx ventureos — setup wizard + start (auto-detects your environment)
|
|
6
|
+
* npx ventureos config — update your configuration
|
|
7
|
+
* npx ventureos start — launch Victor chat (if already installed)
|
|
8
8
|
*
|
|
9
9
|
* Zero npm dependencies — pure Node.js (readline/promises, fs, path, url, fetch)
|
|
10
10
|
* Requires Node.js >= 18.0.0
|
|
@@ -15,61 +15,140 @@ import { stdin as input, stdout as output } from 'process';
|
|
|
15
15
|
import fs from 'fs';
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
18
|
-
import { spawnSync
|
|
18
|
+
import { spawnSync } from 'child_process';
|
|
19
19
|
|
|
20
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
21
|
const PACKAGE_ROOT = path.dirname(__filename);
|
|
22
22
|
|
|
23
|
-
// Must be defined before startChat() runs (accessed synchronously before first await)
|
|
24
23
|
let _spinner;
|
|
25
24
|
|
|
25
|
+
// ─── Providers ─────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
26
27
|
const PROVIDERS = {
|
|
27
28
|
anthropic: {
|
|
28
29
|
label: 'Claude (Anthropic)',
|
|
29
30
|
envVar: 'ANTHROPIC_API_KEY',
|
|
30
31
|
defaultModel: 'claude-opus-4-6',
|
|
32
|
+
keyUrl: 'https://console.anthropic.com',
|
|
31
33
|
},
|
|
32
34
|
openai: {
|
|
33
35
|
label: 'ChatGPT (OpenAI)',
|
|
34
36
|
envVar: 'OPENAI_API_KEY',
|
|
35
37
|
defaultModel: 'gpt-4o',
|
|
38
|
+
keyUrl: 'https://platform.openai.com/api-keys',
|
|
36
39
|
},
|
|
37
40
|
gemini: {
|
|
38
41
|
label: 'Gemini (Google)',
|
|
39
42
|
envVar: 'GOOGLE_API_KEY',
|
|
40
43
|
defaultModel: 'gemini-2.0-flash',
|
|
44
|
+
keyUrl: 'https://aistudio.google.com/app/apikey',
|
|
41
45
|
},
|
|
42
46
|
};
|
|
43
47
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
// ─── Known AI IDEs ─────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
const IDE_TOOLS = [
|
|
51
|
+
{
|
|
52
|
+
id: 'cursor',
|
|
53
|
+
label: 'Cursor',
|
|
54
|
+
description: 'no API key needed',
|
|
55
|
+
cmds: ['cursor'],
|
|
56
|
+
appPaths: ['/Applications/Cursor.app'],
|
|
57
|
+
instructions: (dir) => [
|
|
58
|
+
' 1. Open this folder in Cursor',
|
|
59
|
+
` cursor "${dir}"`,
|
|
60
|
+
' (or: File → Open Folder → select this folder)',
|
|
61
|
+
'',
|
|
62
|
+
' 2. In the Cursor chat panel, type exactly this:',
|
|
63
|
+
' @ventureOS/venture-master.md',
|
|
64
|
+
'',
|
|
65
|
+
' 3. Victor — your AI co-founder — will take it from there.',
|
|
66
|
+
'',
|
|
67
|
+
' No API key. No extra setup. Just open and chat.',
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'windsurf',
|
|
72
|
+
label: 'Windsurf',
|
|
73
|
+
description: 'no API key needed',
|
|
74
|
+
cmds: ['windsurf'],
|
|
75
|
+
appPaths: ['/Applications/Windsurf.app'],
|
|
76
|
+
instructions: (dir) => [
|
|
77
|
+
' 1. Open this folder in Windsurf',
|
|
78
|
+
` windsurf "${dir}"`,
|
|
79
|
+
' (or: File → Open Folder → select this folder)',
|
|
80
|
+
'',
|
|
81
|
+
' 2. In the Windsurf chat panel, type:',
|
|
82
|
+
' @ventureOS/venture-master.md',
|
|
83
|
+
'',
|
|
84
|
+
' 3. Victor — your AI co-founder — will take it from there.',
|
|
85
|
+
'',
|
|
86
|
+
' No API key. No extra setup. Just open and chat.',
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'claude',
|
|
91
|
+
label: 'Claude Code',
|
|
92
|
+
description: 'no API key needed',
|
|
93
|
+
cmds: ['claude'],
|
|
94
|
+
appPaths: [],
|
|
95
|
+
instructions: (dir) => [
|
|
96
|
+
' 1. Open this folder in your terminal and start Claude Code',
|
|
97
|
+
` cd "${dir}"`,
|
|
98
|
+
' claude',
|
|
99
|
+
'',
|
|
100
|
+
' 2. Type this to activate VentureOS:',
|
|
101
|
+
' @ventureOS/venture-master.md',
|
|
102
|
+
'',
|
|
103
|
+
' 3. Victor — your AI co-founder — will take it from there.',
|
|
104
|
+
'',
|
|
105
|
+
' Full markdown rendering, streaming, all native Claude Code features.',
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'antigravity',
|
|
110
|
+
label: 'Antigravity',
|
|
111
|
+
description: 'free, no API key needed',
|
|
112
|
+
cmds: ['antigravity'],
|
|
113
|
+
appPaths: ['/Applications/Antigravity.app'],
|
|
114
|
+
instructions: (dir) => [
|
|
115
|
+
' 1. Open this folder in Antigravity',
|
|
116
|
+
` antigravity "${dir}"`,
|
|
117
|
+
' (or: File → Open Folder → select this folder)',
|
|
118
|
+
'',
|
|
119
|
+
' 2. In the chat panel, type:',
|
|
120
|
+
' @ventureOS/venture-master.md',
|
|
121
|
+
'',
|
|
122
|
+
' 3. Victor — your AI co-founder — will take it from there.',
|
|
123
|
+
'',
|
|
124
|
+
' Free to use. Supports Gemini, Claude, and more.',
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
// ─── Entry point ───────────────────────────────────────────────────────────────
|
|
48
130
|
|
|
49
131
|
const cmd = process.argv[2];
|
|
50
132
|
|
|
51
133
|
if (cmd === 'start') {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
134
|
+
legacyStart().catch(handleFatalError);
|
|
135
|
+
} else if (cmd === 'config') {
|
|
136
|
+
reconfigure().catch(handleFatalError);
|
|
58
137
|
} else {
|
|
59
|
-
|
|
60
|
-
if (err.code !== 'ERR_USE_AFTER_CLOSE') {
|
|
61
|
-
console.error('\n ❌ Installation error:', err.message);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
138
|
+
main().catch(handleFatalError);
|
|
65
139
|
}
|
|
66
140
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
141
|
+
function handleFatalError(err) {
|
|
142
|
+
if (err.code !== 'ERR_USE_AFTER_CLOSE') {
|
|
143
|
+
console.error('\n Error:', err.message, '\n');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
71
146
|
}
|
|
72
147
|
|
|
148
|
+
// ─── Utilities ─────────────────────────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
function line() { return ' ' + '─'.repeat(52); }
|
|
151
|
+
|
|
73
152
|
function parseSimpleYaml(text) {
|
|
74
153
|
const result = {};
|
|
75
154
|
for (const rawLine of text.split('\n')) {
|
|
@@ -88,7 +167,39 @@ function parseSimpleYaml(text) {
|
|
|
88
167
|
return result;
|
|
89
168
|
}
|
|
90
169
|
|
|
91
|
-
|
|
170
|
+
function showBanner() {
|
|
171
|
+
console.log('\n');
|
|
172
|
+
console.log(' ┌' + '─'.repeat(52) + '┐');
|
|
173
|
+
console.log(' │ │');
|
|
174
|
+
console.log(' │ VentureOS │');
|
|
175
|
+
console.log(' │ AI-Powered Venture Building Framework │');
|
|
176
|
+
console.log(' │ │');
|
|
177
|
+
console.log(' │ Build your startup — step by step. │');
|
|
178
|
+
console.log(' │ │');
|
|
179
|
+
console.log(' └' + '─'.repeat(52) + '┘');
|
|
180
|
+
console.log('\n');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ─── IDE Detection ─────────────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
function detectIDEs() {
|
|
186
|
+
const found = [];
|
|
187
|
+
for (const ide of IDE_TOOLS) {
|
|
188
|
+
const byCLI = ide.cmds.some(c => {
|
|
189
|
+
try {
|
|
190
|
+
const r = spawnSync('which', [c], { encoding: 'utf8', stdio: 'pipe' });
|
|
191
|
+
return r.status === 0 && r.stdout.trim();
|
|
192
|
+
} catch { return false; }
|
|
193
|
+
});
|
|
194
|
+
const byApp = ide.appPaths.some(p => {
|
|
195
|
+
try { return fs.existsSync(p); } catch { return false; }
|
|
196
|
+
});
|
|
197
|
+
if (byCLI || byApp) found.push(ide);
|
|
198
|
+
}
|
|
199
|
+
return found;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ─── File helpers ───────────────────────────────────────────────────────────────
|
|
92
203
|
|
|
93
204
|
const SKIP = new Set([
|
|
94
205
|
'install.js', 'package.json', 'package-lock.json',
|
|
@@ -129,7 +240,7 @@ output_folder: "_ventures"
|
|
|
129
240
|
# deep = exhaustive multi-source research with cross-validation
|
|
130
241
|
research_depth: "${researchDepth}"
|
|
131
242
|
|
|
132
|
-
# Your AI provider
|
|
243
|
+
# Your AI provider (used when running via built-in chat)
|
|
133
244
|
# Options: anthropic | openai | gemini
|
|
134
245
|
llm: "${llm}"
|
|
135
246
|
|
|
@@ -140,220 +251,365 @@ default_mode: "${defaultMode}"
|
|
|
140
251
|
`;
|
|
141
252
|
}
|
|
142
253
|
|
|
143
|
-
function
|
|
254
|
+
function installFiles(targetDir, ventureOSDir, configYaml) {
|
|
255
|
+
copyDir(PACKAGE_ROOT, ventureOSDir, SKIP);
|
|
256
|
+
console.log(' ✓ Framework files installed → ventureOS/');
|
|
257
|
+
|
|
258
|
+
fs.writeFileSync(path.join(ventureOSDir, 'config.yaml'), configYaml, 'utf8');
|
|
259
|
+
console.log(' ✓ Configuration saved → ventureOS/config.yaml');
|
|
260
|
+
|
|
261
|
+
const venturesDir = path.join(targetDir, '_ventures');
|
|
262
|
+
if (!fs.existsSync(venturesDir)) {
|
|
263
|
+
fs.mkdirSync(venturesDir, { recursive: true });
|
|
264
|
+
fs.writeFileSync(path.join(venturesDir, '.gitkeep'), '', 'utf8');
|
|
265
|
+
}
|
|
266
|
+
console.log(' ✓ Output folder ready → _ventures/');
|
|
267
|
+
|
|
268
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
269
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
270
|
+
fs.writeFileSync(gitignorePath, '# VentureOS\n_ventures/\nnode_modules/\n.DS_Store\n', 'utf8');
|
|
271
|
+
console.log(' ✓ .gitignore created');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ─── Setup Wizard ──────────────────────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
async function runSetupWizard(rl, { forIDE = false } = {}) {
|
|
144
278
|
console.log('\n' + line());
|
|
145
|
-
console.log('
|
|
146
|
-
console.log(line() + '\n');
|
|
147
|
-
console.log(' Run this command from your project folder:\n');
|
|
148
|
-
console.log(' npx ventureos start\n');
|
|
149
|
-
console.log(' Victor (your VentureOS orchestrator) will:');
|
|
150
|
-
console.log(line() + '\n');
|
|
151
|
-
console.log(' 1. Read your config and venture state');
|
|
152
|
-
console.log(' 2. Display the phase-aware menu');
|
|
153
|
-
console.log(' 3. Guide you through your venture building journey\n');
|
|
154
|
-
console.log(' Commands to get started:');
|
|
155
|
-
console.log(' [NV] New Venture — start a new venture (idea or domain)');
|
|
156
|
-
console.log(' [EX] Explore — run a domain deep dive first');
|
|
157
|
-
console.log(' [VS] Status — view venture status and next actions\n');
|
|
279
|
+
console.log(' A few quick questions so Victor knows how to help you.');
|
|
158
280
|
console.log(line() + '\n');
|
|
281
|
+
|
|
282
|
+
const nameInput = await rl.question(' Your name (press Enter for "Founder"): ');
|
|
283
|
+
const userName = nameInput.trim() || 'Founder';
|
|
284
|
+
|
|
285
|
+
console.log('\n How thorough should VentureOS be with research?\n');
|
|
286
|
+
console.log(' 1. Standard — structured analysis with sourced data (recommended)');
|
|
287
|
+
console.log(' 2. Light — quick overview, faster');
|
|
288
|
+
console.log(' 3. Deep — exhaustive, multi-source\n');
|
|
289
|
+
const depthInput = await rl.question(' Select [1-3]: ');
|
|
290
|
+
const researchDepth = ({ '1': 'standard', '2': 'light', '3': 'deep' })[depthInput.trim()] ?? 'standard';
|
|
291
|
+
|
|
292
|
+
console.log('\n How should workflows run by default?\n');
|
|
293
|
+
console.log(' 1. Guided — pauses so you review each step (recommended)');
|
|
294
|
+
console.log(' 2. Auto — runs everything on its own\n');
|
|
295
|
+
const modeInput = await rl.question(' Select [1-2]: ');
|
|
296
|
+
const defaultMode = modeInput.trim() === '2' ? 'yolo' : 'guided';
|
|
297
|
+
|
|
298
|
+
let llm = 'anthropic';
|
|
299
|
+
if (!forIDE) {
|
|
300
|
+
console.log('\n Which AI service will you use?\n');
|
|
301
|
+
console.log(' 1. Claude (Anthropic) (recommended)');
|
|
302
|
+
console.log(' 2. ChatGPT (OpenAI)');
|
|
303
|
+
console.log(' 3. Gemini (Google)\n');
|
|
304
|
+
const llmInput = await rl.question(' Select [1-3]: ');
|
|
305
|
+
llm = ({ '1': 'anthropic', '2': 'openai', '3': 'gemini' })[llmInput.trim()] ?? 'anthropic';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return { userName, researchDepth, llm, defaultMode };
|
|
159
309
|
}
|
|
160
310
|
|
|
161
|
-
|
|
162
|
-
console.log('\n');
|
|
163
|
-
console.log(' ┌' + '─'.repeat(52) + '┐');
|
|
164
|
-
console.log(' │ │');
|
|
165
|
-
console.log(' │ 🚀 VentureOS │');
|
|
166
|
-
console.log(' │ AI-Powered Venture Building Framework │');
|
|
167
|
-
console.log(' │ │');
|
|
168
|
-
console.log(' └' + '─'.repeat(52) + '┘');
|
|
169
|
-
console.log('\n');
|
|
311
|
+
// ─── API Key Recovery ──────────────────────────────────────────────────────────
|
|
170
312
|
|
|
171
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Prompts for an API key with a recovery menu.
|
|
315
|
+
* Returns the key string, or null if the user wants to switch provider.
|
|
316
|
+
* Exits the process if user picks "show free tools" or "exit".
|
|
317
|
+
*/
|
|
318
|
+
async function promptForAPIKey(rl, providerKey) {
|
|
319
|
+
const provider = PROVIDERS[providerKey];
|
|
172
320
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.log(
|
|
177
|
-
console.log(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
186
|
-
console.log(
|
|
187
|
-
|
|
188
|
-
return;
|
|
321
|
+
while (true) {
|
|
322
|
+
console.log(`\n No ${provider.envVar} found.\n`);
|
|
323
|
+
console.log(' 1. I\'ll paste my key now');
|
|
324
|
+
console.log(` 2. How to get a key → ${provider.keyUrl}`);
|
|
325
|
+
console.log(' 3. Switch to a different AI service');
|
|
326
|
+
console.log(' 4. Use an AI IDE instead — no key needed');
|
|
327
|
+
console.log(' 5. Exit\n');
|
|
328
|
+
|
|
329
|
+
const choice = (await rl.question(' Select [1-5]: ')).trim();
|
|
330
|
+
|
|
331
|
+
if (choice === '1') {
|
|
332
|
+
const key = (await rl.question(`\n Paste your ${provider.label} API key: `)).trim();
|
|
333
|
+
if (key) {
|
|
334
|
+
console.log(`\n Tip: save this to skip the prompt next time:`);
|
|
335
|
+
console.log(` export ${provider.envVar}=your-key\n`);
|
|
336
|
+
return key;
|
|
189
337
|
}
|
|
190
|
-
console.log('\n
|
|
338
|
+
console.log('\n No key entered.\n');
|
|
339
|
+
continue;
|
|
191
340
|
}
|
|
192
341
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
342
|
+
if (choice === '2') {
|
|
343
|
+
console.log(`\n Open this link to get your key:\n ${provider.keyUrl}\n`);
|
|
344
|
+
console.log(' Come back and run npx ventureos once you have it.\n');
|
|
345
|
+
rl.close();
|
|
346
|
+
process.exit(0);
|
|
347
|
+
}
|
|
199
348
|
|
|
200
|
-
|
|
201
|
-
|
|
349
|
+
if (choice === '3') return null; // signal: switch provider
|
|
350
|
+
|
|
351
|
+
if (choice === '4') {
|
|
352
|
+
showFreeToolsHelp();
|
|
353
|
+
rl.close();
|
|
354
|
+
process.exit(0);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (choice === '5') {
|
|
358
|
+
rl.close();
|
|
359
|
+
process.exit(0);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Asks user to pick a provider, then prompts for its API key.
|
|
366
|
+
* Loops until a valid provider + key is chosen.
|
|
367
|
+
*/
|
|
368
|
+
async function selectProviderAndKey(rl) {
|
|
369
|
+
while (true) {
|
|
370
|
+
console.log('\n Which AI service do you have a key for?\n');
|
|
202
371
|
console.log(' 1. Claude (Anthropic) (recommended)');
|
|
203
372
|
console.log(' 2. ChatGPT (OpenAI)');
|
|
204
|
-
console.log(' 3. Gemini (Google)
|
|
205
|
-
|
|
206
|
-
const llmMap = { '1': 'anthropic', '2': 'openai', '3': 'gemini' };
|
|
207
|
-
const llm = llmMap[llmInput.trim()] ?? 'anthropic';
|
|
208
|
-
|
|
209
|
-
// Step 4: Research depth
|
|
210
|
-
console.log('\n 🔍 Research depth for market and domain analysis?\n');
|
|
211
|
-
console.log(' 1. Standard — structured analysis with sourced data (recommended)');
|
|
212
|
-
console.log(' 2. Light — high-level overview, fast');
|
|
213
|
-
console.log(' 3. Deep — exhaustive multi-source research with validation\n');
|
|
214
|
-
const depthInput = await rl.question(' Select [1-3]: ');
|
|
215
|
-
const depthMap = { '1': 'standard', '2': 'light', '3': 'deep' };
|
|
216
|
-
const researchDepth = depthMap[depthInput.trim()] ?? 'standard';
|
|
217
|
-
|
|
218
|
-
// Step 6: Default mode
|
|
219
|
-
console.log('\n ⚙️ Default workflow execution mode?\n');
|
|
220
|
-
console.log(' 1. Guided — agent pauses for your review at each step (recommended)');
|
|
221
|
-
console.log(' 2. Yolo — agent runs full workflows autonomously\n');
|
|
222
|
-
const modeInput = await rl.question(' Select [1-2]: ');
|
|
223
|
-
const defaultMode = modeInput.trim() === '2' ? 'yolo' : 'guided';
|
|
373
|
+
console.log(' 3. Gemini (Google)');
|
|
374
|
+
console.log(' 4. I don\'t have one yet — show me free options\n');
|
|
224
375
|
|
|
225
|
-
rl.
|
|
376
|
+
const choice = (await rl.question(' Select [1-4]: ')).trim();
|
|
226
377
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
378
|
+
if (choice === '4') {
|
|
379
|
+
showFreeToolsHelp();
|
|
380
|
+
rl.close();
|
|
381
|
+
process.exit(0);
|
|
382
|
+
}
|
|
231
383
|
|
|
232
|
-
|
|
233
|
-
|
|
384
|
+
const providerKey = ({ '1': 'anthropic', '2': 'openai', '3': 'gemini' })[choice] ?? 'anthropic';
|
|
385
|
+
const apiKey = await promptForAPIKey(rl, providerKey);
|
|
386
|
+
if (apiKey) return { providerKey, apiKey };
|
|
387
|
+
// null = user wants to switch provider — loop again
|
|
388
|
+
}
|
|
389
|
+
}
|
|
234
390
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
391
|
+
function showFreeToolsHelp() {
|
|
392
|
+
console.log('\n' + line());
|
|
393
|
+
console.log(' AI tools you can use for free — no API key needed:\n');
|
|
394
|
+
console.log(' Antigravity (Google) — free preview, Gemini + Claude + more');
|
|
395
|
+
console.log(' → https://antigravity.dev\n');
|
|
396
|
+
console.log(' Cursor — popular AI code editor');
|
|
397
|
+
console.log(' → https://cursor.com\n');
|
|
398
|
+
console.log(' Windsurf — AI IDE by Codeium');
|
|
399
|
+
console.log(' → https://windsurf.com\n');
|
|
400
|
+
console.log(' Once installed, run npx ventureos again and we\'ll detect it.');
|
|
401
|
+
console.log(line() + '\n');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// ─── Main Flow ─────────────────────────────────────────────────────────────────
|
|
405
|
+
|
|
406
|
+
async function main() {
|
|
407
|
+
showBanner();
|
|
408
|
+
|
|
409
|
+
const projectRoot = process.cwd();
|
|
410
|
+
const ventureOSDir = path.join(projectRoot, 'ventureOS');
|
|
411
|
+
const configPath = path.join(ventureOSDir, 'config.yaml');
|
|
412
|
+
const rl = readline.createInterface({ input, output });
|
|
238
413
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
fs.
|
|
414
|
+
try {
|
|
415
|
+
// ── Already installed ──────────────────────────────────────────────────
|
|
416
|
+
if (fs.existsSync(configPath)) {
|
|
417
|
+
const config = parseSimpleYaml(fs.readFileSync(configPath, 'utf8'));
|
|
418
|
+
console.log(` Welcome back, ${config.user_name || 'Founder'}!\n`);
|
|
419
|
+
console.log(' VentureOS is already set up in this folder.');
|
|
420
|
+
console.log(' To update your settings, run: npx ventureos config\n');
|
|
421
|
+
await launchChat(rl, projectRoot, config);
|
|
422
|
+
return;
|
|
243
423
|
}
|
|
244
|
-
console.log(' ✓ Output folder ready → _ventures/');
|
|
245
424
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
425
|
+
// ── Environment scan ───────────────────────────────────────────────────
|
|
426
|
+
console.log(' Scanning your environment...\n');
|
|
427
|
+
const detectedIDEs = detectIDEs();
|
|
428
|
+
|
|
429
|
+
if (detectedIDEs.length > 0) {
|
|
430
|
+
detectedIDEs.forEach(ide => {
|
|
431
|
+
console.log(` ✓ ${ide.label} detected — ${ide.description}`);
|
|
432
|
+
});
|
|
433
|
+
console.log('');
|
|
434
|
+
} else {
|
|
435
|
+
console.log(' No AI tools found on this computer.\n');
|
|
250
436
|
}
|
|
251
437
|
|
|
438
|
+
// ── Route ──────────────────────────────────────────────────────────────
|
|
439
|
+
let chosenIDE = null;
|
|
440
|
+
|
|
441
|
+
if (detectedIDEs.length === 1) {
|
|
442
|
+
const ide = detectedIDEs[0];
|
|
443
|
+
console.log(` Good news: ${ide.label} comes with AI built in — ${ide.description}.\n`);
|
|
444
|
+
const ans = (await rl.question(` Set up VentureOS for ${ide.label}? (Y/n): `)).trim().toLowerCase();
|
|
445
|
+
if (!ans || ans === 'y' || ans === 'yes') {
|
|
446
|
+
chosenIDE = ide;
|
|
447
|
+
} else {
|
|
448
|
+
// User declined IDE — fall through to API key path
|
|
449
|
+
const { providerKey, apiKey } = await selectProviderAndKey(rl);
|
|
450
|
+
await installAndChat(rl, projectRoot, ventureOSDir, configPath, { forIDE: false, providerKey, apiKey });
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
} else if (detectedIDEs.length > 1) {
|
|
454
|
+
console.log(' Which would you like to use?\n');
|
|
455
|
+
detectedIDEs.forEach((ide, i) => {
|
|
456
|
+
const tag = i === 0 ? ' ← recommended' : '';
|
|
457
|
+
console.log(` ${i + 1}. ${ide.label} — ${ide.description}${tag}`);
|
|
458
|
+
});
|
|
459
|
+
console.log(` ${detectedIDEs.length + 1}. I'd rather use my own API key\n`);
|
|
460
|
+
const idx = parseInt((await rl.question(' Select: ')).trim(), 10) - 1;
|
|
461
|
+
if (idx >= 0 && idx < detectedIDEs.length) {
|
|
462
|
+
chosenIDE = detectedIDEs[idx];
|
|
463
|
+
} else {
|
|
464
|
+
const { providerKey, apiKey } = await selectProviderAndKey(rl);
|
|
465
|
+
await installAndChat(rl, projectRoot, ventureOSDir, configPath, { forIDE: false, providerKey, apiKey });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
// Nothing detected
|
|
470
|
+
console.log(' How would you like to run VentureOS?\n');
|
|
471
|
+
console.log(' 1. I have an API key (OpenAI, Claude, or Gemini)');
|
|
472
|
+
console.log(' 2. Show me free options — no key needed\n');
|
|
473
|
+
const choice = (await rl.question(' Select [1-2]: ')).trim();
|
|
474
|
+
if (choice === '2') {
|
|
475
|
+
showFreeToolsHelp();
|
|
476
|
+
rl.close();
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const { providerKey, apiKey } = await selectProviderAndKey(rl);
|
|
480
|
+
await installAndChat(rl, projectRoot, ventureOSDir, configPath, { forIDE: false, providerKey, apiKey });
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ── IDE setup ──────────────────────────────────────────────────────────
|
|
485
|
+
const { userName, researchDepth, defaultMode } = await runSetupWizard(rl, { forIDE: true });
|
|
486
|
+
rl.close();
|
|
487
|
+
|
|
252
488
|
console.log('\n' + line());
|
|
253
|
-
console.log('
|
|
254
|
-
console.log(line());
|
|
489
|
+
console.log(' Installing...');
|
|
490
|
+
console.log(line() + '\n');
|
|
255
491
|
|
|
256
|
-
|
|
492
|
+
const configYaml = generateConfig({ userName, researchDepth, llm: 'anthropic', defaultMode });
|
|
493
|
+
installFiles(projectRoot, ventureOSDir, configYaml);
|
|
494
|
+
|
|
495
|
+
console.log('\n' + line());
|
|
496
|
+
console.log(' VentureOS is ready!');
|
|
497
|
+
console.log(line() + '\n');
|
|
498
|
+
console.log(" Here's how to start:\n");
|
|
499
|
+
chosenIDE.instructions(projectRoot).forEach(l => console.log(l));
|
|
500
|
+
console.log('\n' + line() + '\n');
|
|
257
501
|
|
|
258
502
|
} catch (err) {
|
|
259
503
|
rl.close();
|
|
260
504
|
if (err.code === 'ERR_USE_AFTER_CLOSE') return;
|
|
261
|
-
|
|
262
|
-
process.exit(1);
|
|
505
|
+
throw err;
|
|
263
506
|
}
|
|
264
507
|
}
|
|
265
508
|
|
|
266
|
-
|
|
509
|
+
async function installAndChat(rl, projectRoot, ventureOSDir, configPath, { forIDE, providerKey, apiKey }) {
|
|
510
|
+
const { userName, researchDepth, llm, defaultMode } = await runSetupWizard(rl, { forIDE });
|
|
267
511
|
|
|
268
|
-
|
|
269
|
-
console.log('
|
|
270
|
-
console.log(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
console.log('\n');
|
|
512
|
+
console.log('\n' + line());
|
|
513
|
+
console.log(' Installing...');
|
|
514
|
+
console.log(line() + '\n');
|
|
515
|
+
|
|
516
|
+
const configYaml = generateConfig({ userName, researchDepth, llm, defaultMode });
|
|
517
|
+
installFiles(projectRoot, ventureOSDir, configYaml);
|
|
518
|
+
|
|
519
|
+
console.log('\n' + line());
|
|
520
|
+
console.log(' All set! Starting Victor...');
|
|
521
|
+
console.log(line() + '\n');
|
|
522
|
+
|
|
523
|
+
const config = parseSimpleYaml(configYaml);
|
|
524
|
+
await launchChat(rl, projectRoot, config, { providerKey, apiKey });
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// ─── Reconfigure ───────────────────────────────────────────────────────────────
|
|
528
|
+
|
|
529
|
+
async function reconfigure() {
|
|
530
|
+
showBanner();
|
|
276
531
|
|
|
277
532
|
const projectRoot = process.cwd();
|
|
278
533
|
const configPath = path.join(projectRoot, 'ventureOS', 'config.yaml');
|
|
279
534
|
|
|
280
535
|
if (!fs.existsSync(configPath)) {
|
|
281
|
-
console.error('
|
|
536
|
+
console.error(' VentureOS is not set up here. Run: npx ventureos\n');
|
|
282
537
|
process.exit(1);
|
|
283
538
|
}
|
|
284
539
|
|
|
540
|
+
const current = parseSimpleYaml(fs.readFileSync(configPath, 'utf8'));
|
|
541
|
+
console.log(' Current settings:\n');
|
|
542
|
+
console.log(` Name: ${current.user_name || '—'}`);
|
|
543
|
+
console.log(` Research: ${current.research_depth || '—'}`);
|
|
544
|
+
console.log(` Mode: ${current.default_mode || '—'}`);
|
|
545
|
+
console.log(` Provider: ${current.llm || '—'}\n`);
|
|
546
|
+
|
|
547
|
+
const rl = readline.createInterface({ input, output });
|
|
548
|
+
try {
|
|
549
|
+
const { userName, researchDepth, llm, defaultMode } = await runSetupWizard(rl, { forIDE: false });
|
|
550
|
+
rl.close();
|
|
551
|
+
const newConfig = generateConfig({ userName, researchDepth, llm, defaultMode });
|
|
552
|
+
fs.writeFileSync(configPath, newConfig, 'utf8');
|
|
553
|
+
console.log('\n ✓ Configuration updated → ventureOS/config.yaml\n');
|
|
554
|
+
} catch (err) {
|
|
555
|
+
rl.close();
|
|
556
|
+
if (err.code === 'ERR_USE_AFTER_CLOSE') return;
|
|
557
|
+
throw err;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// ─── Legacy start command ───────────────────────────────────────────────────────
|
|
562
|
+
|
|
563
|
+
async function legacyStart() {
|
|
564
|
+
showBanner();
|
|
565
|
+
const projectRoot = process.cwd();
|
|
566
|
+
const configPath = path.join(projectRoot, 'ventureOS', 'config.yaml');
|
|
567
|
+
if (!fs.existsSync(configPath)) {
|
|
568
|
+
console.error(' VentureOS is not set up here. Run: npx ventureos\n');
|
|
569
|
+
process.exit(1);
|
|
570
|
+
}
|
|
285
571
|
const config = parseSimpleYaml(fs.readFileSync(configPath, 'utf8'));
|
|
286
572
|
const rl = readline.createInterface({ input, output });
|
|
573
|
+
try {
|
|
574
|
+
await launchChat(rl, projectRoot, config);
|
|
575
|
+
} finally {
|
|
576
|
+
rl.close();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
287
579
|
|
|
288
|
-
|
|
289
|
-
|
|
580
|
+
// ─── Chat ───────────────────────────────────────────────────────────────────────
|
|
581
|
+
|
|
582
|
+
async function launchChat(rl, projectRoot, config, credentials = null) {
|
|
583
|
+
const providerKey = credentials?.providerKey || (PROVIDERS[config.llm] ? config.llm : 'anthropic');
|
|
290
584
|
const provider = PROVIDERS[providerKey];
|
|
291
585
|
|
|
292
|
-
// ──
|
|
293
|
-
let apiKey = process.env[provider.envVar];
|
|
294
|
-
let useCLI = false;
|
|
295
|
-
let cliCmd = null;
|
|
586
|
+
// ── Resolve API key ────────────────────────────────────────────────────────
|
|
587
|
+
let apiKey = credentials?.apiKey || process.env[provider.envVar];
|
|
296
588
|
|
|
297
589
|
if (!apiKey) {
|
|
298
|
-
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
console.log(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const choice = (await rl.question(' Select [1-2]: ')).trim();
|
|
305
|
-
if (choice === '2') {
|
|
306
|
-
useCLI = true;
|
|
307
|
-
cliCmd = cli.cmd;
|
|
308
|
-
} else {
|
|
309
|
-
apiKey = (await rl.question(`\n Enter your ${provider.label} API key: `)).trim();
|
|
310
|
-
if (!apiKey) {
|
|
311
|
-
console.error('\n ❌ No API key provided.\n');
|
|
312
|
-
rl.close();
|
|
313
|
-
process.exit(1);
|
|
314
|
-
}
|
|
315
|
-
console.log(`\n 💡 Tip: export ${provider.envVar}=your-key to skip this next time.\n`);
|
|
316
|
-
}
|
|
317
|
-
} else {
|
|
318
|
-
console.log(`\n 🔑 ${provider.envVar} not set in environment.`);
|
|
319
|
-
apiKey = (await rl.question(` Enter your ${provider.label} API key: `)).trim();
|
|
320
|
-
if (!apiKey) {
|
|
321
|
-
console.error('\n ❌ No API key provided.\n');
|
|
322
|
-
rl.close();
|
|
323
|
-
process.exit(1);
|
|
324
|
-
}
|
|
325
|
-
console.log(`\n 💡 Tip: export ${provider.envVar}=your-key to skip this next time.\n`);
|
|
590
|
+
apiKey = await promptForAPIKey(rl, providerKey);
|
|
591
|
+
if (!apiKey) {
|
|
592
|
+
// user wants to switch provider — tell them to reconfigure
|
|
593
|
+
console.log('\n Run npx ventureos config to change your AI provider.\n');
|
|
594
|
+
rl.close();
|
|
595
|
+
process.exit(0);
|
|
326
596
|
}
|
|
327
597
|
}
|
|
328
598
|
|
|
329
|
-
// ──
|
|
330
|
-
if (useCLI && cliCmd === 'claude') {
|
|
331
|
-
console.log(line());
|
|
332
|
-
console.log(' Use Claude Code natively — it\'s a much better experience.\n');
|
|
333
|
-
console.log(' In Claude Code, start your VentureOS session by typing:\n');
|
|
334
|
-
console.log(' @ventureOS/venture-master.md\n');
|
|
335
|
-
console.log(' That gives you full markdown rendering, streaming, and all');
|
|
336
|
-
console.log(' native Claude Code features — no terminal wrapper needed.');
|
|
337
|
-
console.log(line() + '\n');
|
|
338
|
-
rl.close();
|
|
339
|
-
process.exit(0);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// ── Load system prompt ─────────────────────────────────────────────────────
|
|
599
|
+
// ── Build system prompt ────────────────────────────────────────────────────
|
|
343
600
|
const masterPath = path.join(projectRoot, 'ventureOS', 'venture-master.md');
|
|
344
|
-
|
|
345
|
-
.replace(/\{project-root\}/g,
|
|
601
|
+
const systemPrompt = fs.readFileSync(masterPath, 'utf8')
|
|
602
|
+
.replace(/\{project-root\}/g, projectRoot)
|
|
346
603
|
.replace(/\{communication_language\}/g, 'English')
|
|
347
|
-
.replace(/\{user_name\}/g,
|
|
348
|
-
.replace(/\{llm\}/g,
|
|
349
|
-
.replace(/\{research_depth\}/g,
|
|
350
|
-
.replace(/\{default_mode\}/g,
|
|
351
|
-
.replace(/\{output_folder\}/g,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
const stateContent = fs.existsSync(statePath)
|
|
604
|
+
.replace(/\{user_name\}/g, config.user_name || 'Founder')
|
|
605
|
+
.replace(/\{llm\}/g, config.llm || 'anthropic')
|
|
606
|
+
.replace(/\{research_depth\}/g, config.research_depth || 'standard')
|
|
607
|
+
.replace(/\{default_mode\}/g, config.default_mode || 'guided')
|
|
608
|
+
.replace(/\{output_folder\}/g, config.output_folder || '_ventures');
|
|
609
|
+
|
|
610
|
+
const configContent = fs.readFileSync(path.join(projectRoot, 'ventureOS', 'config.yaml'), 'utf8');
|
|
611
|
+
const statePath = path.join(projectRoot, 'ventureOS', '_memory', 'venture-state.yaml');
|
|
612
|
+
const stateContent = fs.existsSync(statePath)
|
|
357
613
|
? fs.readFileSync(statePath, 'utf8')
|
|
358
614
|
: 'No venture state yet — this is a fresh start.';
|
|
359
615
|
|
|
@@ -363,25 +619,23 @@ async function startChat() {
|
|
|
363
619
|
`--- ventureOS/_memory/venture-state.yaml ---\n${stateContent}`;
|
|
364
620
|
|
|
365
621
|
console.log(line());
|
|
366
|
-
console.log(`
|
|
367
|
-
console.log(
|
|
622
|
+
console.log(` Connected to ${provider.label}`);
|
|
623
|
+
console.log(' Type your message and press Enter. Type "exit" to quit.');
|
|
368
624
|
console.log(line() + '\n');
|
|
369
625
|
|
|
370
626
|
const messages = [];
|
|
371
627
|
|
|
372
|
-
// ──
|
|
628
|
+
// ── Activation call ────────────────────────────────────────────────────────
|
|
373
629
|
showSpinner(' Victor is thinking');
|
|
374
630
|
let firstResponse;
|
|
375
631
|
try {
|
|
376
|
-
firstResponse =
|
|
377
|
-
|
|
378
|
-
|
|
632
|
+
firstResponse = await callLLM(providerKey, apiKey, provider.defaultModel, systemPrompt, [
|
|
633
|
+
{ role: 'user', content: activationMsg },
|
|
634
|
+
]);
|
|
379
635
|
} catch (err) {
|
|
380
636
|
stopSpinner();
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
rl.close();
|
|
384
|
-
process.exit(1);
|
|
637
|
+
await handleConnectionError(rl, err, providerKey, projectRoot, config);
|
|
638
|
+
return;
|
|
385
639
|
}
|
|
386
640
|
stopSpinner();
|
|
387
641
|
|
|
@@ -390,35 +644,30 @@ async function startChat() {
|
|
|
390
644
|
messages.push({ role: 'assistant', content: firstResponse });
|
|
391
645
|
await autoLoadAgents(firstResponse, messages, projectRoot);
|
|
392
646
|
|
|
393
|
-
// ──
|
|
647
|
+
// ── Chat loop ──────────────────────────────────────────────────────────────
|
|
394
648
|
while (true) {
|
|
395
649
|
let userInput;
|
|
396
|
-
try {
|
|
397
|
-
|
|
398
|
-
} catch {
|
|
399
|
-
break; // Ctrl+C
|
|
400
|
-
}
|
|
650
|
+
try { userInput = await rl.question(' You: '); }
|
|
651
|
+
catch { break; }
|
|
401
652
|
|
|
402
653
|
const trimmed = userInput.trim();
|
|
403
654
|
if (!trimmed) continue;
|
|
404
655
|
if (['exit', 'quit', 'da', '/exit'].includes(trimmed.toLowerCase())) {
|
|
405
|
-
console.log('\n Victor: Safe travels. Your venture state has been saved
|
|
656
|
+
console.log('\n Victor: Safe travels. Your venture state has been saved.\n');
|
|
406
657
|
break;
|
|
407
658
|
}
|
|
408
659
|
|
|
409
660
|
messages.push({ role: 'user', content: trimmed });
|
|
410
|
-
|
|
411
661
|
showSpinner(' Victor is thinking');
|
|
662
|
+
|
|
412
663
|
let response;
|
|
413
664
|
try {
|
|
414
|
-
response =
|
|
415
|
-
? await callViaCLI(cliCmd, systemPrompt, messages)
|
|
416
|
-
: await callLLM(providerKey, apiKey, provider.defaultModel, systemPrompt, messages);
|
|
665
|
+
response = await callLLM(providerKey, apiKey, provider.defaultModel, systemPrompt, messages);
|
|
417
666
|
} catch (err) {
|
|
418
667
|
stopSpinner();
|
|
419
|
-
console.error(`\n ❌
|
|
668
|
+
console.error(`\n ❌ ${err.message}\n`);
|
|
420
669
|
messages.pop();
|
|
421
|
-
continue;
|
|
670
|
+
continue; // stay in the loop — user can retry
|
|
422
671
|
}
|
|
423
672
|
stopSpinner();
|
|
424
673
|
|
|
@@ -426,24 +675,37 @@ async function startChat() {
|
|
|
426
675
|
messages.push({ role: 'assistant', content: response });
|
|
427
676
|
await autoLoadAgents(response, messages, projectRoot);
|
|
428
677
|
}
|
|
429
|
-
|
|
430
|
-
rl.close();
|
|
431
678
|
}
|
|
432
679
|
|
|
433
|
-
|
|
680
|
+
async function handleConnectionError(rl, err, providerKey, projectRoot, config) {
|
|
681
|
+
const provider = PROVIDERS[providerKey];
|
|
682
|
+
console.error(`\n ❌ Could not connect to ${provider.label}`);
|
|
683
|
+
console.error(` ${err.message}\n`);
|
|
434
684
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
685
|
+
console.log(' What would you like to do?\n');
|
|
686
|
+
console.log(' 1. Try a different API key');
|
|
687
|
+
console.log(` 2. Get a new key → ${provider.keyUrl}`);
|
|
688
|
+
console.log(' 3. Exit\n');
|
|
689
|
+
|
|
690
|
+
const choice = (await rl.question(' Select [1-3]: ')).trim();
|
|
691
|
+
|
|
692
|
+
if (choice === '1') {
|
|
693
|
+
const key = (await rl.question(`\n Paste your ${provider.label} API key: `)).trim();
|
|
694
|
+
if (key) {
|
|
695
|
+
await launchChat(rl, projectRoot, config, { providerKey, apiKey: key });
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
} else if (choice === '2') {
|
|
699
|
+
console.log(`\n Open: ${provider.keyUrl}`);
|
|
700
|
+
console.log(' Then run npx ventureos again.\n');
|
|
443
701
|
}
|
|
702
|
+
|
|
703
|
+
rl.close();
|
|
704
|
+
process.exit(0);
|
|
444
705
|
}
|
|
445
706
|
|
|
446
|
-
// ─── Spinner
|
|
707
|
+
// ─── Spinner ────────────────────────────────────────────────────────────────────
|
|
708
|
+
|
|
447
709
|
function showSpinner(msg) {
|
|
448
710
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
449
711
|
let i = 0;
|
|
@@ -463,16 +725,13 @@ function stopSpinner() {
|
|
|
463
725
|
async function autoLoadAgents(response, messages, projectRoot) {
|
|
464
726
|
const pattern = /ventureOS\/agents\/([\w-]+\.md)/g;
|
|
465
727
|
const files = [...new Set([...response.matchAll(pattern)].map(m => m[1]))];
|
|
466
|
-
|
|
467
728
|
for (const filename of files) {
|
|
468
729
|
const alreadyLoaded = messages.some(
|
|
469
730
|
m => m.role === 'user' && m.content.includes(`ventureOS/agents/${filename}`)
|
|
470
731
|
);
|
|
471
732
|
if (alreadyLoaded) continue;
|
|
472
|
-
|
|
473
733
|
const agentPath = path.join(projectRoot, 'ventureOS', 'agents', filename);
|
|
474
734
|
if (!fs.existsSync(agentPath)) continue;
|
|
475
|
-
|
|
476
735
|
const content = fs.readFileSync(agentPath, 'utf8');
|
|
477
736
|
messages.push({
|
|
478
737
|
role: 'user',
|
|
@@ -485,53 +744,39 @@ async function autoLoadAgents(response, messages, projectRoot) {
|
|
|
485
744
|
}
|
|
486
745
|
}
|
|
487
746
|
|
|
488
|
-
// ─── Markdown renderer
|
|
747
|
+
// ─── Markdown renderer ────────────────────────────────────────────────────────
|
|
489
748
|
|
|
490
749
|
function indentText(text) {
|
|
491
|
-
const B = '\x1b[1m', D = '\x1b[2m', R = '\x1b[0m'
|
|
750
|
+
const B = '\x1b[1m', D = '\x1b[2m', R = '\x1b[0m';
|
|
492
751
|
let inCode = false;
|
|
493
752
|
const out = [];
|
|
494
|
-
|
|
495
753
|
for (const raw of text.split('\n')) {
|
|
496
|
-
// Code fence toggle
|
|
497
754
|
if (raw.trimStart().startsWith('```')) {
|
|
498
755
|
inCode = !inCode;
|
|
499
756
|
out.push(D + ' ' + '─'.repeat(40) + R);
|
|
500
757
|
continue;
|
|
501
758
|
}
|
|
502
|
-
if (inCode)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if (
|
|
506
|
-
|
|
507
|
-
// Headings
|
|
508
|
-
if (raw.startsWith('### ')) { out.push('\n ' + B + raw.slice(4).trim() + R); continue; }
|
|
509
|
-
if (raw.startsWith('## ')) { out.push('\n ' + B + raw.slice(3).trim() + R); continue; }
|
|
510
|
-
if (raw.startsWith('# ')) { out.push('\n ' + B + raw.slice(2).trim().toUpperCase() + R); continue; }
|
|
511
|
-
|
|
512
|
-
// Table separator — skip
|
|
759
|
+
if (inCode) { out.push(D + ' ' + raw + R); continue; }
|
|
760
|
+
if (/^[-─]{3,}$/.test(raw.trim())) { out.push(' ' + '─'.repeat(52)); continue; }
|
|
761
|
+
if (raw.startsWith('### ')) { out.push('\n ' + B + raw.slice(4).trim() + R); continue; }
|
|
762
|
+
if (raw.startsWith('## ')) { out.push('\n ' + B + raw.slice(3).trim() + R); continue; }
|
|
763
|
+
if (raw.startsWith('# ')) { out.push('\n ' + B + raw.slice(2).trim().toUpperCase() + R); continue; }
|
|
513
764
|
if (/^\|[\s|:-]+\|$/.test(raw.trim())) continue;
|
|
514
|
-
|
|
515
|
-
// Table row
|
|
516
765
|
if (raw.trim().startsWith('|') && raw.trim().endsWith('|')) {
|
|
517
766
|
const cells = raw.split('|').slice(1, -1).map(c => c.trim());
|
|
518
767
|
out.push(' ' + cells.join(' │ ').replace(/\*\*(.+?)\*\*/g, B + '$1' + R));
|
|
519
768
|
continue;
|
|
520
769
|
}
|
|
521
|
-
|
|
522
|
-
// Inline: bold, italic, code
|
|
523
|
-
const line = raw
|
|
770
|
+
const ln = raw
|
|
524
771
|
.replace(/\*\*(.+?)\*\*/g, B + '$1' + R)
|
|
525
772
|
.replace(/\*(.+?)\*/g, '$1')
|
|
526
773
|
.replace(/`([^`]+)`/g, D + '$1' + R);
|
|
527
|
-
|
|
528
|
-
out.push(' ' + line);
|
|
774
|
+
out.push(' ' + ln);
|
|
529
775
|
}
|
|
530
|
-
|
|
531
776
|
return out.join('\n');
|
|
532
777
|
}
|
|
533
778
|
|
|
534
|
-
// ─── LLM API calls
|
|
779
|
+
// ─── LLM API calls ────────────────────────────────────────────────────────────
|
|
535
780
|
|
|
536
781
|
async function callLLM(provider, apiKey, model, system, messages) {
|
|
537
782
|
if (provider === 'anthropic') return callAnthropic(apiKey, model, system, messages);
|
|
@@ -540,46 +785,6 @@ async function callLLM(provider, apiKey, model, system, messages) {
|
|
|
540
785
|
throw new Error(`Unknown provider: ${provider}`);
|
|
541
786
|
}
|
|
542
787
|
|
|
543
|
-
async function callViaCLI(cliCmd, system, messages) {
|
|
544
|
-
// Build full context: system + conversation history + latest user message.
|
|
545
|
-
// Sent via stdin to avoid ARG_MAX limits and CLI option-parsing issues
|
|
546
|
-
// (e.g. prompts that start with "---" being misread as flags).
|
|
547
|
-
let prompt = system + '\n\n';
|
|
548
|
-
|
|
549
|
-
for (const msg of messages.slice(0, -1)) {
|
|
550
|
-
const role = msg.role === 'user' ? 'Human' : 'Assistant';
|
|
551
|
-
prompt += `${role}: ${msg.content}\n\n`;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const lastMsg = messages[messages.length - 1];
|
|
555
|
-
prompt += `Human: ${lastMsg.content}`;
|
|
556
|
-
|
|
557
|
-
return new Promise((resolve, reject) => {
|
|
558
|
-
const proc = spawn(cliCmd, ['--print', '--dangerously-skip-permissions'], {
|
|
559
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
let stdout = '';
|
|
563
|
-
let stderr = '';
|
|
564
|
-
|
|
565
|
-
proc.stdout.on('data', chunk => { stdout += chunk; });
|
|
566
|
-
proc.stderr.on('data', chunk => { stderr += chunk; });
|
|
567
|
-
|
|
568
|
-
proc.on('close', code => {
|
|
569
|
-
if (code !== 0) {
|
|
570
|
-
reject(new Error(stderr.trim() || `${cliCmd} CLI exited with code ${code}`));
|
|
571
|
-
} else {
|
|
572
|
-
resolve(stdout.trim());
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
proc.on('error', reject);
|
|
577
|
-
|
|
578
|
-
proc.stdin.write(prompt, 'utf8');
|
|
579
|
-
proc.stdin.end();
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
|
|
583
788
|
async function callAnthropic(apiKey, model, system, messages) {
|
|
584
789
|
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
585
790
|
method: 'POST',
|