blockend-cli 1.4.2 → 1.4.4

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 (2) hide show
  1. package/dist/index.js +266 -312
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,18 +7,34 @@ import { defineCommand, runMain } from "citty";
7
7
  import path, { join, dirname } from "path";
8
8
  import fs from "fs/promises";
9
9
  import { exec } from "child_process";
10
- import { intro, outro, select, spinner, confirm, isCancel } from "@clack/prompts";
10
+ import { intro, outro, select, spinner, confirm, isCancel, log } from "@clack/prompts";
11
11
  import pc from "picocolors";
12
12
  var REPO_OWNER = "codewithnuh";
13
13
  var REPO_NAME = "blockend";
14
14
  var BRANCH = "master";
15
15
  var RAW_CDN_BASE = `https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/${BRANCH}`;
16
16
  var MANIFEST_URL = `${RAW_CDN_BASE}/registry/index.json`;
17
+ function rewriteFileImports(fileContent, writtenFilePath, blocksRoot, aliasBase) {
18
+ if (!aliasBase || aliasBase.startsWith(".")) {
19
+ return fileContent;
20
+ }
21
+ const importRegex = /((?:from|import)\s*)(['"`])(\.\.?\/[^'"` \n\r]+)(['"`])/g;
22
+ const fileDir = dirname(writtenFilePath);
23
+ return fileContent.replace(importRegex, (match, keyword, openQuote, relativePath, closeQuote) => {
24
+ const absoluteTarget = path.resolve(fileDir, relativePath);
25
+ const fromBlocksRoot = path.relative(blocksRoot, absoluteTarget).replace(/\\/g, "/");
26
+ if (fromBlocksRoot.startsWith("..")) {
27
+ return match;
28
+ }
29
+ const aliased = `${aliasBase}/${fromBlocksRoot}`.replace(/\/+/g, "/");
30
+ return `${keyword}${openQuote}${aliased}${closeQuote}`;
31
+ });
32
+ }
17
33
  function outputError(json, message) {
18
34
  if (json) {
19
35
  process.stdout.write(JSON.stringify({ success: false, error: message }) + "\n");
20
36
  } else {
21
- outro(pc.red(`\u2716 ${message}`));
37
+ log.error(message);
22
38
  }
23
39
  }
24
40
  function outputResult(json, result) {
@@ -26,7 +42,7 @@ function outputResult(json, result) {
26
42
  process.stdout.write(JSON.stringify(result) + "\n");
27
43
  } else {
28
44
  if (result.success) {
29
- outro(pc.cyan(`\u2728 ${result.message}`));
45
+ outro(pc.green(`\u2728 ${result.message}`));
30
46
  } else {
31
47
  outro(pc.yellow(`\u2139 ${result.message}`));
32
48
  }
@@ -34,17 +50,17 @@ function outputResult(json, result) {
34
50
  }
35
51
  function handleCancel(value) {
36
52
  if (isCancel(value)) {
37
- outro(pc.yellow("\u26A0 Operation cancelled. Exiting Blockend CLI cleanly."));
53
+ outro(pc.dim("Operation cancelled."));
38
54
  process.exit(0);
39
55
  }
40
56
  }
41
57
  async function findUp(filename, startDir) {
42
58
  let dir = startDir;
43
59
  while (true) {
44
- const checkPath = join(dir, filename);
60
+ const candidate = join(dir, filename);
45
61
  try {
46
- await fs.access(checkPath);
47
- return checkPath;
62
+ await fs.access(candidate);
63
+ return candidate;
48
64
  } catch {
49
65
  const parent = dirname(dir);
50
66
  if (parent === dir) break;
@@ -56,7 +72,8 @@ async function findUp(filename, startDir) {
56
72
  async function addCommand(blockName, options = {}) {
57
73
  const { yes = false, json = false } = options;
58
74
  if (!json) {
59
- intro(pc.bgBlack(pc.magenta(" Blockend Component Ingestion ")));
75
+ console.log("");
76
+ intro(`${pc.bgCyan(pc.black(" blockend "))} ${pc.dim("add")}`);
60
77
  }
61
78
  const cwd = process.cwd();
62
79
  const configPath = await findUp("blockend.json", cwd);
@@ -67,296 +84,242 @@ async function addCommand(blockName, options = {}) {
67
84
  const rootDir = dirname(configPath);
68
85
  let config;
69
86
  try {
70
- const configFile = await fs.readFile(configPath, "utf-8");
71
- config = JSON.parse(configFile);
72
- } catch (error) {
73
- outputError(json, "Failed to parse blockend.json layout configuration.");
74
- if (!json) console.error(pc.dim(String(error)));
87
+ config = JSON.parse(await fs.readFile(configPath, "utf-8"));
88
+ } catch (err) {
89
+ outputError(json, "Failed to parse blockend.json.");
90
+ if (!json) log.error(pc.dim(String(err)));
75
91
  return;
76
92
  }
77
93
  if (config.language !== "typescript") {
78
- if (json) {
79
- outputError(
80
- json,
81
- "Blockend forces modern architectural standards. Registry exclusively supports TypeScript."
82
- );
83
- } else {
84
- outro(
85
- pc.yellow(
86
- `
87
- \u2139 Blockend forces modern architectural standards. Registry exclusively supports TypeScript.`
88
- )
89
- );
90
- }
94
+ outputError(json, "Blockend currently only supports TypeScript projects.");
95
+ if (!json) outro(pc.dim("Exiting."));
91
96
  return;
92
97
  }
98
+ const physicalBlocksPath = config.paths.blocks;
99
+ const blocksRootAbsolute = path.resolve(rootDir, physicalBlocksPath);
100
+ const aliasBase = config.aliases.blocks;
101
+ const envKey = config.environment;
93
102
  const s = spinner();
94
- if (!json) {
95
- s.start("Connecting to remote Blockend Registry manifest...");
96
- }
103
+ if (!json) s.start("Fetching registry...");
97
104
  let registry;
98
105
  try {
99
- const response = await fetch(MANIFEST_URL);
100
- if (!response.ok) throw new Error(`HTTP Error Status: ${response.status}`);
101
- registry = await response.json();
102
- if (!json) s.stop(pc.green("\u2714 Remote manifest synchronization complete."));
103
- } catch (error) {
106
+ const res = await fetch(MANIFEST_URL);
107
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
108
+ registry = await res.json();
109
+ if (!json) s.stop("Registry synced.");
110
+ } catch (err) {
104
111
  if (!json) {
105
- s.stop(pc.red("\u2716 Failed to fetch the component registry from GitHub network paths."));
106
- console.error(pc.dim(String(error)));
112
+ s.stop("Failed to fetch registry.");
113
+ log.error(pc.dim(String(err)));
107
114
  } else {
108
- outputError(json, "Failed to fetch the component registry from GitHub network paths.");
115
+ outputError(json, "Failed to fetch the component registry from GitHub.");
109
116
  }
110
117
  return;
111
118
  }
112
- let blockMap = {};
113
- if (registry.blocks && typeof registry.blocks === "object" && !Array.isArray(registry.blocks)) {
114
- blockMap = registry.blocks;
115
- } else {
116
- blockMap = registry;
117
- }
118
- const envKey = String(config.environment);
119
- let targetBlock = blockName;
120
- if (yes && !targetBlock) {
121
- outputError(
122
- json,
123
- "Block name required when using --yes flag. Usage: blockend add <block> --yes"
124
- );
119
+ const blockMap = registry.blocks && typeof registry.blocks === "object" && !Array.isArray(registry.blocks) ? registry.blocks : registry;
120
+ if (yes && !blockName) {
121
+ outputError(json, "Block name required with --yes. Usage: blockend add <block> --yes");
125
122
  return;
126
123
  }
124
+ let targetBlock = blockName;
127
125
  if (!targetBlock) {
128
- const availableOptions = Object.entries(blockMap).filter(([_, block]) => {
126
+ const available = Object.entries(blockMap).filter(([, block]) => {
129
127
  if (!block) return false;
130
- const hasAdapter = block.adapters?.[envKey] !== void 0;
131
- const hasEnvironment = block.environments?.[envKey] !== void 0;
132
- return hasAdapter || hasEnvironment;
133
- }).map(([key, block]) => ({
134
- value: key,
135
- label: `${key} - ${pc.dim(block.description)}`
136
- }));
137
- if (availableOptions.length === 0) {
138
- if (json) {
139
- outputError(
140
- json,
141
- `No backend blocks are currently available for your framework layer: [${envKey}].`
142
- );
143
- } else {
144
- outro(
145
- pc.yellow(
146
- `\u26A0 No backend blocks are currently available for your framework layer: [${envKey}].`
147
- )
148
- );
149
- }
128
+ return block.adapters?.[envKey] !== void 0 || block.environments?.[envKey] !== void 0;
129
+ });
130
+ if (available.length === 0) {
131
+ const msg = `No blocks available for environment: ${envKey}`;
132
+ outputError(json, msg);
133
+ if (!json) outro(pc.dim("Exiting."));
150
134
  return;
151
135
  }
152
- const selectBlockPrompt = await select({
153
- message: "Which backend block would you like to inject?",
154
- options: availableOptions
136
+ const maxKeyLen = Math.max(...available.map(([k]) => k.length), 0);
137
+ const selectPrompt = await select({
138
+ message: "Which block would you like to add?",
139
+ options: available.map(([key, block], i) => ({
140
+ value: key,
141
+ label: `${pc.dim(`${i + 1}.`)} ${pc.bold(pc.cyan(key.padEnd(maxKeyLen + 4)))}${pc.dim(
142
+ block.description.length > 60 ? `${block.description.slice(0, 60)}...` : block.description
143
+ )}`
144
+ }))
155
145
  });
156
- handleCancel(selectBlockPrompt);
157
- targetBlock = selectBlockPrompt;
146
+ handleCancel(selectPrompt);
147
+ targetBlock = selectPrompt;
158
148
  }
159
149
  const blockMeta = blockMap[targetBlock];
160
150
  if (!blockMeta) {
161
- outputError(json, `Block "${targetBlock}" does not exist in the remote registry.`);
151
+ outputError(json, `Block "${targetBlock}" does not exist in the registry.`);
162
152
  return;
163
153
  }
164
154
  const adapterContext = blockMeta.adapters?.[envKey] ?? blockMeta.environments?.[envKey];
165
155
  if (!adapterContext) {
166
- outputError(
167
- json,
168
- `The block "${targetBlock}" does not support your environment layout: ${envKey}`
169
- );
156
+ outputError(json, `Block "${targetBlock}" does not support environment: ${envKey}`);
170
157
  return;
171
158
  }
172
- let selectedVariant;
173
- const variantKeys = Object.keys(adapterContext.variants || {});
159
+ const variantKeys = Object.keys(adapterContext.variants ?? {});
174
160
  if (variantKeys.length === 0) {
175
- outputError(
176
- json,
177
- `No architecture storage layout variants found for block framework: ${envKey}`
178
- );
161
+ outputError(json, `No variants found for block "${targetBlock}" in environment "${envKey}"`);
179
162
  return;
180
163
  }
164
+ let selectedVariant;
181
165
  if (yes) {
182
166
  selectedVariant = variantKeys.includes("memory") ? "memory" : variantKeys[0];
183
- } else if (variantKeys.includes("redis") && config.redisEnabled) {
167
+ } else if (variantKeys.includes("redis") && (config.redisEnabled || config.includeRedis)) {
184
168
  selectedVariant = "redis";
185
169
  } else if (variantKeys.length === 1) {
186
170
  selectedVariant = variantKeys[0];
187
171
  } else {
188
- const selectVariantPrompt = await select({
189
- message: "Which architectural storage variant do you want to back this block?",
190
- options: variantKeys.map((vKey) => ({
191
- value: vKey,
192
- label: vKey.toUpperCase()
193
- }))
172
+ const variantPrompt = await select({
173
+ message: "Select a storage variant:",
174
+ options: variantKeys.map((v) => ({ value: v, label: v.toUpperCase() }))
194
175
  });
195
- handleCancel(selectVariantPrompt);
196
- selectedVariant = selectVariantPrompt;
176
+ handleCancel(variantPrompt);
177
+ selectedVariant = variantPrompt;
197
178
  }
198
179
  const variantMeta = adapterContext.variants[selectedVariant];
199
- let physicalPath = config.paths.blocks;
200
- if (physicalPath.startsWith("@")) {
201
- physicalPath = "./src/blocks";
202
- }
203
- const targetFolder = path.resolve(rootDir, physicalPath, targetBlock);
180
+ const targetFolder = path.resolve(blocksRootAbsolute, targetBlock);
204
181
  const packageJsonPath = await findUp("package.json", rootDir);
205
182
  if (!packageJsonPath) {
206
- outputError(json, "Could not locate package.json in your current directory layout hierarchy.");
183
+ outputError(json, "Could not locate package.json.");
207
184
  return;
208
185
  }
209
186
  const packageJsonDir = dirname(packageJsonPath);
210
187
  let packageJson;
211
188
  try {
212
- const packageJsonContent = await fs.readFile(packageJsonPath, "utf-8");
213
- packageJson = JSON.parse(packageJsonContent);
214
- } catch (error) {
215
- outputError(json, "Failed parsing file data configurations from target package.json location.");
216
- if (!json) console.error(pc.dim(String(error)));
189
+ packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
190
+ } catch (err) {
191
+ outputError(json, "Failed to parse package.json.");
192
+ if (!json) log.error(pc.dim(String(err)));
217
193
  return;
218
194
  }
219
195
  const installedDeps = {
220
196
  ...packageJson.dependencies ?? {},
221
197
  ...packageJson.devDependencies ?? {}
222
198
  };
223
- const missingProdDeps = (adapterContext.dependencies ?? []).concat(variantMeta?.dependencies ?? []).filter((dep) => !(dep in installedDeps));
224
- const missingDevDeps = (adapterContext.devDependencies ?? []).concat(variantMeta?.devDependencies ?? []).filter((dep) => !(dep in installedDeps));
225
- const hasMissingDeps = missingProdDeps.length > 0 || missingDevDeps.length > 0;
226
- if (hasMissingDeps) {
227
- const allMissingNames = [...missingProdDeps, ...missingDevDeps];
199
+ const missingProd = [
200
+ ...adapterContext.dependencies ?? [],
201
+ ...variantMeta?.dependencies ?? []
202
+ ].filter((d) => !(d in installedDeps));
203
+ const missingDev = [
204
+ ...adapterContext.devDependencies ?? [],
205
+ ...variantMeta?.devDependencies ?? []
206
+ ].filter((d) => !(d in installedDeps));
207
+ if (missingProd.length > 0 || missingDev.length > 0) {
228
208
  if (!json) {
229
- console.log(
230
- pc.yellow(`
231
- \u26A0\uFE0F Missing required infrastructure packages: ${allMissingNames.join(", ")}`)
232
- );
209
+ log.warn(`Missing dependencies: ${pc.cyan([...missingProd, ...missingDev].join(", "))}`);
233
210
  }
234
- const shouldInstallPrompt = yes ? true : await confirm({
235
- message: "Would you like the CLI to automatically install these dependencies?",
236
- initialValue: true
237
- });
238
- if (!yes) handleCancel(shouldInstallPrompt);
239
- if (shouldInstallPrompt) {
240
- const packageManager = await fs.access(join(packageJsonDir, "pnpm-lock.yaml")).then(() => "pnpm").catch(() => "npm");
241
- if (!json) s.start(`Preparing native workspace via ${packageManager}...`);
211
+ const shouldInstall = yes ? true : await confirm({ message: "Install missing dependencies?", initialValue: true });
212
+ if (!yes) handleCancel(shouldInstall);
213
+ if (shouldInstall) {
214
+ const pm = await fs.access(join(packageJsonDir, "pnpm-lock.yaml")).then(() => "pnpm").catch(() => "npm");
215
+ if (!json) s.start(`Installing via ${pm}...`);
216
+ const tasks = [];
217
+ if (missingProd.length > 0) {
218
+ tasks.push(
219
+ pm === "pnpm" ? `pnpm add ${missingProd.join(" ")}` : `npm install ${missingProd.join(" ")}`
220
+ );
221
+ }
222
+ if (missingDev.length > 0) {
223
+ tasks.push(
224
+ pm === "pnpm" ? `pnpm add -D ${missingDev.join(" ")}` : `npm install --save-dev ${missingDev.join(" ")}`
225
+ );
226
+ }
242
227
  try {
243
- const installTasks = [];
244
- if (missingProdDeps.length > 0) {
245
- installTasks.push(
246
- packageManager === "pnpm" ? `pnpm add ${missingProdDeps.join(" ")}` : `npm install ${missingProdDeps.join(" ")}`
247
- );
248
- }
249
- if (missingDevDeps.length > 0) {
250
- installTasks.push(
251
- packageManager === "pnpm" ? `pnpm add -D ${missingDevDeps.join(" ")}` : `npm install --save-dev ${missingDevDeps.join(" ")}`
252
- );
253
- }
254
- for (const installCmd of installTasks) {
255
- if (!json) s.stop(pc.cyan(`Executing: ${installCmd}
256
- `));
228
+ for (const cmd of tasks) {
229
+ if (!json) s.message(`Running: ${cmd}`);
257
230
  await new Promise((resolve, reject) => {
258
- const child = exec(installCmd, { cwd: packageJsonDir });
259
- child.stdout?.on("data", (data) => {
260
- if (!json) process.stdout.write(pc.dim(data));
231
+ const child = exec(cmd, { cwd: packageJsonDir });
232
+ child.stdout?.on("data", (d) => {
233
+ if (!json) process.stdout.write(pc.dim(d));
261
234
  });
262
- child.stderr?.on("data", (data) => {
263
- if (!json) process.stdout.write(pc.dim(pc.red(data)));
235
+ child.stderr?.on("data", (d) => {
236
+ if (!json) process.stdout.write(pc.dim(pc.red(d)));
264
237
  });
265
238
  child.on(
266
239
  "close",
267
- (code) => code === 0 ? resolve() : reject(new Error(`Exit code ${code}`))
240
+ (code) => code === 0 ? resolve() : reject(new Error(`Exit ${code}`))
268
241
  );
269
242
  });
270
243
  }
244
+ if (!json) s.stop("Dependencies installed.");
245
+ } catch (err) {
271
246
  if (!json) {
272
- s.start(pc.green("\u2714 All dependencies synchronized successfully."));
273
- s.stop();
274
- }
275
- } catch (error) {
276
- if (!json) {
277
- s.stop(pc.red("\u2716 Automated dependency installation failed. Please run setup manually."));
278
- console.error(pc.dim(String(error)));
247
+ s.stop("Installation failed.");
248
+ log.error(pc.dim(String(err)));
279
249
  } else {
280
- outputError(json, "Automated dependency installation failed.");
250
+ outputError(json, "Dependency installation failed.");
281
251
  }
282
252
  return;
283
253
  }
284
254
  }
285
255
  }
286
256
  const filesToDownload = [];
287
- if (blockMeta.baseFiles) {
257
+ if (blockMeta.baseFiles && Array.isArray(blockMeta.baseFiles)) {
288
258
  filesToDownload.push(...blockMeta.baseFiles);
289
259
  }
290
260
  if (adapterContext.core) {
291
261
  filesToDownload.push({
292
262
  source: adapterContext.core,
293
- target: path.basename(adapterContext.core, ".txt")
263
+ target: path.basename(adapterContext.core)
294
264
  });
295
265
  }
296
266
  if (variantMeta && Array.isArray(variantMeta.files)) {
297
- for (const fileEntry of variantMeta.files) {
298
- if (typeof fileEntry === "string") {
299
- filesToDownload.push({
300
- source: fileEntry,
301
- target: path.basename(fileEntry, ".txt")
302
- });
267
+ for (const entry of variantMeta.files) {
268
+ if (typeof entry === "string") {
269
+ filesToDownload.push({ source: entry, target: path.basename(entry) });
303
270
  } else {
304
- filesToDownload.push(fileEntry);
271
+ filesToDownload.push(entry);
305
272
  }
306
273
  }
307
274
  }
308
- let fileExistsConflict = false;
309
- for (const fileMap of filesToDownload) {
310
- const destinationPath = join(targetFolder, fileMap.target);
275
+ let hasConflict = false;
276
+ for (const fm of filesToDownload) {
311
277
  try {
312
- await fs.access(destinationPath);
313
- fileExistsConflict = true;
278
+ await fs.access(join(targetFolder, fm.target));
279
+ hasConflict = true;
314
280
  break;
315
281
  } catch {
316
282
  }
317
283
  }
318
- if (fileExistsConflict) {
319
- const overwritePrompt = yes ? true : await confirm({
320
- message: `\u26A0 Components in [${targetBlock}] already exist. Overwrite custom revisions?`,
284
+ if (hasConflict) {
285
+ const overwrite = yes ? true : await confirm({
286
+ message: `Files for "${targetBlock}" already exist. Overwrite?`,
321
287
  initialValue: false
322
288
  });
323
- if (!yes) handleCancel(overwritePrompt);
324
- if (!overwritePrompt) {
325
- outputResult(json, {
326
- success: false,
327
- reason: "aborted",
328
- message: "Local modifications preserved."
329
- });
289
+ if (!yes) handleCancel(overwrite);
290
+ if (!overwrite) {
291
+ outputResult(json, { success: false, reason: "aborted", message: "Local files preserved." });
330
292
  return;
331
293
  }
332
294
  }
333
- if (!json) {
334
- s.start(`Downloading and building template adapter blocks [${targetBlock}]...`);
335
- }
295
+ if (!json) s.start(`Downloading ${targetBlock}...`);
296
+ const filesWritten = [];
336
297
  try {
337
- for (const fileMap of filesToDownload) {
338
- const fileUrl = `${RAW_CDN_BASE}/${fileMap.source}`;
339
- const response = await fetch(fileUrl);
340
- if (!response.ok) throw new Error(`Download failed for: ${fileMap.source}`);
341
- const fileContent = await response.text();
342
- const localWriteLocation = join(targetFolder, fileMap.target);
343
- await fs.mkdir(dirname(localWriteLocation), { recursive: true });
344
- await fs.writeFile(localWriteLocation, fileContent, "utf-8");
298
+ for (const fm of filesToDownload) {
299
+ const fileUrl = `${RAW_CDN_BASE}/${fm.source}`;
300
+ const res = await fetch(fileUrl);
301
+ if (!res.ok) throw new Error(`Failed to download: ${fm.source} (HTTP ${res.status})`);
302
+ let content = await res.text();
303
+ const writtenFilePath = path.join(targetFolder, fm.target);
304
+ content = rewriteFileImports(content, writtenFilePath, blocksRootAbsolute, aliasBase);
305
+ await fs.mkdir(dirname(writtenFilePath), { recursive: true });
306
+ await fs.writeFile(writtenFilePath, content, "utf-8");
307
+ filesWritten.push(writtenFilePath);
345
308
  }
346
- if (!json) s.stop(pc.green("\u2714 Component isolation structures written smoothly."));
309
+ if (!json) s.stop("Files written.");
347
310
  outputResult(json, {
348
311
  success: true,
349
312
  block: targetBlock,
350
- filesWritten: filesToDownload.map((f) => join(targetFolder, f.target)),
351
- dependenciesInstalled: [...missingProdDeps, ...missingDevDeps],
352
- message: `Source blocks written to ${physicalPath}/${targetBlock}/. Code ownership transferred!`
313
+ filesWritten,
314
+ dependenciesInstalled: [...missingProd, ...missingDev],
315
+ message: `${targetBlock} added to ${physicalBlocksPath}/${targetBlock}`
353
316
  });
354
- } catch (error) {
317
+ } catch (err) {
355
318
  if (!json) {
356
- s.stop(pc.red("\u2716 Fatal error occurred while assembling file mappings."));
357
- console.error(pc.dim(String(error)));
319
+ s.stop("Failed.");
320
+ log.error(pc.dim(String(err)));
358
321
  } else {
359
- outputError(json, "Fatal error occurred while assembling file mappings.");
322
+ outputError(json, "Fatal error while writing block files.");
360
323
  }
361
324
  }
362
325
  }
@@ -365,7 +328,8 @@ async function addCommand(blockName, options = {}) {
365
328
  import path2, { join as join7 } from "path";
366
329
  import fs2 from "fs/promises";
367
330
  import { existsSync as existsSync4 } from "fs";
368
- import { intro as intro2, outro as outro2, select as select2, text, confirm as confirm2, spinner as spinner2, isCancel as isCancel2 } from "@clack/prompts";
331
+ import { intro as intro2, outro as outro2, select as select2, text, confirm as confirm2, spinner as spinner2, isCancel as isCancel2, log as log2 } from "@clack/prompts";
332
+ import pc3 from "picocolors";
369
333
 
370
334
  // src/detectors/index.ts
371
335
  import { join as join6 } from "path";
@@ -478,7 +442,7 @@ function flattenTsPaths(paths) {
478
442
  import pc2 from "picocolors";
479
443
  var theme = {
480
444
  brand: {
481
- primary: pc2.blue,
445
+ primary: pc2.cyan,
482
446
  title: pc2.bold,
483
447
  logo: pc2.white
484
448
  },
@@ -491,7 +455,7 @@ var theme = {
491
455
  success: pc2.green,
492
456
  warning: pc2.yellow,
493
457
  error: pc2.red,
494
- info: pc2.blue
458
+ info: pc2.cyan
495
459
  },
496
460
  emphasis: {
497
461
  strong: pc2.bold,
@@ -502,36 +466,56 @@ var theme = {
502
466
  // src/ui/format.ts
503
467
  var format = {
504
468
  title: (text2) => theme.brand.primary(theme.brand.title(text2)),
505
- success: (text2) => theme.state.success(`\u2714 ${text2}`),
506
- error: (text2) => theme.state.error(`\u2716 ${text2}`),
469
+ success: (text2) => theme.state.success(text2),
470
+ error: (text2) => theme.state.error(text2),
507
471
  warning: (text2) => theme.state.warning(text2),
508
472
  muted: (text2) => theme.text.muted(text2),
509
- info: (text2) => theme.state.info(text2)
473
+ info: (text2) => theme.state.info(text2),
474
+ highlight: (text2) => theme.brand.primary(text2)
510
475
  };
511
476
 
512
477
  // src/commands/init.ts
513
478
  async function resolveTsConfigPaths(cwd) {
514
- const possiblePaths = [join7(cwd, "tsconfig.json"), join7(cwd, "jsconfig.json")];
515
- for (const configPath of possiblePaths) {
516
- if (existsSync4(configPath)) {
517
- try {
518
- const rawContent = await fs2.readFile(configPath, "utf-8");
519
- const cleanJsonContent = rawContent.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1");
520
- const parsed = JSON.parse(cleanJsonContent);
521
- const baseUrl = parsed.compilerOptions?.baseUrl || ".";
522
- const paths = parsed.compilerOptions?.paths || {};
523
- return { baseUrl, paths };
524
- } catch {
525
- }
479
+ const candidates = [join7(cwd, "tsconfig.json"), join7(cwd, "jsconfig.json")];
480
+ for (const configPath of candidates) {
481
+ if (!existsSync4(configPath)) continue;
482
+ try {
483
+ const raw = await fs2.readFile(configPath, "utf-8");
484
+ const clean = raw.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1");
485
+ const parsed = JSON.parse(clean);
486
+ return {
487
+ baseUrl: parsed.compilerOptions?.baseUrl ?? ".",
488
+ paths: parsed.compilerOptions?.paths ?? {}
489
+ };
490
+ } catch {
526
491
  }
527
492
  }
528
493
  return { baseUrl: ".", paths: {} };
529
494
  }
495
+ function deriveBlockAlias(tsPaths, blocksPhysicalPath) {
496
+ const normalizedBlocksPath = blocksPhysicalPath.replace(/^\.\//, "").replace(/\\/g, "/");
497
+ for (const [aliasKey, targets] of Object.entries(tsPaths)) {
498
+ if (!Array.isArray(targets) || targets.length === 0) continue;
499
+ const rawTarget = targets[0].replace(/\\/g, "/");
500
+ const targetRoot = rawTarget.replace(/\*$/, "").replace(/\/$/, "").replace(/^\.\//, "");
501
+ const aliasRoot = aliasKey.replace(/\/\*$/, "").replace(/\*$/, "");
502
+ if (targetRoot === "") {
503
+ return `${aliasRoot}/${normalizedBlocksPath}`.replace(/\/+/g, "/");
504
+ }
505
+ if (normalizedBlocksPath === targetRoot || normalizedBlocksPath.startsWith(`${targetRoot}/`)) {
506
+ const suffix = normalizedBlocksPath.slice(targetRoot.length).replace(/^\//, "");
507
+ const alias = suffix ? `${aliasRoot}/${suffix}` : aliasRoot;
508
+ return alias.replace(/\/+/g, "/");
509
+ }
510
+ }
511
+ return void 0;
512
+ }
530
513
  function outputInitError(json, message) {
531
514
  if (json) {
532
515
  process.stdout.write(JSON.stringify({ success: false, error: message }) + "\n");
533
516
  } else {
534
- outro2(format.error(message));
517
+ log2.error(format.error(message));
518
+ process.exit(1);
535
519
  }
536
520
  }
537
521
  function outputInitResult(json, result) {
@@ -546,15 +530,8 @@ async function initCommand(options = {}) {
546
530
  const cwd = process.cwd();
547
531
  const configPath = join7(cwd, "blockend.json");
548
532
  if (!json) {
549
- console.log(`
550
- \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
551
- \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
552
- \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
553
- \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
554
- \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
555
- \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D
556
- `);
557
- intro2(theme.brand.primary(" Blockend \xB7 Intelligent Backend Blocks Setup "));
533
+ console.log("");
534
+ intro2(`${pc3.bgCyan(pc3.black(" blockend "))} ${theme.text.muted("setup")}`);
558
535
  }
559
536
  if (existsSync4(configPath)) {
560
537
  let action;
@@ -562,21 +539,21 @@ async function initCommand(options = {}) {
562
539
  action = "overwrite";
563
540
  } else {
564
541
  const actionPrompt = await select2({
565
- message: "blockend.json already exists. What do you want to do?",
542
+ message: "A blockend.json already exists. What would you like to do?",
566
543
  options: [
567
- { value: "keep", label: "Keep existing config (cancel init)" },
568
- { value: "overwrite", label: "Overwrite config" },
569
- { value: "regenerate", label: "Delete and regenerate" }
544
+ { value: "keep", label: "Keep existing (cancel setup)" },
545
+ { value: "overwrite", label: "Overwrite current configuration" },
546
+ { value: "regenerate", label: "Delete and regenerate from scratch" }
570
547
  ]
571
548
  });
572
549
  if (isCancel2(actionPrompt) || actionPrompt === "keep") {
573
550
  if (json) {
574
551
  outputInitResult(json, {
575
552
  success: false,
576
- message: "Initialization cancelled. Existing config preserved."
553
+ message: "Setup cancelled. Existing config preserved."
577
554
  });
578
555
  } else {
579
- outro2(format.muted("Initialization cancelled. Existing config preserved."));
556
+ outro2(format.muted("Setup cancelled. Existing config preserved."));
580
557
  }
581
558
  return;
582
559
  }
@@ -584,22 +561,22 @@ async function initCommand(options = {}) {
584
561
  }
585
562
  if (action === "regenerate") {
586
563
  await fs2.unlink(configPath);
587
- if (!json) console.log(format.error("Existing config deleted"));
564
+ if (!json) log2.warn("Existing configuration deleted.");
588
565
  }
589
566
  }
590
567
  const s = spinner2();
591
- if (!json) s.start("Scanning project layout...");
568
+ if (!json) s.start("Scanning project architecture...");
592
569
  const context = await detectProject(cwd);
593
570
  const hasSrcDir = existsSync4(join7(cwd, "src"));
594
571
  const tsConfig = await resolveTsConfigPaths(cwd);
595
- if (!json) s.stop(format.success("Project architecture scanned"));
572
+ if (!json) s.stop("Project scanned.");
596
573
  let framework = context.framework;
597
574
  if (!framework) {
598
575
  if (yes) {
599
576
  framework = "express";
600
577
  } else {
601
578
  const frameworkSelect = await select2({
602
- message: "Framework could not be auto-detected. Select framework environment manually:",
579
+ message: "Which framework does this project use?",
603
580
  options: [
604
581
  { value: "express", label: "Express.js" },
605
582
  { value: "fastify", label: "Fastify" },
@@ -609,67 +586,42 @@ async function initCommand(options = {}) {
609
586
  });
610
587
  if (isCancel2(frameworkSelect)) {
611
588
  if (json) {
612
- outputInitResult(json, { success: false, message: "Initialization cancelled." });
589
+ outputInitResult(json, { success: false, message: "Setup cancelled." });
613
590
  } else {
614
- outro2(format.muted("Initialization cancelled."));
591
+ outro2(format.muted("Setup cancelled."));
615
592
  }
616
593
  return;
617
594
  }
618
595
  framework = frameworkSelect;
619
596
  }
620
597
  } else if (!json) {
621
- console.log(
622
- `${format.success("\u2714")} Framework environment detected: ${theme.state.info(framework)}`
623
- );
598
+ log2.info(`Framework detected: ${theme.state.info(framework)}`);
624
599
  }
625
600
  const defaultDir = hasSrcDir ? "src/blocks" : "blocks";
626
601
  let rawPhysicalInput = defaultDir;
627
602
  if (!yes) {
628
603
  const directoryPrompt = await text({
629
- message: "Configure the targeted physical directory destination for blocks:",
604
+ message: "Where would you like to install blocks?",
630
605
  placeholder: defaultDir,
631
606
  initialValue: defaultDir,
632
607
  validate(value) {
633
- if (value?.trim().length === 0) return "Physical path location directory cannot be empty.";
608
+ if (!value || value.trim().length === 0) return "Directory path cannot be empty.";
634
609
  }
635
610
  });
636
611
  if (isCancel2(directoryPrompt)) {
637
- outro2(format.muted("Initialization cancelled."));
612
+ outro2(format.muted("Setup cancelled."));
638
613
  return;
639
614
  }
640
615
  rawPhysicalInput = String(directoryPrompt).trim();
641
616
  }
642
- const relativeBlocksPath = path2.relative(cwd, path2.resolve(cwd, rawPhysicalInput));
643
- let finalPath = relativeBlocksPath.replace(/\\/g, "/");
644
- if (!finalPath.startsWith(".") && !finalPath.startsWith("/")) {
645
- finalPath = `./${finalPath}`;
646
- }
647
- let finalBlockAlias = "@/blocks";
648
- const configuredAliases = Object.keys(tsConfig.paths);
649
- if (configuredAliases.length > 0) {
650
- const matchedAliasKey = configuredAliases.find((alias) => {
651
- const targets = tsConfig.paths[alias];
652
- if (Array.isArray(targets) && targets.length > 0) {
653
- const targetSubPath = targets[0].replace(/\*$/, "").replace(/\\/g, "/");
654
- return finalPath.replace(/^\.\//, "").startsWith(targetSubPath);
655
- }
656
- return false;
657
- });
658
- if (matchedAliasKey) {
659
- const cleanKey = matchedAliasKey.replace(/\*$/, "");
660
- const targets = tsConfig.paths[matchedAliasKey];
661
- const targetSubPath = targets[0].replace(/\*$/, "").replace(/\\/g, "/");
662
- const relativeSuffix = finalPath.replace(/^\.\//, "").replace(targetSubPath, "");
663
- finalBlockAlias = `${cleanKey}${relativeSuffix}`.replace(/\/$/, "");
664
- } else {
665
- const primaryAlias = configuredAliases.find((k) => k.startsWith("@")) || configuredAliases[0];
666
- const inferredBaseAlias = primaryAlias.replace(/\*$/, "");
667
- const folderSegmentName = path2.basename(finalPath);
668
- finalBlockAlias = `${inferredBaseAlias}${folderSegmentName}`;
669
- }
670
- } else {
671
- const folderSegmentName = path2.basename(finalPath);
672
- finalBlockAlias = `@/${folderSegmentName}`;
617
+ const relativeBlocksPath = path2.relative(cwd, path2.resolve(cwd, rawPhysicalInput)).replace(/\\/g, "/");
618
+ const finalPath = relativeBlocksPath.startsWith(".") ? relativeBlocksPath : `./${relativeBlocksPath}`;
619
+ const hasTsPaths = Object.keys(tsConfig.paths).length > 0;
620
+ const finalBlockAlias = hasTsPaths ? deriveBlockAlias(tsConfig.paths, relativeBlocksPath) : void 0;
621
+ if (!json) {
622
+ log2.info(
623
+ `Import alias: ${theme.state.info(finalBlockAlias === void 0 ? " relative imports" : finalBlockAlias)}`
624
+ );
673
625
  }
674
626
  let includeRedis = false;
675
627
  if (context.hasRedis) {
@@ -677,7 +629,7 @@ async function initCommand(options = {}) {
677
629
  includeRedis = true;
678
630
  } else {
679
631
  const redisConfirm = await confirm2({
680
- message: "Redis detected. Enable Redis-backed block variants automatically?",
632
+ message: "Redis detected in project. Enable Redis-backed variants?",
681
633
  initialValue: true
682
634
  });
683
635
  if (!isCancel2(redisConfirm)) {
@@ -686,38 +638,40 @@ async function initCommand(options = {}) {
686
638
  }
687
639
  }
688
640
  const configPayload = {
689
- $schema: "https://blockend.dev/schema.json",
641
+ $schema: "https://blockend.noorulhassan.com/schema.json",
690
642
  environment: framework || "express",
691
643
  language: context.language || "typescript",
692
644
  includeRedis,
693
645
  aliases: {
694
- blocks: finalBlockAlias
646
+ ...finalBlockAlias ? { blocks: finalBlockAlias } : {}
695
647
  },
696
648
  paths: {
697
649
  blocks: finalPath
650
+ // e.g. "./src/blocks" — always a real physical path
698
651
  }
699
652
  };
700
- if (!json) s.start("Finalizing configuration...");
653
+ if (!json) s.start("Writing configuration...");
701
654
  try {
702
655
  await fs2.writeFile(configPath, JSON.stringify(configPayload, null, 2), "utf-8");
703
- if (!json) s.stop(format.success("blockend.json ready"));
656
+ if (!json) s.stop("Configuration saved.");
704
657
  outputInitResult(json, {
705
658
  success: true,
706
- message: "\u2728 Blockend initialized successfully. Run: npx blockend add <block>",
659
+ message: "Blockend initialized! Run: npx blockend add <block>",
707
660
  config: configPayload
708
661
  });
709
662
  } catch {
710
663
  if (!json) {
711
- s.stop(format.error("Failed to write configuration"));
664
+ s.stop("Failed");
665
+ outputInitError(false, "Failed to write configuration file.");
712
666
  } else {
713
- outputInitError(json, "Failed to write architectural layout configuration map.");
667
+ outputInitError(true, "Failed to write configuration file.");
714
668
  }
715
669
  }
716
670
  }
717
671
 
718
672
  // src/commands/detect.ts
719
673
  import { outro as outro3 } from "@clack/prompts";
720
- import pc3 from "picocolors";
674
+ import pc4 from "picocolors";
721
675
  async function detectCommand(options = {}) {
722
676
  const { json = false } = options;
723
677
  try {
@@ -727,27 +681,27 @@ async function detectCommand(options = {}) {
727
681
  return;
728
682
  }
729
683
  console.log();
730
- console.log(pc3.bold(" Detected project configuration:"));
684
+ console.log(pc4.bold(" Detected project configuration:"));
731
685
  console.log();
732
- console.log(` Framework: ${pc3.cyan(context.framework)}`);
733
- console.log(` Language: ${pc3.cyan(context.language)}`);
734
- console.log(` Package manager: ${pc3.cyan(context.packageManager)}`);
735
- console.log(` Source dir: ${pc3.dim(context.srcDir)}`);
686
+ console.log(` Framework: ${pc4.cyan(context.framework)}`);
687
+ console.log(` Language: ${pc4.cyan(context.language)}`);
688
+ console.log(` Package manager: ${pc4.cyan(context.packageManager)}`);
689
+ console.log(` Source dir: ${pc4.dim(context.srcDir)}`);
736
690
  console.log(
737
- ` Redis: ${context.hasRedis ? pc3.green("detected") : pc3.dim("not found")}`
691
+ ` Redis: ${context.hasRedis ? pc4.green("detected") : pc4.dim("not found")}`
738
692
  );
739
693
  console.log(
740
- ` Prisma: ${context.hasPrisma ? pc3.green("detected") : pc3.dim("not found")}`
694
+ ` Prisma: ${context.hasPrisma ? pc4.green("detected") : pc4.dim("not found")}`
741
695
  );
742
696
  console.log(
743
- ` Drizzle: ${context.hasDrizzle ? pc3.green("detected") : pc3.dim("not found")}`
697
+ ` Drizzle: ${context.hasDrizzle ? pc4.green("detected") : pc4.dim("not found")}`
744
698
  );
745
699
  console.log();
746
700
  } catch (error) {
747
701
  if (json) {
748
702
  process.stdout.write(JSON.stringify({ success: false, error: String(error) }) + "\n");
749
703
  } else {
750
- outro3(pc3.red(`\u2716 Detection failed: ${String(error)}`));
704
+ outro3(pc4.red(`\u2716 Detection failed: ${String(error)}`));
751
705
  }
752
706
  process.exit(1);
753
707
  }
@@ -756,7 +710,7 @@ async function detectCommand(options = {}) {
756
710
  // src/commands/list.ts
757
711
  import { dirname as dirname2, join as join8 } from "path";
758
712
  import fs3 from "fs/promises";
759
- import pc4 from "picocolors";
713
+ import pc5 from "picocolors";
760
714
  import { outro as outro4, spinner as spinner3 } from "@clack/prompts";
761
715
  var REPO_OWNER2 = "codewithnuh";
762
716
  var REPO_NAME2 = "blockend";
@@ -787,7 +741,7 @@ async function listCommand(options = {}) {
787
741
  JSON.stringify({ success: false, error: "blockend.json not found." }) + "\n"
788
742
  );
789
743
  } else {
790
- outro4(pc4.red("\u2716 blockend.json not found. Run 'npx blockend init' first."));
744
+ outro4(pc5.red("\u2716 blockend.json not found. Run 'npx blockend init' first."));
791
745
  }
792
746
  return;
793
747
  }
@@ -801,7 +755,7 @@ async function listCommand(options = {}) {
801
755
  JSON.stringify({ success: false, error: "Failed to parse configuration matrix." }) + "\n"
802
756
  );
803
757
  } else {
804
- outro4(pc4.red("\u2716 Failed to parse blockend.json layout configuration."));
758
+ outro4(pc5.red("\u2716 Failed to parse blockend.json layout configuration."));
805
759
  }
806
760
  return;
807
761
  }
@@ -815,14 +769,14 @@ async function listCommand(options = {}) {
815
769
  const response = await fetch(MANIFEST_URL2);
816
770
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
817
771
  registry = await response.json();
818
- if (!json) s.stop(pc4.green("\u2714 Synced available block registries."));
772
+ if (!json) s.stop(pc5.green("\u2714 Synced available block registries."));
819
773
  } catch {
820
774
  if (json) {
821
775
  process.stdout.write(
822
776
  JSON.stringify({ success: false, error: "Network sync failure." }) + "\n"
823
777
  );
824
778
  } else {
825
- s.stop(pc4.red("\u2716 Failed to fetch the component registry from GitHub network paths."));
779
+ s.stop(pc5.red("\u2716 Failed to fetch the component registry from GitHub network paths."));
826
780
  }
827
781
  return;
828
782
  }
@@ -851,15 +805,15 @@ async function listCommand(options = {}) {
851
805
  );
852
806
  } else {
853
807
  console.log(`
854
- Available backend blocks for ${pc4.magenta(envKey)}:`);
808
+ Available backend blocks for ${pc5.magenta(envKey)}:`);
855
809
  if (blocksForEnv.length === 0) {
856
- console.log(pc4.dim(" No blocks found for this framework."));
810
+ console.log(pc5.dim(" No blocks found for this framework."));
857
811
  } else {
858
812
  blocksForEnv.forEach((b) => {
859
813
  console.log(`
860
- ${pc4.cyan(b.name)}`);
861
- console.log(` ${pc4.dim(b.description)}`);
862
- console.log(` Storage Variants: ${b.variants.map((v) => pc4.yellow(v)).join(", ")}`);
814
+ ${pc5.cyan(b.name)}`);
815
+ console.log(` ${pc5.dim(b.description)}`);
816
+ console.log(` Storage Variants: ${b.variants.map((v) => pc5.yellow(v)).join(", ")}`);
863
817
  });
864
818
  console.log();
865
819
  }
@@ -869,7 +823,7 @@ Available backend blocks for ${pc4.magenta(envKey)}:`);
869
823
  // src/commands/mcp.ts
870
824
  import path3, { join as join9 } from "path";
871
825
  import fs4 from "fs/promises";
872
- import pc5 from "picocolors";
826
+ import pc6 from "picocolors";
873
827
  import { outro as outro5, spinner as spinner4, select as select3, confirm as confirm3 } from "@clack/prompts";
874
828
  var CLIENT_MATRIX = {
875
829
  claude: {
@@ -992,22 +946,22 @@ async function mcpInitCommand(options) {
992
946
  }
993
947
  const meta = CLIENT_MATRIX[chosenClient];
994
948
  if (!meta) {
995
- outro5(pc5.red(`\u2716 Unsupported client identifier profile: ${options.client}`));
949
+ outro5(pc6.red(`\u2716 Unsupported client identifier profile: ${options.client}`));
996
950
  return;
997
951
  }
998
952
  const absoluteConfigTarget = join9(cwd, meta.relativePath);
999
953
  if (options.dryRun) {
1000
954
  console.log(
1001
- pc5.cyan(`
1002
- \u2139 Dry-run mode active. Intended path location: ${pc5.dim(meta.relativePath)}`)
955
+ pc6.cyan(`
956
+ \u2139 Dry-run mode active. Intended path location: ${pc6.dim(meta.relativePath)}`)
1003
957
  );
1004
958
  if (meta.format === "json") {
1005
959
  const jsonConfig = meta.isCustomSchema ? { "mcp.mcpServers": { blockend: BLOCKEND_SERVER } } : { mcpServers: { blockend: BLOCKEND_SERVER } };
1006
- console.log(pc5.gray(JSON.stringify(jsonConfig, null, 2)));
960
+ console.log(pc6.gray(JSON.stringify(jsonConfig, null, 2)));
1007
961
  } else {
1008
- console.log(pc5.gray(generateToml(BLOCKEND_SERVER)));
962
+ console.log(pc6.gray(generateToml(BLOCKEND_SERVER)));
1009
963
  }
1010
- outro5(pc5.green("\u2714 Dry run evaluation complete."));
964
+ outro5(pc6.green("\u2714 Dry run evaluation complete."));
1011
965
  return;
1012
966
  }
1013
967
  let fileConflict = false;
@@ -1022,12 +976,12 @@ async function mcpInitCommand(options) {
1022
976
  initialValue: false
1023
977
  });
1024
978
  if (!shouldOverwrite || typeof shouldOverwrite === "symbol") {
1025
- outro5(pc5.yellow("\u2139 Operations halted. Project layout revisions preserved."));
979
+ outro5(pc6.yellow("\u2139 Operations halted. Project layout revisions preserved."));
1026
980
  return;
1027
981
  }
1028
982
  }
1029
983
  const s = spinner4();
1030
- s.start(`Writing localized project configurations inside ${pc5.dim(meta.relativePath)}...`);
984
+ s.start(`Writing localized project configurations inside ${pc6.dim(meta.relativePath)}...`);
1031
985
  try {
1032
986
  await fs4.mkdir(path3.dirname(absoluteConfigTarget), { recursive: true });
1033
987
  if (meta.format === "json") {
@@ -1052,11 +1006,11 @@ async function mcpInitCommand(options) {
1052
1006
  await fs4.writeFile(absoluteConfigTarget, generateToml(BLOCKEND_SERVER), "utf-8");
1053
1007
  }
1054
1008
  s.stop(
1055
- pc5.green(`\u2714 Local workspace integration fully complete! File target: ${meta.relativePath}`)
1009
+ pc6.green(`\u2714 Local workspace integration fully complete! File target: ${meta.relativePath}`)
1056
1010
  );
1057
1011
  } catch (error) {
1058
- s.stop(pc5.red("\u2716 Fatal crash reading or creating local configuration maps."));
1059
- console.error(pc5.dim(String(error)));
1012
+ s.stop(pc6.red("\u2716 Fatal crash reading or creating local configuration maps."));
1013
+ console.error(pc6.dim(String(error)));
1060
1014
  }
1061
1015
  }
1062
1016
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blockend-cli",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "CLI for installing production-ready backend blocks into Next.js, Express, Hono, NestJS, and other Node.js applications.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",