create-specra 0.2.2 → 0.2.3

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.
Files changed (42) hide show
  1. package/dist/{api-client-4XZPF7GL.js → api-client-VHQARPDT.js} +3 -3
  2. package/dist/chunk-5765WX4D.js +192 -0
  3. package/dist/chunk-5765WX4D.js.map +1 -0
  4. package/dist/{chunk-CIM73PDF.js → chunk-72RDEJR2.js} +2 -2
  5. package/dist/chunk-SQ2MMFUZ.js +102 -0
  6. package/dist/chunk-SQ2MMFUZ.js.map +1 -0
  7. package/dist/cli.js +7 -7
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{deploy-ETWFNB4Z.js → deploy-V4JO2D6B.js} +32 -6
  10. package/dist/deploy-V4JO2D6B.js.map +1 -0
  11. package/dist/{doctor-IFELWGQB.js → doctor-ICALAJ4N.js} +41 -2
  12. package/dist/doctor-ICALAJ4N.js.map +1 -0
  13. package/dist/index.js +40 -107
  14. package/dist/index.js.map +1 -1
  15. package/dist/{login-DRPP77G4.js → login-UG3WU7DY.js} +17 -11
  16. package/dist/login-UG3WU7DY.js.map +1 -0
  17. package/dist/{logout-UJFYUAQC.js → logout-WJKHJZT6.js} +2 -2
  18. package/dist/{logs-K2CSCKOE.js → logs-BLUJPWNO.js} +3 -3
  19. package/dist/{projects-NORBBC4D.js → projects-LJ57GK3D.js} +3 -3
  20. package/package.json +1 -1
  21. package/templates/book-docs/.env.sample +1 -0
  22. package/templates/book-docs/package.json +1 -1
  23. package/templates/book-docs/src/routes/+layout.server.ts +3 -0
  24. package/templates/book-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +1 -1
  25. package/templates/book-docs/svelte.config.js +6 -1
  26. package/templates/jbrains-docs/.env.sample +1 -0
  27. package/templates/jbrains-docs/package.json +1 -1
  28. package/templates/jbrains-docs/src/routes/+layout.server.ts +3 -0
  29. package/templates/jbrains-docs/src/routes/docs/[version]/[...slug]/+page.server.ts +1 -1
  30. package/templates/jbrains-docs/svelte.config.js +6 -1
  31. package/templates/minimal/.env.sample +1 -0
  32. package/templates/minimal/svelte.config.js +1 -1
  33. package/dist/chunk-ATRUBLRX.js +0 -102
  34. package/dist/chunk-ATRUBLRX.js.map +0 -1
  35. package/dist/deploy-ETWFNB4Z.js.map +0 -1
  36. package/dist/doctor-IFELWGQB.js.map +0 -1
  37. package/dist/login-DRPP77G4.js.map +0 -1
  38. /package/dist/{api-client-4XZPF7GL.js.map → api-client-VHQARPDT.js.map} +0 -0
  39. /package/dist/{chunk-CIM73PDF.js.map → chunk-72RDEJR2.js.map} +0 -0
  40. /package/dist/{logout-UJFYUAQC.js.map → logout-WJKHJZT6.js.map} +0 -0
  41. /package/dist/{logs-K2CSCKOE.js.map → logs-BLUJPWNO.js.map} +0 -0
  42. /package/dist/{projects-NORBBC4D.js.map → projects-LJ57GK3D.js.map} +0 -0
@@ -4,12 +4,12 @@ import {
4
4
  apiRequest,
5
5
  apiUpload,
6
6
  formatError
7
- } from "./chunk-CIM73PDF.js";
8
- import "./chunk-ATRUBLRX.js";
7
+ } from "./chunk-72RDEJR2.js";
8
+ import "./chunk-5765WX4D.js";
9
9
  export {
10
10
  ApiError,
11
11
  apiRequest,
12
12
  apiUpload,
13
13
  formatError
14
14
  };
