🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@01.software/init

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@01.software/init - npm Package Compare versions

Comparing version
0.8.0
to
0.9.0
+821
dist/chunk-2IGKOSK7.js
#!/usr/bin/env node
import {
fetchTenantContext,
generateClaudeMd,
getSkillFiles
} from "./chunk-5K2CB2Y5.js";
import {
chmodSecretFile,
readEnvValue,
replaceTomlMcpSection,
setEnvValue,
writeEnvFile,
writeSecretGlobalConfig
} from "./chunk-UA7WNT2F.js";
import {
CODEX_MCP_SECTION_MARKER,
getAnalyticsTemplate,
getClientTemplate,
getCodexMcpTomlSection,
getEnvContent,
getMcpConfigTemplate,
getMcpRootKey,
getMcpServerEntry,
getQueryProviderTemplate,
getServerTemplate
} from "./chunk-TBGKXE3Q.js";
// src/init.ts
import fs2 from "fs";
import path2 from "path";
import os from "os";
import { execSync } from "child_process";
import pc2 from "picocolors";
import prompts from "prompts";
// src/detect.ts
import fs from "fs";
import path from "path";
function detectProject(cwd) {
const pkgPath = path.join(cwd, "package.json");
const hasPackageJson = fs.existsSync(pkgPath);
let env = "node";
let hasSdk = false;
let hasReactQuery = false;
let parseError = false;
if (hasPackageJson) {
let pkg;
try {
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
} catch {
return {
hasPackageJson: true,
parseError: true,
env: "node",
packageManager: null,
hasSdk: false,
hasReactQuery: false,
srcDir: false
};
}
const deps = {
...pkg.dependencies || {},
...pkg.devDependencies || {}
};
hasSdk = "@01.software/sdk" in deps;
hasReactQuery = "@tanstack/react-query" in deps;
if ("next" in deps) {
env = "nextjs";
} else if ("astro" in deps || "@astrojs/node" in deps) {
env = "other";
} else if ("@remix-run/node" in deps || "@remix-run/react" in deps) {
env = "other";
} else if ("@sveltejs/kit" in deps) {
env = "other";
} else if ("react" in deps) {
if ("vite" in deps) {
env = "react-vite";
} else if ("react-scripts" in deps) {
env = "react-cra";
} else {
env = "node";
}
}
}
let packageManager = null;
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
packageManager = "pnpm";
} else if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
packageManager = "yarn";
} else if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) {
packageManager = "bun";
} else if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
packageManager = "npm";
}
const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src"));
return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir };
}
function needsClient(env) {
return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla";
}
function needsServer(env) {
return env === "nextjs" || env === "node" || env === "edge";
}
function needsReactQuery(env) {
return env === "nextjs" || env === "react-vite" || env === "react-cra";
}
function getPublishableKeyEnvVar(env) {
switch (env) {
case "nextjs":
return "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY";
case "react-vite":
return "VITE_SOFTWARE_PUBLISHABLE_KEY";
case "react-cra":
return "REACT_APP_SOFTWARE_PUBLISHABLE_KEY";
default:
return "SOFTWARE_PUBLISHABLE_KEY";
}
}
// src/browser-auth.ts
import { randomBytes } from "crypto";
import { createServer } from "http";
import { execFile, exec } from "child_process";
import { platform } from "os";
import { URL } from "url";
import pc from "picocolors";
var DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || "https://01.software";
var TIMEOUT_MS = 5 * 60 * 1e3;
function escapeHtml(s) {
return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function openBrowser(url) {
const os2 = platform();
const onError = () => {
console.log(
pc.yellow(
`Could not open browser automatically. Open this URL manually:
${url}`
)
);
};
if (os2 === "win32") {
exec(`start "" "${url}"`, (err) => {
if (err) onError();
});
} else {
const cmd = os2 === "darwin" ? "open" : "xdg-open";
execFile(cmd, [url], (err) => {
if (err) onError();
});
}
}
var PAGE_STYLE = `*{margin:0;box-sizing:border-box}
body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}
@media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}
.card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}
.icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}
.icon.ok{background:rgba(0,0,0,.05);color:#252525}
.icon.err{background:rgba(220,38,38,.08);color:#dc2626}
@media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}
h1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}
p{font-size:.75rem;color:#737373;line-height:1.5}`;
var SUCCESS_HTML = `<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login</title>
<style>${PAGE_STYLE}</style>
</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>`;
var ERROR_HTML = (msg) => `<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login Error</title>
<style>${PAGE_STYLE}</style>
</head><body><div class="card"><div class="icon err">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`;
async function exchangeCode(webUrl, code) {
const url = `${webUrl}/api/cli/exchange`;
try {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code })
});
if (!res.ok) {
const body = await res.text().catch(() => "");
console.error(
pc.red(
`Exchange failed: HTTP ${res.status} from ${url}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`
)
);
return null;
}
const data = await res.json();
if (typeof data.publishableKey !== "string" || typeof data.secretKey !== "string" || typeof data.tenantName !== "string" || typeof data.tenantId !== "string") {
console.error(pc.red(`Exchange failed: malformed response from ${url}`));
return null;
}
return {
publishableKey: data.publishableKey,
secretKey: data.secretKey,
tenantName: data.tenantName,
tenantId: data.tenantId
};
} catch (err) {
console.error(
pc.red(
`Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`
)
);
return null;
}
}
async function startBrowserAuth(options) {
const state = randomBytes(32).toString("hex");
const webUrl = options?.webUrl ?? DEFAULT_WEB_URL;
return new Promise((resolve, reject) => {
const server = createServer((req, res) => {
if (!req.url) {
res.writeHead(400).end();
return;
}
const url = new URL(req.url, `http://localhost`);
if (url.pathname !== "/callback" || req.method !== "GET") {
res.writeHead(404).end();
return;
}
const error = url.searchParams.get("error");
if (error) {
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML(error));
console.error(pc.red(`Login failed: ${error}`));
cleanup(new Error(`Login failed: ${error}`));
return;
}
const code = url.searchParams.get("code");
const receivedState = url.searchParams.get("state");
if (!code || !receivedState) {
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Missing code or state."));
cleanup(new Error("Login failed: missing code or state."));
return;
}
if (receivedState !== state) {
res.writeHead(403, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("State mismatch."));
console.error(pc.red("Login failed: state mismatch."));
cleanup(new Error("Login failed: state mismatch."));
return;
}
exchangeCode(webUrl, code).then((creds) => {
if (!creds) {
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Invalid or expired code."));
cleanup(new Error("Login failed: code exchange failed."));
return;
}
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(SUCCESS_HTML);
console.log(pc.green(`
Logged in successfully!`));
console.log(pc.dim(`Tenant: ${creds.tenantName}`));
cleanup(null, creds);
});
});
let timeout;
let completed = false;
function cleanup(err, result) {
if (completed) return;
completed = true;
clearTimeout(timeout);
server.closeAllConnections?.();
server.close(() => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
}
server.listen(0, "127.0.0.1", () => {
const addr = server.address();
if (!addr || typeof addr === "string") {
reject(new Error("Failed to start local server."));
return;
}
const port = addr.port;
timeout = setTimeout(() => {
console.error(pc.red("\nLogin timed out (5 minutes). Please try again."));
cleanup(new Error("Login timed out"));
}, TIMEOUT_MS);
const params = new URLSearchParams({ port: String(port), state });
if (options?.tenantId) {
params.set("tenantId", options.tenantId);
}
const loginUrl = `${webUrl}/cli-auth?${params.toString()}`;
console.log(pc.dim("Opening browser for login..."));
console.log(pc.dim(`If the browser does not open, visit:
${loginUrl}`));
openBrowser(loginUrl);
});
server.on("error", (err) => {
reject(err);
});
});
}
// src/init.ts
var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY";
async function init(cwd, info, answers, deps = {}) {
const { packageManager, srcDir } = info;
const env = answers.env;
const baseDir = srcDir ? path2.join(cwd, "src") : cwd;
const browserAuth = deps.startBrowserAuth ?? startBrowserAuth;
const publishableKeyEnvVar = getPublishableKeyEnvVar(env);
const wantsClient = needsClient(env);
const wantsServer = needsServer(env);
const wantsReactQuery = needsReactQuery(env);
const plan = await planConflictsAndEnv(cwd, baseDir, env, answers);
const installResult = deps.skipInstall ? {
installFailed: false,
installSkipped: true,
installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [
"@01.software/sdk"
])
} : installDeps(
cwd,
packageManager,
info.hasSdk,
info.hasReactQuery,
wantsReactQuery
);
if (wantsClient || wantsReactQuery || wantsServer) {
fs2.mkdirSync(path2.join(baseDir, "lib", "software"), { recursive: true });
}
const libDir = path2.join(baseDir, "lib", "software");
if (wantsClient) {
await writeFileWithPolicy(
cwd,
path2.join(libDir, "client.ts"),
getClientTemplate(env, publishableKeyEnvVar),
plan.policy
);
await writeFileWithPolicy(
cwd,
path2.join(libDir, "analytics.ts"),
getAnalyticsTemplate(env, publishableKeyEnvVar),
plan.policy
);
}
if (wantsReactQuery) {
await writeFileWithPolicy(
cwd,
path2.join(libDir, "query-provider.tsx"),
getQueryProviderTemplate(env),
plan.policy
);
}
if (wantsServer) {
await writeFileWithPolicy(
cwd,
path2.join(libDir, "server.ts"),
getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),
plan.policy
);
}
if (plan.envFile && answers.authMethod !== "browser") {
await writeEnv(
cwd,
plan.envFile,
answers.publishableKey || "",
answers.secretKey || "",
publishableKeyEnvVar,
wantsServer ? SECRET_KEY_ENV_VAR : null,
plan.policy
);
}
let publishableKey = answers.publishableKey;
let secretKey = answers.secretKey;
let tenantName = "";
if (answers.authMethod === "browser" && answers.aiTools.length > 0) {
try {
console.log();
const creds = await browserAuth();
publishableKey = creds.publishableKey;
secretKey = creds.secretKey;
tenantName = creds.tenantName;
if (plan.envFile && publishableKey) {
await writeEnv(
cwd,
plan.envFile,
publishableKey,
secretKey,
publishableKeyEnvVar,
wantsServer ? SECRET_KEY_ENV_VAR : null,
"overwrite",
true
);
}
} catch (err) {
console.log(
pc2.yellow(" Browser auth skipped:"),
err instanceof Error ? err.message : String(err)
);
}
}
if (answers.aiTools.length > 0) {
for (const tool of answers.aiTools) {
await writeMcpConfig(tool, cwd);
}
addToGitignore(cwd, answers.aiTools);
if (answers.aiTools.includes("claude")) {
await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy);
}
}
return {
...installResult,
publishableKey: publishableKey || void 0,
secretKey: secretKey || void 0,
tenantName: tenantName || void 0
};
}
function installDeps(cwd, pm, hasSdk, hasReactQuery, wantsReactQuery) {
const allDeps = [
{ name: "@01.software/sdk", installed: hasSdk, needed: true },
{ name: "@tanstack/react-query", installed: hasReactQuery, needed: wantsReactQuery }
];
const fullList = allDeps.filter((d) => d.needed).map((d) => d.name);
const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name);
const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList);
if (toInstall.length === 0) {
console.log(pc2.dim(` Dependencies already installed: ${fullList.join(", ")}`));
return { installFailed: false, installSkipped: true, installCmd: fullCmd };
}
const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall);
console.log(pc2.dim(` Installing ${toInstall.join(" and ")}...`));
const wsPatched = pm === "pnpm" && patchPnpmWorkspace(cwd);
let installFailed = false;
try {
execSync(addCmd, { cwd, stdio: "pipe" });
console.log(pc2.green(" Installed"), toInstall.join(", "));
} catch (error) {
installFailed = true;
const err = error;
const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error);
console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding"));
const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n");
if (firstLines) console.log(pc2.dim(firstLines));
console.log(pc2.dim(` Run manually: ${addCmd}`));
} finally {
if (wsPatched) restorePnpmWorkspace(cwd);
}
return { installFailed, installSkipped: false, installCmd: addCmd };
}
function buildAddCmd(pm, hasPnpmWs, deps) {
const pkgs = deps.join(" ");
switch (pm) {
case "pnpm":
return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`;
case "yarn":
return `yarn add ${pkgs}`;
case "bun":
return `bun add ${pkgs}`;
default:
return `npm install ${pkgs}`;
}
}
async function planConflictsAndEnv(cwd, baseDir, env, answers) {
const candidates = [];
const libDir = path2.join(baseDir, "lib", "software");
if (needsClient(env)) candidates.push(path2.join(libDir, "client.ts"));
if (needsReactQuery(env)) candidates.push(path2.join(libDir, "query-provider.tsx"));
if (needsServer(env)) candidates.push(path2.join(libDir, "server.ts"));
if (answers.aiTools.includes("claude")) {
for (const { dirName } of getSkillFiles()) {
candidates.push(path2.join(cwd, ".claude", "skills", dirName, "SKILL.md"));
}
}
const conflicts = candidates.filter((p) => fs2.existsSync(p));
let policy = "skip";
if (conflicts.length > 0) {
console.log(pc2.yellow(` ${conflicts.length} file(s) already exist:`));
for (const c of conflicts) console.log(pc2.dim(` ${path2.relative(cwd, c)}`));
const { selected } = await prompts({
type: "select",
name: "selected",
message: "How should I handle existing files?",
choices: [
{ title: "Keep existing (skip)", value: "skip" },
{ title: "Overwrite all", value: "overwrite" },
{ title: "Ask for each", value: "ask" }
],
initial: 0
});
policy = selected ?? "skip";
}
const envFile = env === "vanilla" || env === "edge" ? "" : await pickEnvFile(cwd, env);
return { policy, envFile };
}
async function pickEnvFile(cwd, env) {
const candidates = [".env.local", ".env", ".env.development"];
const existing = candidates.filter((f) => fs2.existsSync(path2.join(cwd, f)));
const preferred = env === "nextjs" ? ".env.local" : ".env";
if (existing.length === 0) return preferred;
if (existing.length === 1 && existing[0] === preferred) return existing[0];
const options = Array.from(/* @__PURE__ */ new Set([...existing, preferred]));
const choices = options.map((f) => ({
title: f,
description: existing.includes(f) ? "exists" : "create",
value: f
}));
const initial = Math.max(
0,
choices.findIndex((c) => c.value === preferred)
);
const { file } = await prompts({
type: "select",
name: "file",
message: "Which env file should I write SDK credentials to?",
choices,
initial
});
return file ?? preferred;
}
async function writeFileWithPolicy(cwd, filePath, content, policy) {
const rel = path2.relative(cwd, filePath);
if (!fs2.existsSync(filePath)) {
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
fs2.writeFileSync(filePath, content);
console.log(pc2.green(" Created"), rel);
return;
}
const existing = fs2.readFileSync(filePath, "utf-8");
if (existing === content) {
console.log(pc2.dim(" Unchanged"), rel);
return;
}
let shouldWrite = false;
if (policy === "overwrite") {
shouldWrite = true;
} else if (policy === "ask") {
const { confirm } = await prompts({
type: "confirm",
name: "confirm",
message: `Overwrite ${rel}?`,
initial: false
});
shouldWrite = !!confirm;
}
if (shouldWrite) {
fs2.writeFileSync(filePath, content);
console.log(pc2.green(" Overwrote"), rel);
} else {
console.log(pc2.yellow(" Skipped"), rel, pc2.dim("(already exists)"));
}
}
async function writeEnv(cwd, envFile, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar, policy, fromBrowserAuth = false) {
const envPath = path2.join(cwd, envFile);
const targets = [
{ name: publishableKeyEnvVar, value: publishableKey }
];
if (secretKeyEnvVar) {
targets.push({ name: secretKeyEnvVar, value: secretKey });
}
if (!fs2.existsSync(envPath)) {
const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar);
writeEnvFile(envPath, initial.trimStart());
console.log(pc2.green(" Created"), envFile);
return;
}
let content = fs2.readFileSync(envPath, "utf-8");
let modified = false;
let appendedHeader = false;
const headerAlreadyPresent = targets.some(
(t) => readEnvValue(content, t.name) !== null
);
for (const { name, value } of targets) {
const existing = readEnvValue(content, name);
if (existing === null) {
if (!headerAlreadyPresent && !appendedHeader) {
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
content += "\n# 01.software\n";
appendedHeader = true;
}
content = setEnvValue(content, name, value);
modified = true;
continue;
}
if (existing === value) continue;
if (!value) continue;
let shouldOverwrite = false;
if (policy === "overwrite") {
shouldOverwrite = true;
} else if (policy === "ask") {
const { confirm } = await prompts({
type: "confirm",
name: "confirm",
message: `${name} already set in ${envFile}. Overwrite?`,
initial: fromBrowserAuth
});
shouldOverwrite = !!confirm;
}
if (shouldOverwrite) {
content = setEnvValue(content, name, value);
modified = true;
}
}
if (modified) {
writeEnvFile(envPath, content);
console.log(
pc2.green(" Updated"),
envFile,
fromBrowserAuth ? pc2.dim("(SDK credentials)") : ""
);
} else {
chmodSecretFile(envPath);
console.log(pc2.dim(" Unchanged"), envFile);
}
}
function resolveMcpLocation(tool, cwd) {
const home = os.homedir();
switch (tool) {
case "claude":
return {
kind: "json",
absolutePath: path2.join(cwd, ".mcp.json"),
displayPath: ".mcp.json",
gitignoreEntry: ".mcp.json"
};
case "cursor":
return {
kind: "json",
absolutePath: path2.join(cwd, ".cursor", "mcp.json"),
displayPath: ".cursor/mcp.json",
gitignoreEntry: ".cursor/mcp.json"
};
case "vscode":
return {
kind: "json",
absolutePath: path2.join(cwd, ".vscode", "mcp.json"),
jsonClient: "vscode",
displayPath: ".vscode/mcp.json",
gitignoreEntry: ".vscode/mcp.json"
};
case "windsurf": {
if (!home) return null;
const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
return {
kind: "json",
absolutePath: p,
jsonClient: "windsurf",
displayPath: p,
gitignoreEntry: null,
global: true
};
}
case "codex": {
if (!home) return null;
const p = path2.join(home, ".codex", "config.toml");
return { kind: "toml", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true };
}
case "gemini": {
if (!home) return null;
const p = path2.join(home, ".gemini", "settings.json");
return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true };
}
}
}
async function writeMcpConfig(tool, cwd) {
const loc = resolveMcpLocation(tool, cwd);
if (!loc) {
console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)"));
return;
}
if (!loc.global) {
fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true });
}
try {
if (loc.kind === "json") {
writeJsonMcp(loc);
} else {
writeTomlMcp(loc);
}
} catch (err) {
console.log(
pc2.yellow(` Skipped ${loc.displayPath}`),
pc2.dim(err instanceof Error ? err.message : String(err))
);
}
}
function writeJsonMcp(loc) {
const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content);
if (!fs2.existsSync(loc.absolutePath)) {
writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient));
console.log(pc2.green(" Created"), loc.displayPath);
return;
}
const rootKey = getMcpRootKey(loc.jsonClient);
let existing;
try {
existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8"));
} catch {
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)"));
return;
}
const nextEntry = getMcpServerEntry(loc.jsonClient);
const existingServers = existing[rootKey] ?? void 0;
if (existingServers?.["01software"] && JSON.stringify(existingServers["01software"]) === JSON.stringify(nextEntry)) {
console.log(pc2.dim(" Unchanged"), loc.displayPath);
return;
}
const servers = existing[rootKey] ?? {};
servers["01software"] = nextEntry;
existing[rootKey] = servers;
writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n");
console.log(pc2.green(" Updated"), loc.displayPath);
}
function writeTomlMcp(loc) {
const section = getCodexMcpTomlSection();
const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content);
if (!fs2.existsSync(loc.absolutePath)) {
writeFile(loc.absolutePath, section.trimStart());
console.log(pc2.green(" Created"), loc.displayPath);
return;
}
const existing = fs2.readFileSync(loc.absolutePath, "utf-8");
if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {
const sep = existing.endsWith("\n") ? "" : "\n";
writeFile(loc.absolutePath, existing + sep + section);
console.log(pc2.green(" Updated"), loc.displayPath);
return;
}
const replaced = replaceTomlMcpSection(existing, section);
if (replaced === existing) {
console.log(pc2.dim(" Unchanged"), loc.displayPath);
return;
}
writeFile(loc.absolutePath, replaced);
console.log(pc2.green(" Updated"), loc.displayPath);
}
function addToGitignore(cwd, tools) {
const entries = [];
for (const tool of tools) {
const loc = resolveMcpLocation(tool, cwd);
if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry);
}
if (entries.length === 0) return;
const gitignorePath = path2.join(cwd, ".gitignore");
const existing = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf-8") : "";
const toAdd = entries.filter((e) => !existing.includes(e));
if (toAdd.length === 0) return;
const content = "\n# MCP configs\n" + toAdd.join("\n") + "\n";
if (fs2.existsSync(gitignorePath)) {
fs2.appendFileSync(gitignorePath, content);
} else {
fs2.writeFileSync(gitignorePath, content.trimStart());
}
console.log(pc2.green(" Updated"), ".gitignore", pc2.dim(`(added ${toAdd.join(", ")})`));
}
async function writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, policy) {
let ctx = {
tenantName: tenantName || "Your Tenant",
features: void 0,
collections: void 0
};
if (publishableKey && secretKey) {
const fetched = await fetchTenantContext(publishableKey, secretKey);
if (fetched) {
ctx = {
tenantName: fetched.tenantName || ctx.tenantName,
features: fetched.features,
collections: fetched.collections
};
}
}
const claudeDir = path2.join(cwd, ".claude");
const softwareDir = path2.join(claudeDir, "01software");
const skillsDir = path2.join(claudeDir, "skills");
fs2.mkdirSync(softwareDir, { recursive: true });
fs2.mkdirSync(skillsDir, { recursive: true });
const contextPath = path2.join(softwareDir, "context.md");
const contextExists = fs2.existsSync(contextPath);
fs2.writeFileSync(contextPath, generateClaudeMd(ctx));
console.log(pc2.green(contextExists ? " Updated" : " Created"), ".claude/01software/context.md");
const claudeMdPath = path2.join(claudeDir, "CLAUDE.md");
const importLine = "@.claude/01software/context.md";
if (!fs2.existsSync(claudeMdPath)) {
fs2.writeFileSync(claudeMdPath, importLine + "\n");
console.log(pc2.green(" Created"), ".claude/CLAUDE.md");
} else {
const existing = fs2.readFileSync(claudeMdPath, "utf-8");
if (!existing.includes(importLine)) {
const prefix = existing.endsWith("\n") ? "\n" : "\n\n";
fs2.appendFileSync(claudeMdPath, prefix + importLine + "\n");
console.log(pc2.green(" Updated"), ".claude/CLAUDE.md", pc2.dim("(added @import)"));
} else {
console.log(pc2.dim(" Unchanged"), ".claude/CLAUDE.md");
}
}
for (const { dirName, content } of getSkillFiles()) {
const skillDir = path2.join(skillsDir, dirName);
const skillPath = path2.join(skillDir, "SKILL.md");
fs2.mkdirSync(skillDir, { recursive: true });
await writeFileWithPolicy(cwd, skillPath, content, policy);
}
}
var WS_FILE = "pnpm-workspace.yaml";
var WS_BACKUP = "pnpm-workspace.yaml.bak";
function hasPnpmWorkspace(cwd) {
return fs2.existsSync(path2.join(cwd, WS_FILE));
}
function patchPnpmWorkspace(cwd) {
const wsPath = path2.join(cwd, WS_FILE);
if (!fs2.existsSync(wsPath)) return false;
const content = fs2.readFileSync(wsPath, "utf-8");
if (content.includes("packages:")) return false;
fs2.copyFileSync(wsPath, path2.join(cwd, WS_BACKUP));
fs2.writeFileSync(wsPath, content.trimEnd() + "\npackages: []\n");
return true;
}
function restorePnpmWorkspace(cwd) {
const backupPath = path2.join(cwd, WS_BACKUP);
if (!fs2.existsSync(backupPath)) return;
fs2.copyFileSync(backupPath, path2.join(cwd, WS_FILE));
fs2.unlinkSync(backupPath);
}
export {
detectProject,
init
};
//# sourceMappingURL=chunk-2IGKOSK7.js.map
{"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/browser-auth.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { execSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport prompts from 'prompts'\nimport type { PackageManager, ProjectEnv, ProjectInfo } from './detect'\nimport {\n needsClient,\n needsServer,\n needsReactQuery,\n getPublishableKeyEnvVar,\n} from './detect'\nimport type { AiTool, InitAnswers } from './prompts'\nimport {\n getClientTemplate,\n getAnalyticsTemplate,\n getQueryProviderTemplate,\n getServerTemplate,\n getEnvContent,\n getMcpConfigTemplate,\n getMcpRootKey,\n getMcpServerEntry,\n getCodexMcpTomlSection,\n CODEX_MCP_SECTION_MARKER,\n} from './templates'\nimport { startBrowserAuth } from './browser-auth'\nimport {\n generateClaudeMd,\n getSkillFiles,\n fetchTenantContext,\n} from './ai-docs'\nimport {\n readEnvValue,\n setEnvValue,\n replaceTomlMcpSection,\n writeEnvFile,\n chmodSecretFile,\n writeSecretGlobalConfig,\n} from './file-ops'\n\ntype ResolvedProjectInfo = Omit<ProjectInfo, 'packageManager'> & {\n packageManager: PackageManager\n}\n\nconst SECRET_KEY_ENV_VAR = 'SOFTWARE_SECRET_KEY'\n\nexport type ConflictPolicy = 'overwrite' | 'skip' | 'ask'\n\nexport interface InitResult {\n installFailed: boolean\n installSkipped: boolean\n installCmd: string\n /** Set when browser-auth or manual entry produced a publishable key the\n * caller should prefer over `answers.publishableKey` when computing\n * next-step messages. */\n publishableKey?: string\n /** Set when browser-auth or manual entry produced a secret key. */\n secretKey?: string\n /** Tenant display name from browser-auth, when available. */\n tenantName?: string\n}\n\nexport interface InitDeps {\n /** Test seam — replaces the real browser-auth flow. */\n startBrowserAuth?: typeof startBrowserAuth\n /** Test seam — skips actually invoking the package manager. */\n skipInstall?: boolean\n}\n\nexport async function init(\n cwd: string,\n info: ResolvedProjectInfo,\n answers: InitAnswers,\n deps: InitDeps = {},\n): Promise<InitResult> {\n const { packageManager, srcDir } = info\n const env = answers.env\n const baseDir = srcDir ? path.join(cwd, 'src') : cwd\n const browserAuth = deps.startBrowserAuth ?? startBrowserAuth\n\n const publishableKeyEnvVar = getPublishableKeyEnvVar(env)\n const wantsClient = needsClient(env)\n const wantsServer = needsServer(env)\n const wantsReactQuery = needsReactQuery(env)\n\n // 0. Plan: scan for conflicts and pick the env file. Both prompts are\n // front-loaded so the rest of init() doesn't interleave I/O with prompts.\n const plan = await planConflictsAndEnv(cwd, baseDir, env, answers)\n\n // 1. Install dependencies — skip whatever's already in package.json\n const installResult = deps.skipInstall\n ? {\n installFailed: false,\n installSkipped: true,\n installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [\n '@01.software/sdk',\n ]),\n }\n : installDeps(\n cwd,\n packageManager,\n info.hasSdk,\n info.hasReactQuery,\n wantsReactQuery,\n )\n\n // 2. Write lib/software/ files\n if (wantsClient || wantsReactQuery || wantsServer) {\n fs.mkdirSync(path.join(baseDir, 'lib', 'software'), { recursive: true })\n }\n const libDir = path.join(baseDir, 'lib', 'software')\n\n if (wantsClient) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'client.ts'),\n getClientTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'analytics.ts'),\n getAnalyticsTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n }\n if (wantsReactQuery) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'query-provider.tsx'),\n getQueryProviderTemplate(env),\n plan.policy,\n )\n }\n if (wantsServer) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'server.ts'),\n getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),\n plan.policy,\n )\n }\n\n // 3. Append to env file (browser auth handles its own write in step 4)\n if (plan.envFile && answers.authMethod !== 'browser') {\n await writeEnv(\n cwd,\n plan.envFile,\n answers.publishableKey || '',\n answers.secretKey || '',\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n plan.policy,\n )\n }\n\n // 4. Browser auth — get real credentials and force-write env\n let publishableKey = answers.publishableKey\n let secretKey = answers.secretKey\n let tenantName = ''\n\n if (answers.authMethod === 'browser' && answers.aiTools.length > 0) {\n try {\n console.log()\n const creds = await browserAuth()\n publishableKey = creds.publishableKey\n secretKey = creds.secretKey\n tenantName = creds.tenantName\n\n // Browser auth just minted fresh credentials → policy is overwrite.\n if (plan.envFile && publishableKey) {\n await writeEnv(\n cwd,\n plan.envFile,\n publishableKey,\n secretKey,\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n 'overwrite',\n true,\n )\n }\n } catch (err) {\n console.log(\n pc.yellow(' Browser auth skipped:'),\n err instanceof Error ? err.message : String(err),\n )\n }\n }\n\n // 5. AI tool configs (MCP + Claude docs)\n if (answers.aiTools.length > 0) {\n for (const tool of answers.aiTools) {\n await writeMcpConfig(tool, cwd)\n }\n\n addToGitignore(cwd, answers.aiTools)\n\n if (answers.aiTools.includes('claude')) {\n await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy)\n }\n }\n\n return {\n ...installResult,\n publishableKey: publishableKey || undefined,\n secretKey: secretKey || undefined,\n tenantName: tenantName || undefined,\n }\n}\n\n// ── Install ──────────────────────────────────────────────────────────\n\ninterface DepSpec {\n name: string\n installed: boolean\n needed: boolean\n}\n\nfunction installDeps(\n cwd: string,\n pm: PackageManager,\n hasSdk: boolean,\n hasReactQuery: boolean,\n wantsReactQuery: boolean,\n): InitResult {\n const allDeps: DepSpec[] = [\n { name: '@01.software/sdk', installed: hasSdk, needed: true },\n { name: '@tanstack/react-query', installed: hasReactQuery, needed: wantsReactQuery },\n ]\n const fullList = allDeps.filter((d) => d.needed).map((d) => d.name)\n const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name)\n const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList)\n\n if (toInstall.length === 0) {\n console.log(pc.dim(` Dependencies already installed: ${fullList.join(', ')}`))\n return { installFailed: false, installSkipped: true, installCmd: fullCmd }\n }\n\n const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall)\n console.log(pc.dim(` Installing ${toInstall.join(' and ')}...`))\n\n const wsPatched = pm === 'pnpm' && patchPnpmWorkspace(cwd)\n let installFailed = false\n\n try {\n execSync(addCmd, { cwd, stdio: 'pipe' })\n console.log(pc.green(' Installed'), toInstall.join(', '))\n } catch (error) {\n installFailed = true\n const err = error as { stdout?: Buffer; stderr?: Buffer }\n const msg =\n String(err.stderr || '').trim() ||\n String(err.stdout || '').trim() ||\n String(error)\n console.log(pc.yellow(' Install failed — continuing with scaffolding'))\n const firstLines = msg.split('\\n').slice(0, 3).map((l) => ` ${l}`).join('\\n')\n if (firstLines) console.log(pc.dim(firstLines))\n console.log(pc.dim(` Run manually: ${addCmd}`))\n } finally {\n if (wsPatched) restorePnpmWorkspace(cwd)\n }\n\n return { installFailed, installSkipped: false, installCmd: addCmd }\n}\n\nfunction buildAddCmd(\n pm: PackageManager,\n hasPnpmWs: boolean,\n deps: string[],\n): string {\n const pkgs = deps.join(' ')\n switch (pm) {\n case 'pnpm':\n return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`\n case 'yarn':\n return `yarn add ${pkgs}`\n case 'bun':\n return `bun add ${pkgs}`\n default:\n return `npm install ${pkgs}`\n }\n}\n\n// ── Conflict planning ────────────────────────────────────────────────\n\ninterface PlanResult {\n policy: ConflictPolicy\n /** Empty string when env file is not used (vanilla / edge) */\n envFile: string\n}\n\nasync function planConflictsAndEnv(\n cwd: string,\n baseDir: string,\n env: ProjectEnv,\n answers: InitAnswers,\n): Promise<PlanResult> {\n const candidates: string[] = []\n const libDir = path.join(baseDir, 'lib', 'software')\n if (needsClient(env)) candidates.push(path.join(libDir, 'client.ts'))\n if (needsReactQuery(env)) candidates.push(path.join(libDir, 'query-provider.tsx'))\n if (needsServer(env)) candidates.push(path.join(libDir, 'server.ts'))\n if (answers.aiTools.includes('claude')) {\n for (const { dirName } of getSkillFiles()) {\n candidates.push(path.join(cwd, '.claude', 'skills', dirName, 'SKILL.md'))\n }\n }\n\n const conflicts = candidates.filter((p) => fs.existsSync(p))\n\n let policy: ConflictPolicy = 'skip'\n if (conflicts.length > 0) {\n console.log(pc.yellow(` ${conflicts.length} file(s) already exist:`))\n for (const c of conflicts) console.log(pc.dim(` ${path.relative(cwd, c)}`))\n const { selected } = await prompts({\n type: 'select',\n name: 'selected',\n message: 'How should I handle existing files?',\n choices: [\n { title: 'Keep existing (skip)', value: 'skip' },\n { title: 'Overwrite all', value: 'overwrite' },\n { title: 'Ask for each', value: 'ask' },\n ],\n initial: 0,\n })\n policy = (selected as ConflictPolicy) ?? 'skip'\n }\n\n const envFile =\n env === 'vanilla' || env === 'edge' ? '' : await pickEnvFile(cwd, env)\n\n return { policy, envFile }\n}\n\nasync function pickEnvFile(cwd: string, env: ProjectEnv): Promise<string> {\n // Order matters — `.env.local` first so it's the default selection on\n // Next.js, where it's the conventional secret store.\n const candidates = ['.env.local', '.env', '.env.development']\n const existing = candidates.filter((f) => fs.existsSync(path.join(cwd, f)))\n const preferred = env === 'nextjs' ? '.env.local' : '.env'\n\n if (existing.length === 0) return preferred\n if (existing.length === 1 && existing[0] === preferred) return existing[0]\n\n // Multiple files exist OR the only existing file isn't the preferred default.\n const options = Array.from(new Set([...existing, preferred]))\n const choices = options.map((f) => ({\n title: f,\n description: existing.includes(f) ? 'exists' : 'create',\n value: f,\n }))\n const initial = Math.max(\n 0,\n choices.findIndex((c) => c.value === preferred),\n )\n const { file } = await prompts({\n type: 'select',\n name: 'file',\n message: 'Which env file should I write SDK credentials to?',\n choices,\n initial,\n })\n return file ?? preferred\n}\n\n// ── Generic file write ───────────────────────────────────────────────\n\nasync function writeFileWithPolicy(\n cwd: string,\n filePath: string,\n content: string,\n policy: ConflictPolicy,\n): Promise<void> {\n const rel = path.relative(cwd, filePath)\n if (!fs.existsSync(filePath)) {\n fs.mkdirSync(path.dirname(filePath), { recursive: true })\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Created'), rel)\n return\n }\n\n const existing = fs.readFileSync(filePath, 'utf-8')\n if (existing === content) {\n console.log(pc.dim(' Unchanged'), rel)\n return\n }\n\n let shouldWrite = false\n if (policy === 'overwrite') {\n shouldWrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `Overwrite ${rel}?`,\n initial: false,\n })\n shouldWrite = !!confirm\n }\n\n if (shouldWrite) {\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Overwrote'), rel)\n } else {\n console.log(pc.yellow(' Skipped'), rel, pc.dim('(already exists)'))\n }\n}\n\n// ── Env file write ───────────────────────────────────────────────────\n\nasync function writeEnv(\n cwd: string,\n envFile: string,\n publishableKey: string,\n secretKey: string,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n policy: ConflictPolicy,\n fromBrowserAuth = false,\n): Promise<void> {\n const envPath = path.join(cwd, envFile)\n\n const targets: { name: string; value: string }[] = [\n { name: publishableKeyEnvVar, value: publishableKey },\n ]\n if (secretKeyEnvVar) {\n targets.push({ name: secretKeyEnvVar, value: secretKey })\n }\n\n if (!fs.existsSync(envPath)) {\n const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar)\n writeEnvFile(envPath, initial.trimStart())\n console.log(pc.green(' Created'), envFile)\n return\n }\n\n let content = fs.readFileSync(envPath, 'utf-8')\n let modified = false\n let appendedHeader = false\n const headerAlreadyPresent = targets.some(\n (t) => readEnvValue(content, t.name) !== null,\n )\n\n for (const { name, value } of targets) {\n const existing = readEnvValue(content, name)\n\n if (existing === null) {\n // Append. Add the `# 01.software` section header on first append, but\n // only if no other 01.software keys are already in the file.\n if (!headerAlreadyPresent && !appendedHeader) {\n if (content.length > 0 && !content.endsWith('\\n')) content += '\\n'\n content += '\\n# 01.software\\n'\n appendedHeader = true\n }\n content = setEnvValue(content, name, value)\n modified = true\n continue\n }\n\n if (existing === value) continue\n // Don't overwrite an existing real value with empty (e.g. user skipped key entry).\n if (!value) continue\n\n let shouldOverwrite = false\n if (policy === 'overwrite') {\n shouldOverwrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `${name} already set in ${envFile}. Overwrite?`,\n initial: fromBrowserAuth,\n })\n shouldOverwrite = !!confirm\n }\n\n if (shouldOverwrite) {\n content = setEnvValue(content, name, value)\n modified = true\n }\n }\n\n if (modified) {\n writeEnvFile(envPath, content)\n console.log(\n pc.green(' Updated'),\n envFile,\n fromBrowserAuth ? pc.dim('(SDK credentials)') : '',\n )\n } else {\n // Tighten mode on existing files we did not modify, so re-runs harden\n // permissions on env files created by an earlier looser version.\n chmodSecretFile(envPath)\n console.log(pc.dim(' Unchanged'), envFile)\n }\n}\n\n// ── MCP targets registry ─────────────────────────────────────────────\n\ntype McpFormat = 'json' | 'toml'\ntype McpJsonClient = 'generic' | 'windsurf' | 'vscode'\ninterface McpLocation {\n kind: McpFormat\n absolutePath: string\n jsonClient?: McpJsonClient\n displayPath: string\n gitignoreEntry: string | null // null = global path, not worth ignoring\n /** Global config under user HOME — apply 0o600 mode, atomic rename, and\n * symlink/hardlink rejection per spec §\"Secret Handling Rules\". */\n global?: boolean\n}\n\nfunction resolveMcpLocation(tool: AiTool, cwd: string): McpLocation | null {\n const home = os.homedir()\n\n switch (tool) {\n case 'claude':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.mcp.json'),\n displayPath: '.mcp.json',\n gitignoreEntry: '.mcp.json',\n }\n case 'cursor':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.cursor', 'mcp.json'),\n displayPath: '.cursor/mcp.json',\n gitignoreEntry: '.cursor/mcp.json',\n }\n case 'vscode':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.vscode', 'mcp.json'),\n jsonClient: 'vscode',\n displayPath: '.vscode/mcp.json',\n gitignoreEntry: '.vscode/mcp.json',\n }\n case 'windsurf': {\n if (!home) return null\n const p = path.join(home, '.codeium', 'windsurf', 'mcp_config.json')\n return {\n kind: 'json',\n absolutePath: p,\n jsonClient: 'windsurf',\n displayPath: p,\n gitignoreEntry: null,\n global: true,\n }\n }\n case 'codex': {\n if (!home) return null\n const p = path.join(home, '.codex', 'config.toml')\n return { kind: 'toml', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n case 'gemini': {\n if (!home) return null\n const p = path.join(home, '.gemini', 'settings.json')\n return { kind: 'json', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n }\n}\n\nasync function writeMcpConfig(\n tool: AiTool,\n cwd: string,\n): Promise<void> {\n const loc = resolveMcpLocation(tool, cwd)\n if (!loc) {\n console.log(pc.yellow(` Skipped ${tool}`), pc.dim('(HOME not set)'))\n return\n }\n\n // For global secret-bearing config, parent-dir creation with 0o700 is\n // handled inside `writeSecretGlobalConfig`. Repo-local writes (Claude Code,\n // Cursor, VS Code) keep loose mkdir — they only contain OAuth/discovery URLs.\n if (!loc.global) {\n fs.mkdirSync(path.dirname(loc.absolutePath), { recursive: true })\n }\n\n try {\n if (loc.kind === 'json') {\n writeJsonMcp(loc)\n } else {\n writeTomlMcp(loc)\n }\n } catch (err) {\n console.log(\n pc.yellow(` Skipped ${loc.displayPath}`),\n pc.dim(err instanceof Error ? err.message : String(err)),\n )\n }\n}\n\nfunction writeJsonMcp(loc: McpLocation): void {\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient))\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n // VS Code uses `servers` as the config root; all other JSON clients use `mcpServers`.\n const rootKey = getMcpRootKey(loc.jsonClient)\n\n let existing: Record<string, unknown>\n try {\n existing = JSON.parse(fs.readFileSync(loc.absolutePath, 'utf-8'))\n } catch {\n console.log(pc.yellow(' Skipped'), loc.displayPath, pc.dim('(could not parse existing file)'))\n return\n }\n\n const nextEntry = getMcpServerEntry(loc.jsonClient)\n const existingServers = (existing[rootKey] as Record<string, unknown> | undefined) ?? undefined\n if (\n existingServers?.['01software'] &&\n JSON.stringify(existingServers['01software']) === JSON.stringify(nextEntry)\n ) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n const servers = (existing[rootKey] as Record<string, unknown> | undefined) ?? {}\n servers['01software'] = nextEntry\n existing[rootKey] = servers\n writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + '\\n')\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction writeTomlMcp(loc: McpLocation): void {\n const section = getCodexMcpTomlSection()\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, section.trimStart())\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n const existing = fs.readFileSync(loc.absolutePath, 'utf-8')\n\n if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {\n const sep = existing.endsWith('\\n') ? '' : '\\n'\n // Atomic rewrite (read-modify-write) — `appendFileSync` would bypass the\n // symlink/hardlink guards and the 0o600 mode of `writeSecretGlobalConfig`.\n writeFile(loc.absolutePath, existing + sep + section)\n console.log(pc.green(' Updated'), loc.displayPath)\n return\n }\n\n const replaced = replaceTomlMcpSection(existing, section)\n if (replaced === existing) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n writeFile(loc.absolutePath, replaced)\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction addToGitignore(cwd: string, tools: AiTool[]): void {\n const entries: string[] = []\n for (const tool of tools) {\n const loc = resolveMcpLocation(tool, cwd)\n if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry)\n }\n\n if (entries.length === 0) return\n\n const gitignorePath = path.join(cwd, '.gitignore')\n const existing = fs.existsSync(gitignorePath)\n ? fs.readFileSync(gitignorePath, 'utf-8')\n : ''\n const toAdd = entries.filter((e) => !existing.includes(e))\n if (toAdd.length === 0) return\n\n const content = '\\n# MCP configs\\n' + toAdd.join('\\n') + '\\n'\n if (fs.existsSync(gitignorePath)) {\n fs.appendFileSync(gitignorePath, content)\n } else {\n fs.writeFileSync(gitignorePath, content.trimStart())\n }\n console.log(pc.green(' Updated'), '.gitignore', pc.dim(`(added ${toAdd.join(', ')})`))\n}\n\nasync function writeClaudeDocs(\n cwd: string,\n publishableKey: string,\n secretKey: string,\n tenantName: string,\n policy: ConflictPolicy,\n): Promise<void> {\n let ctx = {\n tenantName: tenantName || 'Your Tenant',\n features: undefined as string[] | undefined,\n collections: undefined as string[] | undefined,\n }\n\n if (publishableKey && secretKey) {\n const fetched = await fetchTenantContext(publishableKey, secretKey)\n if (fetched) {\n ctx = {\n tenantName: fetched.tenantName || ctx.tenantName,\n features: fetched.features,\n collections: fetched.collections,\n }\n }\n }\n\n const claudeDir = path.join(cwd, '.claude')\n const softwareDir = path.join(claudeDir, '01software')\n const skillsDir = path.join(claudeDir, 'skills')\n fs.mkdirSync(softwareDir, { recursive: true })\n fs.mkdirSync(skillsDir, { recursive: true })\n\n // context.md is intentionally always overwritten so re-running init\n // refreshes tenant data; users don't customize this file.\n const contextPath = path.join(softwareDir, 'context.md')\n const contextExists = fs.existsSync(contextPath)\n fs.writeFileSync(contextPath, generateClaudeMd(ctx))\n console.log(pc.green(contextExists ? ' Updated' : ' Created'), '.claude/01software/context.md')\n\n // CLAUDE.md — append @import line (idempotent, never overwrites user content)\n const claudeMdPath = path.join(claudeDir, 'CLAUDE.md')\n const importLine = '@.claude/01software/context.md'\n if (!fs.existsSync(claudeMdPath)) {\n fs.writeFileSync(claudeMdPath, importLine + '\\n')\n console.log(pc.green(' Created'), '.claude/CLAUDE.md')\n } else {\n const existing = fs.readFileSync(claudeMdPath, 'utf-8')\n if (!existing.includes(importLine)) {\n const prefix = existing.endsWith('\\n') ? '\\n' : '\\n\\n'\n fs.appendFileSync(claudeMdPath, prefix + importLine + '\\n')\n console.log(pc.green(' Updated'), '.claude/CLAUDE.md', pc.dim('(added @import)'))\n } else {\n console.log(pc.dim(' Unchanged'), '.claude/CLAUDE.md')\n }\n }\n\n // Skill files honour the user's conflict policy (templates the user may have\n // tuned to their workflow).\n for (const { dirName, content } of getSkillFiles()) {\n const skillDir = path.join(skillsDir, dirName)\n const skillPath = path.join(skillDir, 'SKILL.md')\n fs.mkdirSync(skillDir, { recursive: true })\n await writeFileWithPolicy(cwd, skillPath, content, policy)\n }\n}\n\nconst WS_FILE = 'pnpm-workspace.yaml'\nconst WS_BACKUP = 'pnpm-workspace.yaml.bak'\n\nfunction hasPnpmWorkspace(cwd: string): boolean {\n return fs.existsSync(path.join(cwd, WS_FILE))\n}\n\nfunction patchPnpmWorkspace(cwd: string): boolean {\n const wsPath = path.join(cwd, WS_FILE)\n if (!fs.existsSync(wsPath)) return false\n const content = fs.readFileSync(wsPath, 'utf-8')\n if (content.includes('packages:')) return false\n fs.copyFileSync(wsPath, path.join(cwd, WS_BACKUP))\n fs.writeFileSync(wsPath, content.trimEnd() + '\\npackages: []\\n')\n return true\n}\n\nfunction restorePnpmWorkspace(cwd: string): void {\n const backupPath = path.join(cwd, WS_BACKUP)\n if (!fs.existsSync(backupPath)) return\n fs.copyFileSync(backupPath, path.join(cwd, WS_FILE))\n fs.unlinkSync(backupPath)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\nexport type ProjectEnv =\n | 'nextjs'\n | 'react-vite'\n | 'react-cra'\n | 'vanilla'\n | 'node'\n | 'edge'\n | 'other' // Astro, Remix, SvelteKit, etc. — print guidance only\n\nexport interface ProjectInfo {\n hasPackageJson: boolean\n parseError: boolean\n env: ProjectEnv\n packageManager: PackageManager | null\n hasSdk: boolean\n hasReactQuery: boolean\n srcDir: boolean\n}\n\nexport function detectProject(cwd: string): ProjectInfo {\n const pkgPath = path.join(cwd, 'package.json')\n const hasPackageJson = fs.existsSync(pkgPath)\n\n let env: ProjectEnv = 'node'\n let hasSdk = false\n let hasReactQuery = false\n let parseError = false\n\n if (hasPackageJson) {\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n } catch {\n return {\n hasPackageJson: true,\n parseError: true,\n env: 'node',\n packageManager: null,\n hasSdk: false,\n hasReactQuery: false,\n srcDir: false,\n }\n }\n\n const deps = {\n ...((pkg.dependencies as Record<string, string>) || {}),\n ...((pkg.devDependencies as Record<string, string>) || {}),\n }\n hasSdk = '@01.software/sdk' in deps\n hasReactQuery = '@tanstack/react-query' in deps\n\n if ('next' in deps) {\n env = 'nextjs'\n } else if ('astro' in deps || '@astrojs/node' in deps) {\n env = 'other'\n } else if ('@remix-run/node' in deps || '@remix-run/react' in deps) {\n env = 'other'\n } else if ('@sveltejs/kit' in deps) {\n env = 'other'\n } else if ('react' in deps) {\n if ('vite' in deps) {\n env = 'react-vite'\n } else if ('react-scripts' in deps) {\n env = 'react-cra'\n } else {\n // React + unknown bundler (Webpack, Parcel, etc.) — leave as 'node'\n // so the prompt selector is shown\n env = 'node'\n }\n }\n // else: no react/next → 'node' (covers real Node.js, Deno, Bun, etc.)\n }\n\n // Detect package manager from lockfile\n let packageManager: PackageManager | null = null\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\n packageManager = 'pnpm'\n } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\n packageManager = 'yarn'\n } else if (\n fs.existsSync(path.join(cwd, 'bun.lockb')) ||\n fs.existsSync(path.join(cwd, 'bun.lock'))\n ) {\n packageManager = 'bun'\n } else if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {\n packageManager = 'npm'\n }\n\n // Detect src directory structure\n const srcDir =\n env === 'nextjs'\n ? fs.existsSync(path.join(cwd, 'src', 'app'))\n : fs.existsSync(path.join(cwd, 'src'))\n\n return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir }\n}\n\n/** Environments that generate a browser client file */\nexport function needsClient(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra' || env === 'vanilla'\n}\n\n/** Environments that generate a server client file */\nexport function needsServer(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Environments that generate a query-provider file */\nexport function needsReactQuery(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra'\n}\n\n/** Environments that support MCP setup (need both client + secret key) */\nexport function supportsMcp(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Get the env var name for the public publishable key */\nexport function getPublishableKeyEnvVar(env: ProjectEnv): string {\n switch (env) {\n case 'nextjs':\n return 'NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-vite':\n return 'VITE_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-cra':\n return 'REACT_APP_SOFTWARE_PUBLISHABLE_KEY'\n default:\n return 'SOFTWARE_PUBLISHABLE_KEY'\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport { createServer } from 'node:http'\nimport { execFile, exec } from 'node:child_process'\nimport { platform } from 'node:os'\nimport { URL } from 'node:url'\nimport pc from 'picocolors'\n\nconst DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || 'https://01.software'\nconst TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction openBrowser(url: string): void {\n const os = platform()\n\n const onError = () => {\n console.log(\n pc.yellow(\n `Could not open browser automatically. Open this URL manually:\\n${url}`,\n ),\n )\n }\n\n if (os === 'win32') {\n exec(`start \"\" \"${url}\"`, (err) => {\n if (err) onError()\n })\n } else {\n const cmd = os === 'darwin' ? 'open' : 'xdg-open'\n execFile(cmd, [url], (err) => {\n if (err) onError()\n })\n }\n}\n\nconst PAGE_STYLE = `*{margin:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}\n@media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}\n.card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}\n.icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}\n.icon.ok{background:rgba(0,0,0,.05);color:#252525}\n.icon.err{background:rgba(220,38,38,.08);color:#dc2626}\n@media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}\nh1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}\np{font-size:.75rem;color:#737373;line-height:1.5}`\n\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login</title>\n<style>${PAGE_STYLE}</style>\n</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>`\n\nconst ERROR_HTML = (msg: string) => `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login Error</title>\n<style>${PAGE_STYLE}</style>\n</head><body><div class=\"card\"><div class=\"icon err\">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`\n\ninterface ExchangeResponse {\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId: string\n}\n\nasync function exchangeCode(\n webUrl: string,\n code: string,\n): Promise<ExchangeResponse | null> {\n const url = `${webUrl}/api/cli/exchange`\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code }),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n console.error(\n pc.red(\n `Exchange failed: HTTP ${res.status} from ${url}${body ? ` — ${body.slice(0, 200)}` : ''}`,\n ),\n )\n return null\n }\n const data = (await res.json()) as Partial<ExchangeResponse>\n if (\n typeof data.publishableKey !== 'string' ||\n typeof data.secretKey !== 'string' ||\n typeof data.tenantName !== 'string' ||\n typeof data.tenantId !== 'string'\n ) {\n console.error(pc.red(`Exchange failed: malformed response from ${url}`))\n return null\n }\n return {\n publishableKey: data.publishableKey,\n secretKey: data.secretKey,\n tenantName: data.tenantName,\n tenantId: data.tenantId,\n }\n } catch (err) {\n console.error(\n pc.red(\n `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`,\n ),\n )\n return null\n }\n}\n\nexport async function startBrowserAuth(options?: {\n webUrl?: string\n tenantId?: string\n}): Promise<{\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId?: string\n}> {\n const state = randomBytes(32).toString('hex')\n const webUrl = options?.webUrl ?? DEFAULT_WEB_URL\n\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400).end()\n return\n }\n\n const url = new URL(req.url, `http://localhost`)\n\n if (url.pathname !== '/callback' || req.method !== 'GET') {\n res.writeHead(404).end()\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML(error))\n console.error(pc.red(`Login failed: ${error}`))\n cleanup(new Error(`Login failed: ${error}`))\n return\n }\n\n const code = url.searchParams.get('code')\n const receivedState = url.searchParams.get('state')\n\n if (!code || !receivedState) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Missing code or state.'))\n cleanup(new Error('Login failed: missing code or state.'))\n return\n }\n\n if (receivedState !== state) {\n res\n .writeHead(403, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('State mismatch.'))\n console.error(pc.red('Login failed: state mismatch.'))\n cleanup(new Error('Login failed: state mismatch.'))\n return\n }\n\n exchangeCode(webUrl, code).then((creds) => {\n if (!creds) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Invalid or expired code.'))\n cleanup(new Error('Login failed: code exchange failed.'))\n return\n }\n\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(SUCCESS_HTML)\n\n console.log(pc.green(`\\nLogged in successfully!`))\n console.log(pc.dim(`Tenant: ${creds.tenantName}`))\n\n cleanup(null, creds)\n })\n })\n\n let timeout: ReturnType<typeof setTimeout>\n let completed = false\n\n function cleanup(err: Error | null, result?: ExchangeResponse) {\n if (completed) return\n completed = true\n clearTimeout(timeout)\n server.closeAllConnections?.()\n server.close(() => {\n if (err) {\n reject(err)\n } else {\n resolve(result!)\n }\n })\n }\n\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address()\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to start local server.'))\n return\n }\n\n const port = addr.port\n\n timeout = setTimeout(() => {\n console.error(pc.red('\\nLogin timed out (5 minutes). Please try again.'))\n cleanup(new Error('Login timed out'))\n }, TIMEOUT_MS)\n\n const params = new URLSearchParams({ port: String(port), state })\n if (options?.tenantId) {\n params.set('tenantId', options.tenantId)\n }\n const loginUrl = `${webUrl}/cli-auth?${params.toString()}`\n\n console.log(pc.dim('Opening browser for login...'))\n console.log(pc.dim(`If the browser does not open, visit:\\n${loginUrl}`))\n openBrowser(loginUrl)\n })\n\n server.on('error', (err) => {\n reject(err)\n })\n })\n}\n"],"mappings":"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","names":["fs","path","pc","os","path","fs","pc"]}
#!/usr/bin/env node
// src/ai-docs.ts
function normalizeActiveCollections(collections) {
if (Array.isArray(collections?.active)) return collections.active;
return [];
}
var HTTP_MCP_TOOLS = [
"get-collection-schema",
"get-tenant-context",
"list-configurable-fields",
"update-field-config",
"sdk-get-recipe",
"sdk-search-docs",
"sdk-get-auth-setup",
"sdk-get-collection-pattern"
];
function generateClaudeMd(ctx) {
const featuresSection = ctx.features && ctx.features.length > 0 ? ctx.features.map((f) => `- ${f}`).join("\n") : "- See console";
const collectionsSection = ctx.collections && ctx.collections.length > 0 ? ctx.collections.join(", ") : "Run `01 schema list`";
return `# 01.software SDK \u2014 ${ctx.tenantName}
## Connection
- Publishable Key: \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` (env)
- Secret Key: \`SOFTWARE_SECRET_KEY\` (env)
- MCP: \`.mcp.json\`
- Agent discovery: \`https://01.software/llms.txt\`
- Agent skill: \`https://docs.01.software/skill.md\`
- OpenAPI: \`https://docs.01.software/api/openapi\`
## Active Features
${featuresSection}
## Active Collections
${collectionsSection}
## Hosted HTTP MCP Quick Reference
The hosted OAuth MCP server exposes these 8 tools:
${HTTP_MCP_TOOLS.map((tool) => `- \`${tool}\``).join("\n")}
Use \`get-collection-schema\` for live field introspection before generating code.
Use SDK/server code or local CLI stdio for collection reads, order workflows, and cart workflows.
## CLI
- \`01 query <collection>\` \u2014 query data
- \`01 schema show <collection>\` \u2014 inspect fields
- \`01 schema list\` \u2014 list all collections
- \`npx @01.software/cli mcp\` \u2014 start trusted local stdio MCP for full server-key workflows
## Initial Setup
Run \`/01software-field-config\` in Claude Code to configure field visibility for your use case.
Use \`get-collection-schema\` or \`01 schema show <collection>\` for live field introspection instead of relying on this document as a schema snapshot.
`;
}
function getSkillFiles() {
return [
{
dirName: "01software-field-config",
content: `---
name: 01software-field-config
description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP
disable-model-invocation: true
---
Steps:
1. Use \`list-configurable-fields\` to see current visibility settings
2. Identify fields/collections not needed for your use case
3. Use \`update-field-config\` to hide them
Common setups:
- Blog only: hide \`ecommerce\`, \`customers\`, \`videos\` collections
- Store: hide \`articles\`, \`documents\`, \`galleries\`, \`canvas\` collections
- Minimal: hide all except the collections you actively use
Ask me: "Show current field config" or "Hide ecommerce fields"
`
},
{
dirName: "01software-query",
content: `---
name: 01software-query
description: Query 01.software collections via SDK or CLI with filter, sort, and pagination examples
---
Hosted HTTP MCP does not expose collection query tools. Use the SDK/server client or CLI for collection queries.
HTTP MCP:
- Use \`get-collection-schema\` before assuming fields.
- Use \`sdk-get-collection-pattern\` for collection-specific code patterns.
CLI examples:
- \`01 query products --limit 10\`
- \`01 query orders --where '{"status":{"equals":"paid"}}'\`
- \`01 schema show products\` \u2014 inspect available fields
SDK (server):
\`\`\`typescript
const { docs } = await serverClient.collections.from('products').find({
where: { status: { equals: 'published' } },
sort: '-createdAt',
limit: 10,
})
\`\`\`
`
},
{
dirName: "01software-order-flow",
content: `---
name: 01software-order-flow
description: Order lifecycle reference \u2014 create, pay, fulfill, and return flows for 01.software
---
Complete order flow from creation to fulfillment.
States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2192 confirmed
1. Create the order from server code with the SDK/server client or Order API.
2. Mark paid only after the payment gateway confirms.
3. Create fulfillment with items and carrier/trackingNumber from a trusted server path.
4. Handle returns and refunds from a trusted server path.
Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid
Hosted HTTP MCP is for schema, tenant context, field config, and SDK guidance.
For trusted local stdio MCP workflows, run \`npx @01.software/cli mcp\`.
CLI: \`01 order create --help\` for full options
`
},
{
dirName: "01software-schema",
content: `---
name: 01software-schema
description: Inspect 01.software collection schemas and available fields via MCP or CLI
---
Inspect collection schemas to understand available fields.
MCP: use \`get-collection-schema\` with collection
CLI:
- \`01 schema list\` \u2014 all available collections
- \`01 schema show <collection>\` \u2014 field names, types, required status
Common collections: products, orders, customers, articles, documents, images
Use \`get-tenant-context\` to see which collections are active for this tenant.
`
}
];
}
async function fetchTenantContext(publishableKey, secretKey) {
try {
const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software";
const res = await fetch(`${apiUrl}/api/tenants/context`, {
headers: {
"X-Publishable-Key": publishableKey,
Authorization: `Bearer ${secretKey}`
}
});
if (!res.ok) return null;
const data = await res.json();
return {
tenantName: data.tenant?.name || "",
features: data.features || [],
collections: normalizeActiveCollections(data.collections)
};
} catch {
return null;
}
}
export {
generateClaudeMd,
getSkillFiles,
fetchTenantContext
};
//# sourceMappingURL=chunk-5K2CB2Y5.js.map
{"version":3,"sources":["../src/ai-docs.ts"],"sourcesContent":["export interface TenantContext {\n tenantName: string\n features?: string[]\n collections?: string[]\n}\n\ninterface TenantContextApiResponse {\n tenant?: { name?: string }\n features?: string[]\n collections?: { active?: string[]; inactive?: string[] }\n}\n\nfunction normalizeActiveCollections(\n collections: TenantContextApiResponse['collections'],\n): string[] {\n if (Array.isArray(collections?.active)) return collections.active\n return []\n}\n\nconst HTTP_MCP_TOOLS = [\n 'get-collection-schema',\n 'get-tenant-context',\n 'list-configurable-fields',\n 'update-field-config',\n 'sdk-get-recipe',\n 'sdk-search-docs',\n 'sdk-get-auth-setup',\n 'sdk-get-collection-pattern',\n] as const\n\n// ── CLAUDE.md ────────────────────────────────────────────────────────\n\nexport function generateClaudeMd(ctx: TenantContext): string {\n const featuresSection =\n ctx.features && ctx.features.length > 0\n ? ctx.features.map((f) => `- ${f}`).join('\\n')\n : '- See console'\n\n const collectionsSection =\n ctx.collections && ctx.collections.length > 0\n ? ctx.collections.join(', ')\n : 'Run `01 schema list`'\n\n return `# 01.software SDK — ${ctx.tenantName}\n\n## Connection\n- Publishable Key: \\`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\\` (env)\n- Secret Key: \\`SOFTWARE_SECRET_KEY\\` (env)\n- MCP: \\`.mcp.json\\`\n- Agent discovery: \\`https://01.software/llms.txt\\`\n- Agent skill: \\`https://docs.01.software/skill.md\\`\n- OpenAPI: \\`https://docs.01.software/api/openapi\\`\n\n## Active Features\n${featuresSection}\n\n## Active Collections\n${collectionsSection}\n\n## Hosted HTTP MCP Quick Reference\nThe hosted OAuth MCP server exposes these 8 tools:\n\n${HTTP_MCP_TOOLS.map((tool) => `- \\`${tool}\\``).join('\\n')}\n\nUse \\`get-collection-schema\\` for live field introspection before generating code.\nUse SDK/server code or local CLI stdio for collection reads, order workflows, and cart workflows.\n\n## CLI\n- \\`01 query <collection>\\` — query data\n- \\`01 schema show <collection>\\` — inspect fields\n- \\`01 schema list\\` — list all collections\n- \\`npx @01.software/cli mcp\\` — start trusted local stdio MCP for full server-key workflows\n\n## Initial Setup\nRun \\`/01software-field-config\\` in Claude Code to configure field visibility for your use case.\nUse \\`get-collection-schema\\` or \\`01 schema show <collection>\\` for live field introspection instead of relying on this document as a schema snapshot.\n`\n}\n\n// ── Skill files ──────────────────────────────────────────────────────\n\nexport function getSkillFiles(): Array<{ dirName: string; content: string }> {\n return [\n {\n dirName: '01software-field-config',\n content: `---\nname: 01software-field-config\ndescription: Configure field visibility for this tenant — hide unused collections and fields via MCP\ndisable-model-invocation: true\n---\n\nSteps:\n1. Use \\`list-configurable-fields\\` to see current visibility settings\n2. Identify fields/collections not needed for your use case\n3. Use \\`update-field-config\\` to hide them\n\nCommon setups:\n- Blog only: hide \\`ecommerce\\`, \\`customers\\`, \\`videos\\` collections\n- Store: hide \\`articles\\`, \\`documents\\`, \\`galleries\\`, \\`canvas\\` collections\n- Minimal: hide all except the collections you actively use\n\nAsk me: \"Show current field config\" or \"Hide ecommerce fields\"\n`,\n },\n {\n dirName: '01software-query',\n content: `---\nname: 01software-query\ndescription: Query 01.software collections via SDK or CLI with filter, sort, and pagination examples\n---\n\nHosted HTTP MCP does not expose collection query tools. Use the SDK/server client or CLI for collection queries.\n\nHTTP MCP:\n- Use \\`get-collection-schema\\` before assuming fields.\n- Use \\`sdk-get-collection-pattern\\` for collection-specific code patterns.\n\nCLI examples:\n- \\`01 query products --limit 10\\`\n- \\`01 query orders --where '{\"status\":{\"equals\":\"paid\"}}'\\`\n- \\`01 schema show products\\` — inspect available fields\n\nSDK (server):\n\\`\\`\\`typescript\nconst { docs } = await serverClient.collections.from('products').find({\n where: { status: { equals: 'published' } },\n sort: '-createdAt',\n limit: 10,\n})\n\\`\\`\\`\n`,\n },\n {\n dirName: '01software-order-flow',\n content: `---\nname: 01software-order-flow\ndescription: Order lifecycle reference — create, pay, fulfill, and return flows for 01.software\n---\n\nComplete order flow from creation to fulfillment.\n\nStates: pending → paid → preparing → shipped → delivered → confirmed\n\n1. Create the order from server code with the SDK/server client or Order API.\n2. Mark paid only after the payment gateway confirms.\n3. Create fulfillment with items and carrier/trackingNumber from a trusted server path.\n4. Handle returns and refunds from a trusted server path.\n\nFree orders: omit paymentId, totalAmount=0 → auto-transitions to paid\n\nHosted HTTP MCP is for schema, tenant context, field config, and SDK guidance.\nFor trusted local stdio MCP workflows, run \\`npx @01.software/cli mcp\\`.\nCLI: \\`01 order create --help\\` for full options\n`,\n },\n {\n dirName: '01software-schema',\n content: `---\nname: 01software-schema\ndescription: Inspect 01.software collection schemas and available fields via MCP or CLI\n---\n\nInspect collection schemas to understand available fields.\n\nMCP: use \\`get-collection-schema\\` with collection\n\nCLI:\n- \\`01 schema list\\` — all available collections\n- \\`01 schema show <collection>\\` — field names, types, required status\n\nCommon collections: products, orders, customers, articles, documents, images\nUse \\`get-tenant-context\\` to see which collections are active for this tenant.\n`,\n },\n ]\n}\n\n// ── Tenant context fetch ─────────────────────────────────────────────\n\nexport async function fetchTenantContext(\n publishableKey: string,\n secretKey: string,\n): Promise<TenantContext | null> {\n try {\n const apiUrl = process.env.SOFTWARE_API_URL || 'https://api.01.software'\n // secretKey is now an opaque sk01_/pat01_ bearer token — send it directly.\n const res = await fetch(`${apiUrl}/api/tenants/context`, {\n headers: {\n 'X-Publishable-Key': publishableKey,\n Authorization: `Bearer ${secretKey}`,\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as TenantContextApiResponse\n return {\n tenantName: data.tenant?.name || '',\n features: data.features || [],\n collections: normalizeActiveCollections(data.collections),\n }\n } catch {\n return null\n }\n}\n"],"mappings":";;;AAYA,SAAS,2BACP,aACU;AACV,MAAI,MAAM,QAAQ,aAAa,MAAM,EAAG,QAAO,YAAY;AAC3D,SAAO,CAAC;AACV;AAEA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,SAAS,iBAAiB,KAA4B;AAC3D,QAAM,kBACJ,IAAI,YAAY,IAAI,SAAS,SAAS,IAClC,IAAI,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,IAC3C;AAEN,QAAM,qBACJ,IAAI,eAAe,IAAI,YAAY,SAAS,IACxC,IAAI,YAAY,KAAK,IAAI,IACzB;AAEN,SAAO,4BAAuB,IAAI,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5C,eAAe;AAAA;AAAA;AAAA,EAGf,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKlB,eAAe,IAAI,CAAC,SAAS,OAAO,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAe1D;AAIO,SAAS,gBAA6D;AAC3E,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAyBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAoBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBX;AAAA,EACF;AACF;AAIA,eAAsB,mBACpB,gBACA,WAC+B;AAC/B,MAAI;AACF,UAAM,SAAS,QAAQ,IAAI,oBAAoB;AAE/C,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,wBAAwB;AAAA,MACvD,SAAS;AAAA,QACP,qBAAqB;AAAA,QACrB,eAAe,UAAU,SAAS;AAAA,MACpC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO;AAAA,MACL,YAAY,KAAK,QAAQ,QAAQ;AAAA,MACjC,UAAU,KAAK,YAAY,CAAC;AAAA,MAC5B,aAAa,2BAA2B,KAAK,WAAW;AAAA,IAC1D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
+1
-1

@@ -6,3 +6,3 @@ #!/usr/bin/env node

getSkillFiles
} from "./chunk-3MXIG3ZL.js";
} from "./chunk-5K2CB2Y5.js";
export {

@@ -9,0 +9,0 @@ fetchTenantContext,

@@ -5,4 +5,4 @@ #!/usr/bin/env node

init
} from "./chunk-3RQE6YVO.js";
import "./chunk-3MXIG3ZL.js";
} from "./chunk-2IGKOSK7.js";
import "./chunk-5K2CB2Y5.js";
import "./chunk-UA7WNT2F.js";

