@01.software/init 0.7.0 → 0.9.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/dist/index.js CHANGED
@@ -1,112 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- fetchTenantContext,
4
- generateClaudeMd,
5
- getSkillFiles
6
- } from "./chunk-T3A5SLEJ.js";
7
- import {
8
- readEnvValue,
9
- replaceTomlMcpSection,
10
- setEnvValue
11
- } from "./chunk-JT3G6B66.js";
12
- import {
13
- CODEX_MCP_SECTION_MARKER,
14
- getAnalyticsTemplate,
15
- getClientTemplate,
16
- getCodexMcpTomlSection,
17
- getEnvContent,
18
- getMcpConfigTemplate,
19
- getMcpServerEntry,
20
- getQueryProviderTemplate,
21
- getServerTemplate
22
- } from "./chunk-S3KHPWCE.js";
3
+ detectProject,
4
+ init
5
+ } from "./chunk-2IGKOSK7.js";
6
+ import "./chunk-5K2CB2Y5.js";
7
+ import "./chunk-UA7WNT2F.js";
8
+ import "./chunk-TBGKXE3Q.js";
23
9
 
24
10
  // src/index.ts
25
- import pc3 from "picocolors";
26
-
27
- // src/detect.ts
28
- import fs from "fs";
29
- import path from "path";
30
- function detectProject(cwd) {
31
- const pkgPath = path.join(cwd, "package.json");
32
- const hasPackageJson = fs.existsSync(pkgPath);
33
- let env = "node";
34
- let hasSdk = false;
35
- let hasReactQuery = false;
36
- let parseError = false;
37
- if (hasPackageJson) {
38
- let pkg;
39
- try {
40
- pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
41
- } catch {
42
- return {
43
- hasPackageJson: true,
44
- parseError: true,
45
- env: "node",
46
- packageManager: null,
47
- hasSdk: false,
48
- hasReactQuery: false,
49
- srcDir: false
50
- };
51
- }
52
- const deps = {
53
- ...pkg.dependencies || {},
54
- ...pkg.devDependencies || {}
55
- };
56
- hasSdk = "@01.software/sdk" in deps;
57
- hasReactQuery = "@tanstack/react-query" in deps;
58
- if ("next" in deps) {
59
- env = "nextjs";
60
- } else if ("astro" in deps || "@astrojs/node" in deps) {
61
- env = "other";
62
- } else if ("@remix-run/node" in deps || "@remix-run/react" in deps) {
63
- env = "other";
64
- } else if ("@sveltejs/kit" in deps) {
65
- env = "other";
66
- } else if ("react" in deps) {
67
- if ("vite" in deps) {
68
- env = "react-vite";
69
- } else if ("react-scripts" in deps) {
70
- env = "react-cra";
71
- } else {
72
- env = "node";
73
- }
74
- }
75
- }
76
- let packageManager = null;
77
- if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
78
- packageManager = "pnpm";
79
- } else if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
80
- packageManager = "yarn";
81
- } else if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) {
82
- packageManager = "bun";
83
- } else if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
84
- packageManager = "npm";
85
- }
86
- const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src"));
87
- return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir };
88
- }
89
- function needsClient(env) {
90
- return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla";
91
- }
92
- function needsServer(env) {
93
- return env === "nextjs" || env === "node" || env === "edge";
94
- }
95
- function needsReactQuery(env) {
96
- return env === "nextjs" || env === "react-vite" || env === "react-cra";
97
- }
98
- function getPublishableKeyEnvVar(env) {
99
- switch (env) {
100
- case "nextjs":
101
- return "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY";
102
- case "react-vite":
103
- return "VITE_SOFTWARE_PUBLISHABLE_KEY";
104
- case "react-cra":
105
- return "REACT_APP_SOFTWARE_PUBLISHABLE_KEY";
106
- default:
107
- return "SOFTWARE_PUBLISHABLE_KEY";
108
- }
109
- }
11
+ import pc from "picocolors";
110
12
 
111
13
  // src/prompts.ts
112
14
  import prompts from "prompts";
@@ -269,682 +171,6 @@ async function promptUser(hasSdk, detectedEnv, detectedPm) {
269
171
  };
270
172
  }
271
173
 
