create-venture 0.1.1 → 0.2.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/index.mjs +163 -22
- package/package.json +2 -2
package/index.mjs
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// create-venture: Naive's generator, the way create-next-app is Next.js's (I-1, I-3, I-7).
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// create-venture: Naive's generator, the way create-next-app is Next.js's (I-1, I-3, I-7, EE-082). Before
|
|
3
|
+
// anything is scaffolded it asks what you are building (described in a few words and classified by a
|
|
4
|
+
// model, or picked from the list) and what role you hold, then who else is founding it, the phase, and
|
|
5
|
+
// which functions (hats) each person owns. The venture type decides the packages and first decisions:
|
|
6
|
+
// a VC fund starts with a thesis and terms, a software startup with its product questions.
|
|
5
7
|
//
|
|
6
|
-
// npx create-venture [dir] [--yes] [--
|
|
8
|
+
// npx create-venture [dir] [--yes] [--type <type> | --describe "<a few words>"] [--role <role>]
|
|
9
|
+
// [--name "<your name>"] [--team "<name>, <name>"] [--phase <phase>]
|
|
10
|
+
// [--source <function>=<system>[:<address>],... ...] [--rho-fixture]
|
|
11
|
+
// [--layers a,b] [--legal-form <form>] [--dump <file>]
|
|
7
12
|
// [--use-npm | --use-pnpm | --use-yarn | --use-bun] [--skip-install] [--no-git]
|
|
8
13
|
// [--framework <spec>] [--reset-preferences]
|
|
9
14
|
//
|
|
10
|
-
// --
|
|
11
|
-
//
|
|
12
|
-
//
|
|
15
|
+
// --source says where a function keeps its truth (schema/systems-catalog.json), recorded as pointers in
|
|
16
|
+
// knowledge/sources.json: --source products=local-repo:../app,github:acme/app,vercel:acme/app
|
|
17
|
+
// --source finance=rho. Rho is synced at setup: its sandbox, the founder's own with RHO_API_TOKEN, or a
|
|
18
|
+
// saved answer with --rho-fixture (offline; recorded as a fixture).
|
|
19
|
+
//
|
|
20
|
+
// --yes takes the recommended defaults: a software startup, you as its solo founder wearing every hat,
|
|
21
|
+
// the discovery phase, the type's packages, checks on, and AGENTS.md. --describe classifies only when
|
|
22
|
+
// TYPESAFE_API_KEY is set; otherwise pick with --type. Answers are kept in
|
|
23
|
+
// ~/.config/naive/create-venture.json, so the next venture can reuse them.
|
|
13
24
|
|
|
14
25
|
import { spawnSync } from "node:child_process";
|
|
15
26
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -30,11 +41,13 @@ const fw = JSON.parse(readFileSync(join(framework, "package.json"), "utf8"));
|
|
|
30
41
|
const { layers: PHASE_LAYERS } = JSON.parse(readFileSync(join(framework, "schema/scaffold/defaults.json"), "utf8"));
|
|
31
42
|
const PHASES = Object.keys(PHASE_LAYERS);
|
|
32
43
|
const LAYERS = ["purpose", "knowledge", "operations", "evidence", "governance", "interface"];
|
|
44
|
+
// The legal forms venture.json accepts, from its schema, so the question can only offer valid answers.
|
|
45
|
+
const FORMS = JSON.parse(readFileSync(join(framework, "schema/venture.schema.json"), "utf8")).properties.form.properties.legal_form.enum;
|
|
33
46
|
|
|
34
47
|
const argv = process.argv.slice(2);
|
|
35
48
|
const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
36
49
|
const flag = (n) => argv.includes(`--${n}`);
|
|
37
|
-
const valued = new Set(["phase", "layers", "legal-form", "dump", "framework"]);
|
|
50
|
+
const valued = new Set(["phase", "layers", "legal-form", "dump", "framework", "type", "describe", "role", "name", "team"]);
|
|
38
51
|
const positional = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && valued.has(argv[i - 1].slice(2))));
|
|
39
52
|
const bold = (s) => (process.stdout.isTTY ? `\x1b[1m${s}\x1b[22m` : s);
|
|
40
53
|
const cyan = (s) => (process.stdout.isTTY ? `\x1b[36m${s}\x1b[39m` : s);
|
|
@@ -73,20 +86,111 @@ const id = slug(basename(dir));
|
|
|
73
86
|
if (!id) fail(`${basename(dir)} has no letters or digits to make a venture id from`);
|
|
74
87
|
if (existsSync(dir) && readdirSync(dir).length) fail(`${dir} already exists and is not empty. create-venture only creates new ventures; to bring an existing project in, create one here and run naive port or naive migrate.`);
|
|
75
88
|
|
|
76
|
-
//
|
|
89
|
+
// The framework's registries: venture types, roles and functions (EE-082).
|
|
90
|
+
const { types: TYPES, roles: ROLES, classify, canClassify } = await import(join(framework, "schema/venture-type.mjs")).then((m) => ({ types: m.types(), roles: m.roles(), classify: m.classify, canClassify: m.canClassify }));
|
|
91
|
+
const DEPARTMENTS = JSON.parse(readFileSync(join(framework, "operations/departments.json"), "utf8")).departments;
|
|
92
|
+
const deptLabel = (d) => DEPARTMENTS.find((x) => x.id === d)?.label ?? d;
|
|
93
|
+
const roleOf = (r) => ROLES.find((x) => x.id === r);
|
|
94
|
+
const gitName = () => spawnSync("git", ["config", "user.name"], { encoding: "utf8" }).stdout?.trim() || null;
|
|
95
|
+
|
|
96
|
+
// 2. What you are building: in a few words for the model, or from the list. Nothing is scaffolded until it is set.
|
|
97
|
+
let vtype = opt("type") ? TYPES.find((t) => t.id === opt("type")) : null;
|
|
98
|
+
if (opt("type") && !vtype) fail(`unknown venture type ${opt("type")}: one of ${TYPES.map((t) => t.id).join(", ")}`);
|
|
99
|
+
const pickFromList = async () => {
|
|
100
|
+
TYPES.forEach((t, i) => console.log(` ${String(i + 1).padStart(2)}. ${t.label}: ${t.description}`));
|
|
101
|
+
const a = await ask("Which kind of venture is it? (number or id)", "1");
|
|
102
|
+
const t = TYPES[Number(a) - 1] ?? TYPES.find((x) => x.id === a);
|
|
103
|
+
if (!t) { console.log(" choose a number from the list"); if (piped && !piped.length) fail("no answer left to choose with"); return pickFromList(); }
|
|
104
|
+
return t;
|
|
105
|
+
};
|
|
106
|
+
const classified = async (words) => {
|
|
107
|
+
if (!canClassify()) { console.log(" (no TYPESAFE_API_KEY, so pick from the list)"); return null; }
|
|
108
|
+
try {
|
|
109
|
+
const c = classify(words);
|
|
110
|
+
if (c.uncertain) { console.log(` not sure what kind of venture that is (best guess ${c.ranked[0].label}, ${Math.round(c.ranked[0].p * 100)}%), so pick from the list`); return null; }
|
|
111
|
+
return TYPES.find((t) => t.id === c.best.id);
|
|
112
|
+
} catch (e) { console.log(` ${e.message}; pick from the list`); return null; }
|
|
113
|
+
};
|
|
114
|
+
if (!vtype && opt("describe")) {
|
|
115
|
+
vtype = await classified(opt("describe"));
|
|
116
|
+
if (!vtype && !interactive) fail(`could not classify "${opt("describe")}": pass --type, one of ${TYPES.map((t) => t.id).join(", ")}`);
|
|
117
|
+
if (vtype) console.log(` ${opt("describe")}: a ${vtype.label.toLowerCase()}`);
|
|
118
|
+
}
|
|
119
|
+
if (!vtype && interactive) {
|
|
120
|
+
const words = await ask("What are you building? Describe it in a few words, or press Enter to pick from the list", "");
|
|
121
|
+
if (words) {
|
|
122
|
+
const t = await classified(words);
|
|
123
|
+
if (t && (await choose(`That sounds like a ${t.label.toLowerCase()}. Is that right?`, ["yes", "no"], "yes")) === "yes") vtype = t;
|
|
124
|
+
}
|
|
125
|
+
if (!vtype) vtype = await pickFromList();
|
|
126
|
+
}
|
|
127
|
+
// --yes with no type: the recommended default, a software startup.
|
|
128
|
+
if (!vtype) vtype = TYPES.find((t) => t.id === "software-startup");
|
|
129
|
+
|
|
130
|
+
// 3. Your role in it, from the roles this type has. Investors are a role too: a general partner runs a fund.
|
|
131
|
+
let role = opt("role") ?? (interactive ? await choose("What is your role in it?", vtype.roles, vtype.default_role) : vtype.default_role);
|
|
132
|
+
if (!vtype.roles.includes(role)) fail(`a ${vtype.label.toLowerCase()} has no ${role} role: one of ${vtype.roles.join(", ")}`);
|
|
133
|
+
|
|
134
|
+
// 4. You, and whether you are building it solo or with a team.
|
|
135
|
+
const me = opt("name") ?? (interactive ? await ask("What is your name?", gitName() ?? "Founder") : gitName() ?? "Founder");
|
|
136
|
+
let others = opt("team") ? opt("team").split(",").map((x) => x.trim()).filter(Boolean) : [];
|
|
137
|
+
if (!opt("team") && interactive && (await choose("Are you building it solo or with a team?", ["solo", "team"], prefs?.mode ?? "solo")) === "team") {
|
|
138
|
+
others = (await ask("Who else is founding it? (names, comma separated)", "")).split(",").map((x) => x.trim()).filter(Boolean);
|
|
139
|
+
if (!others.length) console.log(" no other names, so you are building it solo");
|
|
140
|
+
}
|
|
141
|
+
const mode = others.length ? "team" : "solo";
|
|
142
|
+
const coRole = vtype.id === "vc-fund" ? "general-partner" : "co-founder";
|
|
143
|
+
const people = [{ id: slug(me) || "founder", name: me, role }, ...others.map((n) => ({ id: slug(n), name: n, role: coRole }))];
|
|
144
|
+
if (new Set(people.map((p) => p.id)).size !== people.length) fail("two people would have the same id; give each a distinct name");
|
|
145
|
+
|
|
146
|
+
// 5. The phase, the hats, and the rest of the defaults (I-3, I-7).
|
|
77
147
|
const recommended = { phase: "discovery", layers: null, legalForm: null, dump: null };
|
|
78
148
|
let answers = { ...recommended };
|
|
79
149
|
const given = opt("phase") || opt("layers") || opt("legal-form") || opt("dump");
|
|
80
150
|
if (given) answers = { phase: opt("phase") ?? "discovery", layers: opt("layers") ?? null, legalForm: opt("legal-form") ?? null, dump: opt("dump") ?? null };
|
|
81
|
-
else if (interactive)
|
|
151
|
+
else if (interactive) answers.phase = await choose("Which phase is the venture in?", PHASES, prefs?.phase ?? "discovery");
|
|
152
|
+
// A solo founder owns every function the type imports; on a team, each function is asked once.
|
|
153
|
+
const owners = people.filter((p) => roleOf(p.role)?.owns_when_solo);
|
|
154
|
+
for (const p of people) p.hats = [];
|
|
155
|
+
if (owners.length === 1 || !interactive) for (const d of vtype.departments) (owners[0] ?? { hats: [] }).hats.push(d);
|
|
156
|
+
else for (const d of vtype.departments) {
|
|
157
|
+
const who = await choose(`Who owns ${deptLabel(d)}?`, owners.map((p) => p.id), owners[0].id);
|
|
158
|
+
owners.find((p) => p.id === who).hats.push(d);
|
|
159
|
+
}
|
|
160
|
+
// 6. Where each function keeps its truth (EE-083): pointers to its systems of record, in the founder's words when none fits.
|
|
161
|
+
const CATALOG = JSON.parse(readFileSync(join(framework, "schema/systems-catalog.json"), "utf8"));
|
|
162
|
+
const asked = [...vtype.departments.filter((d) => CATALOG.functions[d]), "legal"];
|
|
163
|
+
const parseSystems = (text) => {
|
|
164
|
+
const items = text.split(",").map((x) => x.trim()).filter(Boolean);
|
|
165
|
+
const systems = items.map((x) => { const i = x.indexOf(":"); const sys = i < 0 ? x : x.slice(0, i); return CATALOG.systems[sys] ? { system: sys, ...(i < 0 ? {} : { address: x.slice(i + 1) }) } : null; });
|
|
166
|
+
return systems.every(Boolean) ? { systems } : { systems: [], note: text.trim() };
|
|
167
|
+
};
|
|
168
|
+
const functions = {};
|
|
169
|
+
const sourceFlags = argv.flatMap((a, i) => (a === "--source" ? [argv[i + 1]] : []));
|
|
170
|
+
for (const f of sourceFlags) {
|
|
171
|
+
const [fn, ...rest] = f.split("=");
|
|
172
|
+
if (!CATALOG.functions[fn]) fail(`--source ${f}: ${fn} is not a function systems are kept for (${Object.keys(CATALOG.functions).join(", ")})`);
|
|
173
|
+
functions[fn] = parseSystems(rest.join("="));
|
|
174
|
+
for (const s of functions[fn].systems) if (!CATALOG.functions[fn].includes(s.system)) fail(`--source ${f}: ${s.system} is not offered for ${fn} (${CATALOG.functions[fn].join(", ")})`);
|
|
175
|
+
}
|
|
176
|
+
if (interactive && !sourceFlags.length) {
|
|
177
|
+
console.log("\nNow point Naive at where each function keeps its truth. Name systems (comma separated, add :address if you know it), type none, or say it in your own words.");
|
|
178
|
+
for (const fn of asked) {
|
|
179
|
+
const options = CATALOG.functions[fn];
|
|
180
|
+
const a = await ask(`Where is the source of truth for ${fn === "legal" ? "legal documents" : deptLabel(fn)}? ${options.join(" / ")} / none`, "none");
|
|
181
|
+
if (a.toLowerCase() === "none") continue;
|
|
182
|
+
functions[fn] = parseSystems(a);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (interactive && !given) {
|
|
82
187
|
const modes = prefs ? ["recommended", "previous", "customize"] : ["recommended", "customize"];
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
const l = await ask(`Which layers should be on? (comma separated, from ${LAYERS.join(", ")}; empty for the phase's defaults: ${PHASE_LAYERS[answers.phase].join(", ")})`, "");
|
|
188
|
+
const pick = await choose("Would you like to use the recommended Naive defaults for the rest?", modes, "recommended");
|
|
189
|
+
if (pick === "previous") answers = { ...answers, layers: prefs.layers ?? null, legalForm: prefs.legalForm ?? null };
|
|
190
|
+
if (pick === "customize") {
|
|
191
|
+
const l = await ask(`Which layers should be on? (comma separated, from ${LAYERS.join(", ")}; empty for the defaults)`, "");
|
|
88
192
|
answers.layers = l || null;
|
|
89
|
-
const f = await choose("Is the venture a legal entity yet?",
|
|
193
|
+
const f = await choose("Is the venture a legal entity yet?", FORMS, "none");
|
|
90
194
|
answers.legalForm = f === "none" ? null : f;
|
|
91
195
|
const d = await ask("Paste the path of an idea dump to start from (optional)", "");
|
|
92
196
|
answers.dump = d || null;
|
|
@@ -94,6 +198,7 @@ else if (interactive) {
|
|
|
94
198
|
}
|
|
95
199
|
if (!PHASES.includes(answers.phase)) fail(`unknown phase ${answers.phase}: one of ${PHASES.join(", ")}`);
|
|
96
200
|
if (answers.dump && !existsSync(resolve(answers.dump))) fail(`no idea dump at ${answers.dump}`);
|
|
201
|
+
if (answers.legalForm && !FORMS.includes(answers.legalForm)) fail(`unknown legal form ${answers.legalForm}: one of ${FORMS.join(", ")}`);
|
|
97
202
|
|
|
98
203
|
// 3. The package manager: the flag, else whichever ran this (npm_config_user_agent), else npm.
|
|
99
204
|
const pm = ["npm", "pnpm", "yarn", "bun"].find((p) => flag(`use-${p}`)) ?? (process.env.npm_config_user_agent ?? "").split("/")[0].replace(/^npx$/, "npm") ?? "npm";
|
|
@@ -101,20 +206,55 @@ const manager = ["npm", "pnpm", "yarn", "bun"].includes(pm) ? pm : "npm";
|
|
|
101
206
|
rl?.close();
|
|
102
207
|
|
|
103
208
|
// 4. The venture itself, by the framework's own scaffold.
|
|
104
|
-
console.log(`\nCreating a new
|
|
105
|
-
const init = spawnSync("node", [join(framework, "schema/init-venture.mjs"), dir, id, "--phase", answers.phase,
|
|
209
|
+
console.log(`\nCreating a new ${vtype.label.toLowerCase()} in ${bold(dir)}, with ${mode === "solo" ? `${me} as its ${role}, wearing every hat` : `${people.length} founders`}.\n`);
|
|
210
|
+
const init = spawnSync("node", [join(framework, "schema/init-venture.mjs"), dir, id, "--phase", answers.phase, "--type", vtype.id,
|
|
106
211
|
...(answers.layers ? ["--layers", answers.layers] : []), ...(answers.legalForm ? ["--legal-form", answers.legalForm] : [])], { encoding: "utf8" });
|
|
107
212
|
if (init.status !== 0) fail((init.stderr || init.stdout).trim());
|
|
108
213
|
const vfile = join(dir, "venture.json");
|
|
109
214
|
const venture = JSON.parse(readFileSync(vfile, "utf8"));
|
|
110
215
|
venture.name = basename(dir);
|
|
216
|
+
if (mode === "team") venture.members = people.slice(1).map((p) => ({ role: p.role === "general-partner" ? "partner" : p.role, kind: "person" }));
|
|
111
217
|
writeFileSync(vfile, JSON.stringify(venture, null, 2) + "\n");
|
|
112
218
|
|
|
219
|
+
// people/: the roster and one file per founder, the venture.json of human capital (EE-082).
|
|
113
220
|
const { agentFromEnv, record } = await import(join(framework, "interface/provenance.mjs"));
|
|
221
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
222
|
+
mkdirSync(join(dir, "people/founders"), { recursive: true });
|
|
223
|
+
const asPerson = agentFromEnv("person", "cli", { id: people[0].id });
|
|
224
|
+
for (const p of people.filter((x) => roleOf(x.role)?.founder)) {
|
|
225
|
+
p.founder_file = `people/founders/${p.id}.json`;
|
|
226
|
+
writeFileSync(join(dir, p.founder_file), JSON.stringify({ id: p.id, name: p.name, role: p.role, hats: p.hats, joined: today }, null, 2) + "\n");
|
|
227
|
+
record(dir, { entity: p.founder_file, route: `people/founders/${p.id}`, activity: "write", agent: asPerson, note: `${p.name}, ${p.role}, as answered at create-venture` });
|
|
228
|
+
}
|
|
229
|
+
writeFileSync(join(dir, "people/people.json"), JSON.stringify({ venture: id, mode, people: people.map(({ id: pid, name, role: r, hats, founder_file }) => ({ id: pid, name, role: r, hats, ...(founder_file ? { founder_file } : {}) })), updated: today }, null, 2) + "\n");
|
|
230
|
+
record(dir, { entity: "people/people.json", route: "people", activity: "write", agent: asPerson, note: `${mode}: ${people.map((p) => `${p.name} (${p.role})`).join(", ")}` });
|
|
114
231
|
if (answers.dump) {
|
|
115
232
|
mkdirSync(join(dir, "raw"), { recursive: true });
|
|
116
233
|
copyFileSync(resolve(answers.dump), join(dir, "raw/dump.md"));
|
|
117
|
-
record(dir, { entity: "raw/dump.md", route: "raw/dump", activity: "
|
|
234
|
+
record(dir, { entity: "raw/dump.md", route: "raw/dump", activity: "write", agent: agentFromEnv("person", "cli"), note: `the founder's idea dump, verbatim, from ${resolve(answers.dump)}` });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// knowledge/sources.json: each function's systems of record, as pointers (EE-083). Rho is read by code, so it is synced now.
|
|
238
|
+
const rhoScript = join(framework, "platform/integrations/rho.mjs");
|
|
239
|
+
let rhoNote = null;
|
|
240
|
+
for (const f of Object.values(functions)) for (const s of f.systems) {
|
|
241
|
+
s.status = "pointed";
|
|
242
|
+
if (s.system !== "rho") continue;
|
|
243
|
+
if (!existsSync(rhoScript)) { rhoNote = "Rho: recorded as pointed; this copy of Naive has no Rho integration"; continue; }
|
|
244
|
+
const fixture = flag("rho-fixture") ? join(framework, "platform/integrations/fixtures/rho-sandbox.json") : null;
|
|
245
|
+
const live = !fixture && process.env.RHO_API_TOKEN;
|
|
246
|
+
const r = spawnSync("node", [rhoScript, "sync", "--venture", dir, ...(fixture ? ["--fixture", fixture] : live ? ["--live"] : [])], { encoding: "utf8", env: process.env });
|
|
247
|
+
if (r.status === 0) {
|
|
248
|
+
Object.assign(s, { status: "connected", synced: new Date().toISOString().replace(/\.\d+Z$/, "Z"), ...(fixture ? { fixture: true } : {}), address: s.address ?? (fixture ? "saved sandbox answer" : live ? "live" : "sandbox") });
|
|
249
|
+
rhoNote = `Rho: synced ${fixture ? "from a saved sandbox answer (a fixture, not a live account)" : live ? "from your Rho account" : "from Rho's sandbox (a fictional company)"} into money/`;
|
|
250
|
+
} else rhoNote = `Rho: could not sync (${(r.stderr || r.stdout).trim().split("\n").at(-1)}); recorded as pointed`;
|
|
251
|
+
}
|
|
252
|
+
if (Object.keys(functions).length) {
|
|
253
|
+
const map = join(dir, "knowledge/sources.json");
|
|
254
|
+
mkdirSync(dirname(map), { recursive: true });
|
|
255
|
+
writeFileSync(map, JSON.stringify({ sources: [], records: {}, truth: "local", functions }, null, 2) + "\n");
|
|
256
|
+
record(dir, { entity: "knowledge/sources.json", route: "knowledge/knowledge-sources", activity: "write", agent: asPerson,
|
|
257
|
+
note: `systems of record: ${Object.entries(functions).map(([fn, f]) => `${fn} ${f.systems.map((s) => `${s.system} (${s.status})`).join(", ") || "in the founder's words"}`).join("; ")}` });
|
|
118
258
|
}
|
|
119
259
|
|
|
120
260
|
// The venture's package.json: the framework as a dev dependency and the four scripts (I-17).
|
|
@@ -126,7 +266,7 @@ writeFileSync(join(dir, "package.json"), JSON.stringify({
|
|
|
126
266
|
scripts: { dev: "naive dev", build: "naive build", start: "naive start", check: "naive check" },
|
|
127
267
|
devDependencies: { [fw.name]: spec },
|
|
128
268
|
}, null, 2) + "\n");
|
|
129
|
-
writeFileSync(join(dir, ".gitignore"), "node_modules/\n.naive/\n.DS_Store\n# Founder-supplied originals stay out of git (EE-070).\n**/raw/**/original.*\n");
|
|
269
|
+
writeFileSync(join(dir, ".gitignore"), "node_modules/\n.naive/\n.DS_Store\n# Founder-supplied originals stay out of git (EE-070).\n**/raw/**/original.*\n# A data stream's activity (bank transactions, CRM events) stays out of git; only its stream.json and summary.md are versioned.\n/*/_streams/*/*\n!/*/_streams/*/stream.json\n!/*/_streams/*/summary.md\n");
|
|
130
270
|
|
|
131
271
|
// Editor completion for records (I-26): every record file is mapped to its JSON Schema in the installed framework.
|
|
132
272
|
const { routes } = await import(join(framework, "interface/router.mjs"));
|
|
@@ -151,13 +291,14 @@ if (!flag("no-git") && spawnSync("git", ["--version"]).status === 0 && spawnSync
|
|
|
151
291
|
git = c.status === 0 ? "initialized, with a first commit" : "initialized; the first commit was skipped because git has no user.name and user.email";
|
|
152
292
|
} else if (!flag("no-git")) git = "skipped (already inside a git repository)";
|
|
153
293
|
|
|
154
|
-
try { mkdirSync(dirname(prefsFile), { recursive: true }); writeFileSync(prefsFile, JSON.stringify({ phase: answers.phase, layers: answers.layers, legalForm: answers.legalForm }, null, 2) + "\n"); } catch {}
|
|
294
|
+
try { mkdirSync(dirname(prefsFile), { recursive: true }); writeFileSync(prefsFile, JSON.stringify({ type: vtype.id, role, mode, phase: answers.phase, layers: answers.layers, legalForm: answers.legalForm }, null, 2) + "\n"); } catch {}
|
|
155
295
|
|
|
156
296
|
const runScript = (s) => (manager === "npm" ? `npm run ${s}` : `${manager} ${s}`);
|
|
157
297
|
const cd = relative(process.cwd(), dir) || ".";
|
|
298
|
+
const legal = functions.legal?.systems.length ? null : "Legal documents: none connected yet. Point Naive at them later with --source legal=google-drive:<folder> (or docusign, local-folder), and draft the founder agreement with the founding-agreement kit.";
|
|
158
299
|
console.log(`
|
|
159
300
|
${bold("Success!")} Created ${basename(dir)} at ${dir}
|
|
160
|
-
Git: ${git}
|
|
301
|
+
Git: ${git}${rhoNote ? `\n${rhoNote}` : ""}${legal ? `\n${legal}` : ""}
|
|
161
302
|
|
|
162
303
|
Inside that directory, you can run:
|
|
163
304
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-venture",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Create a new Naive venture: npx create-venture my-venture",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,6 @@
|
|
|
20
20
|
"LICENSE"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"naive-framework": "^0.
|
|
23
|
+
"naive-framework": "^0.2.0"
|
|
24
24
|
}
|
|
25
25
|
}
|