@@ -203,3 +203,3 @@ import "./chunk-TBGKXE3Q.js";

4. Docs: https://01.software/docs/developers/sdk/client
4. Docs: https://docs.01.software/docs/developers/sdk/client
`;

@@ -311,2 +311,8 @@ async function main() {

console.log(pc.dim(" MCP config uses OAuth discovery."));
console.log(pc.dim(" Agent discovery flow:"));
console.log(pc.cyan(" 1. Read https://01.software/llms.txt"));
console.log(pc.cyan(" 2. Connect https://mcp.01.software/mcp"));
console.log(pc.cyan(" 3. Read https://docs.01.software/skill.md"));
console.log(pc.cyan(" 4. Inspect https://docs.01.software/api/openapi"));
console.log(pc.cyan(" 5. Run local lint, typecheck, tests, and build"));
console.log();

@@ -313,0 +319,0 @@ }

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/prompts.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { detectProject } from './detect'\nimport type { ProjectEnv } from './detect'\nimport { promptUser } from './prompts'\nimport { init } from './init'\n\nconst ENV_LABELS: Record<ProjectEnv, string> = {\n nextjs: 'Next.js',\n 'react-vite': 'React + Vite',\n 'react-cra': 'React + Webpack/CRA',\n vanilla: 'Vanilla JS',\n node: 'Node.js',\n edge: 'Edge runtime',\n other: 'Other framework',\n}\n\n// Manual setup guide for unsupported frameworks\nconst OTHER_FRAMEWORK_GUIDE = `\n Astro, Remix, SvelteKit, and other meta-frameworks have their own\n environment conventions. Manual setup:\n\n 1. Install the SDK:\n npm install @01.software/sdk\n\n 2. Browser client (client islands / RSC):\n import { createClient } from '@01.software/sdk'\n export const client = createClient({ publishableKey: 'YOUR_PUBLISHABLE_KEY' })\n\n 3. Server client (SSR / loaders / endpoints):\n import { createServerClient } from '@01.software/sdk'\n export const serverClient = createServerClient({\n publishableKey: process.env.PUBLIC_SOFTWARE_PUBLISHABLE_KEY!, // prefix varies by framework\n secretKey: process.env.SOFTWARE_SECRET_KEY!,\n })\n\n 4. Docs: https://01.software/docs/developers/sdk/client\n`\n\nasync function main() {\n const cwd = process.cwd()\n\n console.log()\n console.log(pc.bold(' @01.software/init'))\n console.log(pc.dim(' Initialize 01.software SDK in your project'))\n console.log()\n\n // 1. Detect project\n const info = detectProject(cwd)\n\n if (!info.hasPackageJson || info.parseError) {\n if (info.parseError) {\n console.log(pc.red(' Could not parse package.json (invalid JSON).'))\n console.log(pc.dim(' Fix the syntax error and try again.'))\n } else {\n console.log(pc.red(' No package.json found in the current directory.'))\n console.log(pc.dim(' Run this command inside an existing project.'))\n }\n console.log()\n process.exit(1)\n }\n\n // Show detected environment\n const detectedParts: string[] = [ENV_LABELS[info.env]]\n if (info.packageManager) detectedParts.push(info.packageManager)\n if (info.srcDir) detectedParts.push('src/')\n\n if (info.env !== 'node') {\n // Only show detected label when it's unambiguous\n console.log(pc.dim(` Detected: ${detectedParts.join(' / ')}`))\n console.log()\n }\n\n try {\n // 2. Prompt\n const answers = await promptUser(info.hasSdk, info.env, info.packageManager)\n if (!answers) {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n\n // \"Other\" framework — print guide and exit\n if (answers.env === 'other') {\n console.log(pc.yellow(' Manual setup required for your framework:'))\n console.log(OTHER_FRAMEWORK_GUIDE)\n process.exit(0)\n }\n\n // Resolve package manager from detection or user selection\n const resolvedPm = info.packageManager ?? answers.packageManager ?? 'npm'\n const resolvedInfo = { ...info, packageManager: resolvedPm }\n\n // 3. Init\n console.log()\n const result = await init(cwd, resolvedInfo, answers)\n\n // 4. Next steps\n const env = answers.env\n const run = resolvedPm === 'npm' ? 'npm run' : resolvedPm\n\n console.log()\n console.log(pc.green(' Done!'))\n console.log()\n console.log(' Next steps:')\n console.log()\n\n if (result.installFailed) {\n console.log(pc.yellow(' Install the SDK manually:'))\n console.log(pc.cyan(` ${result.installCmd}`))\n console.log()\n }\n\n if (env === 'nextjs') {\n console.log(pc.dim(' Add QueryProvider to your root layout:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from '@/lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider>{children}</QueryProvider>'))\n console.log()\n console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from '@/lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\n console.log()\n } else if (env === 'react-vite' || env === 'react-cra') {\n console.log(pc.dim(' Wrap your app entry with QueryProvider:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from './lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider><App /></QueryProvider>'))\n console.log()\n console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from './lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\n console.log()\n } else if (env === 'vanilla') {\n console.log(pc.dim(' Replace YOUR_PUBLISHABLE_KEY in lib/software/client.ts'))\n console.log()\n console.log(pc.cyan(\" import { client } from './lib/software/client'\"))\n console.log(pc.cyan(\" const articles = await client.collections.from('articles').find()\"))\n console.log()\n console.log(pc.dim(' Optional: wire the analytics helper in lib/software/analytics.ts'))\n console.log()\n } else if (env === 'node') {\n console.log(pc.dim(' Use the server client:'))\n console.log()\n console.log(pc.cyan(\" import { serverClient } from './lib/software/server'\"))\n console.log(pc.cyan(\" const articles = await serverClient.collections.from('articles').find()\"))\n console.log()\n } else if (env === 'edge') {\n console.log(pc.dim(' Pass your env bindings to createEdgeClient():'))\n console.log()\n console.log(pc.cyan(\" import { createEdgeClient } from './lib/software/server'\"))\n console.log(pc.cyan(' const serverClient = createEdgeClient(env.PUBLISHABLE_KEY, env.SECRET_KEY)'))\n console.log()\n }\n\n // Prefer keys returned by `init()` (e.g. from browser-auth) over the\n // original prompt answers. Browser-auth credentials are written directly\n // and must not leave the user looking at stale \"Update .env\" instructions.\n const effectivePublishableKey = result.publishableKey ?? answers.publishableKey\n const effectiveSecretKey = result.secretKey ?? answers.secretKey\n const missingPublishableKey = env !== 'vanilla' && !effectivePublishableKey\n const missingSecretKey = (env === 'nextjs' || env === 'node') && !effectiveSecretKey\n if (missingPublishableKey || missingSecretKey) {\n console.log(pc.dim(' Update .env with your SDK credentials'))\n console.log()\n }\n if (answers.aiTools.length > 0) {\n console.log(pc.dim(' MCP config uses OAuth discovery.'))\n console.log()\n }\n if (env !== 'vanilla') {\n console.log(pc.cyan(` ${run} dev`))\n console.log()\n }\n } catch (error) {\n if (error instanceof Error && error.message === 'cancelled') {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n console.error(pc.red(' Error:'), error)\n process.exit(1)\n }\n}\n\nmain()\n","import prompts from 'prompts'\nimport type { PackageManager, ProjectEnv } from './detect'\n\nexport type AiTool =\n | 'claude'\n | 'cursor'\n | 'vscode'\n | 'windsurf'\n | 'codex'\n | 'gemini'\n\nexport type AuthMethod = 'browser' | 'manual' | 'skip'\n\nexport interface InitAnswers {\n env: ProjectEnv\n publishableKey: string\n secretKey: string\n aiTools: AiTool[]\n authMethod: AuthMethod\n packageManager?: PackageManager\n}\n\n// Envs that need the user to disambiguate (couldn't be auto-detected)\nconst AMBIGUOUS_ENVS: ProjectEnv[] = ['node']\n\nexport async function promptUser(\n hasSdk: boolean,\n detectedEnv: ProjectEnv,\n detectedPm: PackageManager | null,\n): Promise<InitAnswers | null> {\n const onCancel = () => {\n throw new Error('cancelled')\n }\n\n // Confirm re-init if SDK already installed\n if (hasSdk) {\n const { proceed } = await prompts(\n {\n type: 'confirm',\n name: 'proceed',\n message: '@01.software/sdk is already installed. Re-initialize?',\n initial: false,\n },\n { onCancel },\n )\n if (!proceed) return null\n }\n\n // Ask for environment when auto-detection is ambiguous\n let env = detectedEnv\n if (AMBIGUOUS_ENVS.includes(detectedEnv)) {\n const { selectedEnv } = await prompts(\n {\n type: 'select',\n name: 'selectedEnv',\n message: 'Which environment are you using?',\n choices: [\n {\n title: 'Next.js',\n description: 'Full-stack React (client + server)',\n value: 'nextjs',\n },\n {\n title: 'React + Vite',\n description: 'Client-only SPA with Vite',\n value: 'react-vite',\n },\n {\n title: 'React + Webpack / other',\n description: 'Client-only SPA, CRA or custom bundler',\n value: 'react-cra',\n },\n {\n title: 'Vanilla JS',\n description: 'Browser app without a framework',\n value: 'vanilla',\n },\n {\n title: 'Node.js / Bun / Deno',\n description: 'Server-only, no browser client',\n value: 'node',\n },\n {\n title: 'Edge runtime',\n description: 'Cloudflare Workers, Vercel Edge Functions',\n value: 'edge',\n },\n {\n title: 'Other (Astro / Remix / SvelteKit…)',\n description: 'Print manual setup guide for your framework',\n value: 'other',\n },\n ],\n },\n { onCancel },\n )\n env = selectedEnv as ProjectEnv\n }\n\n // \"Other\" frameworks — we can't scaffold correctly; exit with guidance\n if (env === 'other') {\n return { env, publishableKey: '', secretKey: '', aiTools: [], authMethod: 'skip', packageManager: undefined }\n }\n\n // Ask for package manager if no lockfile detected\n let packageManager: PackageManager | undefined\n if (!detectedPm) {\n const { pm } = await prompts(\n {\n type: 'select',\n name: 'pm',\n message: 'Which package manager do you use?',\n choices: [\n { title: 'npm', value: 'npm' },\n { title: 'pnpm', value: 'pnpm' },\n { title: 'yarn', value: 'yarn' },\n { title: 'bun', value: 'bun' },\n ],\n },\n { onCancel },\n )\n packageManager = pm\n }\n\n const needsSecretKey = env === 'nextjs' || env === 'node' || env === 'edge'\n\n const keyPrompts: prompts.PromptObject[] = []\n\n // Vanilla JS doesn't use env vars — no prompts for keys\n if (env !== 'vanilla') {\n keyPrompts.push({\n type: 'text',\n name: 'publishableKey',\n message: 'Publishable Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n if (needsSecretKey) {\n keyPrompts.push({\n type: 'text',\n name: 'secretKey',\n message: 'Secret Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n // AI tool multi-select (empty selection = skip)\n const { selectedTools } = await prompts(\n {\n type: 'multiselect',\n name: 'selectedTools',\n message: 'Connect AI tools (leave empty to skip):',\n choices: [\n { title: 'Claude Code', description: '.mcp.json + .claude/ docs', value: 'claude' },\n { title: 'Cursor', description: '.cursor/mcp.json', value: 'cursor' },\n { title: 'VS Code', description: '.vscode/mcp.json', value: 'vscode' },\n { title: 'Windsurf', description: '~/.codeium/windsurf/mcp_config.json', value: 'windsurf' },\n { title: 'Codex CLI', description: '~/.codex/config.toml', value: 'codex' },\n { title: 'Gemini CLI', description: '~/.gemini/settings.json', value: 'gemini' },\n ],\n hint: 'space to select, enter to confirm',\n },\n { onCancel },\n )\n\n const aiTools: AiTool[] = Array.isArray(selectedTools) ? (selectedTools as AiTool[]) : []\n\n // Auth method — only if tools selected and env supports secrets\n let authMethod: AuthMethod = 'skip'\n let keys: Record<string, string> = {}\n\n if (aiTools.length > 0 && env !== 'vanilla') {\n const { method } = await prompts(\n {\n type: 'select',\n name: 'method',\n message: 'SDK credentials:',\n choices: [\n { title: 'Browser login (recommended)', value: 'browser' },\n { title: 'Enter manually', value: 'manual' },\n { title: 'Skip for now', value: 'skip' },\n ],\n },\n { onCancel },\n )\n authMethod = (method as AuthMethod) ?? 'skip'\n\n if (authMethod === 'manual') {\n keys = await prompts(keyPrompts, { onCancel })\n }\n } else if (env !== 'vanilla') {\n // No AI tools selected — still collect keys from the key prompts if any were defined\n if (keyPrompts.length > 0) {\n keys = await prompts(keyPrompts, { onCancel })\n }\n authMethod = 'skip'\n }\n\n return {\n env,\n publishableKey: authMethod === 'browser' ? '' : (keys.publishableKey ?? ''),\n secretKey: authMethod === 'browser' ? '' : (keys.secretKey ?? ''),\n aiTools,\n authMethod,\n packageManager,\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,QAAQ;;;ACAf,OAAO,aAAa;AAuBpB,IAAM,iBAA+B,CAAC,MAAM;AAE5C,eAAsB,WACpB,QACA,aACA,YAC6B;AAC7B,QAAM,WAAW,MAAM;AACrB,UAAM,IAAI,MAAM,WAAW;AAAA,EAC7B;AAGA,MAAI,QAAQ;AACV,UAAM,EAAE,QAAQ,IAAI,MAAM;AAAA,MACxB;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,MACA,EAAE,SAAS;AAAA,IACb;AACA,QAAI,CAAC,QAAS,QAAO;AAAA,EACvB;AAGA,MAAI,MAAM;AACV,MAAI,eAAe,SAAS,WAAW,GAAG;AACxC,UAAM,EAAE,YAAY,IAAI,MAAM;AAAA,MAC5B;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,UACP;AAAA,YACE,OAAO;AAAA,YACP,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,aAAa;AAAA,YACb,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,MACA,EAAE,SAAS;AAAA,IACb;AACA,UAAM;AAAA,EACR;AAGA,MAAI,QAAQ,SAAS;AACnB,WAAO,EAAE,KAAK,gBAAgB,IAAI,WAAW,IAAI,SAAS,CAAC,GAAG,YAAY,QAAQ,gBAAgB,OAAU;AAAA,EAC9G;AAGA,MAAI;AACJ,MAAI,CAAC,YAAY;AACf,UAAM,EAAE,GAAG,IAAI,MAAM;AAAA,MACnB;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,UACP,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,UAC7B,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,UAC/B,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,UAC/B,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,QAC/B;AAAA,MACF;AAAA,MACA,EAAE,SAAS;AAAA,IACb;AACA,qBAAiB;AAAA,EACnB;AAEA,QAAM,iBAAiB,QAAQ,YAAY,QAAQ,UAAU,QAAQ;AAErE,QAAM,aAAqC,CAAC;AAG5C,MAAI,QAAQ,WAAW;AACrB,eAAW,KAAK;AAAA,MACd,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,MAAI,gBAAgB;AAClB,eAAW,KAAK;AAAA,MACd,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,QAAM,EAAE,cAAc,IAAI,MAAM;AAAA,IAC9B;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,OAAO,eAAe,aAAa,6BAA6B,OAAO,SAAS;AAAA,QAClF,EAAE,OAAO,UAAU,aAAa,oBAAoB,OAAO,SAAS;AAAA,QACpE,EAAE,OAAO,WAAW,aAAa,oBAAoB,OAAO,SAAS;AAAA,QACrE,EAAE,OAAO,YAAY,aAAa,uCAAuC,OAAO,WAAW;AAAA,QAC3F,EAAE,OAAO,aAAa,aAAa,wBAAwB,OAAO,QAAQ;AAAA,QAC1E,EAAE,OAAO,cAAc,aAAa,2BAA2B,OAAO,SAAS;AAAA,MACjF;AAAA,MACA,MAAM;AAAA,IACR;AAAA,IACA,EAAE,SAAS;AAAA,EACb;AAEA,QAAM,UAAoB,MAAM,QAAQ,aAAa,IAAK,gBAA6B,CAAC;AAGxF,MAAI,aAAyB;AAC7B,MAAI,OAA+B,CAAC;AAEpC,MAAI,QAAQ,SAAS,KAAK,QAAQ,WAAW;AAC3C,UAAM,EAAE,OAAO,IAAI,MAAM;AAAA,MACvB;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,UACP,EAAE,OAAO,+BAA+B,OAAO,UAAU;AAAA,UACzD,EAAE,OAAO,kBAAkB,OAAO,SAAS;AAAA,UAC3C,EAAE,OAAO,gBAAgB,OAAO,OAAO;AAAA,QACzC;AAAA,MACF;AAAA,MACA,EAAE,SAAS;AAAA,IACb;AACA,iBAAc,UAAyB;AAEvC,QAAI,eAAe,UAAU;AAC3B,aAAO,MAAM,QAAQ,YAAY,EAAE,SAAS,CAAC;AAAA,IAC/C;AAAA,EACF,WAAW,QAAQ,WAAW;AAE5B,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,MAAM,QAAQ,YAAY,EAAE,SAAS,CAAC;AAAA,IAC/C;AACA,iBAAa;AAAA,EACf;AAEA,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,eAAe,YAAY,KAAM,KAAK,kBAAkB;AAAA,IACxE,WAAW,eAAe,YAAY,KAAM,KAAK,aAAa;AAAA,IAC9D;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ADzMA,IAAM,aAAyC;AAAA,EAC7C,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,aAAa;AAAA,EACb,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAGA,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqB9B,eAAe,OAAO;AACpB,QAAM,MAAM,QAAQ,IAAI;AAExB,UAAQ,IAAI;AACZ,UAAQ,IAAI,GAAG,KAAK,qBAAqB,CAAC;AAC1C,UAAQ,IAAI,GAAG,IAAI,8CAA8C,CAAC;AAClE,UAAQ,IAAI;AAGZ,QAAM,OAAO,cAAc,GAAG;AAE9B,MAAI,CAAC,KAAK,kBAAkB,KAAK,YAAY;AAC3C,QAAI,KAAK,YAAY;AACnB,cAAQ,IAAI,GAAG,IAAI,gDAAgD,CAAC;AACpE,cAAQ,IAAI,GAAG,IAAI,uCAAuC,CAAC;AAAA,IAC7D,OAAO;AACL,cAAQ,IAAI,GAAG,IAAI,mDAAmD,CAAC;AACvE,cAAQ,IAAI,GAAG,IAAI,gDAAgD,CAAC;AAAA,IACtE;AACA,YAAQ,IAAI;AACZ,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,gBAA0B,CAAC,WAAW,KAAK,GAAG,CAAC;AACrD,MAAI,KAAK,eAAgB,eAAc,KAAK,KAAK,cAAc;AAC/D,MAAI,KAAK,OAAQ,eAAc,KAAK,MAAM;AAE1C,MAAI,KAAK,QAAQ,QAAQ;AAEvB,YAAQ,IAAI,GAAG,IAAI,eAAe,cAAc,KAAK,KAAK,CAAC,EAAE,CAAC;AAC9D,YAAQ,IAAI;AAAA,EACd;AAEA,MAAI;AAEF,UAAM,UAAU,MAAM,WAAW,KAAK,QAAQ,KAAK,KAAK,KAAK,cAAc;AAC3E,QAAI,CAAC,SAAS;AACZ,cAAQ,IAAI,GAAG,OAAO,cAAc,CAAC;AACrC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI,QAAQ,QAAQ,SAAS;AAC3B,cAAQ,IAAI,GAAG,OAAO,6CAA6C,CAAC;AACpE,cAAQ,IAAI,qBAAqB;AACjC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,aAAa,KAAK,kBAAkB,QAAQ,kBAAkB;AACpE,UAAM,eAAe,EAAE,GAAG,MAAM,gBAAgB,WAAW;AAG3D,YAAQ,IAAI;AACZ,UAAM,SAAS,MAAM,KAAK,KAAK,cAAc,OAAO;AAGpD,UAAM,MAAM,QAAQ;AACpB,UAAM,MAAM,eAAe,QAAQ,YAAY;AAE/C,YAAQ,IAAI;AACZ,YAAQ,IAAI,GAAG,MAAM,SAAS,CAAC;AAC/B,YAAQ,IAAI;AACZ,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI;AAEZ,QAAI,OAAO,eAAe;AACxB,cAAQ,IAAI,GAAG,OAAO,6BAA6B,CAAC;AACpD,cAAQ,IAAI,GAAG,KAAK,OAAO,OAAO,UAAU,EAAE,CAAC;AAC/C,cAAQ,IAAI;AAAA,IACd;AAEA,QAAI,QAAQ,UAAU;AACpB,cAAQ,IAAI,GAAG,IAAI,0CAA0C,CAAC;AAC9D,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,KAAK,mEAAmE,CAAC;AACxF,cAAQ,IAAI,GAAG,KAAK,+CAA+C,CAAC;AACpE,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,IAAI,gEAAgE,CAAC;AACpF,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,KAAK,0DAA0D,CAAC;AAC/E,cAAQ,IAAI,GAAG,KAAK,+BAA+B,CAAC;AACpD,cAAQ,IAAI;AAAA,IACd,WAAW,QAAQ,gBAAgB,QAAQ,aAAa;AACtD,cAAQ,IAAI,GAAG,IAAI,2CAA2C,CAAC;AAC/D,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,KAAK,mEAAmE,CAAC;AACxF,cAAQ,IAAI,GAAG,KAAK,4CAA4C,CAAC;AACjE,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,IAAI,gEAAgE,CAAC;AACpF,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,KAAK,0DAA0D,CAAC;AAC/E,cAAQ,IAAI,GAAG,KAAK,+BAA+B,CAAC;AACpD,cAAQ,IAAI;AAAA,IACd,WAAW,QAAQ,WAAW;AAC5B,cAAQ,IAAI,GAAG,IAAI,0DAA0D,CAAC;AAC9E,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,KAAK,oDAAoD,CAAC;AACzE,cAAQ,IAAI,GAAG,KAAK,uEAAuE,CAAC;AAC5F,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,IAAI,oEAAoE,CAAC;AACxF,cAAQ,IAAI;AAAA,IACd,WAAW,QAAQ,QAAQ;AACzB,cAAQ,IAAI,GAAG,IAAI,0BAA0B,CAAC;AAC9C,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,KAAK,0DAA0D,CAAC;AAC/E,cAAQ,IAAI,GAAG,KAAK,6EAA6E,CAAC;AAClG,cAAQ,IAAI;AAAA,IACd,WAAW,QAAQ,QAAQ;AACzB,cAAQ,IAAI,GAAG,IAAI,iDAAiD,CAAC;AACrE,cAAQ,IAAI;AACZ,cAAQ,IAAI,GAAG,KAAK,8DAA8D,CAAC;AACnF,cAAQ,IAAI,GAAG,KAAK,gFAAgF,CAAC;AACrG,cAAQ,IAAI;AAAA,IACd;AAKA,UAAM,0BAA0B,OAAO,kBAAkB,QAAQ;AACjE,UAAM,qBAAqB,OAAO,aAAa,QAAQ;AACvD,UAAM,wBAAwB,QAAQ,aAAa,CAAC;AACpD,UAAM,oBAAoB,QAAQ,YAAY,QAAQ,WAAW,CAAC;AAClE,QAAI,yBAAyB,kBAAkB;AAC7C,cAAQ,IAAI,GAAG,IAAI,yCAAyC,CAAC;AAC7D,cAAQ,IAAI;AAAA,IACd;AACA,QAAI,QAAQ,QAAQ,SAAS,GAAG;AAC9B,cAAQ,IAAI,GAAG,IAAI,oCAAoC,CAAC;AACxD,cAAQ,IAAI;AAAA,IACd;AACA,QAAI,QAAQ,WAAW;AACrB,cAAQ,IAAI,GAAG,KAAK,OAAO,GAAG,MAAM,CAAC;AACrC,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,MAAM,YAAY,aAAa;AAC3D,cAAQ,IAAI,GAAG,OAAO,cAAc,CAAC;AACrC,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,MAAM,GAAG,IAAI,UAAU,GAAG,KAAK;AACvC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;","names":[]}
{"version":3,"sources":["../src/index.ts","../src/prompts.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { detectProject } from './detect'\nimport type { ProjectEnv } from './detect'\nimport { promptUser } from './prompts'\nimport { init } from './init'\n\nconst ENV_LABELS: Record<ProjectEnv, string> = {\n nextjs: 'Next.js',\n 'react-vite': 'React + Vite',\n 'react-cra': 'React + Webpack/CRA',\n vanilla: 'Vanilla JS',\n node: 'Node.js',\n edge: 'Edge runtime',\n other: 'Other framework',\n}\n\n// Manual setup guide for unsupported frameworks\nconst OTHER_FRAMEWORK_GUIDE = `\n Astro, Remix, SvelteKit, and other meta-frameworks have their own\n environment conventions. Manual setup:\n\n 1. Install the SDK:\n npm install @01.software/sdk\n\n 2. Browser client (client islands / RSC):\n import { createClient } from '@01.software/sdk'\n export const client = createClient({ publishableKey: 'YOUR_PUBLISHABLE_KEY' })\n\n 3. Server client (SSR / loaders / endpoints):\n import { createServerClient } from '@01.software/sdk'\n export const serverClient = createServerClient({\n publishableKey: process.env.PUBLIC_SOFTWARE_PUBLISHABLE_KEY!, // prefix varies by framework\n secretKey: process.env.SOFTWARE_SECRET_KEY!,\n })\n\n 4. Docs: https://docs.01.software/docs/developers/sdk/client\n`\n\nasync function main() {\n const cwd = process.cwd()\n\n console.log()\n console.log(pc.bold(' @01.software/init'))\n console.log(pc.dim(' Initialize 01.software SDK in your project'))\n console.log()\n\n // 1. Detect project\n const info = detectProject(cwd)\n\n if (!info.hasPackageJson || info.parseError) {\n if (info.parseError) {\n console.log(pc.red(' Could not parse package.json (invalid JSON).'))\n console.log(pc.dim(' Fix the syntax error and try again.'))\n } else {\n console.log(pc.red(' No package.json found in the current directory.'))\n console.log(pc.dim(' Run this command inside an existing project.'))\n }\n console.log()\n process.exit(1)\n }\n\n // Show detected environment\n const detectedParts: string[] = [ENV_LABELS[info.env]]\n if (info.packageManager) detectedParts.push(info.packageManager)\n if (info.srcDir) detectedParts.push('src/')\n\n if (info.env !== 'node') {\n // Only show detected label when it's unambiguous\n console.log(pc.dim(` Detected: ${detectedParts.join(' / ')}`))\n console.log()\n }\n\n try {\n // 2. Prompt\n const answers = await promptUser(info.hasSdk, info.env, info.packageManager)\n if (!answers) {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n\n // \"Other\" framework — print guide and exit\n if (answers.env === 'other') {\n console.log(pc.yellow(' Manual setup required for your framework:'))\n console.log(OTHER_FRAMEWORK_GUIDE)\n process.exit(0)\n }\n\n // Resolve package manager from detection or user selection\n const resolvedPm = info.packageManager ?? answers.packageManager ?? 'npm'\n const resolvedInfo = { ...info, packageManager: resolvedPm }\n\n // 3. Init\n console.log()\n const result = await init(cwd, resolvedInfo, answers)\n\n // 4. Next steps\n const env = answers.env\n const run = resolvedPm === 'npm' ? 'npm run' : resolvedPm\n\n console.log()\n console.log(pc.green(' Done!'))\n console.log()\n console.log(' Next steps:')\n console.log()\n\n if (result.installFailed) {\n console.log(pc.yellow(' Install the SDK manually:'))\n console.log(pc.cyan(` ${result.installCmd}`))\n console.log()\n }\n\n if (env === 'nextjs') {\n console.log(pc.dim(' Add QueryProvider to your root layout:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from '@/lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider>{children}</QueryProvider>'))\n console.log()\n console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from '@/lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\n console.log()\n } else if (env === 'react-vite' || env === 'react-cra') {\n console.log(pc.dim(' Wrap your app entry with QueryProvider:'))\n console.log()\n console.log(pc.cyan(\" import { QueryProvider } from './lib/software/query-provider'\"))\n console.log(pc.cyan(' <QueryProvider><App /></QueryProvider>'))\n console.log()\n console.log(pc.dim(' Optional: start browser analytics with the generated helper:'))\n console.log()\n console.log(pc.cyan(\" import { analytics } from './lib/software/analytics'\"))\n console.log(pc.cyan(\" analytics.track('signup')\"))\n console.log()\n } else if (env === 'vanilla') {\n console.log(pc.dim(' Replace YOUR_PUBLISHABLE_KEY in lib/software/client.ts'))\n console.log()\n console.log(pc.cyan(\" import { client } from './lib/software/client'\"))\n console.log(pc.cyan(\" const articles = await client.collections.from('articles').find()\"))\n console.log()\n console.log(pc.dim(' Optional: wire the analytics helper in lib/software/analytics.ts'))\n console.log()\n } else if (env === 'node') {\n console.log(pc.dim(' Use the server client:'))\n console.log()\n console.log(pc.cyan(\" import { serverClient } from './lib/software/server'\"))\n console.log(pc.cyan(\" const articles = await serverClient.collections.from('articles').find()\"))\n console.log()\n } else if (env === 'edge') {\n console.log(pc.dim(' Pass your env bindings to createEdgeClient():'))\n console.log()\n console.log(pc.cyan(\" import { createEdgeClient } from './lib/software/server'\"))\n console.log(pc.cyan(' const serverClient = createEdgeClient(env.PUBLISHABLE_KEY, env.SECRET_KEY)'))\n console.log()\n }\n\n // Prefer keys returned by `init()` (e.g. from browser-auth) over the\n // original prompt answers. Browser-auth credentials are written directly\n // and must not leave the user looking at stale \"Update .env\" instructions.\n const effectivePublishableKey = result.publishableKey ?? answers.publishableKey\n const effectiveSecretKey = result.secretKey ?? answers.secretKey\n const missingPublishableKey = env !== 'vanilla' && !effectivePublishableKey\n const missingSecretKey = (env === 'nextjs' || env === 'node') && !effectiveSecretKey\n if (missingPublishableKey || missingSecretKey) {\n console.log(pc.dim(' Update .env with your SDK credentials'))\n console.log()\n }\n if (answers.aiTools.length > 0) {\n console.log(pc.dim(' MCP config uses OAuth discovery.'))\n console.log(pc.dim(' Agent discovery flow:'))\n console.log(pc.cyan(' 1. Read https://01.software/llms.txt'))\n console.log(pc.cyan(' 2. Connect https://mcp.01.software/mcp'))\n console.log(pc.cyan(' 3. Read https://docs.01.software/skill.md'))\n console.log(pc.cyan(' 4. Inspect https://docs.01.software/api/openapi'))\n console.log(pc.cyan(' 5. Run local lint, typecheck, tests, and build'))\n console.log()\n }\n if (env !== 'vanilla') {\n console.log(pc.cyan(` ${run} dev`))\n console.log()\n }\n } catch (error) {\n if (error instanceof Error && error.message === 'cancelled') {\n console.log(pc.yellow(' Cancelled.'))\n process.exit(0)\n }\n console.error(pc.red(' Error:'), error)\n process.exit(1)\n }\n}\n\nmain()\n","import prompts from 'prompts'\nimport type { PackageManager, ProjectEnv } from './detect'\n\nexport type AiTool =\n | 'claude'\n | 'cursor'\n | 'vscode'\n | 'windsurf'\n | 'codex'\n | 'gemini'\n\nexport type AuthMethod = 'browser' | 'manual' | 'skip'\n\nexport interface InitAnswers {\n env: ProjectEnv\n publishableKey: string\n secretKey: string\n aiTools: AiTool[]\n authMethod: AuthMethod\n packageManager?: PackageManager\n}\n\n// Envs that need the user to disambiguate (couldn't be auto-detected)\nconst AMBIGUOUS_ENVS: ProjectEnv[] = ['node']\n\nexport async function promptUser(\n hasSdk: boolean,\n detectedEnv: ProjectEnv,\n detectedPm: PackageManager | null,\n): Promise<InitAnswers | null> {\n const onCancel = () => {\n throw new Error('cancelled')\n }\n\n // Confirm re-init if SDK already installed\n if (hasSdk) {\n const { proceed } = await prompts(\n {\n type: 'confirm',\n name: 'proceed',\n message: '@01.software/sdk is already installed. Re-initialize?',\n initial: false,\n },\n { onCancel },\n )\n if (!proceed) return null\n }\n\n // Ask for environment when auto-detection is ambiguous\n let env = detectedEnv\n if (AMBIGUOUS_ENVS.includes(detectedEnv)) {\n const { selectedEnv } = await prompts(\n {\n type: 'select',\n name: 'selectedEnv',\n message: 'Which environment are you using?',\n choices: [\n {\n title: 'Next.js',\n description: 'Full-stack React (client + server)',\n value: 'nextjs',\n },\n {\n title: 'React + Vite',\n description: 'Client-only SPA with Vite',\n value: 'react-vite',\n },\n {\n title: 'React + Webpack / other',\n description: 'Client-only SPA, CRA or custom bundler',\n value: 'react-cra',\n },\n {\n title: 'Vanilla JS',\n description: 'Browser app without a framework',\n value: 'vanilla',\n },\n {\n title: 'Node.js / Bun / Deno',\n description: 'Server-only, no browser client',\n value: 'node',\n },\n {\n title: 'Edge runtime',\n description: 'Cloudflare Workers, Vercel Edge Functions',\n value: 'edge',\n },\n {\n title: 'Other (Astro / Remix / SvelteKit…)',\n description: 'Print manual setup guide for your framework',\n value: 'other',\n },\n ],\n },\n { onCancel },\n )\n env = selectedEnv as ProjectEnv\n }\n\n // \"Other\" frameworks — we can't scaffold correctly; exit with guidance\n if (env === 'other') {\n return { env, publishableKey: '', secretKey: '', aiTools: [], authMethod: 'skip', packageManager: undefined }\n }\n\n // Ask for package manager if no lockfile detected\n let packageManager: PackageManager | undefined\n if (!detectedPm) {\n const { pm } = await prompts(\n {\n type: 'select',\n name: 'pm',\n message: 'Which package manager do you use?',\n choices: [\n { title: 'npm', value: 'npm' },\n { title: 'pnpm', value: 'pnpm' },\n { title: 'yarn', value: 'yarn' },\n { title: 'bun', value: 'bun' },\n ],\n },\n { onCancel },\n )\n packageManager = pm\n }\n\n const needsSecretKey = env === 'nextjs' || env === 'node' || env === 'edge'\n\n const keyPrompts: prompts.PromptObject[] = []\n\n // Vanilla JS doesn't use env vars — no prompts for keys\n if (env !== 'vanilla') {\n keyPrompts.push({\n type: 'text',\n name: 'publishableKey',\n message: 'Publishable Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n if (needsSecretKey) {\n keyPrompts.push({\n type: 'text',\n name: 'secretKey',\n message: 'Secret Key (optional, saved to .env)',\n initial: '',\n })\n }\n\n // AI tool multi-select (empty selection = skip)\n const { selectedTools } = await prompts(\n {\n type: 'multiselect',\n name: 'selectedTools',\n message: 'Connect AI tools (leave empty to skip):',\n choices: [\n { title: 'Claude Code', description: '.mcp.json + .claude/ docs', value: 'claude' },\n { title: 'Cursor', description: '.cursor/mcp.json', value: 'cursor' },\n { title: 'VS Code', description: '.vscode/mcp.json', value: 'vscode' },\n { title: 'Windsurf', description: '~/.codeium/windsurf/mcp_config.json', value: 'windsurf' },\n { title: 'Codex CLI', description: '~/.codex/config.toml', value: 'codex' },\n { title: 'Gemini CLI', description: '~/.gemini/settings.json', value: 'gemini' },\n ],\n hint: 'space to select, enter to confirm',\n },\n { onCancel },\n )\n\n const aiTools: AiTool[] = Array.isArray(selectedTools) ? (selectedTools as AiTool[]) : []\n\n // Auth method — only if tools selected and env supports secrets\n let authMethod: AuthMethod = 'skip'\n let keys: Record<string, string> = {}\n\n if (aiTools.length > 0 && env !== 'vanilla') {\n const { method } = await prompts(\n {\n type: 'select',\n name: 'method',\n message: 'SDK credentials:',\n choices: [\n { title: 'Browser login (recommended)', value: 'browser' },\n { title: 'Enter manually', value: 'manual' },\n { title: 'Skip for now', value: 'skip' },\n ],\n },\n { onCancel },\n )\n authMethod = (method as AuthMethod) ?? 'skip'\n\n if (authMethod === 'manual') {\n keys = await prompts(keyPrompts, { onCancel })\n }\n } else if (env !== 'vanilla') {\n // No AI tools selected — still collect keys from the key prompts if any were defined\n if (keyPrompts.length > 0) {\n keys = await prompts(keyPrompts, { onCancel })\n }\n authMethod = 'skip'\n }\n\n return {\n env,\n publishableKey: authMethod === 'browser' ? '' : (keys.publishableKey ?? ''),\n secretKey: authMethod === 'browser' ? '' : (keys.secretKey ?? ''),\n aiTools,\n authMethod,\n packageManager,\n }\n}\n"],"mappings":"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","names":[]}
#!/usr/bin/env node
import {
init
} from "./chunk-3RQE6YVO.js";
import "./chunk-3MXIG3ZL.js";
} from "./chunk-2IGKOSK7.js";
import "./chunk-5K2CB2Y5.js";
import "./chunk-UA7WNT2F.js";

@@ -7,0 +7,0 @@ import "./chunk-TBGKXE3Q.js";

{
"name": "@01.software/init",
"version": "0.8.0",
"version": "0.9.0",
"description": "Initialize 01.software SDK in your project (Next.js, React, Vanilla JS, Node.js, Edge)",

@@ -24,3 +24,3 @@ "type": "module",

"typescript": "^5.9.3",
"@01.software/sdk": "0.20.0"
"@01.software/sdk": "0.26.0"
},

@@ -27,0 +27,0 @@ "author": "<office@01.works>",

#!/usr/bin/env node
// src/ai-docs.ts
function normalizeActiveCollections(collections) {
if (Array.isArray(collections?.active)) return collections.active;
return [];
}
function generateClaudeMd(ctx) {
const featuresSection = ctx.features && ctx.features.length > 0 ? ctx.features.map((f) => `- ${f}`).join("\n") : "- See console";
const collectionsSection = ctx.collections && ctx.collections.length > 0 ? ctx.collections.join(", ") : "Run `01 schema list`";
return `# 01.software SDK \u2014 ${ctx.tenantName}
## Connection
- Publishable Key: \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` (env)
- Secret Key: \`SOFTWARE_SECRET_KEY\` (env)
- MCP: \`.mcp.json\`
## Active Features
${featuresSection}
## Active Collections
${collectionsSection}
## MCP Quick Reference
| Tool | Use |
|------|-----|
| \`query-collection\` | List/filter documents |
| \`get-collection-schema\` | Inspect tenant-aware fields for a collection |
| \`update-field-config\` | Hide unused fields |
| \`get-tenant-context\` | Show active features & collections |
## CLI
- \`01 query <collection>\` \u2014 query data
- \`01 schema show <collection>\` \u2014 inspect fields
- \`01 schema list\` \u2014 list all collections
## Initial Setup
Run \`/01software-field-config\` in Claude Code to configure field visibility for your use case.
Use \`get-collection-schema\` or \`01 schema show <collection>\` for live field introspection instead of relying on this document as a schema snapshot.
`;
}
function getSkillFiles() {
return [
{
dirName: "01software-field-config",
content: `---
name: 01software-field-config
description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP
disable-model-invocation: true
---
Steps:
1. Use \`list-configurable-fields\` to see current visibility settings
2. Identify fields/collections not needed for your use case
3. Use \`update-field-config\` to hide them
Common setups:
- Blog only: hide \`ecommerce\`, \`customers\`, \`videos\` collections
- Store: hide \`articles\`, \`documents\`, \`galleries\`, \`canvas\` collections
- Minimal: hide all except the collections you actively use
Ask me: "Show current field config" or "Hide ecommerce fields"
`
},
{
dirName: "01software-query",
content: `---
name: 01software-query
description: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples
---
Query collections using the MCP \`query-collection\` tool or CLI.
MCP examples:
- List products: \`query-collection\` with collection="products", limit=10
- Filter by status: add where={"status":{"equals":"published"}}
- Sort by date: sort="-createdAt"
- Paginate: page=2, limit=20
CLI examples:
- \`01 query products --limit 10\`
- \`01 query orders --where '{"status":{"equals":"paid"}}'\`
- \`01 schema show products\` \u2014 inspect available fields
SDK (server):
\`\`\`typescript
const { docs } = await serverClient.collections.from('products').find({
where: { status: { equals: 'published' } },
sort: '-createdAt',
limit: 10,
})
\`\`\`
`
},
{
dirName: "01software-order-flow",
content: `---
name: 01software-order-flow
description: Order lifecycle reference \u2014 create, pay, fulfill, and return flows for 01.software
---
Complete order flow from creation to fulfillment.
States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2192 confirmed
1. Create order: \`create-order\` with orderNumber, customerSnapshot, orderProducts, totalAmount
2. Mark paid: \`update-order\` with status="paid" (after payment gateway confirms)
3. Fulfill: \`create-fulfillment\` with items and carrier/trackingNumber
4. Returns: \`create-return\` or \`return-with-refund\` (atomic)
Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid
CLI: \`01 order create --help\` for full options
`
},
{
dirName: "01software-schema",
content: `---
name: 01software-schema
description: Inspect 01.software collection schemas and available fields via MCP or CLI
---
Inspect collection schemas to understand available fields.
MCP: use \`get-collection-schema\` with collection
CLI:
- \`01 schema list\` \u2014 all available collections
- \`01 schema show <collection>\` \u2014 field names, types, required status
Common collections: products, orders, customers, articles, documents, images
Use \`get-tenant-context\` to see which collections are active for this tenant.
`
}
];
}
async function fetchTenantContext(publishableKey, secretKey) {
try {
const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software";
const res = await fetch(`${apiUrl}/api/tenants/context`, {
headers: {
"X-Publishable-Key": publishableKey,
Authorization: `Bearer ${secretKey}`
}
});
if (!res.ok) return null;
const data = await res.json();
return {
tenantName: data.tenant?.name || "",
features: data.features || [],
collections: normalizeActiveCollections(data.collections)
};
} catch {
return null;
}
}
export {
generateClaudeMd,
getSkillFiles,
fetchTenantContext
};
//# sourceMappingURL=chunk-3MXIG3ZL.js.map
{"version":3,"sources":["../src/ai-docs.ts"],"sourcesContent":["export interface TenantContext {\n tenantName: string\n features?: string[]\n collections?: string[]\n}\n\ninterface TenantContextApiResponse {\n tenant?: { name?: string }\n features?: string[]\n collections?: { active?: string[]; inactive?: string[] }\n}\n\nfunction normalizeActiveCollections(\n collections: TenantContextApiResponse['collections'],\n): string[] {\n if (Array.isArray(collections?.active)) return collections.active\n return []\n}\n\n// ── CLAUDE.md ────────────────────────────────────────────────────────\n\nexport function generateClaudeMd(ctx: TenantContext): string {\n const featuresSection =\n ctx.features && ctx.features.length > 0\n ? ctx.features.map((f) => `- ${f}`).join('\\n')\n : '- See console'\n\n const collectionsSection =\n ctx.collections && ctx.collections.length > 0\n ? ctx.collections.join(', ')\n : 'Run `01 schema list`'\n\n return `# 01.software SDK — ${ctx.tenantName}\n\n## Connection\n- Publishable Key: \\`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\\` (env)\n- Secret Key: \\`SOFTWARE_SECRET_KEY\\` (env)\n- MCP: \\`.mcp.json\\`\n\n## Active Features\n${featuresSection}\n\n## Active Collections\n${collectionsSection}\n\n## MCP Quick Reference\n| Tool | Use |\n|------|-----|\n| \\`query-collection\\` | List/filter documents |\n| \\`get-collection-schema\\` | Inspect tenant-aware fields for a collection |\n| \\`update-field-config\\` | Hide unused fields |\n| \\`get-tenant-context\\` | Show active features & collections |\n\n## CLI\n- \\`01 query <collection>\\` — query data\n- \\`01 schema show <collection>\\` — inspect fields\n- \\`01 schema list\\` — list all collections\n\n## Initial Setup\nRun \\`/01software-field-config\\` in Claude Code to configure field visibility for your use case.\nUse \\`get-collection-schema\\` or \\`01 schema show <collection>\\` for live field introspection instead of relying on this document as a schema snapshot.\n`\n}\n\n// ── Skill files ──────────────────────────────────────────────────────\n\nexport function getSkillFiles(): Array<{ dirName: string; content: string }> {\n return [\n {\n dirName: '01software-field-config',\n content: `---\nname: 01software-field-config\ndescription: Configure field visibility for this tenant — hide unused collections and fields via MCP\ndisable-model-invocation: true\n---\n\nSteps:\n1. Use \\`list-configurable-fields\\` to see current visibility settings\n2. Identify fields/collections not needed for your use case\n3. Use \\`update-field-config\\` to hide them\n\nCommon setups:\n- Blog only: hide \\`ecommerce\\`, \\`customers\\`, \\`videos\\` collections\n- Store: hide \\`articles\\`, \\`documents\\`, \\`galleries\\`, \\`canvas\\` collections\n- Minimal: hide all except the collections you actively use\n\nAsk me: \"Show current field config\" or \"Hide ecommerce fields\"\n`,\n },\n {\n dirName: '01software-query',\n content: `---\nname: 01software-query\ndescription: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples\n---\n\nQuery collections using the MCP \\`query-collection\\` tool or CLI.\n\nMCP examples:\n- List products: \\`query-collection\\` with collection=\"products\", limit=10\n- Filter by status: add where={\"status\":{\"equals\":\"published\"}}\n- Sort by date: sort=\"-createdAt\"\n- Paginate: page=2, limit=20\n\nCLI examples:\n- \\`01 query products --limit 10\\`\n- \\`01 query orders --where '{\"status\":{\"equals\":\"paid\"}}'\\`\n- \\`01 schema show products\\` — inspect available fields\n\nSDK (server):\n\\`\\`\\`typescript\nconst { docs } = await serverClient.collections.from('products').find({\n where: { status: { equals: 'published' } },\n sort: '-createdAt',\n limit: 10,\n})\n\\`\\`\\`\n`,\n },\n {\n dirName: '01software-order-flow',\n content: `---\nname: 01software-order-flow\ndescription: Order lifecycle reference — create, pay, fulfill, and return flows for 01.software\n---\n\nComplete order flow from creation to fulfillment.\n\nStates: pending → paid → preparing → shipped → delivered → confirmed\n\n1. Create order: \\`create-order\\` with orderNumber, customerSnapshot, orderProducts, totalAmount\n2. Mark paid: \\`update-order\\` with status=\"paid\" (after payment gateway confirms)\n3. Fulfill: \\`create-fulfillment\\` with items and carrier/trackingNumber\n4. Returns: \\`create-return\\` or \\`return-with-refund\\` (atomic)\n\nFree orders: omit paymentId, totalAmount=0 → auto-transitions to paid\n\nCLI: \\`01 order create --help\\` for full options\n`,\n },\n {\n dirName: '01software-schema',\n content: `---\nname: 01software-schema\ndescription: Inspect 01.software collection schemas and available fields via MCP or CLI\n---\n\nInspect collection schemas to understand available fields.\n\nMCP: use \\`get-collection-schema\\` with collection\n\nCLI:\n- \\`01 schema list\\` — all available collections\n- \\`01 schema show <collection>\\` — field names, types, required status\n\nCommon collections: products, orders, customers, articles, documents, images\nUse \\`get-tenant-context\\` to see which collections are active for this tenant.\n`,\n },\n ]\n}\n\n// ── Tenant context fetch ─────────────────────────────────────────────\n\nexport async function fetchTenantContext(\n publishableKey: string,\n secretKey: string,\n): Promise<TenantContext | null> {\n try {\n const apiUrl = process.env.SOFTWARE_API_URL || 'https://api.01.software'\n // secretKey is now an opaque sk01_/pat01_ bearer token — send it directly.\n const res = await fetch(`${apiUrl}/api/tenants/context`, {\n headers: {\n 'X-Publishable-Key': publishableKey,\n Authorization: `Bearer ${secretKey}`,\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as TenantContextApiResponse\n return {\n tenantName: data.tenant?.name || '',\n features: data.features || [],\n collections: normalizeActiveCollections(data.collections),\n }\n } catch {\n return null\n }\n}\n"],"mappings":";;;AAYA,SAAS,2BACP,aACU;AACV,MAAI,MAAM,QAAQ,aAAa,MAAM,EAAG,QAAO,YAAY;AAC3D,SAAO,CAAC;AACV;AAIO,SAAS,iBAAiB,KAA4B;AAC3D,QAAM,kBACJ,IAAI,YAAY,IAAI,SAAS,SAAS,IAClC,IAAI,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,IAC3C;AAEN,QAAM,qBACJ,IAAI,eAAe,IAAI,YAAY,SAAS,IACxC,IAAI,YAAY,KAAK,IAAI,IACzB;AAEN,SAAO,4BAAuB,IAAI,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5C,eAAe;AAAA;AAAA;AAAA,EAGf,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBpB;AAIO,SAAS,gBAA6D;AAC3E,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBX;AAAA,EACF;AACF;AAIA,eAAsB,mBACpB,gBACA,WAC+B;AAC/B,MAAI;AACF,UAAM,SAAS,QAAQ,IAAI,oBAAoB;AAE/C,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,wBAAwB;AAAA,MACvD,SAAS;AAAA,QACP,qBAAqB;AAAA,QACrB,eAAe,UAAU,SAAS;AAAA,MACpC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO;AAAA,MACL,YAAY,KAAK,QAAQ,QAAQ;AAAA,MACjC,UAAU,KAAK,YAAY,CAAC;AAAA,MAC5B,aAAa,2BAA2B,KAAK,WAAW;AAAA,IAC1D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
#!/usr/bin/env node
import {
fetchTenantContext,
generateClaudeMd,
getSkillFiles
} from "./chunk-3MXIG3ZL.js";
import {
chmodSecretFile,
readEnvValue,
replaceTomlMcpSection,
setEnvValue,
writeEnvFile,
writeSecretGlobalConfig
} from "./chunk-UA7WNT2F.js";
import {
CODEX_MCP_SECTION_MARKER,
getAnalyticsTemplate,
getClientTemplate,
getCodexMcpTomlSection,
getEnvContent,
getMcpConfigTemplate,
getMcpRootKey,
getMcpServerEntry,
getQueryProviderTemplate,
getServerTemplate
} from "./chunk-TBGKXE3Q.js";
// src/init.ts
import fs2 from "fs";
import path2 from "path";
import os from "os";
import { execSync } from "child_process";
import pc2 from "picocolors";
import prompts from "prompts";
// src/detect.ts
import fs from "fs";
import path from "path";
function detectProject(cwd) {
const pkgPath = path.join(cwd, "package.json");
const hasPackageJson = fs.existsSync(pkgPath);
let env = "node";
let hasSdk = false;
let hasReactQuery = false;
let parseError = false;
if (hasPackageJson) {
let pkg;
try {
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
} catch {
return {
hasPackageJson: true,
parseError: true,
env: "node",
packageManager: null,
hasSdk: false,
hasReactQuery: false,
srcDir: false
};
}
const deps = {
...pkg.dependencies || {},
...pkg.devDependencies || {}
};
hasSdk = "@01.software/sdk" in deps;
hasReactQuery = "@tanstack/react-query" in deps;
if ("next" in deps) {
env = "nextjs";
} else if ("astro" in deps || "@astrojs/node" in deps) {
env = "other";
} else if ("@remix-run/node" in deps || "@remix-run/react" in deps) {
env = "other";
} else if ("@sveltejs/kit" in deps) {
env = "other";
} else if ("react" in deps) {
if ("vite" in deps) {
env = "react-vite";
} else if ("react-scripts" in deps) {
env = "react-cra";
} else {
env = "node";
}
}
}
let packageManager = null;
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
packageManager = "pnpm";
} else if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
packageManager = "yarn";
} else if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) {
packageManager = "bun";
} else if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
packageManager = "npm";
}
const srcDir = env === "nextjs" ? fs.existsSync(path.join(cwd, "src", "app")) : fs.existsSync(path.join(cwd, "src"));
return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir };
}
function needsClient(env) {
return env === "nextjs" || env === "react-vite" || env === "react-cra" || env === "vanilla";
}
function needsServer(env) {
return env === "nextjs" || env === "node" || env === "edge";
}
function needsReactQuery(env) {
return env === "nextjs" || env === "react-vite" || env === "react-cra";
}
function getPublishableKeyEnvVar(env) {
switch (env) {
case "nextjs":
return "NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY";
case "react-vite":
return "VITE_SOFTWARE_PUBLISHABLE_KEY";
case "react-cra":
return "REACT_APP_SOFTWARE_PUBLISHABLE_KEY";
default:
return "SOFTWARE_PUBLISHABLE_KEY";
}
}
// src/browser-auth.ts
import { randomBytes } from "crypto";
import { createServer } from "http";
import { execFile, exec } from "child_process";
import { platform } from "os";
import { URL } from "url";
import pc from "picocolors";
var DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || "https://01.software";
var TIMEOUT_MS = 5 * 60 * 1e3;
function escapeHtml(s) {
return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function openBrowser(url) {
const os2 = platform();
const onError = () => {
console.log(
pc.yellow(
`Could not open browser automatically. Open this URL manually:
${url}`
)
);
};
if (os2 === "win32") {
exec(`start "" "${url}"`, (err) => {
if (err) onError();
});
} else {
const cmd = os2 === "darwin" ? "open" : "xdg-open";
execFile(cmd, [url], (err) => {
if (err) onError();
});
}
}
var PAGE_STYLE = `*{margin:0;box-sizing:border-box}
body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}
@media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}
.card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}
.icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}
.icon.ok{background:rgba(0,0,0,.05);color:#252525}
.icon.err{background:rgba(220,38,38,.08);color:#dc2626}
@media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}
h1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}
p{font-size:.75rem;color:#737373;line-height:1.5}`;
var SUCCESS_HTML = `<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login</title>
<style>${PAGE_STYLE}</style>
</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>`;
var ERROR_HTML = (msg) => `<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login Error</title>
<style>${PAGE_STYLE}</style>
</head><body><div class="card"><div class="icon err">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`;
async function exchangeCode(webUrl, code) {
const url = `${webUrl}/api/cli/exchange`;
try {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code })
});
if (!res.ok) {
const body = await res.text().catch(() => "");
console.error(
pc.red(
`Exchange failed: HTTP ${res.status} from ${url}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`
)
);
return null;
}
const data = await res.json();
if (typeof data.publishableKey !== "string" || typeof data.secretKey !== "string" || typeof data.tenantName !== "string" || typeof data.tenantId !== "string") {
console.error(pc.red(`Exchange failed: malformed response from ${url}`));
return null;
}
return {
publishableKey: data.publishableKey,
secretKey: data.secretKey,
tenantName: data.tenantName,
tenantId: data.tenantId
};
} catch (err) {
console.error(
pc.red(
`Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`
)
);
return null;
}
}
async function startBrowserAuth(options) {
const state = randomBytes(32).toString("hex");
const webUrl = options?.webUrl ?? DEFAULT_WEB_URL;
return new Promise((resolve, reject) => {
const server = createServer((req, res) => {
if (!req.url) {
res.writeHead(400).end();
return;
}
const url = new URL(req.url, `http://localhost`);
if (url.pathname !== "/callback" || req.method !== "GET") {
res.writeHead(404).end();
return;
}
const error = url.searchParams.get("error");
if (error) {
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML(error));
console.error(pc.red(`Login failed: ${error}`));
cleanup(new Error(`Login failed: ${error}`));
return;
}
const code = url.searchParams.get("code");
const receivedState = url.searchParams.get("state");
if (!code || !receivedState) {
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Missing code or state."));
cleanup(new Error("Login failed: missing code or state."));
return;
}
if (receivedState !== state) {
res.writeHead(403, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("State mismatch."));
console.error(pc.red("Login failed: state mismatch."));
cleanup(new Error("Login failed: state mismatch."));
return;
}
exchangeCode(webUrl, code).then((creds) => {
if (!creds) {
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Invalid or expired code."));
cleanup(new Error("Login failed: code exchange failed."));
return;
}
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(SUCCESS_HTML);
console.log(pc.green(`
Logged in successfully!`));
console.log(pc.dim(`Tenant: ${creds.tenantName}`));
cleanup(null, creds);
});
});
let timeout;
let completed = false;
function cleanup(err, result) {
if (completed) return;
completed = true;
clearTimeout(timeout);
server.closeAllConnections?.();
server.close(() => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
}
server.listen(0, "127.0.0.1", () => {
const addr = server.address();
if (!addr || typeof addr === "string") {
reject(new Error("Failed to start local server."));
return;
}
const port = addr.port;
timeout = setTimeout(() => {
console.error(pc.red("\nLogin timed out (5 minutes). Please try again."));
cleanup(new Error("Login timed out"));
}, TIMEOUT_MS);
const params = new URLSearchParams({ port: String(port), state });
if (options?.tenantId) {
params.set("tenantId", options.tenantId);
}
const loginUrl = `${webUrl}/cli-auth?${params.toString()}`;
console.log(pc.dim("Opening browser for login..."));
console.log(pc.dim(`If the browser does not open, visit:
${loginUrl}`));
openBrowser(loginUrl);
});
server.on("error", (err) => {
reject(err);
});
});
}
// src/init.ts
var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY";
async function init(cwd, info, answers, deps = {}) {
const { packageManager, srcDir } = info;
const env = answers.env;
const baseDir = srcDir ? path2.join(cwd, "src") : cwd;
const browserAuth = deps.startBrowserAuth ?? startBrowserAuth;
const publishableKeyEnvVar = getPublishableKeyEnvVar(env);
const wantsClient = needsClient(env);
const wantsServer = needsServer(env);
const wantsReactQuery = needsReactQuery(env);
const plan = await planConflictsAndEnv(cwd, baseDir, env, answers);
const installResult = deps.skipInstall ? {
installFailed: false,
installSkipped: true,
installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [
"@01.software/sdk"
])
} : installDeps(
cwd,
packageManager,
info.hasSdk,
info.hasReactQuery,
wantsReactQuery
);
if (wantsClient || wantsReactQuery || wantsServer) {
fs2.mkdirSync(path2.join(baseDir, "lib", "software"), { recursive: true });
}
const libDir = path2.join(baseDir, "lib", "software");
if (wantsClient) {
await writeFileWithPolicy(
cwd,
path2.join(libDir, "client.ts"),
getClientTemplate(env, publishableKeyEnvVar),
plan.policy
);
await writeFileWithPolicy(
cwd,
path2.join(libDir, "analytics.ts"),
getAnalyticsTemplate(env, publishableKeyEnvVar),
plan.policy
);
}
if (wantsReactQuery) {
await writeFileWithPolicy(
cwd,
path2.join(libDir, "query-provider.tsx"),
getQueryProviderTemplate(env),
plan.policy
);
}
if (wantsServer) {
await writeFileWithPolicy(
cwd,
path2.join(libDir, "server.ts"),
getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),
plan.policy
);
}
if (plan.envFile && answers.authMethod !== "browser") {
await writeEnv(
cwd,
plan.envFile,
answers.publishableKey || "",
answers.secretKey || "",
publishableKeyEnvVar,
wantsServer ? SECRET_KEY_ENV_VAR : null,
plan.policy
);
}
let publishableKey = answers.publishableKey;
let secretKey = answers.secretKey;
let tenantName = "";
if (answers.authMethod === "browser" && answers.aiTools.length > 0) {
try {
console.log();
const creds = await browserAuth();
publishableKey = creds.publishableKey;
secretKey = creds.secretKey;
tenantName = creds.tenantName;
if (plan.envFile && publishableKey) {
await writeEnv(
cwd,
plan.envFile,
publishableKey,
secretKey,
publishableKeyEnvVar,
wantsServer ? SECRET_KEY_ENV_VAR : null,
"overwrite",
true
);
}
} catch (err) {
console.log(
pc2.yellow(" Browser auth skipped:"),
err instanceof Error ? err.message : String(err)
);
}
}
if (answers.aiTools.length > 0) {
for (const tool of answers.aiTools) {
await writeMcpConfig(tool, cwd);
}
addToGitignore(cwd, answers.aiTools);
if (answers.aiTools.includes("claude")) {
await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy);
}
}
return {
...installResult,
publishableKey: publishableKey || void 0,
secretKey: secretKey || void 0,
tenantName: tenantName || void 0
};
}
function installDeps(cwd, pm, hasSdk, hasReactQuery, wantsReactQuery) {
const allDeps = [
{ name: "@01.software/sdk", installed: hasSdk, needed: true },
{ name: "@tanstack/react-query", installed: hasReactQuery, needed: wantsReactQuery }
];
const fullList = allDeps.filter((d) => d.needed).map((d) => d.name);
const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name);
const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList);
if (toInstall.length === 0) {
console.log(pc2.dim(` Dependencies already installed: ${fullList.join(", ")}`));
return { installFailed: false, installSkipped: true, installCmd: fullCmd };
}
const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall);
console.log(pc2.dim(` Installing ${toInstall.join(" and ")}...`));
const wsPatched = pm === "pnpm" && patchPnpmWorkspace(cwd);
let installFailed = false;
try {
execSync(addCmd, { cwd, stdio: "pipe" });
console.log(pc2.green(" Installed"), toInstall.join(", "));
} catch (error) {
installFailed = true;
const err = error;
const msg = String(err.stderr || "").trim() || String(err.stdout || "").trim() || String(error);
console.log(pc2.yellow(" Install failed \u2014 continuing with scaffolding"));
const firstLines = msg.split("\n").slice(0, 3).map((l) => ` ${l}`).join("\n");
if (firstLines) console.log(pc2.dim(firstLines));
console.log(pc2.dim(` Run manually: ${addCmd}`));
} finally {
if (wsPatched) restorePnpmWorkspace(cwd);
}
return { installFailed, installSkipped: false, installCmd: addCmd };
}
function buildAddCmd(pm, hasPnpmWs, deps) {
const pkgs = deps.join(" ");
switch (pm) {
case "pnpm":
return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`;
case "yarn":
return `yarn add ${pkgs}`;
case "bun":
return `bun add ${pkgs}`;
default:
return `npm install ${pkgs}`;
}
}
async function planConflictsAndEnv(cwd, baseDir, env, answers) {
const candidates = [];
const libDir = path2.join(baseDir, "lib", "software");
if (needsClient(env)) candidates.push(path2.join(libDir, "client.ts"));
if (needsReactQuery(env)) candidates.push(path2.join(libDir, "query-provider.tsx"));
if (needsServer(env)) candidates.push(path2.join(libDir, "server.ts"));
if (answers.aiTools.includes("claude")) {
for (const { dirName } of getSkillFiles()) {
candidates.push(path2.join(cwd, ".claude", "skills", dirName, "SKILL.md"));
}
}
const conflicts = candidates.filter((p) => fs2.existsSync(p));
let policy = "skip";
if (conflicts.length > 0) {
console.log(pc2.yellow(` ${conflicts.length} file(s) already exist:`));
for (const c of conflicts) console.log(pc2.dim(` ${path2.relative(cwd, c)}`));
const { selected } = await prompts({
type: "select",
name: "selected",
message: "How should I handle existing files?",
choices: [
{ title: "Keep existing (skip)", value: "skip" },
{ title: "Overwrite all", value: "overwrite" },
{ title: "Ask for each", value: "ask" }
],
initial: 0
});
policy = selected ?? "skip";
}
const envFile = env === "vanilla" || env === "edge" ? "" : await pickEnvFile(cwd, env);
return { policy, envFile };
}
async function pickEnvFile(cwd, env) {
const candidates = [".env.local", ".env", ".env.development"];
const existing = candidates.filter((f) => fs2.existsSync(path2.join(cwd, f)));
const preferred = env === "nextjs" ? ".env.local" : ".env";
if (existing.length === 0) return preferred;
if (existing.length === 1 && existing[0] === preferred) return existing[0];
const options = Array.from(/* @__PURE__ */ new Set([...existing, preferred]));
const choices = options.map((f) => ({
title: f,
description: existing.includes(f) ? "exists" : "create",
value: f
}));
const initial = Math.max(
0,
choices.findIndex((c) => c.value === preferred)
);
const { file } = await prompts({
type: "select",
name: "file",
message: "Which env file should I write SDK credentials to?",
choices,
initial
});
return file ?? preferred;
}
async function writeFileWithPolicy(cwd, filePath, content, policy) {
const rel = path2.relative(cwd, filePath);
if (!fs2.existsSync(filePath)) {
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
fs2.writeFileSync(filePath, content);
console.log(pc2.green(" Created"), rel);
return;
}
const existing = fs2.readFileSync(filePath, "utf-8");
if (existing === content) {
console.log(pc2.dim(" Unchanged"), rel);
return;
}
let shouldWrite = false;
if (policy === "overwrite") {
shouldWrite = true;
} else if (policy === "ask") {
const { confirm } = await prompts({
type: "confirm",
name: "confirm",
message: `Overwrite ${rel}?`,
initial: false
});
shouldWrite = !!confirm;
}
if (shouldWrite) {
fs2.writeFileSync(filePath, content);
console.log(pc2.green(" Overwrote"), rel);
} else {
console.log(pc2.yellow(" Skipped"), rel, pc2.dim("(already exists)"));
}
}
async function writeEnv(cwd, envFile, publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar, policy, fromBrowserAuth = false) {
const envPath = path2.join(cwd, envFile);
const targets = [
{ name: publishableKeyEnvVar, value: publishableKey }
];
if (secretKeyEnvVar) {
targets.push({ name: secretKeyEnvVar, value: secretKey });
}
if (!fs2.existsSync(envPath)) {
const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar);
writeEnvFile(envPath, initial.trimStart());
console.log(pc2.green(" Created"), envFile);
return;
}
let content = fs2.readFileSync(envPath, "utf-8");
let modified = false;
let appendedHeader = false;
const headerAlreadyPresent = targets.some(
(t) => readEnvValue(content, t.name) !== null
);
for (const { name, value } of targets) {
const existing = readEnvValue(content, name);
if (existing === null) {
if (!headerAlreadyPresent && !appendedHeader) {
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
content += "\n# 01.software\n";
appendedHeader = true;
}
content = setEnvValue(content, name, value);
modified = true;
continue;
}
if (existing === value) continue;
if (!value) continue;
let shouldOverwrite = false;
if (policy === "overwrite") {
shouldOverwrite = true;
} else if (policy === "ask") {
const { confirm } = await prompts({
type: "confirm",
name: "confirm",
message: `${name} already set in ${envFile}. Overwrite?`,
initial: fromBrowserAuth
});
shouldOverwrite = !!confirm;
}
if (shouldOverwrite) {
content = setEnvValue(content, name, value);
modified = true;
}
}
if (modified) {
writeEnvFile(envPath, content);
console.log(
pc2.green(" Updated"),
envFile,
fromBrowserAuth ? pc2.dim("(SDK credentials)") : ""
);
} else {
chmodSecretFile(envPath);
console.log(pc2.dim(" Unchanged"), envFile);
}
}
function resolveMcpLocation(tool, cwd) {
const home = os.homedir();
switch (tool) {
case "claude":
return {
kind: "json",
absolutePath: path2.join(cwd, ".mcp.json"),
displayPath: ".mcp.json",
gitignoreEntry: ".mcp.json"
};
case "cursor":
return {
kind: "json",
absolutePath: path2.join(cwd, ".cursor", "mcp.json"),
displayPath: ".cursor/mcp.json",
gitignoreEntry: ".cursor/mcp.json"
};
case "vscode":
return {
kind: "json",
absolutePath: path2.join(cwd, ".vscode", "mcp.json"),
jsonClient: "vscode",
displayPath: ".vscode/mcp.json",
gitignoreEntry: ".vscode/mcp.json"
};
case "windsurf": {
if (!home) return null;
const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
return {
kind: "json",
absolutePath: p,
jsonClient: "windsurf",
displayPath: p,
gitignoreEntry: null,
global: true
};
}
case "codex": {
if (!home) return null;
const p = path2.join(home, ".codex", "config.toml");
return { kind: "toml", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true };
}
case "gemini": {
if (!home) return null;
const p = path2.join(home, ".gemini", "settings.json");
return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null, global: true };
}
}
}
async function writeMcpConfig(tool, cwd) {
const loc = resolveMcpLocation(tool, cwd);
if (!loc) {
console.log(pc2.yellow(` Skipped ${tool}`), pc2.dim("(HOME not set)"));
return;
}
if (!loc.global) {
fs2.mkdirSync(path2.dirname(loc.absolutePath), { recursive: true });
}
try {
if (loc.kind === "json") {
writeJsonMcp(loc);
} else {
writeTomlMcp(loc);
}
} catch (err) {
console.log(
pc2.yellow(` Skipped ${loc.displayPath}`),
pc2.dim(err instanceof Error ? err.message : String(err))
);
}
}
function writeJsonMcp(loc) {
const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content);
if (!fs2.existsSync(loc.absolutePath)) {
writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient));
console.log(pc2.green(" Created"), loc.displayPath);
return;
}
const rootKey = getMcpRootKey(loc.jsonClient);
let existing;
try {
existing = JSON.parse(fs2.readFileSync(loc.absolutePath, "utf-8"));
} catch {
console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(could not parse existing file)"));
return;
}
const nextEntry = getMcpServerEntry(loc.jsonClient);
const existingServers = existing[rootKey] ?? void 0;
if (existingServers?.["01software"] && JSON.stringify(existingServers["01software"]) === JSON.stringify(nextEntry)) {
console.log(pc2.dim(" Unchanged"), loc.displayPath);
return;
}
const servers = existing[rootKey] ?? {};
servers["01software"] = nextEntry;
existing[rootKey] = servers;
writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n");
console.log(pc2.green(" Updated"), loc.displayPath);
}
function writeTomlMcp(loc) {
const section = getCodexMcpTomlSection();
const writeFile = loc.global ? (target, content) => writeSecretGlobalConfig(target, content) : (target, content) => fs2.writeFileSync(target, content);
if (!fs2.existsSync(loc.absolutePath)) {
writeFile(loc.absolutePath, section.trimStart());
console.log(pc2.green(" Created"), loc.displayPath);
return;
}
const existing = fs2.readFileSync(loc.absolutePath, "utf-8");
if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {
const sep = existing.endsWith("\n") ? "" : "\n";
writeFile(loc.absolutePath, existing + sep + section);
console.log(pc2.green(" Updated"), loc.displayPath);
return;
}
const replaced = replaceTomlMcpSection(existing, section);
if (replaced === existing) {
console.log(pc2.dim(" Unchanged"), loc.displayPath);
return;
}
writeFile(loc.absolutePath, replaced);
console.log(pc2.green(" Updated"), loc.displayPath);
}
function addToGitignore(cwd, tools) {
const entries = [];
for (const tool of tools) {
const loc = resolveMcpLocation(tool, cwd);
if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry);
}
if (entries.length === 0) return;
const gitignorePath = path2.join(cwd, ".gitignore");
const existing = fs2.existsSync(gitignorePath) ? fs2.readFileSync(gitignorePath, "utf-8") : "";
const toAdd = entries.filter((e) => !existing.includes(e));
if (toAdd.length === 0) return;
const content = "\n# MCP configs\n" + toAdd.join("\n") + "\n";
if (fs2.existsSync(gitignorePath)) {
fs2.appendFileSync(gitignorePath, content);
} else {
fs2.writeFileSync(gitignorePath, content.trimStart());
}
console.log(pc2.green(" Updated"), ".gitignore", pc2.dim(`(added ${toAdd.join(", ")})`));
}
async function writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, policy) {
let ctx = {
tenantName: tenantName || "Your Tenant",
features: void 0,
collections: void 0
};
if (publishableKey && secretKey) {
const fetched = await fetchTenantContext(publishableKey, secretKey);
if (fetched) {
ctx = {
tenantName: fetched.tenantName || ctx.tenantName,
features: fetched.features,
collections: fetched.collections
};
}
}
const claudeDir = path2.join(cwd, ".claude");
const softwareDir = path2.join(claudeDir, "01software");
const skillsDir = path2.join(claudeDir, "skills");
fs2.mkdirSync(softwareDir, { recursive: true });
fs2.mkdirSync(skillsDir, { recursive: true });
const contextPath = path2.join(softwareDir, "context.md");
const contextExists = fs2.existsSync(contextPath);
fs2.writeFileSync(contextPath, generateClaudeMd(ctx));
console.log(pc2.green(contextExists ? " Updated" : " Created"), ".claude/01software/context.md");
const claudeMdPath = path2.join(claudeDir, "CLAUDE.md");
const importLine = "@.claude/01software/context.md";
if (!fs2.existsSync(claudeMdPath)) {
fs2.writeFileSync(claudeMdPath, importLine + "\n");
console.log(pc2.green(" Created"), ".claude/CLAUDE.md");
} else {
const existing = fs2.readFileSync(claudeMdPath, "utf-8");
if (!existing.includes(importLine)) {
const prefix = existing.endsWith("\n") ? "\n" : "\n\n";
fs2.appendFileSync(claudeMdPath, prefix + importLine + "\n");
console.log(pc2.green(" Updated"), ".claude/CLAUDE.md", pc2.dim("(added @import)"));
} else {
console.log(pc2.dim(" Unchanged"), ".claude/CLAUDE.md");
}
}
for (const { dirName, content } of getSkillFiles()) {
const skillDir = path2.join(skillsDir, dirName);
const skillPath = path2.join(skillDir, "SKILL.md");
fs2.mkdirSync(skillDir, { recursive: true });
await writeFileWithPolicy(cwd, skillPath, content, policy);
}
}
var WS_FILE = "pnpm-workspace.yaml";
var WS_BACKUP = "pnpm-workspace.yaml.bak";
function hasPnpmWorkspace(cwd) {
return fs2.existsSync(path2.join(cwd, WS_FILE));
}
function patchPnpmWorkspace(cwd) {
const wsPath = path2.join(cwd, WS_FILE);
if (!fs2.existsSync(wsPath)) return false;
const content = fs2.readFileSync(wsPath, "utf-8");
if (content.includes("packages:")) return false;
fs2.copyFileSync(wsPath, path2.join(cwd, WS_BACKUP));
fs2.writeFileSync(wsPath, content.trimEnd() + "\npackages: []\n");
return true;
}
function restorePnpmWorkspace(cwd) {
const backupPath = path2.join(cwd, WS_BACKUP);
if (!fs2.existsSync(backupPath)) return;
fs2.copyFileSync(backupPath, path2.join(cwd, WS_FILE));
fs2.unlinkSync(backupPath);
}
export {
detectProject,
init
};
//# sourceMappingURL=chunk-3RQE6YVO.js.map
{"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/browser-auth.ts"],"sourcesContent":["import fs from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { execSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport prompts from 'prompts'\nimport type { PackageManager, ProjectEnv, ProjectInfo } from './detect'\nimport {\n needsClient,\n needsServer,\n needsReactQuery,\n getPublishableKeyEnvVar,\n} from './detect'\nimport type { AiTool, InitAnswers } from './prompts'\nimport {\n getClientTemplate,\n getAnalyticsTemplate,\n getQueryProviderTemplate,\n getServerTemplate,\n getEnvContent,\n getMcpConfigTemplate,\n getMcpRootKey,\n getMcpServerEntry,\n getCodexMcpTomlSection,\n CODEX_MCP_SECTION_MARKER,\n} from './templates'\nimport { startBrowserAuth } from './browser-auth'\nimport {\n generateClaudeMd,\n getSkillFiles,\n fetchTenantContext,\n} from './ai-docs'\nimport {\n readEnvValue,\n setEnvValue,\n replaceTomlMcpSection,\n writeEnvFile,\n chmodSecretFile,\n writeSecretGlobalConfig,\n} from './file-ops'\n\ntype ResolvedProjectInfo = Omit<ProjectInfo, 'packageManager'> & {\n packageManager: PackageManager\n}\n\nconst SECRET_KEY_ENV_VAR = 'SOFTWARE_SECRET_KEY'\n\nexport type ConflictPolicy = 'overwrite' | 'skip' | 'ask'\n\nexport interface InitResult {\n installFailed: boolean\n installSkipped: boolean\n installCmd: string\n /** Set when browser-auth or manual entry produced a publishable key the\n * caller should prefer over `answers.publishableKey` when computing\n * next-step messages. */\n publishableKey?: string\n /** Set when browser-auth or manual entry produced a secret key. */\n secretKey?: string\n /** Tenant display name from browser-auth, when available. */\n tenantName?: string\n}\n\nexport interface InitDeps {\n /** Test seam — replaces the real browser-auth flow. */\n startBrowserAuth?: typeof startBrowserAuth\n /** Test seam — skips actually invoking the package manager. */\n skipInstall?: boolean\n}\n\nexport async function init(\n cwd: string,\n info: ResolvedProjectInfo,\n answers: InitAnswers,\n deps: InitDeps = {},\n): Promise<InitResult> {\n const { packageManager, srcDir } = info\n const env = answers.env\n const baseDir = srcDir ? path.join(cwd, 'src') : cwd\n const browserAuth = deps.startBrowserAuth ?? startBrowserAuth\n\n const publishableKeyEnvVar = getPublishableKeyEnvVar(env)\n const wantsClient = needsClient(env)\n const wantsServer = needsServer(env)\n const wantsReactQuery = needsReactQuery(env)\n\n // 0. Plan: scan for conflicts and pick the env file. Both prompts are\n // front-loaded so the rest of init() doesn't interleave I/O with prompts.\n const plan = await planConflictsAndEnv(cwd, baseDir, env, answers)\n\n // 1. Install dependencies — skip whatever's already in package.json\n const installResult = deps.skipInstall\n ? {\n installFailed: false,\n installSkipped: true,\n installCmd: buildAddCmd(packageManager, hasPnpmWorkspace(cwd), [\n '@01.software/sdk',\n ]),\n }\n : installDeps(\n cwd,\n packageManager,\n info.hasSdk,\n info.hasReactQuery,\n wantsReactQuery,\n )\n\n // 2. Write lib/software/ files\n if (wantsClient || wantsReactQuery || wantsServer) {\n fs.mkdirSync(path.join(baseDir, 'lib', 'software'), { recursive: true })\n }\n const libDir = path.join(baseDir, 'lib', 'software')\n\n if (wantsClient) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'client.ts'),\n getClientTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'analytics.ts'),\n getAnalyticsTemplate(env, publishableKeyEnvVar),\n plan.policy,\n )\n }\n if (wantsReactQuery) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'query-provider.tsx'),\n getQueryProviderTemplate(env),\n plan.policy,\n )\n }\n if (wantsServer) {\n await writeFileWithPolicy(\n cwd,\n path.join(libDir, 'server.ts'),\n getServerTemplate(env, publishableKeyEnvVar, SECRET_KEY_ENV_VAR),\n plan.policy,\n )\n }\n\n // 3. Append to env file (browser auth handles its own write in step 4)\n if (plan.envFile && answers.authMethod !== 'browser') {\n await writeEnv(\n cwd,\n plan.envFile,\n answers.publishableKey || '',\n answers.secretKey || '',\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n plan.policy,\n )\n }\n\n // 4. Browser auth — get real credentials and force-write env\n let publishableKey = answers.publishableKey\n let secretKey = answers.secretKey\n let tenantName = ''\n\n if (answers.authMethod === 'browser' && answers.aiTools.length > 0) {\n try {\n console.log()\n const creds = await browserAuth()\n publishableKey = creds.publishableKey\n secretKey = creds.secretKey\n tenantName = creds.tenantName\n\n // Browser auth just minted fresh credentials → policy is overwrite.\n if (plan.envFile && publishableKey) {\n await writeEnv(\n cwd,\n plan.envFile,\n publishableKey,\n secretKey,\n publishableKeyEnvVar,\n wantsServer ? SECRET_KEY_ENV_VAR : null,\n 'overwrite',\n true,\n )\n }\n } catch (err) {\n console.log(\n pc.yellow(' Browser auth skipped:'),\n err instanceof Error ? err.message : String(err),\n )\n }\n }\n\n // 5. AI tool configs (MCP + Claude docs)\n if (answers.aiTools.length > 0) {\n for (const tool of answers.aiTools) {\n await writeMcpConfig(tool, cwd)\n }\n\n addToGitignore(cwd, answers.aiTools)\n\n if (answers.aiTools.includes('claude')) {\n await writeClaudeDocs(cwd, publishableKey, secretKey, tenantName, plan.policy)\n }\n }\n\n return {\n ...installResult,\n publishableKey: publishableKey || undefined,\n secretKey: secretKey || undefined,\n tenantName: tenantName || undefined,\n }\n}\n\n// ── Install ──────────────────────────────────────────────────────────\n\ninterface DepSpec {\n name: string\n installed: boolean\n needed: boolean\n}\n\nfunction installDeps(\n cwd: string,\n pm: PackageManager,\n hasSdk: boolean,\n hasReactQuery: boolean,\n wantsReactQuery: boolean,\n): InitResult {\n const allDeps: DepSpec[] = [\n { name: '@01.software/sdk', installed: hasSdk, needed: true },\n { name: '@tanstack/react-query', installed: hasReactQuery, needed: wantsReactQuery },\n ]\n const fullList = allDeps.filter((d) => d.needed).map((d) => d.name)\n const toInstall = allDeps.filter((d) => d.needed && !d.installed).map((d) => d.name)\n const fullCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), fullList)\n\n if (toInstall.length === 0) {\n console.log(pc.dim(` Dependencies already installed: ${fullList.join(', ')}`))\n return { installFailed: false, installSkipped: true, installCmd: fullCmd }\n }\n\n const addCmd = buildAddCmd(pm, hasPnpmWorkspace(cwd), toInstall)\n console.log(pc.dim(` Installing ${toInstall.join(' and ')}...`))\n\n const wsPatched = pm === 'pnpm' && patchPnpmWorkspace(cwd)\n let installFailed = false\n\n try {\n execSync(addCmd, { cwd, stdio: 'pipe' })\n console.log(pc.green(' Installed'), toInstall.join(', '))\n } catch (error) {\n installFailed = true\n const err = error as { stdout?: Buffer; stderr?: Buffer }\n const msg =\n String(err.stderr || '').trim() ||\n String(err.stdout || '').trim() ||\n String(error)\n console.log(pc.yellow(' Install failed — continuing with scaffolding'))\n const firstLines = msg.split('\\n').slice(0, 3).map((l) => ` ${l}`).join('\\n')\n if (firstLines) console.log(pc.dim(firstLines))\n console.log(pc.dim(` Run manually: ${addCmd}`))\n } finally {\n if (wsPatched) restorePnpmWorkspace(cwd)\n }\n\n return { installFailed, installSkipped: false, installCmd: addCmd }\n}\n\nfunction buildAddCmd(\n pm: PackageManager,\n hasPnpmWs: boolean,\n deps: string[],\n): string {\n const pkgs = deps.join(' ')\n switch (pm) {\n case 'pnpm':\n return hasPnpmWs ? `pnpm add -w ${pkgs}` : `pnpm add ${pkgs}`\n case 'yarn':\n return `yarn add ${pkgs}`\n case 'bun':\n return `bun add ${pkgs}`\n default:\n return `npm install ${pkgs}`\n }\n}\n\n// ── Conflict planning ────────────────────────────────────────────────\n\ninterface PlanResult {\n policy: ConflictPolicy\n /** Empty string when env file is not used (vanilla / edge) */\n envFile: string\n}\n\nasync function planConflictsAndEnv(\n cwd: string,\n baseDir: string,\n env: ProjectEnv,\n answers: InitAnswers,\n): Promise<PlanResult> {\n const candidates: string[] = []\n const libDir = path.join(baseDir, 'lib', 'software')\n if (needsClient(env)) candidates.push(path.join(libDir, 'client.ts'))\n if (needsReactQuery(env)) candidates.push(path.join(libDir, 'query-provider.tsx'))\n if (needsServer(env)) candidates.push(path.join(libDir, 'server.ts'))\n if (answers.aiTools.includes('claude')) {\n for (const { dirName } of getSkillFiles()) {\n candidates.push(path.join(cwd, '.claude', 'skills', dirName, 'SKILL.md'))\n }\n }\n\n const conflicts = candidates.filter((p) => fs.existsSync(p))\n\n let policy: ConflictPolicy = 'skip'\n if (conflicts.length > 0) {\n console.log(pc.yellow(` ${conflicts.length} file(s) already exist:`))\n for (const c of conflicts) console.log(pc.dim(` ${path.relative(cwd, c)}`))\n const { selected } = await prompts({\n type: 'select',\n name: 'selected',\n message: 'How should I handle existing files?',\n choices: [\n { title: 'Keep existing (skip)', value: 'skip' },\n { title: 'Overwrite all', value: 'overwrite' },\n { title: 'Ask for each', value: 'ask' },\n ],\n initial: 0,\n })\n policy = (selected as ConflictPolicy) ?? 'skip'\n }\n\n const envFile =\n env === 'vanilla' || env === 'edge' ? '' : await pickEnvFile(cwd, env)\n\n return { policy, envFile }\n}\n\nasync function pickEnvFile(cwd: string, env: ProjectEnv): Promise<string> {\n // Order matters — `.env.local` first so it's the default selection on\n // Next.js, where it's the conventional secret store.\n const candidates = ['.env.local', '.env', '.env.development']\n const existing = candidates.filter((f) => fs.existsSync(path.join(cwd, f)))\n const preferred = env === 'nextjs' ? '.env.local' : '.env'\n\n if (existing.length === 0) return preferred\n if (existing.length === 1 && existing[0] === preferred) return existing[0]\n\n // Multiple files exist OR the only existing file isn't the preferred default.\n const options = Array.from(new Set([...existing, preferred]))\n const choices = options.map((f) => ({\n title: f,\n description: existing.includes(f) ? 'exists' : 'create',\n value: f,\n }))\n const initial = Math.max(\n 0,\n choices.findIndex((c) => c.value === preferred),\n )\n const { file } = await prompts({\n type: 'select',\n name: 'file',\n message: 'Which env file should I write SDK credentials to?',\n choices,\n initial,\n })\n return file ?? preferred\n}\n\n// ── Generic file write ───────────────────────────────────────────────\n\nasync function writeFileWithPolicy(\n cwd: string,\n filePath: string,\n content: string,\n policy: ConflictPolicy,\n): Promise<void> {\n const rel = path.relative(cwd, filePath)\n if (!fs.existsSync(filePath)) {\n fs.mkdirSync(path.dirname(filePath), { recursive: true })\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Created'), rel)\n return\n }\n\n const existing = fs.readFileSync(filePath, 'utf-8')\n if (existing === content) {\n console.log(pc.dim(' Unchanged'), rel)\n return\n }\n\n let shouldWrite = false\n if (policy === 'overwrite') {\n shouldWrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `Overwrite ${rel}?`,\n initial: false,\n })\n shouldWrite = !!confirm\n }\n\n if (shouldWrite) {\n fs.writeFileSync(filePath, content)\n console.log(pc.green(' Overwrote'), rel)\n } else {\n console.log(pc.yellow(' Skipped'), rel, pc.dim('(already exists)'))\n }\n}\n\n// ── Env file write ───────────────────────────────────────────────────\n\nasync function writeEnv(\n cwd: string,\n envFile: string,\n publishableKey: string,\n secretKey: string,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n policy: ConflictPolicy,\n fromBrowserAuth = false,\n): Promise<void> {\n const envPath = path.join(cwd, envFile)\n\n const targets: { name: string; value: string }[] = [\n { name: publishableKeyEnvVar, value: publishableKey },\n ]\n if (secretKeyEnvVar) {\n targets.push({ name: secretKeyEnvVar, value: secretKey })\n }\n\n if (!fs.existsSync(envPath)) {\n const initial = getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar)\n writeEnvFile(envPath, initial.trimStart())\n console.log(pc.green(' Created'), envFile)\n return\n }\n\n let content = fs.readFileSync(envPath, 'utf-8')\n let modified = false\n let appendedHeader = false\n const headerAlreadyPresent = targets.some(\n (t) => readEnvValue(content, t.name) !== null,\n )\n\n for (const { name, value } of targets) {\n const existing = readEnvValue(content, name)\n\n if (existing === null) {\n // Append. Add the `# 01.software` section header on first append, but\n // only if no other 01.software keys are already in the file.\n if (!headerAlreadyPresent && !appendedHeader) {\n if (content.length > 0 && !content.endsWith('\\n')) content += '\\n'\n content += '\\n# 01.software\\n'\n appendedHeader = true\n }\n content = setEnvValue(content, name, value)\n modified = true\n continue\n }\n\n if (existing === value) continue\n // Don't overwrite an existing real value with empty (e.g. user skipped key entry).\n if (!value) continue\n\n let shouldOverwrite = false\n if (policy === 'overwrite') {\n shouldOverwrite = true\n } else if (policy === 'ask') {\n const { confirm } = await prompts({\n type: 'confirm',\n name: 'confirm',\n message: `${name} already set in ${envFile}. Overwrite?`,\n initial: fromBrowserAuth,\n })\n shouldOverwrite = !!confirm\n }\n\n if (shouldOverwrite) {\n content = setEnvValue(content, name, value)\n modified = true\n }\n }\n\n if (modified) {\n writeEnvFile(envPath, content)\n console.log(\n pc.green(' Updated'),\n envFile,\n fromBrowserAuth ? pc.dim('(SDK credentials)') : '',\n )\n } else {\n // Tighten mode on existing files we did not modify, so re-runs harden\n // permissions on env files created by an earlier looser version.\n chmodSecretFile(envPath)\n console.log(pc.dim(' Unchanged'), envFile)\n }\n}\n\n// ── MCP targets registry ─────────────────────────────────────────────\n\ntype McpFormat = 'json' | 'toml'\ntype McpJsonClient = 'generic' | 'windsurf' | 'vscode'\ninterface McpLocation {\n kind: McpFormat\n absolutePath: string\n jsonClient?: McpJsonClient\n displayPath: string\n gitignoreEntry: string | null // null = global path, not worth ignoring\n /** Global config under user HOME — apply 0o600 mode, atomic rename, and\n * symlink/hardlink rejection per spec §\"Secret Handling Rules\". */\n global?: boolean\n}\n\nfunction resolveMcpLocation(tool: AiTool, cwd: string): McpLocation | null {\n const home = os.homedir()\n\n switch (tool) {\n case 'claude':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.mcp.json'),\n displayPath: '.mcp.json',\n gitignoreEntry: '.mcp.json',\n }\n case 'cursor':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.cursor', 'mcp.json'),\n displayPath: '.cursor/mcp.json',\n gitignoreEntry: '.cursor/mcp.json',\n }\n case 'vscode':\n return {\n kind: 'json',\n absolutePath: path.join(cwd, '.vscode', 'mcp.json'),\n jsonClient: 'vscode',\n displayPath: '.vscode/mcp.json',\n gitignoreEntry: '.vscode/mcp.json',\n }\n case 'windsurf': {\n if (!home) return null\n const p = path.join(home, '.codeium', 'windsurf', 'mcp_config.json')\n return {\n kind: 'json',\n absolutePath: p,\n jsonClient: 'windsurf',\n displayPath: p,\n gitignoreEntry: null,\n global: true,\n }\n }\n case 'codex': {\n if (!home) return null\n const p = path.join(home, '.codex', 'config.toml')\n return { kind: 'toml', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n case 'gemini': {\n if (!home) return null\n const p = path.join(home, '.gemini', 'settings.json')\n return { kind: 'json', absolutePath: p, displayPath: p, gitignoreEntry: null, global: true }\n }\n }\n}\n\nasync function writeMcpConfig(\n tool: AiTool,\n cwd: string,\n): Promise<void> {\n const loc = resolveMcpLocation(tool, cwd)\n if (!loc) {\n console.log(pc.yellow(` Skipped ${tool}`), pc.dim('(HOME not set)'))\n return\n }\n\n // For global secret-bearing config, parent-dir creation with 0o700 is\n // handled inside `writeSecretGlobalConfig`. Repo-local writes (Claude Code,\n // Cursor, VS Code) keep loose mkdir — they only contain OAuth/discovery URLs.\n if (!loc.global) {\n fs.mkdirSync(path.dirname(loc.absolutePath), { recursive: true })\n }\n\n try {\n if (loc.kind === 'json') {\n writeJsonMcp(loc)\n } else {\n writeTomlMcp(loc)\n }\n } catch (err) {\n console.log(\n pc.yellow(` Skipped ${loc.displayPath}`),\n pc.dim(err instanceof Error ? err.message : String(err)),\n )\n }\n}\n\nfunction writeJsonMcp(loc: McpLocation): void {\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient))\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n // VS Code uses `servers` as the config root; all other JSON clients use `mcpServers`.\n const rootKey = getMcpRootKey(loc.jsonClient)\n\n let existing: Record<string, unknown>\n try {\n existing = JSON.parse(fs.readFileSync(loc.absolutePath, 'utf-8'))\n } catch {\n console.log(pc.yellow(' Skipped'), loc.displayPath, pc.dim('(could not parse existing file)'))\n return\n }\n\n const nextEntry = getMcpServerEntry(loc.jsonClient)\n const existingServers = (existing[rootKey] as Record<string, unknown> | undefined) ?? undefined\n if (\n existingServers?.['01software'] &&\n JSON.stringify(existingServers['01software']) === JSON.stringify(nextEntry)\n ) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n const servers = (existing[rootKey] as Record<string, unknown> | undefined) ?? {}\n servers['01software'] = nextEntry\n existing[rootKey] = servers\n writeFile(loc.absolutePath, JSON.stringify(existing, null, 2) + '\\n')\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction writeTomlMcp(loc: McpLocation): void {\n const section = getCodexMcpTomlSection()\n const writeFile = loc.global\n ? (target: string, content: string) => writeSecretGlobalConfig(target, content)\n : (target: string, content: string) => fs.writeFileSync(target, content)\n\n if (!fs.existsSync(loc.absolutePath)) {\n writeFile(loc.absolutePath, section.trimStart())\n console.log(pc.green(' Created'), loc.displayPath)\n return\n }\n\n const existing = fs.readFileSync(loc.absolutePath, 'utf-8')\n\n if (!existing.includes(CODEX_MCP_SECTION_MARKER)) {\n const sep = existing.endsWith('\\n') ? '' : '\\n'\n // Atomic rewrite (read-modify-write) — `appendFileSync` would bypass the\n // symlink/hardlink guards and the 0o600 mode of `writeSecretGlobalConfig`.\n writeFile(loc.absolutePath, existing + sep + section)\n console.log(pc.green(' Updated'), loc.displayPath)\n return\n }\n\n const replaced = replaceTomlMcpSection(existing, section)\n if (replaced === existing) {\n console.log(pc.dim(' Unchanged'), loc.displayPath)\n return\n }\n\n writeFile(loc.absolutePath, replaced)\n console.log(pc.green(' Updated'), loc.displayPath)\n}\n\nfunction addToGitignore(cwd: string, tools: AiTool[]): void {\n const entries: string[] = []\n for (const tool of tools) {\n const loc = resolveMcpLocation(tool, cwd)\n if (loc?.gitignoreEntry) entries.push(loc.gitignoreEntry)\n }\n\n if (entries.length === 0) return\n\n const gitignorePath = path.join(cwd, '.gitignore')\n const existing = fs.existsSync(gitignorePath)\n ? fs.readFileSync(gitignorePath, 'utf-8')\n : ''\n const toAdd = entries.filter((e) => !existing.includes(e))\n if (toAdd.length === 0) return\n\n const content = '\\n# MCP configs\\n' + toAdd.join('\\n') + '\\n'\n if (fs.existsSync(gitignorePath)) {\n fs.appendFileSync(gitignorePath, content)\n } else {\n fs.writeFileSync(gitignorePath, content.trimStart())\n }\n console.log(pc.green(' Updated'), '.gitignore', pc.dim(`(added ${toAdd.join(', ')})`))\n}\n\nasync function writeClaudeDocs(\n cwd: string,\n publishableKey: string,\n secretKey: string,\n tenantName: string,\n policy: ConflictPolicy,\n): Promise<void> {\n let ctx = {\n tenantName: tenantName || 'Your Tenant',\n features: undefined as string[] | undefined,\n collections: undefined as string[] | undefined,\n }\n\n if (publishableKey && secretKey) {\n const fetched = await fetchTenantContext(publishableKey, secretKey)\n if (fetched) {\n ctx = {\n tenantName: fetched.tenantName || ctx.tenantName,\n features: fetched.features,\n collections: fetched.collections,\n }\n }\n }\n\n const claudeDir = path.join(cwd, '.claude')\n const softwareDir = path.join(claudeDir, '01software')\n const skillsDir = path.join(claudeDir, 'skills')\n fs.mkdirSync(softwareDir, { recursive: true })\n fs.mkdirSync(skillsDir, { recursive: true })\n\n // context.md is intentionally always overwritten so re-running init\n // refreshes tenant data; users don't customize this file.\n const contextPath = path.join(softwareDir, 'context.md')\n const contextExists = fs.existsSync(contextPath)\n fs.writeFileSync(contextPath, generateClaudeMd(ctx))\n console.log(pc.green(contextExists ? ' Updated' : ' Created'), '.claude/01software/context.md')\n\n // CLAUDE.md — append @import line (idempotent, never overwrites user content)\n const claudeMdPath = path.join(claudeDir, 'CLAUDE.md')\n const importLine = '@.claude/01software/context.md'\n if (!fs.existsSync(claudeMdPath)) {\n fs.writeFileSync(claudeMdPath, importLine + '\\n')\n console.log(pc.green(' Created'), '.claude/CLAUDE.md')\n } else {\n const existing = fs.readFileSync(claudeMdPath, 'utf-8')\n if (!existing.includes(importLine)) {\n const prefix = existing.endsWith('\\n') ? '\\n' : '\\n\\n'\n fs.appendFileSync(claudeMdPath, prefix + importLine + '\\n')\n console.log(pc.green(' Updated'), '.claude/CLAUDE.md', pc.dim('(added @import)'))\n } else {\n console.log(pc.dim(' Unchanged'), '.claude/CLAUDE.md')\n }\n }\n\n // Skill files honour the user's conflict policy (templates the user may have\n // tuned to their workflow).\n for (const { dirName, content } of getSkillFiles()) {\n const skillDir = path.join(skillsDir, dirName)\n const skillPath = path.join(skillDir, 'SKILL.md')\n fs.mkdirSync(skillDir, { recursive: true })\n await writeFileWithPolicy(cwd, skillPath, content, policy)\n }\n}\n\nconst WS_FILE = 'pnpm-workspace.yaml'\nconst WS_BACKUP = 'pnpm-workspace.yaml.bak'\n\nfunction hasPnpmWorkspace(cwd: string): boolean {\n return fs.existsSync(path.join(cwd, WS_FILE))\n}\n\nfunction patchPnpmWorkspace(cwd: string): boolean {\n const wsPath = path.join(cwd, WS_FILE)\n if (!fs.existsSync(wsPath)) return false\n const content = fs.readFileSync(wsPath, 'utf-8')\n if (content.includes('packages:')) return false\n fs.copyFileSync(wsPath, path.join(cwd, WS_BACKUP))\n fs.writeFileSync(wsPath, content.trimEnd() + '\\npackages: []\\n')\n return true\n}\n\nfunction restorePnpmWorkspace(cwd: string): void {\n const backupPath = path.join(cwd, WS_BACKUP)\n if (!fs.existsSync(backupPath)) return\n fs.copyFileSync(backupPath, path.join(cwd, WS_FILE))\n fs.unlinkSync(backupPath)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\nexport type ProjectEnv =\n | 'nextjs'\n | 'react-vite'\n | 'react-cra'\n | 'vanilla'\n | 'node'\n | 'edge'\n | 'other' // Astro, Remix, SvelteKit, etc. — print guidance only\n\nexport interface ProjectInfo {\n hasPackageJson: boolean\n parseError: boolean\n env: ProjectEnv\n packageManager: PackageManager | null\n hasSdk: boolean\n hasReactQuery: boolean\n srcDir: boolean\n}\n\nexport function detectProject(cwd: string): ProjectInfo {\n const pkgPath = path.join(cwd, 'package.json')\n const hasPackageJson = fs.existsSync(pkgPath)\n\n let env: ProjectEnv = 'node'\n let hasSdk = false\n let hasReactQuery = false\n let parseError = false\n\n if (hasPackageJson) {\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n } catch {\n return {\n hasPackageJson: true,\n parseError: true,\n env: 'node',\n packageManager: null,\n hasSdk: false,\n hasReactQuery: false,\n srcDir: false,\n }\n }\n\n const deps = {\n ...((pkg.dependencies as Record<string, string>) || {}),\n ...((pkg.devDependencies as Record<string, string>) || {}),\n }\n hasSdk = '@01.software/sdk' in deps\n hasReactQuery = '@tanstack/react-query' in deps\n\n if ('next' in deps) {\n env = 'nextjs'\n } else if ('astro' in deps || '@astrojs/node' in deps) {\n env = 'other'\n } else if ('@remix-run/node' in deps || '@remix-run/react' in deps) {\n env = 'other'\n } else if ('@sveltejs/kit' in deps) {\n env = 'other'\n } else if ('react' in deps) {\n if ('vite' in deps) {\n env = 'react-vite'\n } else if ('react-scripts' in deps) {\n env = 'react-cra'\n } else {\n // React + unknown bundler (Webpack, Parcel, etc.) — leave as 'node'\n // so the prompt selector is shown\n env = 'node'\n }\n }\n // else: no react/next → 'node' (covers real Node.js, Deno, Bun, etc.)\n }\n\n // Detect package manager from lockfile\n let packageManager: PackageManager | null = null\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\n packageManager = 'pnpm'\n } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\n packageManager = 'yarn'\n } else if (\n fs.existsSync(path.join(cwd, 'bun.lockb')) ||\n fs.existsSync(path.join(cwd, 'bun.lock'))\n ) {\n packageManager = 'bun'\n } else if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {\n packageManager = 'npm'\n }\n\n // Detect src directory structure\n const srcDir =\n env === 'nextjs'\n ? fs.existsSync(path.join(cwd, 'src', 'app'))\n : fs.existsSync(path.join(cwd, 'src'))\n\n return { hasPackageJson, parseError, env, packageManager, hasSdk, hasReactQuery, srcDir }\n}\n\n/** Environments that generate a browser client file */\nexport function needsClient(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra' || env === 'vanilla'\n}\n\n/** Environments that generate a server client file */\nexport function needsServer(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Environments that generate a query-provider file */\nexport function needsReactQuery(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'react-vite' || env === 'react-cra'\n}\n\n/** Environments that support MCP setup (need both client + secret key) */\nexport function supportsMcp(env: ProjectEnv): boolean {\n return env === 'nextjs' || env === 'node' || env === 'edge'\n}\n\n/** Get the env var name for the public publishable key */\nexport function getPublishableKeyEnvVar(env: ProjectEnv): string {\n switch (env) {\n case 'nextjs':\n return 'NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-vite':\n return 'VITE_SOFTWARE_PUBLISHABLE_KEY'\n case 'react-cra':\n return 'REACT_APP_SOFTWARE_PUBLISHABLE_KEY'\n default:\n return 'SOFTWARE_PUBLISHABLE_KEY'\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport { createServer } from 'node:http'\nimport { execFile, exec } from 'node:child_process'\nimport { platform } from 'node:os'\nimport { URL } from 'node:url'\nimport pc from 'picocolors'\n\nconst DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || 'https://01.software'\nconst TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction openBrowser(url: string): void {\n const os = platform()\n\n const onError = () => {\n console.log(\n pc.yellow(\n `Could not open browser automatically. Open this URL manually:\\n${url}`,\n ),\n )\n }\n\n if (os === 'win32') {\n exec(`start \"\" \"${url}\"`, (err) => {\n if (err) onError()\n })\n } else {\n const cmd = os === 'darwin' ? 'open' : 'xdg-open'\n execFile(cmd, [url], (err) => {\n if (err) onError()\n })\n }\n}\n\nconst PAGE_STYLE = `*{margin:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}\n@media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}\n.card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}\n.icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}\n.icon.ok{background:rgba(0,0,0,.05);color:#252525}\n.icon.err{background:rgba(220,38,38,.08);color:#dc2626}\n@media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}\nh1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}\np{font-size:.75rem;color:#737373;line-height:1.5}`\n\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login</title>\n<style>${PAGE_STYLE}</style>\n</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>`\n\nconst ERROR_HTML = (msg: string) => `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login Error</title>\n<style>${PAGE_STYLE}</style>\n</head><body><div class=\"card\"><div class=\"icon err\">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`\n\ninterface ExchangeResponse {\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId: string\n}\n\nasync function exchangeCode(\n webUrl: string,\n code: string,\n): Promise<ExchangeResponse | null> {\n const url = `${webUrl}/api/cli/exchange`\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code }),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n console.error(\n pc.red(\n `Exchange failed: HTTP ${res.status} from ${url}${body ? ` — ${body.slice(0, 200)}` : ''}`,\n ),\n )\n return null\n }\n const data = (await res.json()) as Partial<ExchangeResponse>\n if (\n typeof data.publishableKey !== 'string' ||\n typeof data.secretKey !== 'string' ||\n typeof data.tenantName !== 'string' ||\n typeof data.tenantId !== 'string'\n ) {\n console.error(pc.red(`Exchange failed: malformed response from ${url}`))\n return null\n }\n return {\n publishableKey: data.publishableKey,\n secretKey: data.secretKey,\n tenantName: data.tenantName,\n tenantId: data.tenantId,\n }\n } catch (err) {\n console.error(\n pc.red(\n `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`,\n ),\n )\n return null\n }\n}\n\nexport async function startBrowserAuth(options?: {\n webUrl?: string\n tenantId?: string\n}): Promise<{\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId?: string\n}> {\n const state = randomBytes(32).toString('hex')\n const webUrl = options?.webUrl ?? DEFAULT_WEB_URL\n\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400).end()\n return\n }\n\n const url = new URL(req.url, `http://localhost`)\n\n if (url.pathname !== '/callback' || req.method !== 'GET') {\n res.writeHead(404).end()\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML(error))\n console.error(pc.red(`Login failed: ${error}`))\n cleanup(new Error(`Login failed: ${error}`))\n return\n }\n\n const code = url.searchParams.get('code')\n const receivedState = url.searchParams.get('state')\n\n if (!code || !receivedState) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Missing code or state.'))\n cleanup(new Error('Login failed: missing code or state.'))\n return\n }\n\n if (receivedState !== state) {\n res\n .writeHead(403, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('State mismatch.'))\n console.error(pc.red('Login failed: state mismatch.'))\n cleanup(new Error('Login failed: state mismatch.'))\n return\n }\n\n exchangeCode(webUrl, code).then((creds) => {\n if (!creds) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Invalid or expired code.'))\n cleanup(new Error('Login failed: code exchange failed.'))\n return\n }\n\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(SUCCESS_HTML)\n\n console.log(pc.green(`\\nLogged in successfully!`))\n console.log(pc.dim(`Tenant: ${creds.tenantName}`))\n\n cleanup(null, creds)\n })\n })\n\n let timeout: ReturnType<typeof setTimeout>\n let completed = false\n\n function cleanup(err: Error | null, result?: ExchangeResponse) {\n if (completed) return\n completed = true\n clearTimeout(timeout)\n server.closeAllConnections?.()\n server.close(() => {\n if (err) {\n reject(err)\n } else {\n resolve(result!)\n }\n })\n }\n\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address()\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to start local server.'))\n return\n }\n\n const port = addr.port\n\n timeout = setTimeout(() => {\n console.error(pc.red('\\nLogin timed out (5 minutes). Please try again.'))\n cleanup(new Error('Login timed out'))\n }, TIMEOUT_MS)\n\n const params = new URLSearchParams({ port: String(port), state })\n if (options?.tenantId) {\n params.set('tenantId', options.tenantId)\n }\n const loginUrl = `${webUrl}/cli-auth?${params.toString()}`\n\n console.log(pc.dim('Opening browser for login...'))\n console.log(pc.dim(`If the browser does not open, visit:\\n${loginUrl}`))\n openBrowser(loginUrl)\n })\n\n server.on('error', (err) => {\n reject(err)\n })\n })\n}\n"],"mappings":"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","names":["fs","path","pc","os","path","fs","pc"]}