15
- //# sourceMappingURL=api-client-4XZPF7GL.js.map
15
+ //# sourceMappingURL=api-client-VHQARPDT.js.map
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config.ts
4
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
5
+ import { join, resolve } from "path";
6
+ import { homedir } from "os";
7
+ var GLOBAL_CONFIG_DIR = join(homedir(), ".specra");
8
+ var GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, "config.json");
9
+ var DEFAULT_TOKEN_ENV = "SPECRA_TOKEN";
10
+ var DEFAULT_GLOBAL_CONFIG = {
11
+ apiUrl: "https://specra-docs.com"
12
+ };
13
+ function getEnvFilePath(dir) {
14
+ return join(resolve(dir || "."), ".env");
15
+ }
16
+ function readEnvFile(dir) {
17
+ const envPath = getEnvFilePath(dir);
18
+ try {
19
+ const content = readFileSync(envPath, "utf-8");
20
+ const env = {};
21
+ for (const line of content.split("\n")) {
22
+ const trimmed = line.trim();
23
+ if (!trimmed || trimmed.startsWith("#")) continue;
24
+ const eqIdx = trimmed.indexOf("=");
25
+ if (eqIdx === -1) continue;
26
+ const key = trimmed.slice(0, eqIdx).trim();
27
+ const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
28
+ env[key] = value;
29
+ }
30
+ return env;
31
+ } catch {
32
+ return {};
33
+ }
34
+ }
35
+ function writeEnvVar(key, value, dir) {
36
+ const envPath = getEnvFilePath(dir);
37
+ let content = "";
38
+ try {
39
+ content = readFileSync(envPath, "utf-8");
40
+ } catch {
41
+ }
42
+ const lines = content.split("\n");
43
+ let found = false;
44
+ for (let i = 0; i < lines.length; i++) {
45
+ if (lines[i].trim().startsWith(`${key}=`)) {
46
+ lines[i] = `${key}=${value}`;
47
+ found = true;
48
+ break;
49
+ }
50
+ }
51
+ if (!found) {
52
+ if (lines.length > 0 && lines[lines.length - 1] !== "") {
53
+ lines.push("");
54
+ }
55
+ lines.push(`${key}=${value}`);
56
+ }
57
+ writeFileSync(envPath, lines.join("\n") + "\n");
58
+ }
59
+ function removeEnvVar(key, dir) {
60
+ const envPath = getEnvFilePath(dir);
61
+ try {
62
+ const content = readFileSync(envPath, "utf-8");
63
+ const lines = content.split("\n").filter((line) => !line.trim().startsWith(`${key}=`));
64
+ writeFileSync(envPath, lines.join("\n"));
65
+ } catch {
66
+ }
67
+ }
68
+ function ensureGitignore(dir) {
69
+ const gitignorePath = join(resolve(dir || "."), ".gitignore");
70
+ try {
71
+ let content = "";
72
+ try {
73
+ content = readFileSync(gitignorePath, "utf-8");
74
+ } catch {
75
+ }
76
+ if (!content.split("\n").some((line) => line.trim() === ".env")) {
77
+ const newline = content && !content.endsWith("\n") ? "\n" : "";
78
+ writeFileSync(gitignorePath, content + newline + ".env\n");
79
+ }
80
+ } catch {
81
+ }
82
+ }
83
+ function getGlobalConfig() {
84
+ try {
85
+ const raw = readFileSync(GLOBAL_CONFIG_FILE, "utf-8");
86
+ return { ...DEFAULT_GLOBAL_CONFIG, ...JSON.parse(raw) };
87
+ } catch {
88
+ return DEFAULT_GLOBAL_CONFIG;
89
+ }
90
+ }
91
+ function saveGlobalConfig(config) {
92
+ mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
93
+ const current = getGlobalConfig();
94
+ const merged = { ...current, ...config };
95
+ writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(merged, null, 2));
96
+ }
97
+ function clearGlobalToken() {
98
+ const config = getGlobalConfig();
99
+ delete config.token;
100
+ mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
101
+ writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2));
102
+ }
103
+ function getLocalConfigPath(dir) {
104
+ return join(resolve(dir || "."), "specra.config.json");
105
+ }
106
+ function readLocalConfig(dir) {
107
+ const configPath = getLocalConfigPath(dir);
108
+ try {
109
+ return JSON.parse(readFileSync(configPath, "utf-8"));
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ function getLocalToken(dir) {
115
+ const config = readLocalConfig(dir);
116
+ if (!config?.auth) return void 0;
117
+ if (config.auth.tokenEnv) {
118
+ const envName = config.auth.tokenEnv;
119
+ if (process.env[envName]) return process.env[envName];
120
+ const envVars = readEnvFile(dir);
121
+ return envVars[envName];
122
+ }
123
+ if (config.auth.source === "global") {
124
+ return getGlobalConfig().token;
125
+ }
126
+ return config.auth.token;
127
+ }
128
+ function saveLocalToken(token, dir) {
129
+ const configPath = getLocalConfigPath(dir);
130
+ if (!existsSync(configPath)) {
131
+ throw new Error(`specra.config.json not found in ${resolve(dir || ".")}. Are you in a Specra project?`);
132
+ }
133
+ writeEnvVar(DEFAULT_TOKEN_ENV, token, dir);
134
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
135
+ config.auth = { tokenEnv: DEFAULT_TOKEN_ENV };
136
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
137
+ ensureGitignore(dir);
138
+ }
139
+ function clearLocalToken(dir) {
140
+ removeEnvVar(DEFAULT_TOKEN_ENV, dir);
141
+ const configPath = getLocalConfigPath(dir);
142
+ if (!existsSync(configPath)) return;
143
+ try {
144
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
145
+ if (config.auth) {
146
+ delete config.auth;
147
+ }
148
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
149
+ } catch {
150
+ }
151
+ }
152
+ function getToken(dir) {
153
+ return getLocalToken(dir) || getGlobalConfig().token;
154
+ }
155
+ function getConfig() {
156
+ return getGlobalConfig();
157
+ }
158
+ function isAuthenticated(dir) {
159
+ return !!getToken(dir);
160
+ }
161
+ function saveToken(token, options) {
162
+ if (options?.global) {
163
+ saveGlobalConfig({ token });
164
+ const configPath = getLocalConfigPath(options?.dir);
165
+ if (existsSync(configPath)) {
166
+ try {
167
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
168
+ config.auth = { source: "global" };
169
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
170
+ } catch {
171
+ }
172
+ }
173
+ } else {
174
+ saveLocalToken(token, options?.dir);
175
+ }
176
+ }
177
+ function clearToken(options) {
178
+ if (options?.global) {
179
+ clearGlobalToken();
180
+ } else {
181
+ clearLocalToken(options?.dir);
182
+ }
183
+ }
184
+
185
+ export {
186
+ getToken,
187
+ getConfig,
188
+ isAuthenticated,
189
+ saveToken,
190
+ clearToken
191
+ };
192
+ //# sourceMappingURL=chunk-5765WX4D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { homedir } from 'os'\n\n// Global config: ~/.specra/config.json\nconst GLOBAL_CONFIG_DIR = join(homedir(), '.specra')\nconst GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, 'config.json')\n\nconst DEFAULT_TOKEN_ENV = 'SPECRA_TOKEN'\n\ninterface GlobalConfig {\n apiUrl: string\n token?: string\n defaultProject?: string\n}\n\nconst DEFAULT_GLOBAL_CONFIG: GlobalConfig = {\n apiUrl: 'https://specra-docs.com',\n}\n\n// --------------- .env file helpers ---------------\n\nfunction getEnvFilePath(dir?: string): string {\n return join(resolve(dir || '.'), '.env')\n}\n\nfunction readEnvFile(dir?: string): Record<string, string> {\n const envPath = getEnvFilePath(dir)\n try {\n const content = readFileSync(envPath, 'utf-8')\n const env: Record<string, string> = {}\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#')) continue\n const eqIdx = trimmed.indexOf('=')\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim().replace(/^[\"']|[\"']$/g, '')\n env[key] = value\n }\n return env\n } catch {\n return {}\n }\n}\n\nfunction writeEnvVar(key: string, value: string, dir?: string) {\n const envPath = getEnvFilePath(dir)\n let content = ''\n try {\n content = readFileSync(envPath, 'utf-8')\n } catch {\n // file doesn't exist yet\n }\n\n const lines = content.split('\\n')\n let found = false\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].trim().startsWith(`${key}=`)) {\n lines[i] = `${key}=${value}`\n found = true\n break\n }\n }\n\n if (!found) {\n // Ensure trailing newline before appending\n if (lines.length > 0 && lines[lines.length - 1] !== '') {\n lines.push('')\n }\n lines.push(`${key}=${value}`)\n }\n\n writeFileSync(envPath, lines.join('\\n') + '\\n')\n}\n\nfunction removeEnvVar(key: string, dir?: string) {\n const envPath = getEnvFilePath(dir)\n try {\n const content = readFileSync(envPath, 'utf-8')\n const lines = content.split('\\n').filter(line => !line.trim().startsWith(`${key}=`))\n writeFileSync(envPath, lines.join('\\n'))\n } catch {\n // file doesn't exist, nothing to remove\n }\n}\n\nfunction ensureGitignore(dir?: string) {\n const gitignorePath = join(resolve(dir || '.'), '.gitignore')\n try {\n let content = ''\n try {\n content = readFileSync(gitignorePath, 'utf-8')\n } catch {\n // no .gitignore yet\n }\n if (!content.split('\\n').some(line => line.trim() === '.env')) {\n const newline = content && !content.endsWith('\\n') ? '\\n' : ''\n writeFileSync(gitignorePath, content + newline + '.env\\n')\n }\n } catch {\n // ignore\n }\n}\n\n// --------------- Global config ---------------\n\nexport function getGlobalConfig(): GlobalConfig {\n try {\n const raw = readFileSync(GLOBAL_CONFIG_FILE, 'utf-8')\n return { ...DEFAULT_GLOBAL_CONFIG, ...JSON.parse(raw) }\n } catch {\n return DEFAULT_GLOBAL_CONFIG\n }\n}\n\nexport function saveGlobalConfig(config: Partial<GlobalConfig>) {\n mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true })\n const current = getGlobalConfig()\n const merged = { ...current, ...config }\n writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(merged, null, 2))\n}\n\nexport function clearGlobalToken() {\n const config = getGlobalConfig()\n delete config.token\n mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true })\n writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2))\n}\n\n// --------------- Local config (specra.config.json) ---------------\n\nfunction getLocalConfigPath(dir?: string): string {\n return join(resolve(dir || '.'), 'specra.config.json')\n}\n\nfunction readLocalConfig(dir?: string): Record<string, any> | null {\n const configPath = getLocalConfigPath(dir)\n try {\n return JSON.parse(readFileSync(configPath, 'utf-8'))\n } catch {\n return null\n }\n}\n\nexport function getLocalToken(dir?: string): string | undefined {\n const config = readLocalConfig(dir)\n if (!config?.auth) return undefined\n\n // Env var reference: auth.tokenEnv → read from process.env, then .env file\n if (config.auth.tokenEnv) {\n const envName = config.auth.tokenEnv\n if (process.env[envName]) return process.env[envName]\n const envVars = readEnvFile(dir)\n return envVars[envName]\n }\n\n // Global reference: auth.source === \"global\" → read from ~/.specra/config.json\n if (config.auth.source === 'global') {\n return getGlobalConfig().token\n }\n\n // Legacy: direct auth.token (backwards compat for existing projects)\n return config.auth.token\n}\n\nexport function saveLocalToken(token: string, dir?: string) {\n const configPath = getLocalConfigPath(dir)\n if (!existsSync(configPath)) {\n throw new Error(`specra.config.json not found in ${resolve(dir || '.')}. Are you in a Specra project?`)\n }\n\n // Save the actual token to .env\n writeEnvVar(DEFAULT_TOKEN_ENV, token, dir)\n\n // Point specra.config.json at the env var (never store raw token)\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n config.auth = { tokenEnv: DEFAULT_TOKEN_ENV }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n\n // Make sure .env is gitignored\n ensureGitignore(dir)\n}\n\nexport function clearLocalToken(dir?: string) {\n // Remove token from .env\n removeEnvVar(DEFAULT_TOKEN_ENV, dir)\n\n // Clean up auth section from specra.config.json\n const configPath = getLocalConfigPath(dir)\n if (!existsSync(configPath)) return\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n if (config.auth) {\n delete config.auth\n }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n } catch {\n // ignore parse errors\n }\n}\n\n// --------------- Unified accessors ---------------\n\n/** Get token: checks local specra.config.json first, then global ~/.specra/config.json */\nexport function getToken(dir?: string): string | undefined {\n return getLocalToken(dir) || getGlobalConfig().token\n}\n\n/** Get the API URL from global config */\nexport function getConfig(): GlobalConfig {\n return getGlobalConfig()\n}\n\nexport function isAuthenticated(dir?: string): boolean {\n return !!getToken(dir)\n}\n\n/** Save token to local .env (default) or global ~/.specra/config.json */\nexport function saveToken(token: string, options?: { global?: boolean; dir?: string }) {\n if (options?.global) {\n saveGlobalConfig({ token })\n // If specra.config.json exists locally, point it at global\n const configPath = getLocalConfigPath(options?.dir)\n if (existsSync(configPath)) {\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n config.auth = { source: 'global' }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n } catch {\n // ignore\n }\n }\n } else {\n saveLocalToken(token, options?.dir)\n }\n}\n\n/** Clear token from local .env (default) or global config */\nexport function clearToken(options?: { global?: boolean; dir?: string }) {\n if (options?.global) {\n clearGlobalToken()\n } else {\n clearLocalToken(options?.dir)\n }\n}\n\n// Legacy exports for backwards compat\nexport function saveConfig(config: Partial<GlobalConfig>) {\n saveGlobalConfig(config)\n}\n\nexport function clearConfig() {\n if (existsSync(GLOBAL_CONFIG_FILE)) {\n unlinkSync(GLOBAL_CONFIG_FILE)\n }\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getConfig,
4
4
  getToken
5
- } from "./chunk-ATRUBLRX.js";
5
+ } from "./chunk-5765WX4D.js";
6
6
 
