superslate 0.4.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +113 -38
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1681,7 +1681,7 @@ import { existsSync, readdirSync, statSync } from "fs";
|
|
|
1681
1681
|
import { mkdir as mkdir5, mkdtemp as mkdtemp2, readFile as readFile6, readdir as readdir2, rename as rename2, rm as rm5, rmdir, stat as stat5 } from "fs/promises";
|
|
1682
1682
|
import { createConnection, createServer } from "net";
|
|
1683
1683
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1684
|
-
import { dirname as
|
|
1684
|
+
import { dirname as dirname3, join as join8, relative as relative2, resolve as resolve4, sep } from "path";
|
|
1685
1685
|
|
|
1686
1686
|
// src/constants.ts
|
|
1687
1687
|
var CLI_NAME = "superslate";
|
|
@@ -1694,6 +1694,7 @@ var BUYER_CLI_NAME = "create-app";
|
|
|
1694
1694
|
var BUYER_CLI_PACKAGE_NAME = "create-app";
|
|
1695
1695
|
var DEFAULT_SCOPE = "@app";
|
|
1696
1696
|
var SOURCE_BRAND_NAME = "superslate";
|
|
1697
|
+
var DOCS_BASE_URL = "https://docs.superslate.dev";
|
|
1697
1698
|
var DELIVERY_BASE_URL = "https://dl.superslate.dev";
|
|
1698
1699
|
var DELIVERY_URL_VARIABLE = "SUPERSLATE_DELIVERY_URL";
|
|
1699
1700
|
var LICENSE_KEY_VARIABLE2 = "SUPERSLATE_LICENSE_KEY";
|
|
@@ -2319,9 +2320,17 @@ function isExecutable(path) {
|
|
|
2319
2320
|
|
|
2320
2321
|
// src/agent-setup.ts
|
|
2321
2322
|
var CLIENTS = ["codex", "claude", "cursor"];
|
|
2322
|
-
var SERVER_NAME = "
|
|
2323
|
-
var CODEX_START = "#
|
|
2324
|
-
var CODEX_END = "#
|
|
2323
|
+
var SERVER_NAME = "agent-docs";
|
|
2324
|
+
var CODEX_START = "# agent-context:start";
|
|
2325
|
+
var CODEX_END = "# agent-context:end";
|
|
2326
|
+
var AGENT_CONTEXT_TOOLS = [
|
|
2327
|
+
"search_docs",
|
|
2328
|
+
"search_implementation",
|
|
2329
|
+
"get_repository_map",
|
|
2330
|
+
"plan_change",
|
|
2331
|
+
"get_invariants",
|
|
2332
|
+
"get_verification"
|
|
2333
|
+
];
|
|
2325
2334
|
function takeValue(args, index, flag) {
|
|
2326
2335
|
const value = args[index + 1];
|
|
2327
2336
|
if (!value || value.startsWith("-"))
|
|
@@ -2352,7 +2361,7 @@ function parseAgentSetupArguments(argv) {
|
|
|
2352
2361
|
function agentSetupUsage(cliName) {
|
|
2353
2362
|
return `Usage: ${cliName} agent setup [options]
|
|
2354
2363
|
|
|
2355
|
-
Connect local coding clients to this checkout's read-only
|
|
2364
|
+
Connect local coding clients to this checkout's read-only project context server.
|
|
2356
2365
|
|
|
2357
2366
|
Options:
|
|
2358
2367
|
--clients <list> Comma-separated clients: codex,claude,cursor
|
|
@@ -2373,12 +2382,12 @@ async function findAgentRepositoryRoot(start = process.cwd()) {
|
|
|
2373
2382
|
}
|
|
2374
2383
|
const parent = dirname(current);
|
|
2375
2384
|
if (parent === current)
|
|
2376
|
-
throw new Error("Run agent setup inside a
|
|
2385
|
+
throw new Error("Run agent setup inside a project repository.");
|
|
2377
2386
|
current = parent;
|
|
2378
2387
|
}
|
|
2379
2388
|
}
|
|
2380
2389
|
function serverCommand(root) {
|
|
2381
|
-
return { command: "pnpm", args: ["--dir", resolve(root), "
|
|
2390
|
+
return { command: "pnpm", args: ["--dir", resolve(root), "agent:mcp"] };
|
|
2382
2391
|
}
|
|
2383
2392
|
function codexManagedBlock(root) {
|
|
2384
2393
|
const server = serverCommand(root);
|
|
@@ -2388,7 +2397,7 @@ function codexManagedBlock(root) {
|
|
|
2388
2397
|
command = ${JSON.stringify(server.command)}
|
|
2389
2398
|
args = [${args}]
|
|
2390
2399
|
required = false
|
|
2391
|
-
enabled_tools =
|
|
2400
|
+
enabled_tools = ${JSON.stringify(AGENT_CONTEXT_TOOLS)}
|
|
2392
2401
|
${CODEX_END}`;
|
|
2393
2402
|
}
|
|
2394
2403
|
function mergeCodexConfig(existing, root) {
|
|
@@ -2397,14 +2406,14 @@ function mergeCodexConfig(existing, root) {
|
|
|
2397
2406
|
const end = existing.indexOf(CODEX_END);
|
|
2398
2407
|
if (start >= 0 || end >= 0) {
|
|
2399
2408
|
if (start < 0 || end < start)
|
|
2400
|
-
throw new Error("Codex config has an incomplete
|
|
2409
|
+
throw new Error("Codex config has an incomplete agent-context block.");
|
|
2401
2410
|
const after = end + CODEX_END.length;
|
|
2402
2411
|
return `${existing.slice(0, start).trimEnd()}
|
|
2403
2412
|
|
|
2404
2413
|
${managed}${existing.slice(after)}`.trimStart();
|
|
2405
2414
|
}
|
|
2406
|
-
if (/^\[mcp_servers\.(?:
|
|
2407
|
-
throw new Error("Codex already has an unmanaged
|
|
2415
|
+
if (/^\[mcp_servers\.(?:agent-docs|agent_docs|"agent-docs")\]$/m.test(existing)) {
|
|
2416
|
+
throw new Error("Codex already has an unmanaged agent-docs server; remove or rename it first.");
|
|
2408
2417
|
}
|
|
2409
2418
|
return `${existing.trimEnd()}${existing.trim() ? `
|
|
2410
2419
|
|
|
@@ -2430,7 +2439,7 @@ function mergeCursorConfig(existing, root) {
|
|
|
2430
2439
|
const current = servers[SERVER_NAME];
|
|
2431
2440
|
const desired = serverCommand(root);
|
|
2432
2441
|
if (current !== undefined && JSON.stringify(current) !== JSON.stringify(desired)) {
|
|
2433
|
-
throw new Error("Cursor already has a different
|
|
2442
|
+
throw new Error("Cursor already has a different agent-docs server; remove or rename it first.");
|
|
2434
2443
|
}
|
|
2435
2444
|
config.mcpServers = { ...servers, [SERVER_NAME]: desired };
|
|
2436
2445
|
return `${JSON.stringify(config, null, 2)}
|
|
@@ -2460,7 +2469,7 @@ async function addGitInfoExcludes(root, paths) {
|
|
|
2460
2469
|
const prefix = existing && !existing.endsWith(`
|
|
2461
2470
|
`) ? `
|
|
2462
2471
|
` : "";
|
|
2463
|
-
const marker = "#
|
|
2472
|
+
const marker = "# Local agent configuration";
|
|
2464
2473
|
const heading = existing.split(/\r?\n/).includes(marker) ? "" : `${marker}
|
|
2465
2474
|
`;
|
|
2466
2475
|
await atomicWrite(excludePath, `${existing}${prefix}${heading}${missing.join(`
|
|
@@ -2491,10 +2500,10 @@ async function configureClaude(root) {
|
|
|
2491
2500
|
if (existing.exitCode === 0) {
|
|
2492
2501
|
const combined = `${existing.stdout}
|
|
2493
2502
|
${existing.stderr}`;
|
|
2494
|
-
if (combined.includes("
|
|
2503
|
+
if (combined.includes("agent:mcp") && combined.includes(root)) {
|
|
2495
2504
|
return "Claude Code already has the matching local server.";
|
|
2496
2505
|
}
|
|
2497
|
-
throw new Error("Claude Code already has a different
|
|
2506
|
+
throw new Error("Claude Code already has a different agent-docs server; remove it first.");
|
|
2498
2507
|
}
|
|
2499
2508
|
const server = serverCommand(root);
|
|
2500
2509
|
const result = await capture(["claude", "mcp", "add", SERVER_NAME, "--scope", "local", "--", server.command, ...server.args], root);
|
|
@@ -2505,14 +2514,15 @@ ${existing.stderr}`;
|
|
|
2505
2514
|
return "Configured Claude Code local project scope.";
|
|
2506
2515
|
}
|
|
2507
2516
|
async function smokeServer(root) {
|
|
2508
|
-
const result = await capture(["pnpm", "--dir", root, "
|
|
2517
|
+
const result = await capture(["pnpm", "--dir", root, "agent:mcp", "--check", "--root", root], root);
|
|
2509
2518
|
if (result.exitCode !== 0) {
|
|
2510
2519
|
const output = result.stderr.trim() || result.stdout.trim();
|
|
2511
|
-
throw new Error(`
|
|
2520
|
+
throw new Error(`Agent context MCP self-check failed${output ? `: ${output}` : "."}`);
|
|
2512
2521
|
}
|
|
2513
2522
|
const report = JSON.parse(result.stdout);
|
|
2514
|
-
if (!report.ok || report.tools !==
|
|
2515
|
-
throw new Error("
|
|
2523
|
+
if (!report.ok || report.tools !== AGENT_CONTEXT_TOOLS.length) {
|
|
2524
|
+
throw new Error("Agent context MCP self-check was incomplete.");
|
|
2525
|
+
}
|
|
2516
2526
|
}
|
|
2517
2527
|
function detectedClients() {
|
|
2518
2528
|
return CLIENTS.filter((client) => {
|
|
@@ -2582,7 +2592,7 @@ async function runAgentSetup(argv, cliName) {
|
|
|
2582
2592
|
const clients = await chooseClients(parsed);
|
|
2583
2593
|
if (!clients)
|
|
2584
2594
|
return;
|
|
2585
|
-
intro2("
|
|
2595
|
+
intro2("Local agent context");
|
|
2586
2596
|
if (parsed.dryRun) {
|
|
2587
2597
|
for (const client of clients) {
|
|
2588
2598
|
log2.info(`${client}
|
|
@@ -2600,7 +2610,7 @@ ${dryRunDescription(client, root)}`);
|
|
|
2600
2610
|
log2.success(`${client}: ${result}`);
|
|
2601
2611
|
}
|
|
2602
2612
|
log2.info("Restart configured clients, then ask: “Use plan_change for my next feature.”");
|
|
2603
|
-
outro2("
|
|
2613
|
+
outro2("Agent context is ready.");
|
|
2604
2614
|
}
|
|
2605
2615
|
|
|
2606
2616
|
// src/deployment-cli.ts
|
|
@@ -2735,7 +2745,8 @@ Create a GitHub environment named \`${config.environment}\` and configure:
|
|
|
2735
2745
|
- a \`BACKEND_HCL\` secret containing the selected remote-state backend configuration;
|
|
2736
2746
|
- a \`TFVARS_JSON\` secret containing only:
|
|
2737
2747
|
- ${providerInputs};
|
|
2738
|
-
- \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL
|
|
2748
|
+
- \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a
|
|
2749
|
+
public marketing site.
|
|
2739
2750
|
|
|
2740
2751
|
## Cloud preparation
|
|
2741
2752
|
|
|
@@ -3244,7 +3255,8 @@ Create a GitHub environment named \`${config.environment}\` and configure:
|
|
|
3244
3255
|
- a \`BACKEND_HCL\` secret containing the selected remote-state backend configuration;
|
|
3245
3256
|
- a \`TFVARS_JSON\` secret containing only:
|
|
3246
3257
|
- ${providerInputs};
|
|
3247
|
-
- \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL
|
|
3258
|
+
- \`VITE_TERMS_URL\` and \`VITE_PRIVACY_URL\`, plus \`VITE_MARKETING_URL\` when the product has a
|
|
3259
|
+
public marketing site.
|
|
3248
3260
|
|
|
3249
3261
|
## Cloud preparation
|
|
3250
3262
|
|
|
@@ -3331,7 +3343,7 @@ import { execFile } from "node:child_process";
|
|
|
3331
3343
|
import { createHash } from "node:crypto";
|
|
3332
3344
|
import { cp, lstat, mkdir as mkdir4, mkdtemp, readFile as readFile5, readdir, rm as rm4, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
|
|
3333
3345
|
import { tmpdir } from "node:os";
|
|
3334
|
-
import { basename, join as join7, relative, resolve as resolve3 } from "node:path";
|
|
3346
|
+
import { basename, dirname as dirname2, join as join7, relative, resolve as resolve3 } from "node:path";
|
|
3335
3347
|
import { promisify } from "node:util";
|
|
3336
3348
|
|
|
3337
3349
|
// src/delivery.ts
|
|
@@ -3446,6 +3458,9 @@ var BRAND_MANIFEST = [
|
|
|
3446
3458
|
{ path: "apps/web/public/favicon/site.webmanifest", occurrences: 2 },
|
|
3447
3459
|
{ path: "apps/web/public/offline.html", occurrences: 6 },
|
|
3448
3460
|
{ path: "apps/docs/lib/shared.ts", occurrences: 1 },
|
|
3461
|
+
{ path: "apps/docs/README.md", occurrences: 1 },
|
|
3462
|
+
{ path: "README.md", occurrences: 1 },
|
|
3463
|
+
{ path: "docs/meta.json", occurrences: 1 },
|
|
3449
3464
|
{ path: "apps/server/.env.example", occurrences: 1 },
|
|
3450
3465
|
{ path: "apps/server/src/config/env.ts", occurrences: 2 },
|
|
3451
3466
|
{ path: "apps/server/src/config/env.test.ts", occurrences: 1 },
|
|
@@ -3469,6 +3484,7 @@ var BRAND_MANIFEST = [
|
|
|
3469
3484
|
{ path: "apps/server/email-templates/security-alert.html", occurrences: 4 },
|
|
3470
3485
|
{ path: "apps/server/email-templates/verify-email.html", occurrences: 5 },
|
|
3471
3486
|
{ path: "apps/server/email-templates/reset-password.html", occurrences: 5 },
|
|
3487
|
+
{ path: "apps/server/email-templates/organization-invitation.html", occurrences: 3 },
|
|
3472
3488
|
{ path: "apps/server/email-templates/welcome-email.html", occurrences: 8 }
|
|
3473
3489
|
];
|
|
3474
3490
|
function validateAppName2(value) {
|
|
@@ -3517,7 +3533,7 @@ function rewriteBrandContent(content, brandName) {
|
|
|
3517
3533
|
if (error)
|
|
3518
3534
|
throw new Error(error);
|
|
3519
3535
|
const escapedSourceBrand = SOURCE_BRAND_NAME.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3520
|
-
const sourceBrandPattern = new RegExp(`\\b${escapedSourceBrand}\\b`, "
|
|
3536
|
+
const sourceBrandPattern = new RegExp(`\\b${escapedSourceBrand}\\b`, "gi");
|
|
3521
3537
|
const matches = content.match(sourceBrandPattern);
|
|
3522
3538
|
if (brandName === SOURCE_BRAND_NAME || !matches)
|
|
3523
3539
|
return { content, replacements: 0 };
|
|
@@ -3615,6 +3631,20 @@ function rewriteRootPackageName(content, appName) {
|
|
|
3615
3631
|
return { content: `${JSON.stringify(parsed, null, 2)}
|
|
3616
3632
|
`, replacements: 1 };
|
|
3617
3633
|
}
|
|
3634
|
+
var WORKER_NAME_DECLARATION = /^name = "[^"]*"$/m;
|
|
3635
|
+
function rewriteWorkerName(content, appName) {
|
|
3636
|
+
const error = validateAppName2(appName);
|
|
3637
|
+
if (error)
|
|
3638
|
+
throw new Error(error);
|
|
3639
|
+
const declaration = content.match(WORKER_NAME_DECLARATION)?.[0];
|
|
3640
|
+
if (!declaration) {
|
|
3641
|
+
throw new Error("Cloudflare Worker configuration is missing a name declaration.");
|
|
3642
|
+
}
|
|
3643
|
+
const replacement = `name = "${appName}"`;
|
|
3644
|
+
if (declaration === replacement)
|
|
3645
|
+
return { content, replacements: 0 };
|
|
3646
|
+
return { content: content.replace(WORKER_NAME_DECLARATION, replacement), replacements: 1 };
|
|
3647
|
+
}
|
|
3618
3648
|
function replaceMarkdownSection(content, heading, replacement) {
|
|
3619
3649
|
const marker = `## ${heading}
|
|
3620
3650
|
`;
|
|
@@ -3667,6 +3697,12 @@ into this repository.
|
|
|
3667
3697
|
}
|
|
3668
3698
|
return rewritten;
|
|
3669
3699
|
}
|
|
3700
|
+
var BUYER_README_HEADING = "## Create your product";
|
|
3701
|
+
function stripDistributionReadmeBootstrap(content) {
|
|
3702
|
+
if (!content.includes(BUYER_README_HEADING))
|
|
3703
|
+
return content;
|
|
3704
|
+
return replaceMarkdownSection(content, "Create your product", "");
|
|
3705
|
+
}
|
|
3670
3706
|
var REQUIRED_BUYER_CLI_CONSTANTS = ["CLI_NAME", "CLI_PACKAGE_NAME", "DEFAULT_SCOPE"];
|
|
3671
3707
|
function rewriteBuyerCliConstants(content, scope, brandName) {
|
|
3672
3708
|
const buyerVariablePrefix = BUYER_CLI_NAME.toUpperCase().replaceAll("-", "_");
|
|
@@ -3677,6 +3713,7 @@ function rewriteBuyerCliConstants(content, scope, brandName) {
|
|
|
3677
3713
|
["SOURCE_BRAND_NAME", brandName],
|
|
3678
3714
|
["TEMPLATE_REPOSITORY", "OWNER/REPOSITORY"],
|
|
3679
3715
|
["DELIVERY_BASE_URL", "https://releases.example.com"],
|
|
3716
|
+
["DOCS_BASE_URL", "https://docs.example.com"],
|
|
3680
3717
|
["DELIVERY_URL_VARIABLE", `${buyerVariablePrefix}_DELIVERY_URL`],
|
|
3681
3718
|
["LICENSE_KEY_VARIABLE", `${buyerVariablePrefix}_LICENSE_KEY`],
|
|
3682
3719
|
["LICENSE_KEY_PREFIX", "LICENSE-"]
|
|
@@ -3696,6 +3733,7 @@ function rewriteBuyerCliConstants(content, scope, brandName) {
|
|
|
3696
3733
|
CLI_PACKAGE_NAME,
|
|
3697
3734
|
SOURCE_BRAND_NAME,
|
|
3698
3735
|
DELIVERY_BASE_URL,
|
|
3736
|
+
DOCS_BASE_URL,
|
|
3699
3737
|
DELIVERY_URL_VARIABLE,
|
|
3700
3738
|
LICENSE_KEY_VARIABLE2,
|
|
3701
3739
|
LICENSE_KEY_PREFIX
|
|
@@ -3779,6 +3817,8 @@ function isTemplatePathExcluded(relativePath) {
|
|
|
3779
3817
|
return true;
|
|
3780
3818
|
if (normalized === "internal" || normalized.startsWith("internal/"))
|
|
3781
3819
|
return true;
|
|
3820
|
+
if (normalized === "marketing" || normalized.startsWith("marketing/"))
|
|
3821
|
+
return true;
|
|
3782
3822
|
if (normalized === ".github/workflows/internal-commercial.yml")
|
|
3783
3823
|
return true;
|
|
3784
3824
|
if (normalized === "apps/worker" || normalized.startsWith("apps/worker/"))
|
|
@@ -3799,6 +3839,8 @@ function isTemplatePathExcluded(relativePath) {
|
|
|
3799
3839
|
return true;
|
|
3800
3840
|
if (normalized === "packages/create-app/src/cli-brand-contract.test.ts")
|
|
3801
3841
|
return true;
|
|
3842
|
+
if (normalized === "packages/create-app/src/commercial-contract.test.ts")
|
|
3843
|
+
return true;
|
|
3802
3844
|
if (normalized === "packages/create-app/LICENSE")
|
|
3803
3845
|
return true;
|
|
3804
3846
|
if (normalized === "packages/create-app/README.md")
|
|
@@ -3827,6 +3869,29 @@ async function assertEmptyTarget(targetDirectory) {
|
|
|
3827
3869
|
throw error;
|
|
3828
3870
|
}
|
|
3829
3871
|
}
|
|
3872
|
+
async function trackedTemplatePaths(sourceDirectory) {
|
|
3873
|
+
const topLevel = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
|
|
3874
|
+
cwd: sourceDirectory
|
|
3875
|
+
}).then((result) => resolve3(result.stdout.trim())).catch(() => {
|
|
3876
|
+
return;
|
|
3877
|
+
});
|
|
3878
|
+
if (topLevel !== sourceDirectory)
|
|
3879
|
+
return;
|
|
3880
|
+
const { stdout: stdout2 } = await execFileAsync("git", ["ls-files", "-z"], {
|
|
3881
|
+
cwd: sourceDirectory,
|
|
3882
|
+
maxBuffer: 64 * 1024 * 1024
|
|
3883
|
+
});
|
|
3884
|
+
const tracked = new Set;
|
|
3885
|
+
for (const path of stdout2.split("\x00")) {
|
|
3886
|
+
if (!path)
|
|
3887
|
+
continue;
|
|
3888
|
+
tracked.add(path);
|
|
3889
|
+
for (let parent = dirname2(path);parent !== "."; parent = dirname2(parent)) {
|
|
3890
|
+
tracked.add(parent);
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
return tracked;
|
|
3894
|
+
}
|
|
3830
3895
|
async function copyLocalTemplate(source, target) {
|
|
3831
3896
|
const sourceDirectory = resolve3(source);
|
|
3832
3897
|
const sourceStat = await lstat(sourceDirectory).catch(() => {
|
|
@@ -3834,29 +3899,28 @@ async function copyLocalTemplate(source, target) {
|
|
|
3834
3899
|
});
|
|
3835
3900
|
if (!sourceStat?.isDirectory())
|
|
3836
3901
|
throw new Error(`Template path is not a directory: ${sourceDirectory}`);
|
|
3837
|
-
await
|
|
3902
|
+
const tracked = await trackedTemplatePaths(sourceDirectory);
|
|
3903
|
+
const isTemplatePathIncluded = (relativePath) => relativePath === "" || !isTemplatePathExcluded(relativePath) && (!tracked || tracked.has(relativePath));
|
|
3904
|
+
await assertNoSymbolicLinks(sourceDirectory, sourceDirectory, isTemplatePathIncluded);
|
|
3838
3905
|
await mkdir4(target, { recursive: true });
|
|
3839
3906
|
await cp(sourceDirectory, target, {
|
|
3840
3907
|
recursive: true,
|
|
3841
|
-
filter: (path) =>
|
|
3842
|
-
const relativePath = relative(sourceDirectory, path);
|
|
3843
|
-
return relativePath === "" || !isTemplatePathExcluded(relativePath);
|
|
3844
|
-
}
|
|
3908
|
+
filter: (path) => isTemplatePathIncluded(relative(sourceDirectory, path).replaceAll("\\", "/"))
|
|
3845
3909
|
});
|
|
3846
3910
|
await assertNoSymbolicLinks(target);
|
|
3847
3911
|
}
|
|
3848
|
-
async function assertNoSymbolicLinks(directory, root = directory,
|
|
3912
|
+
async function assertNoSymbolicLinks(directory, root = directory, isIncluded) {
|
|
3849
3913
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
3850
3914
|
for (const entry of entries) {
|
|
3851
3915
|
const path = join7(directory, entry.name);
|
|
3852
|
-
const relativePath = relative(root, path);
|
|
3853
|
-
if (
|
|
3916
|
+
const relativePath = relative(root, path).replaceAll("\\", "/");
|
|
3917
|
+
if (isIncluded && !isIncluded(relativePath))
|
|
3854
3918
|
continue;
|
|
3855
3919
|
if (entry.isSymbolicLink()) {
|
|
3856
3920
|
throw new Error(`Template contains a symbolic link, which is not supported: ${path}`);
|
|
3857
3921
|
}
|
|
3858
3922
|
if (entry.isDirectory())
|
|
3859
|
-
await assertNoSymbolicLinks(path, root,
|
|
3923
|
+
await assertNoSymbolicLinks(path, root, isIncluded);
|
|
3860
3924
|
}
|
|
3861
3925
|
}
|
|
3862
3926
|
function verifyReleaseMetadata(checksum, manifest, artifactName, digest, templateRef) {
|
|
@@ -3953,6 +4017,14 @@ async function applyBrandRewrite(target, brandName) {
|
|
|
3953
4017
|
}
|
|
3954
4018
|
return report;
|
|
3955
4019
|
}
|
|
4020
|
+
async function renameFrontendWorker(target, appName) {
|
|
4021
|
+
const workerPath = join7(target, "apps/web/wrangler.toml");
|
|
4022
|
+
if (!await stat4(workerPath).catch(() => {
|
|
4023
|
+
return;
|
|
4024
|
+
}))
|
|
4025
|
+
return;
|
|
4026
|
+
await rewriteFile(workerPath, (content) => rewriteWorkerName(content, appName));
|
|
4027
|
+
}
|
|
3956
4028
|
async function generateEnvironmentFiles(target, databasePort = 5432) {
|
|
3957
4029
|
const serverExample = await readFile5(join7(target, "apps/server/.env.example"), "utf8");
|
|
3958
4030
|
await writeFile4(join7(target, "apps/server/.env"), generateServerEnv(serverExample, undefined, databasePort));
|
|
@@ -3966,8 +4038,10 @@ async function generateEnvironmentFiles(target, databasePort = 5432) {
|
|
|
3966
4038
|
async function neutralizeAgentInstructions(target) {
|
|
3967
4039
|
const agentPath = join7(target, "AGENTS.md");
|
|
3968
4040
|
const claudePath = join7(target, "CLAUDE.md");
|
|
4041
|
+
const readmePath = join7(target, "README.md");
|
|
3969
4042
|
await writeFile4(agentPath, rewriteBuyerAgentInstructions(await readFile5(agentPath, "utf8")));
|
|
3970
4043
|
await writeFile4(claudePath, rewriteBuyerClaudeInstructions(await readFile5(claudePath, "utf8")));
|
|
4044
|
+
await writeFile4(readmePath, stripDistributionReadmeBootstrap(await readFile5(readmePath, "utf8")));
|
|
3971
4045
|
const rootPackagePath = join7(target, "package.json");
|
|
3972
4046
|
await writeFile4(rootPackagePath, stripSellerOnlyRootScripts(await readFile5(rootPackagePath, "utf8")));
|
|
3973
4047
|
}
|
|
@@ -4036,6 +4110,7 @@ async function scaffold(options) {
|
|
|
4036
4110
|
}
|
|
4037
4111
|
const packagePath = join7(target, "package.json");
|
|
4038
4112
|
const packageReplacements = await rewriteFile(packagePath, (content) => rewriteRootPackageName(content, options.appName));
|
|
4113
|
+
await renameFrontendWorker(target, options.appName);
|
|
4039
4114
|
const scope = await applyScopeRewrite(target, options.scope);
|
|
4040
4115
|
await neutralizeAgentInstructions(target);
|
|
4041
4116
|
await neutralizeSourceCli(target, options.scope, options.brandName);
|
|
@@ -4074,7 +4149,7 @@ Create a buyer-owned SaaS application from your licensed ${suggestBrandName(CLI_
|
|
|
4074
4149
|
Options:
|
|
4075
4150
|
--license-key <key> Licence key from your purchase (or set ${LICENSE_KEY_VARIABLE})
|
|
4076
4151
|
--source-version <version> Pin an immutable release (default: the latest you are entitled to)
|
|
4077
|
-
--from <path> Use a local
|
|
4152
|
+
--from <path> Use a local Git checkout, tracked files only (maintainer/testing)
|
|
4078
4153
|
--scope <namespace> Advanced workspace namespace override (default: derived from brand)
|
|
4079
4154
|
--brand <name> Display name (default: derived from app name)
|
|
4080
4155
|
--yes Accept recommended defaults for omitted options
|
|
@@ -4287,7 +4362,7 @@ async function collectOptions(parsed) {
|
|
|
4287
4362
|
};
|
|
4288
4363
|
}
|
|
4289
4364
|
async function readCliVersion() {
|
|
4290
|
-
const packageJson = JSON.parse(await readFile6(resolve4(
|
|
4365
|
+
const packageJson = JSON.parse(await readFile6(resolve4(dirname3(fileURLToPath2(import.meta.url)), "../package.json"), "utf8"));
|
|
4291
4366
|
if (!packageJson.version)
|
|
4292
4367
|
throw new Error(`${CLI_NAME} package version is missing.`);
|
|
4293
4368
|
return packageJson.version;
|
|
@@ -4443,8 +4518,8 @@ async function createApplication(options) {
|
|
|
4443
4518
|
await assertTargetAvailable(target);
|
|
4444
4519
|
assertSiblingTarget(options.from, target);
|
|
4445
4520
|
const databasePort = await findAvailablePort();
|
|
4446
|
-
await mkdir5(
|
|
4447
|
-
const staging = await mkdtemp2(join8(
|
|
4521
|
+
await mkdir5(dirname3(target), { recursive: true });
|
|
4522
|
+
const staging = await mkdtemp2(join8(dirname3(target), `.${options.appName}-${CLI_NAME}-`));
|
|
4448
4523
|
const preparing = "Preparing your application";
|
|
4449
4524
|
const reporter = createScaffoldReporter(preparing, Boolean(options.licenseKey));
|
|
4450
4525
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superslate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Create a production-oriented SaaS application from superslate.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@clack/prompts": "^1.7.0",
|
|
42
|
-
"@types/node": "^26.
|
|
42
|
+
"@types/node": "^26.2.0",
|
|
43
43
|
"typescript": "^7.0.2",
|
|
44
44
|
"vitest": "^4.1.10"
|
|
45
45
|
},
|