272
- // src/init.ts
273
- import fs2 from "fs";
274
- import path2 from "path";
275
- import os from "os";
276
- import { execSync } from "child_process";
277
- import pc2 from "picocolors";
278
- import prompts2 from "prompts";
279
-
280
- // src/browser-auth.ts
281
- import { randomBytes } from "crypto";
282
- import { createServer } from "http";
283
- import { execFile, exec } from "child_process";
284
- import { platform } from "os";
285
- import { URL } from "url";
286
- import pc from "picocolors";
287
- var DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || "https://01.software";
288
- var TIMEOUT_MS = 5 * 60 * 1e3;
289
- function escapeHtml(s) {
290
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
291
- }
292
- function openBrowser(url) {
293
- const os2 = platform();
294
- const onError = () => {
295
- console.log(
296
- pc.yellow(
297
- `Could not open browser automatically. Open this URL manually:
298
- ${url}`
299
- )
300
- );
301
- };
302
- if (os2 === "win32") {
303
- exec(`start "" "${url}"`, (err) => {
304
- if (err) onError();
305
- });
306
- } else {
307
- const cmd = os2 === "darwin" ? "open" : "xdg-open";
308
- execFile(cmd, [url], (err) => {
309
- if (err) onError();
310
- });
311
- }
312
- }
313
- var PAGE_STYLE = `*{margin:0;box-sizing:border-box}
314
- body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}
315
- @media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}
316
- .card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}
317
- .icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}
318
- .icon.ok{background:rgba(0,0,0,.05);color:#252525}
319
- .icon.err{background:rgba(220,38,38,.08);color:#dc2626}
320
- @media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}
321
- h1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}
322
- p{font-size:.75rem;color:#737373;line-height:1.5}`;
323
- var SUCCESS_HTML = `<!DOCTYPE html>
324
- <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login</title>
325
- <style>${PAGE_STYLE}</style>
326
- </head><body><div class="card"><div class="icon ok">\u2713</div><h1>Authenticated</h1><p>You can close this tab and return to the terminal.</p></div></body></html>`;
327
- var ERROR_HTML = (msg) => `<!DOCTYPE html>
328
- <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login Error</title>
329
- <style>${PAGE_STYLE}</style>
330
- </head><body><div class="card"><div class="icon err">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`;
331
- async function exchangeCode(webUrl, code) {
332
- const url = `${webUrl}/api/cli/exchange`;
333
- try {
334
- const res = await fetch(url, {
335
- method: "POST",
336
- headers: { "Content-Type": "application/json" },
337
- body: JSON.stringify({ code })
338
- });
339
- if (!res.ok) {
340
- const body = await res.text().catch(() => "");
341
- console.error(
342
- pc.red(
343
- `Exchange failed: HTTP ${res.status} from ${url}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`
344
- )
345
- );
346
- return null;
347
- }
348
- const data = await res.json();
349
- if (typeof data.publishableKey !== "string" || typeof data.secretKey !== "string" || typeof data.tenantName !== "string" || typeof data.tenantId !== "string") {
350
- console.error(pc.red(`Exchange failed: malformed response from ${url}`));
351
- return null;
352
- }
353
- return {
354
- publishableKey: data.publishableKey,
355
- secretKey: data.secretKey,
356
- tenantName: data.tenantName,
357
- tenantId: data.tenantId
358
- };
359
- } catch (err) {
360
- console.error(
361
- pc.red(
362
- `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`
363
- )
364
- );
365
- return null;
366
- }
367
- }
368
- async function startBrowserAuth(options) {
369
- const state = randomBytes(32).toString("hex");
370
- const webUrl = options?.webUrl ?? DEFAULT_WEB_URL;
371
- return new Promise((resolve, reject) => {
372
- const server = createServer((req, res) => {
373
- if (!req.url) {
374
- res.writeHead(400).end();
375
- return;
376
- }
377
- const url = new URL(req.url, `http://localhost`);
378
- if (url.pathname !== "/callback" || req.method !== "GET") {
379
- res.writeHead(404).end();
380
- return;
381
- }
382
- const error = url.searchParams.get("error");
383
- if (error) {
384
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML(error));
385
- console.error(pc.red(`Login failed: ${error}`));
386
- cleanup(new Error(`Login failed: ${error}`));
387
- return;
388
- }
389
- const code = url.searchParams.get("code");
390
- const receivedState = url.searchParams.get("state");
391
- if (!code || !receivedState) {
392
- res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Missing code or state."));
393
- cleanup(new Error("Login failed: missing code or state."));
394
- return;
395
- }
396
- if (receivedState !== state) {
397
- res.writeHead(403, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("State mismatch."));
398
- console.error(pc.red("Login failed: state mismatch."));
399
- cleanup(new Error("Login failed: state mismatch."));
400
- return;
401
- }
402
- exchangeCode(webUrl, code).then((creds) => {
403
- if (!creds) {
404
- res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Invalid or expired code."));
405
- cleanup(new Error("Login failed: code exchange failed."));
406
- return;
407
- }
408
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(SUCCESS_HTML);
409
- console.log(pc.green(`
410
- Logged in successfully!`));
411
- console.log(pc.dim(`Tenant: ${creds.tenantName}`));
412
- cleanup(null, creds);
413
- });
414
- });
415
- let timeout;
416
- let completed = false;
417
- function cleanup(err, result) {
418
- if (completed) return;
419
- completed = true;
420
- clearTimeout(timeout);
421
- server.closeAllConnections?.();
422
- server.close(() => {
423
- if (err) {
424
- reject(err);
425
- } else {
426
- resolve(result);
427
- }
428
- });
429
- }
430
- server.listen(0, "127.0.0.1", () => {
431
- const addr = server.address();
432
- if (!addr || typeof addr === "string") {
433
- reject(new Error("Failed to start local server."));
434
- return;
435
- }
436
- const port = addr.port;
437
- timeout = setTimeout(() => {
438
- console.error(pc.red("\nLogin timed out (5 minutes). Please try again."));
439
- cleanup(new Error("Login timed out"));
440
- }, TIMEOUT_MS);
441
- const params = new URLSearchParams({ port: String(port), state });
442
- if (options?.tenantId) {
443
- params.set("tenantId", options.tenantId);
444
- }
445
- const loginUrl = `${webUrl}/cli-auth?${params.toString()}`;
446
- console.log(pc.dim("Opening browser for login..."));
447
- console.log(pc.dim(`If the browser does not open, visit:
448
- ${loginUrl}`));
449
- openBrowser(loginUrl);
450
- });
451
- server.on("error", (err) => {
452
- reject(err);
453
- });
454
- });
455
- }
456
-
457
- // src/init.ts
458
- var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY";
459
- async function init(cwd, info, answers) {
460
- const { packageManager, srcDir } = info;
461
- const env = answers.env;
462
- const baseDir = srcDir ? path2.join(cwd, "src") : cwd;
463
- const publishableKeyEnvVar = getPublishableKeyEnvVar(env);
464
- const wantsClient = needsClient(env);
465
- const wantsServer = needsServer(env);
466
- const wantsReactQuery = needsReactQuery(env);
467
- const plan = await planConflictsAndEnv(cwd, baseDir, env, answers);
468
- const installResult = installDeps(
469
- cwd,
470
- packageManager,
471
- info.hasSdk,
472
- info.hasReactQuery,
473
- wantsReactQuery
474
- );
475
- if (wantsClient || wantsReactQuery || wantsServer) {
476
- fs2.mkdirSync(path2.join(baseDir, "lib", "software"), { recursive: true });
477
- }
478
- const libDir = path2.join(baseDir, "lib", "software");
479
- if (wantsClient) {
480
- await writeFileWithPolicy(
481
- cwd,
482
- path2.join(libDir, "client.ts"),
483
- getClientTemplate(env, publishableKeyEnvVar),
484
- plan.policy
485
- );
486
- await writeFileWithPolicy(
487
- cwd,
488
- path2.join(libDir, "analytics.ts"),
489
- getAnalyticsTemplate(env, publishableKeyEnvVar),
490
- plan.policy
491
- );
492
- }
493
- if (wantsReactQuery) {
494
- await writeFileWithPolicy(
495
- cwd,
496
- path2.join(libDir, "query-provider.tsx"),
497
- getQueryProviderTemplate(env),
498
- plan.policy
499
- );
500
- }
501
- if (wantsServer) {
502
- await writeFileWithPolicy(
503
- cwd,
504
- path2.join(libDir, "server.ts"),
505
- getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),
506
- plan.policy
507
- );
508
- }
509
- if (plan.envFile && answers.authMethod !== "browser") {
510
- await writeEnv(
511
- cwd,
512
- plan.envFile,
513
- answers.publishableKey || "",
514
- answers.secretKey || "",
515
- publishableKeyEnvVar,
516
- wantsServer ? SECRET_KEY_ENV_VAR : null,
517
- plan.policy
518
- );
519
- }
520
- let publishableKey = answers.publishableKey;
521
- let secretKey = answers.secretKey;
522
- let tenantName = "";
523
- if (answers.authMethod === "browser" && answers.aiTools.length > 0) {
524
- try {
525
- console.log();
526
- const creds = await startBrowserAuth();
527
- publishableKey = creds.publishableKey;
528
- secretKey = creds.secretKey;
529
- tenantName = creds.tenantName;
530
- if (plan.envFile && publishableKey) {
531
- await writeEnv(
532
- cwd,
533
- plan.envFile,
534
- publishableKey,
535
- secretKey,
536
- publishableKeyEnvVar,
537
- wantsServer ? SECRET_KEY_ENV_VAR : null,
538
- "overwrite",
539
- true
540
- );
541
- }
542
- } catch (err) {
543
- console.log(
544
- pc2.yellow(" Browser auth skipped:"),
545
- err instanceof Error ? err.message : String(err)
546
- );
547
- }
548
- }
549
- if (answers.aiTools.length > 0) {
550
- for (const tool of answers.aiTools) {
551
- await writeMcpConfig(tool, cwd);
552
- }
553
- addToGitignore(cwd, answers.aiTools);
554
- if (answers.aiTools.includes("claude")) {
555
- await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy);
556
- }
557
- }
558
- return installResult;
559
- }
560
- function installDeps(cwd, pm, hasSdk, hasReactQuery, wantsReactQuery) {
561
- const allDeps = [
562
- { name: "@01.software/sdk", installed: hasSdk, needed: true },
563
- { name: "@tanstack/react-query", installed: hasReactQuery, needed: wantsReactQuery }
564
- ];
565
- const fullList = allDeps.filter((d) => d.needed).map((d) => d.name);
566
- const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name);
567
- const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList);
568
- if (toInstall.length === 0) {
569
- console.log(pc2.dim(` Dependencies already installed: ${fullList.join(", ")}`));
570
- return { installFailed: false, installSkipped: true, installCmd: fullCmd };
571
- }
572
- const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall);
573
- console.log(pc2.dim(` Installing ${toInstall.join(" and ")}...`));
574
- const wsPatched = pm === "pnpm" && patchPnpmWorkspace(cwd);
575
- let installFailed = false;
576
- try {
577
- execSync(addCmd, { cwd, stdio: "pipe" });
578
- console.log(pc2.green(" Installed"), toInstall.join(", "));
579
- } catch (error) {
580
- installFailed = true;
581
- const err = error;
582
- const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error);
583
- console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding"));
584
- const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n");
585
- if (firstLines) console.log(pc2.dim(firstLines));
586
- console.log(pc2.dim(` Run manually: ${addCmd}`));
587
- } finally {
588
- if (wsPatched) restorePnpmWorkspace(cwd);
589
- }
590
- return { installFailed, installSkipped: false, installCmd: addCmd };
591
- }
592
- function buildAddCmd(pm, hasPnpmWs, deps) {
593
- const pkgs = deps.join(" ");
594
- switch (pm) {
595
- case "pnpm":
596
- return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`;
597
- case "yarn":
598
- return `yarn add ${pkgs}`;
599
- case "bun":
600
- return `bun add ${pkgs}`;
601
- default:
602
- return `npm install ${pkgs}`;
603
- }
604
- }
605
- async function planConflictsAndEnv(cwd, baseDir, env, answers) {
606
- const candidates = [];
607
- const libDir = path2.join(baseDir, "lib", "software");
608
- if (needsClient(env)) candidates.push(path2.join(libDir, "client.ts"));
609
- if (needsReactQuery(env)) candidates.push(path2.join(libDir, "query-provider.tsx"));
610
- if (needsServer(env)) candidates.push(path2.join(libDir, "server.ts"));
611
- if (answers.aiTools.includes("claude")) {
612
- for (const { dirName } of getSkillFiles()) {
613
- candidates.push(path2.join(cwd, ".claude", "skills", dirName, "SKILL.md"));
614
- }
615
- }
616
- const conflicts = candidates.filter((p) => fs2.existsSync(p));
617
- let policy = "skip";
618
- if (conflicts.length > 0) {
619
- console.log(pc2.yellow(` ${conflicts.length} file(s) already exist:`));
620
- for (const c of conflicts) console.log(pc2.dim(` ${path2.relative(cwd, c)}`));
621
- const { selected } = await prompts2({
622
- type: "select",
623
- name: "selected",
624
- message: "How should I handle existing files?",
625
- choices: [
626
- { title: "Keep existing (skip)", value: "skip" },
627
- { title: "Overwrite all", value: "overwrite" },
628
- { title: "Ask for each", value: "ask" }
629
- ],
630
- initial: 0
631
- });
632
- policy = selected ?? "skip";
633
- }
634
- const envFile = env === "vanilla" || env === "edge" ? "" : await pickEnvFile(cwd, env);
635
- return { policy, envFile };
636
- }
637
- async function pickEnvFile(cwd, env) {
638
- const candidates = [".env.local", ".env", ".env.development"];
639
- const existing = candidates.filter((f) => fs2.existsSync(path2.join(cwd, f)));
640
- const preferred = env === "nextjs" ? ".env.local" : ".env";
641
- if (existing.length === 0) return preferred;
642
- if (existing.length === 1 && existing[0] === preferred) return existing[0];
643
- const options = Array.from(/* @__PURE__ */ new Set([...existing, preferred]));
644
- const choices = options.map((f) => ({
645
- title: f,
646
- description: existing.includes(f) ? "exists" : "create",
647
- value: f
648
- }));
649
- const initial = Math.max(
650
- 0,
651
- choices.findIndex((c) => c.value === preferred)
652
- );
653
- const { file } = await prompts2({
654
- type: "select",
655
- name: "file",
656
- message: "Which env file should I write SDK credentials to?",
657
- choices,
658
- initial
659
- });
660
- return file ?? preferred;
661
- }
662
- async function writeFileWithPolicy(cwd, filePath, content, policy) {
663
- const rel = path2.relative(cwd, filePath);
664
- if (!fs2.existsSync(filePath)) {
665
- fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
666
- fs2.writeFileSync(filePath, content);
667
- console.log(pc2.green(" Created"), rel);
668
- return;
669
- }
670
- const existing = fs2.readFileSync(filePath, "utf-8");
671
- if (existing === content) {
672
- console.log(pc2.dim(" Unchanged"), rel);
673
- return;
674
- }
675
- let shouldWrite = false;
676
- if (policy === "overwrite") {
677
- shouldWrite = true;
678
- } else if (policy === "ask") {
679
- const { confirm } = await prompts2({
680
- type: "confirm",
681
- name: "confirm",
682
- message: `Overwrite ${rel}?`,
683
- initial: false
684
- });
685
- shouldWrite = !!confirm;
686
- }
687
- if (shouldWrite) {
688
- fs2.writeFileSync(filePath, content);
689
- console.log(pc2.green(" Overwrote"), rel);
690
- } else {
691
- console.log(pc2.yellow(" Skipped"), rel, pc2.dim("(already exists)"));
692
- }
693
- }
694
- async function writeEnv(cwd, envFile, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar, policy, fromBrowserAuth = false) {
695
- const envPath = path2.join(cwd, envFile);
696
- const targets = [
697
- { name: publishableKeyEnvVar, value: publishableKey }
698
- ];
699
- if (secretKeyEnvVar) {
700
- targets.push({ name: secretKeyEnvVar, value: secretKey });
701
- }
702
- if (!fs2.existsSync(envPath)) {
703
- const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar);
704
- fs2.writeFileSync(envPath, initial.trimStart());
705
- console.log(pc2.green(" Created"), envFile);
706
- return;
707
- }
708
- let content = fs2.readFileSync(envPath, "utf-8");
709
- let modified = false;
710
- let appendedHeader = false;
711
- const headerAlreadyPresent = targets.some(
712
- (t) => readEnvValue(content, t.name) !== null
713
- );
714
- for (const { name, value } of targets) {
715
- const existing = readEnvValue(content, name);
716
- if (existing === null) {
717
- if (!headerAlreadyPresent && !appendedHeader) {
718
- if (content.length > 0 && !content.endsWith("\n")) content += "\n";
719
- content += "\n# 01.software\n";
720
- appendedHeader = true;
721
- }
722
- content = setEnvValue(content, name, value);
723
- modified = true;
724
- continue;
725
- }
726
- if (existing === value) continue;
727
- if (!value) continue;
728
- let shouldOverwrite = false;
729
- if (policy === "overwrite") {
730
- shouldOverwrite = true;
731
- } else if (policy === "ask") {
732
- const { confirm } = await prompts2({
733
- type: "confirm",
734
- name: "confirm",
735
- message: `${name} already set in ${envFile}. Overwrite?`,
736
- initial: fromBrowserAuth
737
- });
738
- shouldOverwrite = !!confirm;
739
- }
740
- if (shouldOverwrite) {
741
- content = setEnvValue(content, name, value);
742
- modified = true;
743
- }
744
- }
745
- if (modified) {
746
- fs2.writeFileSync(envPath, content);
747
- console.log(
748
- pc2.green(" Updated"),
749
- envFile,
750
- fromBrowserAuth ? pc2.dim("(SDK credentials)") : ""
751
- );
752
- } else {
753
- console.log(pc2.dim(" Unchanged"), envFile);
754
- }
755
- }
756
- function resolveMcpLocation(tool, cwd) {
757
- const home = os.homedir();
758
- switch (tool) {
759
- case "claude":
760
- return {
761
- kind: "json",
762
- absolutePath: path2.join(cwd, ".mcp.json"),
763
- displayPath: ".mcp.json",
764
- gitignoreEntry: ".mcp.json"
765
- };
766
- case "cursor":
767
- return {
768
- kind: "json",
769
- absolutePath: path2.join(cwd, ".cursor", "mcp.json"),
770
- displayPath: ".cursor/mcp.json",
771
- gitignoreEntry: ".cursor/mcp.json"
772
- };
773
- case "vscode":
774
- return {
775
- kind: "json",
776
- absolutePath: path2.join(cwd, ".vscode", "mcp.json"),
777
- displayPath: ".vscode/mcp.json",
778
- gitignoreEntry: ".vscode/mcp.json"
779
- };
780
- case "windsurf": {
781
- if (!home) return null;
782
- const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
783
- return {
784
- kind: "json",
785
- absolutePath: p,
786
- jsonClient: "windsurf",
787
- displayPath: p,
788
- gitignoreEntry: null
789
- };
790
- }
791
- case "codex": {
792
- if (!home) return null;
793
- const p = path2.join(home, ".codex", "config.toml");
794
- return { kind: "toml", absolutePath: p, displayPath: p, gitignoreEntry: null };
795
- }
796
- case "gemini": {
797
- if (!home) return null;
798
- const p = path2.join(home, ".gemini", "settings.json");
799
- return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null };
800
- }
801
- }
802
- }
803
- async function writeMcpConfig(tool, cwd) {
804
- const loc = resolveMcpLocation(tool, cwd);
805
- if (!loc) {
806
- console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)"));
807
- return;
808
- }
809
- fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true });
810
- if (loc.kind === "json") {
811
- writeJsonMcp(loc);
812
- } else {
813
- writeTomlMcp(loc);
814
- }
815
- }
816
- function writeJsonMcp(loc) {
817
- if (!fs2.existsSync(loc.absolutePath)) {
818
- fs2.writeFileSync(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient));
819
- console.log(pc2.green(" Created"), loc.displayPath);
820
- return;
821
- }
822
- let existing;
823
- try {
824
- existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8"));
825
- } catch {
826
- console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)"));
827
- return;
828
- }
829
- const nextEntry = getMcpServerEntry(loc.jsonClient);
830
- if (existing.mcpServers?.["01software"] && JSON.stringify(existing.mcpServers["01software"]) === JSON.stringify(nextEntry)) {
831
- console.log(pc2.dim(" Unchanged"), loc.displayPath);
832
- return;
833
- }
834
- existing.mcpServers = existing.mcpServers || {};
835
- existing.mcpServers["01software"] = nextEntry;
836
- fs2.writeFileSync(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n");
837
- console.log(pc2.green(" Updated"), loc.displayPath);
838
- }
839
- function writeTomlMcp(loc) {
840
- const section = getCodexMcpTomlSection();
841
- if (!fs2.existsSync(loc.absolutePath)) {
842
- fs2.writeFileSync(loc.absolutePath, section.trimStart());
843
- console.log(pc2.green(" Created"), loc.displayPath);
844
- return;
845
- }
846
- const existing = fs2.readFileSync(loc.absolutePath, "utf-8");
847
- if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {
848
- const sep = existing.endsWith("\n") ? "" : "\n";
849
- fs2.appendFileSync(loc.absolutePath, sep + section);
850
- console.log(pc2.green(" Updated"), loc.displayPath);
851
- return;
852
- }
853
- const replaced = replaceTomlMcpSection(existing, section);
854
- if (replaced === existing) {
855
- console.log(pc2.dim(" Unchanged"), loc.displayPath);
856
- return;
857
- }
858
- fs2.writeFileSync(loc.absolutePath, replaced);
859
- console.log(pc2.green(" Updated"), loc.displayPath);
860
- }
861
- function addToGitignore(cwd, tools) {
862
- const entries = [];
863
- for (const tool of tools) {
864
- const loc = resolveMcpLocation(tool, cwd);
865
- if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry);
866
- }
867
- if (entries.length === 0) return;
868
- const gitignorePath = path2.join(cwd, ".gitignore");
869
- const existing = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf-8") : "";
870
- const toAdd = entries.filter((e) => !existing.includes(e));
871
- if (toAdd.length === 0) return;
872
- const content = "\n# MCP configs\n" + toAdd.join("\n") + "\n";
873
- if (fs2.existsSync(gitignorePath)) {
874
- fs2.appendFileSync(gitignorePath, content);
875
- } else {
876
- fs2.writeFileSync(gitignorePath, content.trimStart());
877
- }
878
- console.log(pc2.green(" Updated"), ".gitignore", pc2.dim(`(added ${toAdd.join(", ")})`));
879
- }
880
- async function writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, policy) {
881
- let ctx = {
882
- tenantName: tenantName || "Your Tenant",
883
- features: void 0,
884
- collections: void 0
885
- };
886
- if (publishableKey && secretKey) {
887
- const fetched = await fetchTenantContext(publishableKey, secretKey);
888
- if (fetched) {
889
- ctx = {
890
- tenantName: fetched.tenantName || ctx.tenantName,
891
- features: fetched.features,
892
- collections: fetched.collections
893
- };
894
- }
895
- }
896
- const claudeDir = path2.join(cwd, ".claude");
897
- const softwareDir = path2.join(claudeDir, "01software");
898
- const skillsDir = path2.join(claudeDir, "skills");
899
- fs2.mkdirSync(softwareDir, { recursive: true });
900
- fs2.mkdirSync(skillsDir, { recursive: true });
901
- const contextPath = path2.join(softwareDir, "context.md");
902
- const contextExists = fs2.existsSync(contextPath);
903
- fs2.writeFileSync(contextPath, generateClaudeMd(ctx));
904
- console.log(pc2.green(contextExists ? " Updated" : " Created"), ".claude/01software/context.md");
905
- const claudeMdPath = path2.join(claudeDir, "CLAUDE.md");
906
- const importLine = "@.claude/01software/context.md";
907
- if (!fs2.existsSync(claudeMdPath)) {
908
- fs2.writeFileSync(claudeMdPath, importLine + "\n");
909
- console.log(pc2.green(" Created"), ".claude/CLAUDE.md");
910
- } else {
911
- const existing = fs2.readFileSync(claudeMdPath, "utf-8");
912
- if (!existing.includes(importLine)) {
913
- const prefix = existing.endsWith("\n") ? "\n" : "\n\n";
914
- fs2.appendFileSync(claudeMdPath, prefix + importLine + "\n");
915
- console.log(pc2.green(" Updated"), ".claude/CLAUDE.md", pc2.dim("(added @import)"));
916
- } else {
917
- console.log(pc2.dim(" Unchanged"), ".claude/CLAUDE.md");
918
- }
919
- }
920
- for (const { dirName, content } of getSkillFiles()) {
921
- const skillDir = path2.join(skillsDir, dirName);
922
- const skillPath = path2.join(skillDir, "SKILL.md");
923
- fs2.mkdirSync(skillDir, { recursive: true });
924
- await writeFileWithPolicy(cwd, skillPath, content, policy);
925
- }
926
- }
927
- var WS_FILE = "pnpm-workspace.yaml";
928
- var WS_BACKUP = "pnpm-workspace.yaml.bak";
929
- function hasPnpmWorkspace(cwd) {
930
- return fs2.existsSync(path2.join(cwd, WS_FILE));
931
- }
932
- function patchPnpmWorkspace(cwd) {
933
- const wsPath = path2.join(cwd, WS_FILE);
934
- if (!fs2.existsSync(wsPath)) return false;
935
- const content = fs2.readFileSync(wsPath, "utf-8");
936
- if (content.includes("packages:")) return false;
937
- fs2.copyFileSync(wsPath, path2.join(cwd, WS_BACKUP));
938
- fs2.writeFileSync(wsPath, content.trimEnd() + "\npackages: []\n");
939
- return true;
940
- }
941
- function restorePnpmWorkspace(cwd) {
942
- const backupPath = path2.join(cwd, WS_BACKUP);
943
- if (!fs2.existsSync(backupPath)) return;
944
- fs2.copyFileSync(backupPath, path2.join(cwd, WS_FILE));
945
- fs2.unlinkSync(backupPath);
946
- }
947
-
948
174
  // src/index.ts
949
175
  var ENV_LABELS = {
950
176
  nextjs: "Next.js",
@@ -973,22 +199,22 @@ var OTHER_FRAMEWORK_GUIDE = `
973
199
  secretKey: process.env.SOFTWARE_SECRET_KEY!,
974
200
  })