7
7
  // src/api-client.ts
8
8
  var ApiError = class extends Error {
@@ -91,4 +91,4 @@ export {
91
91
  apiRequest,
92
92
  apiUpload
93
93
  };
94
- //# sourceMappingURL=chunk-CIM73PDF.js.map
94
+ //# sourceMappingURL=chunk-72RDEJR2.js.map
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils.ts
4
+ import validateNpmPackageName from "validate-npm-package-name";
5
+ import { execSync } from "child_process";
6
+ import fs from "fs";
7
+ import path from "path";
8
+ function validateProjectName(name) {
9
+ const validation = validateNpmPackageName(name);
10
+ if (validation.validForNewPackages) {
11
+ return { valid: true };
12
+ }
13
+ return {
14
+ valid: false,
15
+ problems: [
16
+ ...validation.errors || [],
17
+ ...validation.warnings || []
18
+ ]
19
+ };
20
+ }
21
+ function isWriteable(directory) {
22
+ try {
23
+ fs.accessSync(directory, fs.constants.W_OK);
24
+ return true;
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+ function isFolderEmpty(path2) {
30
+ const files = fs.readdirSync(path2);
31
+ return files.length === 0 || files.length === 1 && files[0] === ".git";
32
+ }
33
+ function getPackageManagerCommand(packageManager) {
34
+ switch (packageManager) {
35
+ case "yarn":
36
+ return {
37
+ install: "yarn install",
38
+ run: (script) => `yarn ${script}`
39
+ };
40
+ case "pnpm":
41
+ return {
42
+ install: "pnpm install",
43
+ run: (script) => `pnpm ${script}`
44
+ };
45
+ case "npm":
46
+ default:
47
+ return {
48
+ install: "npm install",
49
+ run: (script) => `npm run ${script}`
50
+ };
51
+ }
52
+ }
53
+ function detectPackageManager(dir) {
54
+ const configPath = path.join(dir, "specra.config.json");
55
+ try {
56
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
57
+ if (config.packageManager) return config.packageManager;
58
+ } catch {
59
+ }
60
+ if (fs.existsSync(path.join(dir, "pnpm-lock.yaml"))) return "pnpm";
61
+ if (fs.existsSync(path.join(dir, "yarn.lock"))) return "yarn";
62
+ return "npm";
63
+ }
64
+ function tryGitInit(root) {
65
+ try {
66
+ execSync("git --version", { stdio: "ignore" });
67
+ execSync("git init", { cwd: root, stdio: "ignore" });
68
+ execSync("git add -A", { cwd: root, stdio: "ignore" });
69
+ execSync('git commit -m "Initial commit from create-specra"', {
70
+ cwd: root,
71
+ stdio: "ignore"
72
+ });
73
+ return true;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+ function copyRecursive(src, dest) {
79
+ const stat = fs.statSync(src);
80
+ if (stat.isDirectory()) {
81
+ fs.mkdirSync(dest, { recursive: true });
82
+ const entries = fs.readdirSync(src);
83
+ for (const entry of entries) {
84
+ const srcPath = path.join(src, entry);
85
+ const destPath = path.join(dest, entry);
86
+ copyRecursive(srcPath, destPath);
87
+ }
88
+ } else {
89
+ fs.copyFileSync(src, dest);
90
+ }
91
+ }
92
+
93
+ export {
94
+ validateProjectName,
95
+ isWriteable,
96
+ isFolderEmpty,
97
+ getPackageManagerCommand,
98
+ detectPackageManager,
99
+ tryGitInit,
100
+ copyRecursive
101
+ };
102
+ //# sourceMappingURL=chunk-SQ2MMFUZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts"],"sourcesContent":["import validateNpmPackageName from 'validate-npm-package-name'\nimport { execSync } from 'child_process'\nimport fs from 'fs'\nimport path from 'path'\n\nexport function validateProjectName(name: string) {\n const validation = validateNpmPackageName(name)\n\n if (validation.validForNewPackages) {\n return { valid: true }\n }\n\n return {\n valid: false,\n problems: [\n ...(validation.errors || []),\n ...(validation.warnings || []),\n ],\n }\n}\n\nexport function isWriteable(directory: string): boolean {\n try {\n fs.accessSync(directory, fs.constants.W_OK)\n return true\n } catch {\n return false\n }\n}\n\nexport function isFolderEmpty(path: string): boolean {\n const files = fs.readdirSync(path)\n return files.length === 0 || (files.length === 1 && files[0] === '.git')\n}\n\nexport function getPackageManagerCommand(packageManager: string): {\n install: string\n run: (script: string) => string\n} {\n switch (packageManager) {\n case 'yarn':\n return {\n install: 'yarn install',\n run: (script) => `yarn ${script}`,\n }\n case 'pnpm':\n return {\n install: 'pnpm install',\n run: (script) => `pnpm ${script}`,\n }\n case 'npm':\n default:\n return {\n install: 'npm install',\n run: (script) => `npm run ${script}`,\n }\n }\n}\n\nexport function detectPackageManager(dir: string): string {\n // 1. Check specra.config.json for explicit setting\n const configPath = path.join(dir, 'specra.config.json')\n try {\n const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))\n if (config.packageManager) return config.packageManager\n } catch {\n // ignore\n }\n\n // 2. Detect from lockfiles\n if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) return 'pnpm'\n if (fs.existsSync(path.join(dir, 'yarn.lock'))) return 'yarn'\n return 'npm'\n}\n\nexport function tryGitInit(root: string): boolean {\n try {\n execSync('git --version', { stdio: 'ignore' })\n execSync('git init', { cwd: root, stdio: 'ignore' })\n execSync('git add -A', { cwd: root, stdio: 'ignore' })\n execSync('git commit -m \"Initial commit from create-specra\"', {\n cwd: root,\n stdio: 'ignore',\n })\n return true\n } catch {\n return false\n }\n}\n\nexport function copyRecursive(src: string, dest: string) {\n const stat = fs.statSync(src)\n\n if (stat.isDirectory()) {\n fs.mkdirSync(dest, { recursive: true })\n const entries = fs.readdirSync(src)\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry)\n const destPath = path.join(dest, entry)\n copyRecursive(srcPath, destPath)\n }\n } else {\n fs.copyFileSync(src, dest)\n }\n}\n"],"mappings":";;;AAAA,OAAO,4BAA4B;AACnC,SAAS,gBAAgB;AACzB,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,SAAS,oBAAoB,MAAc;AAChD,QAAM,aAAa,uBAAuB,IAAI;AAE9C,MAAI,WAAW,qBAAqB;AAClC,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,MACR,GAAI,WAAW,UAAU,CAAC;AAAA,MAC1B,GAAI,WAAW,YAAY,CAAC;AAAA,IAC9B;AAAA,EACF;AACF;AAEO,SAAS,YAAY,WAA4B;AACtD,MAAI;AACF,OAAG,WAAW,WAAW,GAAG,UAAU,IAAI;AAC1C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAcA,OAAuB;AACnD,QAAM,QAAQ,GAAG,YAAYA,KAAI;AACjC,SAAO,MAAM,WAAW,KAAM,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM;AACnE;AAEO,SAAS,yBAAyB,gBAGvC;AACA,UAAQ,gBAAgB;AAAA,IACtB,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,KAAK,CAAC,WAAW,QAAQ,MAAM;AAAA,MACjC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,KAAK,CAAC,WAAW,QAAQ,MAAM;AAAA,MACjC;AAAA,IACF,KAAK;AAAA,IACL;AACE,aAAO;AAAA,QACL,SAAS;AAAA,QACT,KAAK,CAAC,WAAW,WAAW,MAAM;AAAA,MACpC;AAAA,EACJ;AACF;AAEO,SAAS,qBAAqB,KAAqB;AAExD,QAAM,aAAa,KAAK,KAAK,KAAK,oBAAoB;AACtD,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG,aAAa,YAAY,OAAO,CAAC;AAC9D,QAAI,OAAO,eAAgB,QAAO,OAAO;AAAA,EAC3C,QAAQ;AAAA,EAER;AAGA,MAAI,GAAG,WAAW,KAAK,KAAK,KAAK,gBAAgB,CAAC,EAAG,QAAO;AAC5D,MAAI,GAAG,WAAW,KAAK,KAAK,KAAK,WAAW,CAAC,EAAG,QAAO;AACvD,SAAO;AACT;AAEO,SAAS,WAAW,MAAuB;AAChD,MAAI;AACF,aAAS,iBAAiB,EAAE,OAAO,SAAS,CAAC;AAC7C,aAAS,YAAY,EAAE,KAAK,MAAM,OAAO,SAAS,CAAC;AACnD,aAAS,cAAc,EAAE,KAAK,MAAM,OAAO,SAAS,CAAC;AACrD,aAAS,qDAAqD;AAAA,MAC5D,KAAK;AAAA,MACL,OAAO;AAAA,IACT,CAAC;AACD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,KAAa,MAAc;AACvD,QAAM,OAAO,GAAG,SAAS,GAAG;AAE5B,MAAI,KAAK,YAAY,GAAG;AACtB,OAAG,UAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AACtC,UAAM,UAAU,GAAG,YAAY,GAAG;AAElC,eAAW,SAAS,SAAS;AAC3B,YAAM,UAAU,KAAK,KAAK,KAAK,KAAK;AACpC,YAAM,WAAW,KAAK,KAAK,MAAM,KAAK;AACtC,oBAAc,SAAS,QAAQ;AAAA,IACjC;AAAA,EACF,OAAO;AACL,OAAG,aAAa,KAAK,IAAI;AAAA,EAC3B;AACF;","names":["path"]}
package/dist/cli.js CHANGED
@@ -9,27 +9,27 @@ var { version } = require2("../package.json");
9
9
  var program = new Command();
10
10
  program.name("specra").description("Specra CLI - Deploy and manage your documentation sites").version(version);
11
11
  program.command("login").description("Authenticate with your Specra account").option("-g, --global", "Store credentials in ~/.specra/ instead of local specra.config.json").action(async (options) => {
12
- const { login } = await import("./login-DRPP77G4.js");
12
+ const { login } = await import("./login-UG3WU7DY.js");
13
13
  await login(options);
14
14
  });
15
15
  program.command("logout").description("Clear stored credentials").option("-g, --global", "Clear credentials from ~/.specra/ instead of local specra.config.json").action(async (options) => {
16
- const { logout } = await import("./logout-UJFYUAQC.js");
16
+ const { logout } = await import("./logout-WJKHJZT6.js");
17
17
  await logout(options);
18
18
  });
19
- program.command("deploy").description("Deploy your docs project").option("-p, --project <id>", "Project ID to deploy to").option("-d, --dir <directory>", "Docs directory to deploy", ".").action(async (options) => {
20
- const { deploy } = await import("./deploy-ETWFNB4Z.js");
19
+ program.command("deploy").description("Deploy your docs project").option("-p, --project <id>", "Project ID to deploy to").option("-d, --dir <directory>", "Docs directory to deploy", ".").option("-v, --verbose", "Show detailed build output and logs").action(async (options) => {
20
+ const { deploy } = await import("./deploy-V4JO2D6B.js");
21
21
  await deploy(options);
22
22
  });
23
23
  program.command("projects").description("List your projects").action(async () => {
24
- const { projects } = await import("./projects-NORBBC4D.js");
24
+ const { projects } = await import("./projects-LJ57GK3D.js");
25
25
  await projects();
26
26
  });
27
27
  program.command("logs").description("View deployment logs").argument("<projectId>", "Project ID").option("--deployment <id>", "Specific deployment ID").action(async (projectId, options) => {
28
- const { logs } = await import("./logs-K2CSCKOE.js");
28
+ const { logs } = await import("./logs-BLUJPWNO.js");
29
29
  await logs(projectId, options);
30
30
  });
31
31
  program.command("doctor").description("Check specra.config.json for issues").option("-d, --dir <directory>", "Project directory to check", ".").action(async (options) => {
32
- const { doctor } = await import("./doctor-IFELWGQB.js");
32
+ const { doctor } = await import("./doctor-ICALAJ4N.js");
33
33
  await doctor(options);
34
34
  });
35
35
  program.parse();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander'\nimport pc from 'picocolors'\nimport { createRequire } from 'module'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../package.json')\n\nconst program = new Command()\n\nprogram\n .name('specra')\n .description('Specra CLI - Deploy and manage your documentation sites')\n .version(version)\n\nprogram\n .command('login')\n .description('Authenticate with your Specra account')\n .option('-g, --global', 'Store credentials in ~/.specra/ instead of local specra.config.json')\n .action(async (options) => {\n const { login } = await import('./commands/login.js')\n await login(options)\n })\n\nprogram\n .command('logout')\n .description('Clear stored credentials')\n .option('-g, --global', 'Clear credentials from ~/.specra/ instead of local specra.config.json')\n .action(async (options) => {\n const { logout } = await import('./commands/logout.js')\n await logout(options)\n })\n\nprogram\n .command('deploy')\n .description('Deploy your docs project')\n .option('-p, --project <id>', 'Project ID to deploy to')\n .option('-d, --dir <directory>', 'Docs directory to deploy', '.')\n .action(async (options) => {\n const { deploy } = await import('./commands/deploy.js')\n await deploy(options)\n })\n\nprogram\n .command('projects')\n .description('List your projects')\n .action(async () => {\n const { projects } = await import('./commands/projects.js')\n await projects()\n })\n\nprogram\n .command('logs')\n .description('View deployment logs')\n .argument('<projectId>', 'Project ID')\n .option('--deployment <id>', 'Specific deployment ID')\n .action(async (projectId: string, options) => {\n const { logs } = await import('./commands/logs.js')\n await logs(projectId, options)\n })\n\nprogram\n .command('doctor')\n .description('Check specra.config.json for issues')\n .option('-d, --dir <directory>', 'Project directory to check', '.')\n .action(async (options) => {\n const { doctor } = await import('./commands/doctor.js')\n await doctor(options)\n })\n\nprogram.parse()\n\n// Handle unhandled rejections\nprocess.on('unhandledRejection', (err) => {\n console.error(pc.red('Error:'), err)\n process.exit(1)\n})\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAE9B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,EAAE,QAAQ,IAAIA,SAAQ,iBAAiB;AAE7C,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,QAAQ,EACb,YAAY,yDAAyD,EACrE,QAAQ,OAAO;AAElB,QACG,QAAQ,OAAO,EACf,YAAY,uCAAuC,EACnD,OAAO,gBAAgB,qEAAqE,EAC5F,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,gBAAgB,uEAAuE,EAC9F,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,sBAAsB,yBAAyB,EACtD,OAAO,yBAAyB,4BAA4B,GAAG,EAC/D,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QACG,QAAQ,UAAU,EAClB,YAAY,oBAAoB,EAChC,OAAO,YAAY;AAClB,QAAM,EAAE,SAAS,IAAI,MAAM,OAAO,wBAAwB;AAC1D,QAAM,SAAS;AACjB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,sBAAsB,EAClC,SAAS,eAAe,YAAY,EACpC,OAAO,qBAAqB,wBAAwB,EACpD,OAAO,OAAO,WAAmB,YAAY;AAC5C,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,oBAAoB;AAClD,QAAM,KAAK,WAAW,OAAO;AAC/B,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,OAAO,yBAAyB,8BAA8B,GAAG,EACjE,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QAAQ,MAAM;AAGd,QAAQ,GAAG,sBAAsB,CAAC,QAAQ;AACxC,UAAQ,MAAM,GAAG,IAAI,QAAQ,GAAG,GAAG;AACnC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["require"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander'\nimport pc from 'picocolors'\nimport { createRequire } from 'module'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../package.json')\n\nconst program = new Command()\n\nprogram\n .name('specra')\n .description('Specra CLI - Deploy and manage your documentation sites')\n .version(version)\n\nprogram\n .command('login')\n .description('Authenticate with your Specra account')\n .option('-g, --global', 'Store credentials in ~/.specra/ instead of local specra.config.json')\n .action(async (options) => {\n const { login } = await import('./commands/login.js')\n await login(options)\n })\n\nprogram\n .command('logout')\n .description('Clear stored credentials')\n .option('-g, --global', 'Clear credentials from ~/.specra/ instead of local specra.config.json')\n .action(async (options) => {\n const { logout } = await import('./commands/logout.js')\n await logout(options)\n })\n\nprogram\n .command('deploy')\n .description('Deploy your docs project')\n .option('-p, --project <id>', 'Project ID to deploy to')\n .option('-d, --dir <directory>', 'Docs directory to deploy', '.')\n .option('-v, --verbose', 'Show detailed build output and logs')\n .action(async (options) => {\n const { deploy } = await import('./commands/deploy.js')\n await deploy(options)\n })\n\nprogram\n .command('projects')\n .description('List your projects')\n .action(async () => {\n const { projects } = await import('./commands/projects.js')\n await projects()\n })\n\nprogram\n .command('logs')\n .description('View deployment logs')\n .argument('<projectId>', 'Project ID')\n .option('--deployment <id>', 'Specific deployment ID')\n .action(async (projectId: string, options) => {\n const { logs } = await import('./commands/logs.js')\n await logs(projectId, options)\n })\n\nprogram\n .command('doctor')\n .description('Check specra.config.json for issues')\n .option('-d, --dir <directory>', 'Project directory to check', '.')\n .action(async (options) => {\n const { doctor } = await import('./commands/doctor.js')\n await doctor(options)\n })\n\nprogram.parse()\n\n// Handle unhandled rejections\nprocess.on('unhandledRejection', (err) => {\n console.error(pc.red('Error:'), err)\n process.exit(1)\n})\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAE9B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,EAAE,QAAQ,IAAIA,SAAQ,iBAAiB;AAE7C,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,QAAQ,EACb,YAAY,yDAAyD,EACrE,QAAQ,OAAO;AAElB,QACG,QAAQ,OAAO,EACf,YAAY,uCAAuC,EACnD,OAAO,gBAAgB,qEAAqE,EAC5F,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,gBAAgB,uEAAuE,EAC9F,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,sBAAsB,yBAAyB,EACtD,OAAO,yBAAyB,4BAA4B,GAAG,EAC/D,OAAO,iBAAiB,qCAAqC,EAC7D,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QACG,QAAQ,UAAU,EAClB,YAAY,oBAAoB,EAChC,OAAO,YAAY;AAClB,QAAM,EAAE,SAAS,IAAI,MAAM,OAAO,wBAAwB;AAC1D,QAAM,SAAS;AACjB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,sBAAsB,EAClC,SAAS,eAAe,YAAY,EACpC,OAAO,qBAAqB,wBAAwB,EACpD,OAAO,OAAO,WAAmB,YAAY;AAC5C,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,oBAAoB;AAClD,QAAM,KAAK,WAAW,OAAO;AAC/B,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,OAAO,yBAAyB,8BAA8B,GAAG,EACjE,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QAAQ,MAAM;AAGd,QAAQ,GAAG,sBAAsB,CAAC,QAAQ;AACxC,UAAQ,MAAM,GAAG,IAAI,QAAQ,GAAG,GAAG;AACnC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["require"]}
@@ -1,13 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ detectPackageManager,
4
+ getPackageManagerCommand
5
+ } from "./chunk-SQ2MMFUZ.js";
2
6
  import {
3
7
  apiRequest,
4
8
  apiUpload,
5
9
  formatError
6
- } from "./chunk-CIM73PDF.js";
10
+ } from "./chunk-72RDEJR2.js";
7
11
  import {
8
12
  getConfig,
9
13
  isAuthenticated
10
- } from "./chunk-ATRUBLRX.js";
14
+ } from "./chunk-5765WX4D.js";
11
15
 
12
16
  // src/commands/deploy.ts
13
17
  import pc from "picocolors";
@@ -100,16 +104,35 @@ async function deploy(options) {
100
104
  }
101
105
  projectId = response.project;
102
106
  }
107
+ const verbose = options.verbose ?? false;
108
+ if (verbose) {
109
+ console.log(pc.dim(`Project ID: ${projectId}`));
110
+ console.log(pc.dim(`Directory: ${dir}`));
111
+ }
103
112
  const spinner = ora("Building project...").start();
104
113
  try {
105
114
  const buildDir = join(dir, "build");
106
115
  const { execSync } = await import("child_process");
116
+ const pm = detectPackageManager(dir);
117
+ const pmCmd = getPackageManagerCommand(pm);
118
+ if (verbose) {
119
+ spinner.stop();
120
+ console.log(pc.dim(`Package manager: ${pm}`));
121
+ console.log(pc.dim(`Running: ${pmCmd.run("build")}`));
122
+ console.log();
123
+ }
107
124
  try {
108
- execSync("npm run build", { cwd: dir, stdio: "pipe" });
125
+ execSync(pmCmd.run("build"), { cwd: dir, stdio: verbose ? "inherit" : "pipe" });
109
126
  } catch (err) {
110
- const stderr = err instanceof Error && "stderr" in err ? err.stderr?.toString() : "";
111
- throw new Error(`Build failed:
127
+ if (!verbose) {
128
+ const stderr = err instanceof Error && "stderr" in err ? err.stderr?.toString() : "";
129
+ throw new Error(`Build failed:
112
130
  ${stderr}`);
131
+ }
132
+ throw new Error("Build failed. See output above.");
133
+ }
134
+ if (verbose) {
135
+ spinner.start();
113
136
  }
114
137
  if (!existsSync2(buildDir)) {
115
138
  throw new Error(
@@ -119,6 +142,9 @@ ${stderr}`);
119
142
  spinner.text = "Packaging build output...";
120
143
  const archive = await createArchive(buildDir);
121
144
  spinner.text = `Uploading (${(archive.length / 1024).toFixed(0)}KB)...`;
145
+ if (verbose) {
146
+ console.log(pc.dim(`Archive size: ${(archive.length / 1024).toFixed(1)}KB`));
147
+ }
122
148
  let commitSha;
123
149
  try {
124
150
  commitSha = execSync("git rev-parse HEAD", { cwd: dir }).toString().trim();
@@ -150,4 +176,4 @@ ${stderr}`);
150
176
  export {
151
177
  deploy
152
178
  };
153
- //# sourceMappingURL=deploy-ETWFNB4Z.js.map
179
+ //# sourceMappingURL=deploy-V4JO2D6B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/deploy.ts","../src/archive.ts"],"sourcesContent":["import pc from 'picocolors'\nimport ora from 'ora'\nimport { createArchive } from '../archive.js'\nimport { apiUpload, apiRequest, formatError } from '../api-client.js'\nimport { isAuthenticated, getConfig } from '../config.js'\nimport { existsSync, readFileSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { detectPackageManager, getPackageManagerCommand } from '../utils.js'\n\ninterface DeployOptions {\n project?: string\n dir: string\n verbose?: boolean\n}\n\nexport async function deploy(options: DeployOptions) {\n if (!isAuthenticated()) {\n console.error(pc.red('Not authenticated. Run `specra login` first.'))\n process.exit(1)\n }\n\n const dir = resolve(options.dir)\n let projectId = options.project\n\n // If no project specified, try to read from specra.config.json\n if (!projectId) {\n const configPath = join(dir, 'specra.config.json')\n if (existsSync(configPath)) {\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n projectId = config.projectId\n } catch {\n // ignore\n }\n }\n }\n\n if (!projectId) {\n // List projects and ask user to pick\n let projects: Array<{ id: string; name: string; subdomain: string }>\n try {\n projects = await apiRequest<Array<{ id: string; name: string; subdomain: string }>>(\n '/api/projects'\n )\n } catch (err) {\n console.error(pc.red(formatError('Failed to fetch projects', err)))\n process.exit(1)\n }\n\n if (projects.length === 0) {\n console.error(\n pc.red('No projects found. Create one at https://specra-docs.com/dashboard/projects/new')\n )\n process.exit(1)\n }\n\n console.log(pc.bold('Select a project to deploy to:'))\n projects.forEach((p, i) => {\n console.log(` ${pc.dim(`${i + 1}.`)} ${p.name} ${pc.dim(`(${p.subdomain}.docs.specra.dev)`)}`)\n })\n\n const prompts = await import('prompts')\n const response = await prompts.default({\n type: 'select',\n name: 'project',\n message: 'Project',\n choices: projects.map((p) => ({\n title: p.name,\n value: p.id,\n description: `${p.subdomain}.docs.specra.dev`,\n })),\n })\n\n if (!response.project) {\n console.log('Aborted.')\n process.exit(1)\n }\n\n projectId = response.project\n }\n\n const verbose = options.verbose ?? false\n\n if (verbose) {\n console.log(pc.dim(`Project ID: ${projectId}`))\n console.log(pc.dim(`Directory: ${dir}`))\n }\n\n const spinner = ora('Building project...').start()\n\n try {\n // 1. Build the project locally\n const buildDir = join(dir, 'build')\n const { execSync } = await import('child_process')\n\n const pm = detectPackageManager(dir)\n const pmCmd = getPackageManagerCommand(pm)\n\n if (verbose) {\n spinner.stop()\n console.log(pc.dim(`Package manager: ${pm}`))\n console.log(pc.dim(`Running: ${pmCmd.run('build')}`))\n console.log()\n }\n\n try {\n execSync(pmCmd.run('build'), { cwd: dir, stdio: verbose ? 'inherit' : 'pipe' })\n } catch (err) {\n if (!verbose) {\n const stderr = err instanceof Error && 'stderr' in err\n ? (err as { stderr: Buffer }).stderr?.toString()\n : ''\n throw new Error(`Build failed:\\n${stderr}`)\n }\n throw new Error('Build failed. See output above.')\n }\n\n if (verbose) {\n spinner.start()\n }\n\n if (!existsSync(buildDir)) {\n throw new Error(\n 'Build output not found. Expected a `build/` directory.\\n' +\n 'Make sure your project uses @sveltejs/adapter-static.'\n )\n }\n\n // 2. Archive the build output\n spinner.text = 'Packaging build output...'\n const archive = await createArchive(buildDir)\n spinner.text = `Uploading (${(archive.length / 1024).toFixed(0)}KB)...`\n\n if (verbose) {\n console.log(pc.dim(`Archive size: ${(archive.length / 1024).toFixed(1)}KB`))\n }\n\n // 3. Get git commit SHA if available\n let commitSha: string | undefined\n try {\n commitSha = execSync('git rev-parse HEAD', { cwd: dir })\n .toString()\n .trim()\n } catch {\n // not a git repo\n }\n\n // 4. Upload as pre-built\n const result = await apiUpload(\n `/api/projects/${projectId}/deploy`,\n archive,\n {\n 'X-Deploy-Trigger': 'CLI',\n 'X-Pre-Built': 'true',\n ...(commitSha ? { 'X-Commit-Sha': commitSha } : {}),\n }\n ) as { deploymentId: string }\n\n spinner.succeed(pc.green('Deployed!'))\n console.log()\n console.log(` Deployment ID: ${pc.cyan(result.deploymentId)}`)\n\n const config = getConfig()\n console.log(\n ` View status: ${pc.dim(`${config.apiUrl}/dashboard/projects/${projectId}/deployments`)}`\n )\n console.log()\n } catch (err) {\n spinner.fail(pc.red('Deploy failed'))\n console.error(pc.red(formatError('', err)))\n process.exit(1)\n }\n}\n","import * as tar from 'tar'\nimport { existsSync, statSync } from 'fs'\nimport { resolve } from 'path'\n\nexport async function createArchive(dir: string): Promise<Buffer> {\n const absDir = resolve(dir)\n\n if (!existsSync(absDir)) {\n throw new Error(`Directory not found: ${absDir}`)\n }\n\n if (!statSync(absDir).isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`)\n }\n\n const chunks: Buffer[] = []\n\n const stream = tar.create(\n {\n gzip: true,\n cwd: absDir,\n },\n ['.']\n )\n\n for await (const chunk of stream) {\n chunks.push(chunk as Buffer)\n }\n\n const result = Buffer.concat(chunks)\n if (result.length === 0) {\n throw new Error('Archive is empty — no files found in the build output')\n }\n\n return result\n}\n"],"mappings":"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","names":["existsSync","resolve","resolve","existsSync"]}
@@ -218,7 +218,46 @@ async function doctor(options) {
218
218
  results.push({
219
219
  label: "auth.token in config",
220
220
  status: "warn",
221
- message: "Token found in specra.config.json. Make sure this file is in .gitignore, or use `specra login --global` to store it in ~/.specra/ instead."
221
+ message: "Raw token found in specra.config.json. Run `specra login` again to migrate it to .env."
222
+ });
223
+ } else if (config.auth?.tokenEnv) {
224
+ const envPath = join(dir, ".env");
225
+ if (existsSync(envPath)) {
226
+ try {
227
+ const envContent = readFileSync(envPath, "utf-8");
228
+ const hasVar = envContent.split("\n").some((line) => line.trim().startsWith(`${config.auth.tokenEnv}=`));
229
+ if (hasVar) {
230
+ results.push({
231
+ label: "Auth",
232
+ status: "pass",
233
+ message: `Token stored in .env as ${config.auth.tokenEnv}`
234
+ });
235
+ } else {
236
+ results.push({
237
+ label: "Auth",
238
+ status: "fail",
239
+ message: `specra.config.json references ${config.auth.tokenEnv} but it's not set in .env. Run \`specra login\`.`
240
+ });
241
+ }
242
+ } catch {
243
+ results.push({
244
+ label: "Auth",
245
+ status: "fail",
246
+ message: `.env file unreadable. Run \`specra login\` to re-authenticate.`
247
+ });
248
+ }
249
+ } else {
250
+ results.push({
251
+ label: "Auth",
252
+ status: "fail",
253
+ message: `No .env file found but specra.config.json references ${config.auth.tokenEnv}. Run \`specra login\`.`
254
+ });
255
+ }
256
+ } else if (config.auth?.source === "global") {
257
+ results.push({
258
+ label: "Auth",
259
+ status: "pass",
260
+ message: "Using global credentials from ~/.specra/config.json"
222
261
  });
223
262
  }
224
263
  if (config.navigation?.tabGroups && Array.isArray(config.navigation.tabGroups)) {
@@ -267,4 +306,4 @@ function printResults(results) {
267
306
  export {
268
307
  doctor
269
308
  };
270
- //# sourceMappingURL=doctor-IFELWGQB.js.map
309
+ //# sourceMappingURL=doctor-ICALAJ4N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/doctor.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { existsSync, readFileSync, readdirSync } from 'fs'\nimport { join, resolve } from 'path'\n\ninterface DoctorOptions {\n dir: string\n}\n\ninterface DiagnosticResult {\n label: string\n status: 'pass' | 'warn' | 'fail'\n message: string\n}\n\nexport async function doctor(options: DoctorOptions) {\n const dir = resolve(options.dir)\n const results: DiagnosticResult[] = []\n\n console.log()\n console.log(pc.bold('Specra Doctor'))\n console.log(pc.dim(`Checking project in ${dir}`))\n console.log()\n\n // 1. Check specra.config.json exists\n const configPath = join(dir, 'specra.config.json')\n if (!existsSync(configPath)) {\n results.push({\n label: 'specra.config.json',\n status: 'fail',\n message: 'File not found. Run `create-specra init` to create a new project.',\n })\n printResults(results)\n return\n }\n\n results.push({\n label: 'specra.config.json',\n status: 'pass',\n message: 'Found',\n })\n\n // 2. Parse config\n let config: Record<string, any>\n try {\n const raw = readFileSync(configPath, 'utf-8')\n config = JSON.parse(raw)\n } catch (err) {\n results.push({\n label: 'Config parsing',\n status: 'fail',\n message: `Invalid JSON: ${err instanceof Error ? err.message : err}`,\n })\n printResults(results)\n return\n }\n\n results.push({\n label: 'Config parsing',\n status: 'pass',\n message: 'Valid JSON',\n })\n\n // 3. Check required: site\n if (!config.site) {\n results.push({\n label: 'site',\n status: 'fail',\n message: 'Missing required \"site\" section',\n })\n } else {\n // Check site.title\n if (!config.site.title || typeof config.site.title !== 'string') {\n results.push({\n label: 'site.title',\n status: 'fail',\n message: 'Missing required \"site.title\" (string)',\n })\n } else {\n results.push({\n label: 'site.title',\n status: 'pass',\n message: config.site.title,\n })\n }\n\n // Check site.description\n if (!config.site.description) {\n results.push({\n label: 'site.description',\n status: 'warn',\n message: 'No description set. Recommended for SEO.',\n })\n } else {\n results.push({\n label: 'site.description',\n status: 'pass',\n message: 'Set',\n })\n }\n\n // Check site.url\n if (!config.site.url) {\n results.push({\n label: 'site.url',\n status: 'warn',\n message: 'No URL set. Recommended for canonical URLs and SEO.',\n })\n } else {\n results.push({\n label: 'site.url',\n status: 'pass',\n message: config.site.url,\n })\n }\n }\n\n // 4. Check versioning + docs directory\n const versioning = config.features?.versioning !== false\n const docsDir = join(dir, 'docs')\n\n if (existsSync(docsDir)) {\n results.push({\n label: 'docs/ directory',\n status: 'pass',\n message: 'Found',\n })\n\n if (versioning) {\n // Check for version directories\n const entries = readdirSync(docsDir, { withFileTypes: true })\n const versionDirs = entries.filter((e) => e.isDirectory())\n\n if (versionDirs.length === 0) {\n results.push({\n label: 'Version directories',\n status: 'fail',\n message: 'Versioning enabled but no version directories found in docs/ (e.g., docs/v1.0.0/)',\n })\n } else {\n const names = versionDirs.map((d) => d.name).join(', ')\n results.push({\n label: 'Version directories',\n status: 'pass',\n message: `Found: ${names}`,\n })\n\n // Check activeVersion matches a directory\n if (config.site?.activeVersion) {\n const hasMatch = versionDirs.some((d) => d.name === config.site.activeVersion)\n if (!hasMatch) {\n results.push({\n label: 'site.activeVersion',\n status: 'warn',\n message: `\"${config.site.activeVersion}\" does not match any docs directory (${names})`,\n })\n } else {\n results.push({\n label: 'site.activeVersion',\n status: 'pass',\n message: config.site.activeVersion,\n })\n }\n } else {\n results.push({\n label: 'site.activeVersion',\n status: 'warn',\n message: 'Not set. The first version directory will be used as default.',\n })\n }\n }\n }\n } else {\n results.push({\n label: 'docs/ directory',\n status: 'fail',\n message: 'Not found. Create a docs/ directory with your documentation files.',\n })\n }\n\n // 5. Check search config\n if (config.search?.provider === 'meilisearch') {\n if (!config.search.meilisearch?.host) {\n results.push({\n label: 'search.meilisearch.host',\n status: 'fail',\n message: 'Meilisearch provider selected but no host configured',\n })\n }\n if (!config.search.meilisearch?.indexName) {\n results.push({\n label: 'search.meilisearch.indexName',\n status: 'fail',\n message: 'Meilisearch provider selected but no indexName configured',\n })\n }\n if (config.search.meilisearch?.host && config.search.meilisearch?.indexName) {\n results.push({\n label: 'Search (Meilisearch)',\n status: 'pass',\n message: `${config.search.meilisearch.host} / ${config.search.meilisearch.indexName}`,\n })\n }\n }\n\n // 6. Check i18n config\n if (config.features?.i18n && typeof config.features.i18n === 'object') {\n const i18n = config.features.i18n\n if (!i18n.defaultLocale) {\n results.push({\n label: 'i18n.defaultLocale',\n status: 'fail',\n message: 'i18n enabled but no defaultLocale set',\n })\n }\n if (!i18n.locales || !Array.isArray(i18n.locales) || i18n.locales.length === 0) {\n results.push({\n label: 'i18n.locales',\n status: 'fail',\n message: 'i18n enabled but no locales array defined',\n })\n }\n if (i18n.defaultLocale && i18n.locales?.length) {\n results.push({\n label: 'Internationalization',\n status: 'pass',\n message: `${i18n.locales.join(', ')} (default: ${i18n.defaultLocale})`,\n })\n }\n }\n\n // 7. Check package.json for specra dependency\n const pkgPath = join(dir, 'package.json')\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n const deps = { ...pkg.dependencies, ...pkg.devDependencies }\n if (deps['specra']) {\n results.push({\n label: 'specra dependency',\n status: 'pass',\n message: `${deps['specra']}`,\n })\n } else {\n results.push({\n label: 'specra dependency',\n status: 'fail',\n message: 'specra package not found in dependencies. Run `npm install specra`.',\n })\n }\n } catch {\n // ignore parse errors\n }\n }\n\n // 8. Check auth configuration\n if (config.auth?.token) {\n results.push({\n label: 'auth.token in config',\n status: 'warn',\n message: 'Raw token found in specra.config.json. Run `specra login` again to migrate it to .env.',\n })\n } else if (config.auth?.tokenEnv) {\n // Check the .env file has the referenced var\n const envPath = join(dir, '.env')\n if (existsSync(envPath)) {\n try {\n const envContent = readFileSync(envPath, 'utf-8')\n const hasVar = envContent.split('\\n').some((line: string) => line.trim().startsWith(`${config.auth.tokenEnv}=`))\n if (hasVar) {\n results.push({\n label: 'Auth',\n status: 'pass',\n message: `Token stored in .env as ${config.auth.tokenEnv}`,\n })\n } else {\n results.push({\n label: 'Auth',\n status: 'fail',\n message: `specra.config.json references ${config.auth.tokenEnv} but it's not set in .env. Run \\`specra login\\`.`,\n })\n }\n } catch {\n results.push({\n label: 'Auth',\n status: 'fail',\n message: `.env file unreadable. Run \\`specra login\\` to re-authenticate.`,\n })\n }\n } else {\n results.push({\n label: 'Auth',\n status: 'fail',\n message: `No .env file found but specra.config.json references ${config.auth.tokenEnv}. Run \\`specra login\\`.`,\n })\n }\n } else if (config.auth?.source === 'global') {\n results.push({\n label: 'Auth',\n status: 'pass',\n message: 'Using global credentials from ~/.specra/config.json',\n })\n }\n\n // 9. Check navigation.tabGroups have required fields\n if (config.navigation?.tabGroups && Array.isArray(config.navigation.tabGroups)) {\n const tabGroups = config.navigation.tabGroups\n const invalid = tabGroups.filter((t: any) => !t.id || !t.label)\n if (invalid.length > 0) {\n results.push({\n label: 'navigation.tabGroups',\n status: 'fail',\n message: `${invalid.length} tab group(s) missing required \"id\" or \"label\" fields`,\n })\n } else {\n results.push({\n label: 'navigation.tabGroups',\n status: 'pass',\n message: `${tabGroups.length} tab group(s) configured`,\n })\n }\n }\n\n printResults(results)\n}\n\nfunction printResults(results: DiagnosticResult[]) {\n const icons = {\n pass: pc.green('✓'),\n warn: pc.yellow('⚠'),\n fail: pc.red('✗'),\n }\n\n for (const r of results) {\n const icon = icons[r.status]\n const label = r.status === 'fail' ? pc.red(r.label) : r.status === 'warn' ? pc.yellow(r.label) : r.label\n console.log(` ${icon} ${label}: ${pc.dim(r.message)}`)\n }\n\n const fails = results.filter((r) => r.status === 'fail').length\n const warns = results.filter((r) => r.status === 'warn').length\n const passes = results.filter((r) => r.status === 'pass').length\n\n console.log()\n if (fails > 0) {\n console.log(pc.red(` ${fails} error(s)`), warns > 0 ? pc.yellow(`${warns} warning(s)`) : '', pc.green(`${passes} passed`))\n } else if (warns > 0) {\n console.log(pc.yellow(` ${warns} warning(s)`), pc.green(`${passes} passed`))\n } else {\n console.log(pc.green(` All ${passes} checks passed!`))\n }\n console.log()\n}\n"],"mappings":"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","names":[]}