@01.software/init 0.5.1 → 0.7.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/ai-docs.js +1 -1
- package/dist/chunk-JT3G6B66.js +56 -0
- package/dist/chunk-JT3G6B66.js.map +1 -0
- package/dist/chunk-S3KHPWCE.js +180 -0
- package/dist/chunk-S3KHPWCE.js.map +1 -0
- package/dist/{chunk-SRLZ5OIV.js → chunk-T3A5SLEJ.js} +3 -3
- package/dist/chunk-T3A5SLEJ.js.map +1 -0
- package/dist/file-ops.js +12 -0
- package/dist/file-ops.js.map +1 -0
- package/dist/index.js +300 -109
- package/dist/index.js.map +1 -1
- package/dist/templates.js +3 -1
- package/package.json +3 -2
- package/dist/chunk-OEAQV63E.js +0 -120
- package/dist/chunk-OEAQV63E.js.map +0 -1
- package/dist/chunk-SRLZ5OIV.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,9 +3,15 @@ import {
|
|
|
3
3
|
fetchTenantContext,
|
|
4
4
|
generateClaudeMd,
|
|
5
5
|
getSkillFiles
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-T3A5SLEJ.js";
|
|
7
|
+
import {
|
|
8
|
+
readEnvValue,
|
|
9
|
+
replaceTomlMcpSection,
|
|
10
|
+
setEnvValue
|
|
11
|
+
} from "./chunk-JT3G6B66.js";
|
|
7
12
|
import {
|
|
8
13
|
CODEX_MCP_SECTION_MARKER,
|
|
14
|
+
getAnalyticsTemplate,
|
|
9
15
|
getClientTemplate,
|
|
10
16
|
getCodexMcpTomlSection,
|
|
11
17
|
getEnvContent,
|
|
@@ -13,7 +19,7 @@ import {
|
|
|
13
19
|
getMcpServerEntry,
|
|
14
20
|
getQueryProviderTemplate,
|
|
15
21
|
getServerTemplate
|
|
16
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-S3KHPWCE.js";
|
|
17
23
|
|
|
18
24
|
// src/index.ts
|
|
19
25
|
import pc3 from "picocolors";
|
|
@@ -26,19 +32,29 @@ function detectProject(cwd) {
|
|
|
26
32
|
const hasPackageJson = fs.existsSync(pkgPath);
|
|
27
33
|
let env = "node";
|
|
28
34
|
let hasSdk = false;
|
|
35
|
+
let hasReactQuery = false;
|
|
29
36
|
let parseError = false;
|
|
30
37
|
if (hasPackageJson) {
|
|
31
38
|
let pkg;
|
|
32
39
|
try {
|
|
33
40
|
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
34
41
|
} catch {
|
|
35
|
-
return {
|
|
42
|
+
return {
|
|
43
|
+
hasPackageJson: true,
|
|
44
|
+
parseError: true,
|
|
45
|
+
env: "node",
|
|
46
|
+
packageManager: null,
|
|
47
|
+
hasSdk: false,
|
|
48
|
+
hasReactQuery: false,
|
|
49
|
+
srcDir: false
|
|
50
|
+
};
|
|
36
51
|
}
|
|
37
52
|
const deps = {
|
|
38
53
|
...pkg.dependencies || {},
|
|
39
54
|
...pkg.devDependencies || {}
|
|
40
55
|
};
|
|
41
56
|
hasSdk = "@01.software/sdk" in deps;
|
|
57
|
+
hasReactQuery = "@tanstack/react-query" in deps;
|
|
42
58
|
if ("next" in deps) {
|
|
43
59
|
env = "nextjs";
|
|
44
60
|
} else if ("astro" in deps || "@astrojs/node" in deps) {
|
|
@@ -68,7 +84,7 @@ function detectProject(cwd) {
|
|
|
68
84
|
packageManager = "npm";
|
|
69
85
|
}
|
|
70
86
|
const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src"));
|
|
71
|
-
return { hasPackageJson, parseError, env, packageManager, hasSdk, srcDir };
|
|
87
|
+
return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir };
|
|
72
88
|
}
|
|
73
89
|
function needsClient(env) {
|
|
74
90
|
return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla";
|
|
@@ -224,7 +240,7 @@ async function promptUser(hasSdk, detectedEnv, detectedPm) {
|
|
|
224
240
|
{
|
|
225
241
|
type: "select",
|
|
226
242
|
name: "method",
|
|
227
|
-
message: "
|
|
243
|
+
message: "SDK credentials:",
|
|
228
244
|
choices: [
|
|
229
245
|
{ title: "Browser login (recommended)", value: "browser" },
|
|
230
246
|
{ title: "Enter manually", value: "manual" },
|
|
@@ -259,6 +275,7 @@ import path2 from "path";
|
|
|
259
275
|
import os from "os";
|
|
260
276
|
import { execSync } from "child_process";
|
|
261
277
|
import pc2 from "picocolors";
|
|
278
|
+
import prompts2 from "prompts";
|
|
262
279
|
|
|
263
280
|
// src/browser-auth.ts
|
|
264
281
|
import { randomBytes } from "crypto";
|
|
@@ -447,56 +464,57 @@ async function init(cwd, info, answers) {
|
|
|
447
464
|
const wantsClient = needsClient(env);
|
|
448
465
|
const wantsServer = needsServer(env);
|
|
449
466
|
const wantsReactQuery = needsReactQuery(env);
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
installFailed = true;
|
|
461
|
-
const err = error;
|
|
462
|
-
const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error);
|
|
463
|
-
console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding"));
|
|
464
|
-
const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n");
|
|
465
|
-
if (firstLines) console.log(pc2.dim(firstLines));
|
|
466
|
-
console.log(pc2.dim(` Run manually: ${addCmd}`));
|
|
467
|
-
} finally {
|
|
468
|
-
if (wsPatched) restorePnpmWorkspace(cwd);
|
|
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 });
|
|
469
477
|
}
|
|
470
478
|
const libDir = path2.join(baseDir, "lib", "software");
|
|
471
|
-
fs2.mkdirSync(libDir, { recursive: true });
|
|
472
479
|
if (wantsClient) {
|
|
473
|
-
|
|
480
|
+
await writeFileWithPolicy(
|
|
474
481
|
cwd,
|
|
475
482
|
path2.join(libDir, "client.ts"),
|
|
476
|
-
getClientTemplate(env, publishableKeyEnvVar)
|
|
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
|
|
477
491
|
);
|
|
478
492
|
}
|
|
479
493
|
if (wantsReactQuery) {
|
|
480
|
-
|
|
494
|
+
await writeFileWithPolicy(
|
|
481
495
|
cwd,
|
|
482
496
|
path2.join(libDir, "query-provider.tsx"),
|
|
483
|
-
getQueryProviderTemplate(env)
|
|
497
|
+
getQueryProviderTemplate(env),
|
|
498
|
+
plan.policy
|
|
484
499
|
);
|
|
485
500
|
}
|
|
486
501
|
if (wantsServer) {
|
|
487
|
-
|
|
502
|
+
await writeFileWithPolicy(
|
|
488
503
|
cwd,
|
|
489
504
|
path2.join(libDir, "server.ts"),
|
|
490
|
-
getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR)
|
|
505
|
+
getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),
|
|
506
|
+
plan.policy
|
|
491
507
|
);
|
|
492
508
|
}
|
|
493
|
-
if (
|
|
494
|
-
writeEnv(
|
|
509
|
+
if (plan.envFile && answers.authMethod !== "browser") {
|
|
510
|
+
await writeEnv(
|
|
495
511
|
cwd,
|
|
512
|
+
plan.envFile,
|
|
496
513
|
answers.publishableKey || "",
|
|
497
514
|
answers.secretKey || "",
|
|
498
515
|
publishableKeyEnvVar,
|
|
499
|
-
wantsServer ? SECRET_KEY_ENV_VAR : null
|
|
516
|
+
wantsServer ? SECRET_KEY_ENV_VAR : null,
|
|
517
|
+
plan.policy
|
|
500
518
|
);
|
|
501
519
|
}
|
|
502
520
|
let publishableKey = answers.publishableKey;
|
|
@@ -509,13 +527,15 @@ async function init(cwd, info, answers) {
|
|
|
509
527
|
publishableKey = creds.publishableKey;
|
|
510
528
|
secretKey = creds.secretKey;
|
|
511
529
|
tenantName = creds.tenantName;
|
|
512
|
-
if (
|
|
513
|
-
writeEnv(
|
|
530
|
+
if (plan.envFile && publishableKey) {
|
|
531
|
+
await writeEnv(
|
|
514
532
|
cwd,
|
|
533
|
+
plan.envFile,
|
|
515
534
|
publishableKey,
|
|
516
535
|
secretKey,
|
|
517
536
|
publishableKeyEnvVar,
|
|
518
537
|
wantsServer ? SECRET_KEY_ENV_VAR : null,
|
|
538
|
+
"overwrite",
|
|
519
539
|
true
|
|
520
540
|
);
|
|
521
541
|
}
|
|
@@ -527,16 +547,47 @@ async function init(cwd, info, answers) {
|
|
|
527
547
|
}
|
|
528
548
|
}
|
|
529
549
|
if (answers.aiTools.length > 0) {
|
|
530
|
-
const apiKey = secretKey || "YOUR_API_KEY";
|
|
531
550
|
for (const tool of answers.aiTools) {
|
|
532
|
-
writeMcpConfig(tool, cwd
|
|
551
|
+
await writeMcpConfig(tool, cwd);
|
|
533
552
|
}
|
|
534
553
|
addToGitignore(cwd, answers.aiTools);
|
|
535
554
|
if (answers.aiTools.includes("claude")) {
|
|
536
|
-
await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName);
|
|
555
|
+
await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy);
|
|
537
556
|
}
|
|
538
557
|
}
|
|
539
|
-
return
|
|
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 };
|
|
540
591
|
}
|
|
541
592
|
function buildAddCmd(pm, hasPnpmWs, deps) {
|
|
542
593
|
const pkgs = deps.join(" ");
|
|
@@ -551,34 +602,155 @@ function buildAddCmd(pm, hasPnpmWs, deps) {
|
|
|
551
602
|
return `npm install ${pkgs}`;
|
|
552
603
|
}
|
|
553
604
|
}
|
|
554
|
-
function
|
|
555
|
-
|
|
556
|
-
|
|
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);
|
|
557
673
|
return;
|
|
558
674
|
}
|
|
559
|
-
|
|
560
|
-
|
|
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
|
+
}
|
|
561
693
|
}
|
|
562
|
-
function writeEnv(cwd, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar,
|
|
563
|
-
const envPath = path2.join(cwd,
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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;
|
|
570
721
|
}
|
|
571
|
-
|
|
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;
|
|
572
743
|
}
|
|
573
|
-
|
|
744
|
+
}
|
|
745
|
+
if (modified) {
|
|
746
|
+
fs2.writeFileSync(envPath, content);
|
|
574
747
|
console.log(
|
|
575
748
|
pc2.green(" Updated"),
|
|
576
|
-
|
|
577
|
-
|
|
749
|
+
envFile,
|
|
750
|
+
fromBrowserAuth ? pc2.dim("(SDK credentials)") : ""
|
|
578
751
|
);
|
|
579
752
|
} else {
|
|
580
|
-
|
|
581
|
-
console.log(pc2.green(" Created"), ".env");
|
|
753
|
+
console.log(pc2.dim(" Unchanged"), envFile);
|
|
582
754
|
}
|
|
583
755
|
}
|
|
584
756
|
function resolveMcpLocation(tool, cwd) {
|
|
@@ -608,7 +780,13 @@ function resolveMcpLocation(tool, cwd) {
|
|
|
608
780
|
case "windsurf": {
|
|
609
781
|
if (!home) return null;
|
|
610
782
|
const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
611
|
-
return {
|
|
783
|
+
return {
|
|
784
|
+
kind: "json",
|
|
785
|
+
absolutePath: p,
|
|
786
|
+
jsonClient: "windsurf",
|
|
787
|
+
displayPath: p,
|
|
788
|
+
gitignoreEntry: null
|
|
789
|
+
};
|
|
612
790
|
}
|
|
613
791
|
case "codex": {
|
|
614
792
|
if (!home) return null;
|
|
@@ -622,7 +800,7 @@ function resolveMcpLocation(tool, cwd) {
|
|
|
622
800
|
}
|
|
623
801
|
}
|
|
624
802
|
}
|
|
625
|
-
function writeMcpConfig(tool, cwd
|
|
803
|
+
async function writeMcpConfig(tool, cwd) {
|
|
626
804
|
const loc = resolveMcpLocation(tool, cwd);
|
|
627
805
|
if (!loc) {
|
|
628
806
|
console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)"));
|
|
@@ -630,46 +808,55 @@ function writeMcpConfig(tool, cwd, apiKey) {
|
|
|
630
808
|
}
|
|
631
809
|
fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true });
|
|
632
810
|
if (loc.kind === "json") {
|
|
633
|
-
writeJsonMcp(loc
|
|
811
|
+
writeJsonMcp(loc);
|
|
634
812
|
} else {
|
|
635
|
-
writeTomlMcp(loc
|
|
813
|
+
writeTomlMcp(loc);
|
|
636
814
|
}
|
|
637
815
|
}
|
|
638
|
-
function writeJsonMcp(loc
|
|
639
|
-
if (fs2.existsSync(loc.absolutePath)) {
|
|
640
|
-
|
|
641
|
-
const existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8"));
|
|
642
|
-
if (existing.mcpServers?.["01software"]) {
|
|
643
|
-
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(01software already configured)"));
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
existing.mcpServers = existing.mcpServers || {};
|
|
647
|
-
existing.mcpServers["01software"] = getMcpServerEntry(apiKey);
|
|
648
|
-
fs2.writeFileSync(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n");
|
|
649
|
-
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
650
|
-
} catch {
|
|
651
|
-
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)"));
|
|
652
|
-
}
|
|
653
|
-
} else {
|
|
654
|
-
fs2.writeFileSync(loc.absolutePath, getMcpConfigTemplate(apiKey));
|
|
816
|
+
function writeJsonMcp(loc) {
|
|
817
|
+
if (!fs2.existsSync(loc.absolutePath)) {
|
|
818
|
+
fs2.writeFileSync(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient));
|
|
655
819
|
console.log(pc2.green(" Created"), loc.displayPath);
|
|
820
|
+
return;
|
|
656
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);
|
|
657
838
|
}
|
|
658
|
-
function writeTomlMcp(loc
|
|
659
|
-
const section = getCodexMcpTomlSection(
|
|
660
|
-
if (fs2.existsSync(loc.absolutePath)) {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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)) {
|
|
666
848
|
const sep = existing.endsWith("\n") ? "" : "\n";
|
|
667
849
|
fs2.appendFileSync(loc.absolutePath, sep + section);
|
|
668
850
|
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const replaced = replaceTomlMcpSection(existing, section);
|
|
854
|
+
if (replaced === existing) {
|
|
855
|
+
console.log(pc2.dim(" Unchanged"), loc.displayPath);
|
|
856
|
+
return;
|
|
672
857
|
}
|
|
858
|
+
fs2.writeFileSync(loc.absolutePath, replaced);
|
|
859
|
+
console.log(pc2.green(" Updated"), loc.displayPath);
|
|
673
860
|
}
|
|
674
861
|
function addToGitignore(cwd, tools) {
|
|
675
862
|
const entries = [];
|
|
@@ -682,7 +869,7 @@ function addToGitignore(cwd, tools) {
|
|
|
682
869
|
const existing = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf-8") : "";
|
|
683
870
|
const toAdd = entries.filter((e) => !existing.includes(e));
|
|
684
871
|
if (toAdd.length === 0) return;
|
|
685
|
-
const content = "\n# MCP configs
|
|
872
|
+
const content = "\n# MCP configs\n" + toAdd.join("\n") + "\n";
|
|
686
873
|
if (fs2.existsSync(gitignorePath)) {
|
|
687
874
|
fs2.appendFileSync(gitignorePath, content);
|
|
688
875
|
} else {
|
|
@@ -690,7 +877,7 @@ function addToGitignore(cwd, tools) {
|
|
|
690
877
|
}
|
|
691
878
|
console.log(pc2.green(" Updated"), ".gitignore", pc2.dim(`(added ${toAdd.join(", ")})`));
|
|
692
879
|
}
|
|
693
|
-
async function writeClaudeDocs(cwd, publishableKey, secretKey, tenantName) {
|
|
880
|
+
async function writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, policy) {
|
|
694
881
|
let ctx = {
|
|
695
882
|
tenantName: tenantName || "Your Tenant",
|
|
696
883
|
features: void 0,
|
|
@@ -727,24 +914,16 @@ async function writeClaudeDocs(cwd, publishableKey, secretKey, tenantName) {
|
|
|
727
914
|
fs2.appendFileSync(claudeMdPath, prefix + importLine + "\n");
|
|
728
915
|
console.log(pc2.green(" Updated"), ".claude/CLAUDE.md", pc2.dim("(added @import)"));
|
|
729
916
|
} else {
|
|
730
|
-
console.log(pc2.
|
|
917
|
+
console.log(pc2.dim(" Unchanged"), ".claude/CLAUDE.md");
|
|
731
918
|
}
|
|
732
919
|
}
|
|
733
920
|
for (const { dirName, content } of getSkillFiles()) {
|
|
734
921
|
const skillDir = path2.join(skillsDir, dirName);
|
|
735
922
|
const skillPath = path2.join(skillDir, "SKILL.md");
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
fs2.writeFileSync(skillPath, content);
|
|
739
|
-
console.log(pc2.green(" Created"), `.claude/skills/${dirName}/SKILL.md`);
|
|
740
|
-
} else {
|
|
741
|
-
console.log(pc2.yellow(" Skipped"), `.claude/skills/${dirName}/SKILL.md`, pc2.dim("(already exists)"));
|
|
742
|
-
}
|
|
923
|
+
fs2.mkdirSync(skillDir, { recursive: true });
|
|
924
|
+
await writeFileWithPolicy(cwd, skillPath, content, policy);
|
|
743
925
|
}
|
|
744
926
|
}
|
|
745
|
-
function relativePath(cwd, filePath) {
|
|
746
|
-
return path2.relative(cwd, filePath);
|
|
747
|
-
}
|
|
748
927
|
var WS_FILE = "pnpm-workspace.yaml";
|
|
749
928
|
var WS_BACKUP = "pnpm-workspace.yaml.bak";
|
|
750
929
|
function hasPnpmWorkspace(cwd) {
|
|
@@ -854,23 +1033,35 @@ async function main() {
|
|
|
854
1033
|
console.log(pc3.cyan(" import { QueryProvider } from '@/lib/software/query-provider'"));
|
|
855
1034
|
console.log(pc3.cyan(" <QueryProvider>{children}</QueryProvider>"));
|
|
856
1035
|
console.log();
|
|
1036
|
+
console.log(pc3.dim(" Optional: start browser analytics with the generated helper:"));
|
|
1037
|
+
console.log();
|
|
1038
|
+
console.log(pc3.cyan(" import { analytics } from '@/lib/software/analytics'"));
|
|
1039
|
+
console.log(pc3.cyan(" analytics.track('signup')"));
|
|
1040
|
+
console.log();
|
|
857
1041
|
} else if (env === "react-vite" || env === "react-cra") {
|
|
858
1042
|
console.log(pc3.dim(" Wrap your app entry with QueryProvider:"));
|
|
859
1043
|
console.log();
|
|
860
1044
|
console.log(pc3.cyan(" import { QueryProvider } from './lib/software/query-provider'"));
|
|
861
1045
|
console.log(pc3.cyan(" <QueryProvider><App /></QueryProvider>"));
|
|
862
1046
|
console.log();
|
|
1047
|
+
console.log(pc3.dim(" Optional: start browser analytics with the generated helper:"));
|
|
1048
|
+
console.log();
|
|
1049
|
+
console.log(pc3.cyan(" import { analytics } from './lib/software/analytics'"));
|
|
1050
|
+
console.log(pc3.cyan(" analytics.track('signup')"));
|
|
1051
|
+
console.log();
|
|
863
1052
|
} else if (env === "vanilla") {
|
|
864
1053
|
console.log(pc3.dim(" Replace YOUR_PUBLISHABLE_KEY in lib/software/client.ts"));
|
|
865
1054
|
console.log();
|
|
866
1055
|
console.log(pc3.cyan(" import { client } from './lib/software/client'"));
|
|
867
|
-
console.log(pc3.cyan(" const
|
|
1056
|
+
console.log(pc3.cyan(" const articles = await client.from('articles').find()"));
|
|
1057
|
+
console.log();
|
|
1058
|
+
console.log(pc3.dim(" Optional: wire the analytics helper in lib/software/analytics.ts"));
|
|
868
1059
|
console.log();
|
|
869
1060
|
} else if (env === "node") {
|
|
870
1061
|
console.log(pc3.dim(" Use the server client:"));
|
|
871
1062
|
console.log();
|
|
872
1063
|
console.log(pc3.cyan(" import { serverClient } from './lib/software/server'"));
|
|
873
|
-
console.log(pc3.cyan(" const
|
|
1064
|
+
console.log(pc3.cyan(" const articles = await serverClient.from('articles').find()"));
|
|
874
1065
|
console.log();
|
|
875
1066
|
} else if (env === "edge") {
|
|
876
1067
|
console.log(pc3.dim(" Pass your env bindings to createEdgeClient():"));
|
|
@@ -882,11 +1073,11 @@ async function main() {
|
|
|
882
1073
|
const missingPublishableKey = env !== "vanilla" && !answers.publishableKey;
|
|
883
1074
|
const missingSecretKey = (env === "nextjs" || env === "node") && !answers.secretKey;
|
|
884
1075
|
if (missingPublishableKey || missingSecretKey) {
|
|
885
|
-
console.log(pc3.dim(" Update .env with your
|
|
1076
|
+
console.log(pc3.dim(" Update .env with your SDK credentials"));
|
|
886
1077
|
console.log();
|
|
887
1078
|
}
|
|
888
|
-
if (answers.aiTools.length > 0
|
|
889
|
-
console.log(pc3.dim("
|
|
1079
|
+
if (answers.aiTools.length > 0) {
|
|
1080
|
+
console.log(pc3.dim(" MCP config uses OAuth discovery."));
|
|
890
1081
|
console.log();
|
|
891
1082
|
}
|
|
892
1083
|
if (env !== "vanilla") {
|