975
201
 
976
- 4. Docs: https://01.software/docs/developers/sdk/client
202
+ 4. Docs: https://docs.01.software/docs/developers/sdk/client
977
203
  `;
978
204
  async function main() {
979
205
  const cwd = process.cwd();
980
206
  console.log();
981
- console.log(pc3.bold(" @01.software/init"));
982
- console.log(pc3.dim(" Initialize 01.software SDK in your project"));
207
+ console.log(pc.bold(" @01.software/init"));
208
+ console.log(pc.dim(" Initialize 01.software SDK in your project"));
983
209
  console.log();
984
210
  const info = detectProject(cwd);
985
211
  if (!info.hasPackageJson || info.parseError) {
986
212
  if (info.parseError) {
987
- console.log(pc3.red(" Could not parse package.json (invalid JSON)."));
988
- console.log(pc3.dim(" Fix the syntax error and try again."));
213
+ console.log(pc.red(" Could not parse package.json (invalid JSON)."));
214
+ console.log(pc.dim(" Fix the syntax error and try again."));
989
215
  } else {
990
- console.log(pc3.red(" No package.json found in the current directory."));
991
- console.log(pc3.dim(" Run this command inside an existing project."));
216
+ console.log(pc.red(" No package.json found in the current directory."));
217
+ console.log(pc.dim(" Run this command inside an existing project."));
992
218
  }
993
219
  console.log();
994
220
  process.exit(1);
@@ -997,17 +223,17 @@ async function main() {
997
223
  if (info.packageManager) detectedParts.push(info.packageManager);
998
224
  if (info.srcDir) detectedParts.push("src/");
999
225
  if (info.env !== "node") {
1000
- console.log(pc3.dim(` Detected: ${detectedParts.join(" / ")}`));
226
+ console.log(pc.dim(` Detected: ${detectedParts.join(" / ")}`));
1001
227
  console.log();
1002
228
  }
1003
229
  try {
1004
230
  const answers = await promptUser(info.hasSdk, info.env, info.packageManager);
1005
231
  if (!answers) {
1006
- console.log(pc3.yellow(" Cancelled."));
232
+ console.log(pc.yellow(" Cancelled."));
1007
233
  process.exit(0);
1008
234
  }
1009
235
  if (answers.env === "other") {
1010
- console.log(pc3.yellow(" Manual setup required for your framework:"));
236
+ console.log(pc.yellow(" Manual setup required for your framework:"));
1011
237
  console.log(OTHER_FRAMEWORK_GUIDE);
1012
238
  process.exit(0);
1013
239
  }
@@ -1018,78 +244,86 @@ async function main() {
1018
244
  const env = answers.env;
1019
245
  const run = resolvedPm === "npm" ? "npm run" : resolvedPm;
1020
246
  console.log();
1021
- console.log(pc3.green(" Done!"));
247
+ console.log(pc.green(" Done!"));
1022
248
  console.log();
1023
249
  console.log(" Next steps:");
1024
250
  console.log();
1025
251
  if (result.installFailed) {
1026
- console.log(pc3.yellow(" Install the SDK manually:"));
1027
- console.log(pc3.cyan(` ${result.installCmd}`));
252
+ console.log(pc.yellow(" Install the SDK manually:"));
253
+ console.log(pc.cyan(` ${result.installCmd}`));
1028
254
  console.log();
1029
255
  }
1030
256
  if (env === "nextjs") {
1031
- console.log(pc3.dim(" Add QueryProvider to your root layout:"));
257
+ console.log(pc.dim(" Add QueryProvider to your root layout:"));
1032
258
  console.log();
1033
- console.log(pc3.cyan(" import { QueryProvider } from '@/lib/software/query-provider'"));
1034
- console.log(pc3.cyan(" <QueryProvider>{children}</QueryProvider>"));
259
+ console.log(pc.cyan(" import { QueryProvider } from '@/lib/software/query-provider'"));
260
+ console.log(pc.cyan(" <QueryProvider>{children}</QueryProvider>"));
1035
261
  console.log();
1036
- console.log(pc3.dim(" Optional: start browser analytics with the generated helper:"));
262
+ console.log(pc.dim(" Optional: start browser analytics with the generated helper:"));
1037
263
  console.log();
1038
- console.log(pc3.cyan(" import { analytics } from '@/lib/software/analytics'"));
1039
- console.log(pc3.cyan(" analytics.track('signup')"));
264
+ console.log(pc.cyan(" import { analytics } from '@/lib/software/analytics'"));
265
+ console.log(pc.cyan(" analytics.track('signup')"));
1040
266
  console.log();
1041
267
  } else if (env === "react-vite" || env === "react-cra") {
1042
- console.log(pc3.dim(" Wrap your app entry with QueryProvider:"));
268
+ console.log(pc.dim(" Wrap your app entry with QueryProvider:"));
1043
269
  console.log();
1044
- console.log(pc3.cyan(" import { QueryProvider } from './lib/software/query-provider'"));
1045
- console.log(pc3.cyan(" <QueryProvider><App /></QueryProvider>"));
270
+ console.log(pc.cyan(" import { QueryProvider } from './lib/software/query-provider'"));
271
+ console.log(pc.cyan(" <QueryProvider><App /></QueryProvider>"));
1046
272
  console.log();
1047
- console.log(pc3.dim(" Optional: start browser analytics with the generated helper:"));
273
+ console.log(pc.dim(" Optional: start browser analytics with the generated helper:"));
1048
274
  console.log();
1049
- console.log(pc3.cyan(" import { analytics } from './lib/software/analytics'"));
1050
- console.log(pc3.cyan(" analytics.track('signup')"));
275
+ console.log(pc.cyan(" import { analytics } from './lib/software/analytics'"));
276
+ console.log(pc.cyan(" analytics.track('signup')"));
1051
277
  console.log();
1052
278
  } else if (env === "vanilla") {
1053
- console.log(pc3.dim(" Replace YOUR_PUBLISHABLE_KEY in lib/software/client.ts"));
279
+ console.log(pc.dim(" Replace YOUR_PUBLISHABLE_KEY in lib/software/client.ts"));
1054
280
  console.log();
1055
- console.log(pc3.cyan(" import { client } from './lib/software/client'"));
1056
- console.log(pc3.cyan(" const articles = await client.from('articles').find()"));
281
+ console.log(pc.cyan(" import { client } from './lib/software/client'"));
282
+ console.log(pc.cyan(" const articles = await client.collections.from('articles').find()"));
1057
283
  console.log();
1058
- console.log(pc3.dim(" Optional: wire the analytics helper in lib/software/analytics.ts"));
284
+ console.log(pc.dim(" Optional: wire the analytics helper in lib/software/analytics.ts"));
1059
285
  console.log();
1060
286
  } else if (env === "node") {
1061
- console.log(pc3.dim(" Use the server client:"));
287
+ console.log(pc.dim(" Use the server client:"));
1062
288
  console.log();
1063
- console.log(pc3.cyan(" import { serverClient } from './lib/software/server'"));
1064
- console.log(pc3.cyan(" const articles = await serverClient.from('articles').find()"));
289
+ console.log(pc.cyan(" import { serverClient } from './lib/software/server'"));
290
+ console.log(pc.cyan(" const articles = await serverClient.collections.from('articles').find()"));
1065
291
  console.log();
1066
292
  } else if (env === "edge") {
1067
- console.log(pc3.dim(" Pass your env bindings to createEdgeClient():"));
293
+ console.log(pc.dim(" Pass your env bindings to createEdgeClient():"));
1068
294
  console.log();
1069
- console.log(pc3.cyan(" import { createEdgeClient } from './lib/software/server'"));
1070
- console.log(pc3.cyan(" const serverClient = createEdgeClient(env.PUBLISHABLE_KEY, env.SECRET_KEY)"));
295
+ console.log(pc.cyan(" import { createEdgeClient } from './lib/software/server'"));
296
+ console.log(pc.cyan(" const serverClient = createEdgeClient(env.PUBLISHABLE_KEY, env.SECRET_KEY)"));
1071
297
  console.log();
1072
298
  }
1073
- const missingPublishableKey = env !== "vanilla" && !answers.publishableKey;
1074
- const missingSecretKey = (env === "nextjs" || env === "node") && !answers.secretKey;
299
+ const effectivePublishableKey = result.publishableKey ?? answers.publishableKey;
300
+ const effectiveSecretKey = result.secretKey ?? answers.secretKey;
301
+ const missingPublishableKey = env !== "vanilla" && !effectivePublishableKey;
302
+ const missingSecretKey = (env === "nextjs" || env === "node") && !effectiveSecretKey;
1075
303
  if (missingPublishableKey || missingSecretKey) {
1076
- console.log(pc3.dim(" Update .env with your SDK credentials"));
304
+ console.log(pc.dim(" Update .env with your SDK credentials"));
1077
305
  console.log();
1078
306
  }
1079
307
  if (answers.aiTools.length > 0) {
1080
- console.log(pc3.dim(" MCP config uses OAuth discovery."));
308
+ console.log(pc.dim(" MCP config uses OAuth discovery."));
309
+ console.log(pc.dim(" Agent discovery flow:"));
310
+ console.log(pc.cyan(" 1. Read https://01.software/llms.txt"));
311
+ console.log(pc.cyan(" 2. Connect https://mcp.01.software/mcp"));
312
+ console.log(pc.cyan(" 3. Read https://docs.01.software/skill.md"));
313
+ console.log(pc.cyan(" 4. Inspect https://docs.01.software/api/openapi"));
314
+ console.log(pc.cyan(" 5. Run local lint, typecheck, tests, and build"));
1081
315
  console.log();
1082
316
  }
1083
317
  if (env !== "vanilla") {
1084
- console.log(pc3.cyan(` ${run} dev`));
318
+ console.log(pc.cyan(` ${run} dev`));
1085
319
  console.log();
1086
320
  }
1087
321
  } catch (error) {
1088
322
  if (error instanceof Error && error.message === "cancelled") {
1089
- console.log(pc3.yellow(" Cancelled."));
323
+ console.log(pc.yellow(" Cancelled."));
1090
324
  process.exit(0);
1091
325
  }
1092
- console.error(pc3.red(" Error:"), error);
326
+ console.error(pc.red(" Error:"), error);
1093
327
  process.exit(1);
1094
328
  }
1095
329
  }