blockend-cli 1.4.1 → 1.4.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.
- package/dist/index.js +241 -156
- package/package.json +11 -10
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ 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";
|
|
@@ -18,7 +18,7 @@ function outputError(json, message) {
|
|
|
18
18
|
if (json) {
|
|
19
19
|
process.stdout.write(JSON.stringify({ success: false, error: message }) + "\n");
|
|
20
20
|
} else {
|
|
21
|
-
|
|
21
|
+
log.error(message);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
function outputResult(json, result) {
|
|
@@ -26,7 +26,7 @@ function outputResult(json, result) {
|
|
|
26
26
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
27
27
|
} else {
|
|
28
28
|
if (result.success) {
|
|
29
|
-
outro(pc.
|
|
29
|
+
outro(pc.green(`\u2728 ${result.message}`));
|
|
30
30
|
} else {
|
|
31
31
|
outro(pc.yellow(`\u2139 ${result.message}`));
|
|
32
32
|
}
|
|
@@ -34,7 +34,7 @@ function outputResult(json, result) {
|
|
|
34
34
|
}
|
|
35
35
|
function handleCancel(value) {
|
|
36
36
|
if (isCancel(value)) {
|
|
37
|
-
outro(pc.
|
|
37
|
+
outro(pc.dim("Operation cancelled."));
|
|
38
38
|
process.exit(0);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -56,7 +56,8 @@ async function findUp(filename, startDir) {
|
|
|
56
56
|
async function addCommand(blockName, options = {}) {
|
|
57
57
|
const { yes = false, json = false } = options;
|
|
58
58
|
if (!json) {
|
|
59
|
-
|
|
59
|
+
console.log("");
|
|
60
|
+
intro(`${pc.bgCyan(pc.black(" blockend "))} ${pc.dim("add")}`);
|
|
60
61
|
}
|
|
61
62
|
const cwd = process.cwd();
|
|
62
63
|
const configPath = await findUp("blockend.json", cwd);
|
|
@@ -70,42 +71,35 @@ async function addCommand(blockName, options = {}) {
|
|
|
70
71
|
const configFile = await fs.readFile(configPath, "utf-8");
|
|
71
72
|
config = JSON.parse(configFile);
|
|
72
73
|
} catch (error) {
|
|
73
|
-
outputError(json, "Failed to parse blockend.json
|
|
74
|
-
if (!json)
|
|
74
|
+
outputError(json, "Failed to parse blockend.json.");
|
|
75
|
+
if (!json) log.error(pc.dim(String(error)));
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
if (config.language !== "typescript") {
|
|
78
79
|
if (json) {
|
|
79
|
-
outputError(
|
|
80
|
-
json,
|
|
81
|
-
"Blockend forces modern architectural standards. Registry exclusively supports TypeScript."
|
|
82
|
-
);
|
|
80
|
+
outputError(json, "Registry exclusively supports TypeScript projects.");
|
|
83
81
|
} else {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`
|
|
87
|
-
\u2139 Blockend forces modern architectural standards. Registry exclusively supports TypeScript.`
|
|
88
|
-
)
|
|
89
|
-
);
|
|
82
|
+
log.warn("Blockend currently only supports TypeScript projects.");
|
|
83
|
+
outro(pc.dim("Exiting."));
|
|
90
84
|
}
|
|
91
85
|
return;
|
|
92
86
|
}
|
|
93
87
|
const s = spinner();
|
|
94
88
|
if (!json) {
|
|
95
|
-
s.start("
|
|
89
|
+
s.start("Fetching registry...");
|
|
96
90
|
}
|
|
97
91
|
let registry;
|
|
98
92
|
try {
|
|
99
93
|
const response = await fetch(MANIFEST_URL);
|
|
100
94
|
if (!response.ok) throw new Error(`HTTP Error Status: ${response.status}`);
|
|
101
95
|
registry = await response.json();
|
|
102
|
-
if (!json) s.stop(
|
|
96
|
+
if (!json) s.stop("Registry synced.");
|
|
103
97
|
} catch (error) {
|
|
104
98
|
if (!json) {
|
|
105
|
-
s.stop(
|
|
106
|
-
|
|
99
|
+
s.stop("Failed to fetch registry.");
|
|
100
|
+
log.error(pc.dim(String(error)));
|
|
107
101
|
} else {
|
|
108
|
-
outputError(json, "Failed to fetch the component registry from GitHub
|
|
102
|
+
outputError(json, "Failed to fetch the component registry from GitHub.");
|
|
109
103
|
}
|
|
110
104
|
return;
|
|
111
105
|
}
|
|
@@ -125,32 +119,34 @@ async function addCommand(blockName, options = {}) {
|
|
|
125
119
|
return;
|
|
126
120
|
}
|
|
127
121
|
if (!targetBlock) {
|
|
128
|
-
const
|
|
122
|
+
const filteredBlocks = Object.entries(blockMap).filter(([_, block]) => {
|
|
129
123
|
if (!block) return false;
|
|
130
124
|
const hasAdapter = block.adapters?.[envKey] !== void 0;
|
|
131
125
|
const hasEnvironment = block.environments?.[envKey] !== void 0;
|
|
132
126
|
return hasAdapter || hasEnvironment;
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}));
|
|
137
|
-
if (availableOptions.length === 0) {
|
|
127
|
+
});
|
|
128
|
+
if (filteredBlocks.length === 0) {
|
|
129
|
+
const msg = `No backend blocks are currently available for: ${envKey}.`;
|
|
138
130
|
if (json) {
|
|
139
|
-
outputError(
|
|
140
|
-
json,
|
|
141
|
-
`No backend blocks are currently available for your framework layer: [${envKey}].`
|
|
142
|
-
);
|
|
131
|
+
outputError(json, msg);
|
|
143
132
|
} else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
`\u26A0 No backend blocks are currently available for your framework layer: [${envKey}].`
|
|
147
|
-
)
|
|
148
|
-
);
|
|
133
|
+
log.warn(msg);
|
|
134
|
+
outro(pc.dim("Exiting."));
|
|
149
135
|
}
|
|
150
136
|
return;
|
|
151
137
|
}
|
|
138
|
+
const maxKeyLength = Math.max(...filteredBlocks.map(([key]) => key.length), 0);
|
|
139
|
+
const availableOptions = filteredBlocks.map(([key, block], index) => {
|
|
140
|
+
const paddedKey = key.padEnd(maxKeyLength + 4, " ");
|
|
141
|
+
const maxDescWidth = 60;
|
|
142
|
+
const optimizedDescription = block.description.length > maxDescWidth ? `${block.description.slice(0, maxDescWidth)}...` : block.description;
|
|
143
|
+
return {
|
|
144
|
+
value: key,
|
|
145
|
+
label: `${pc.dim(`${index + 1}.`)} ${pc.bold(pc.cyan(paddedKey))}${pc.dim(optimizedDescription)}`
|
|
146
|
+
};
|
|
147
|
+
});
|
|
152
148
|
const selectBlockPrompt = await select({
|
|
153
|
-
message: "Which
|
|
149
|
+
message: "Which block would you like to add?",
|
|
154
150
|
options: availableOptions
|
|
155
151
|
});
|
|
156
152
|
handleCancel(selectBlockPrompt);
|
|
@@ -158,24 +154,18 @@ async function addCommand(blockName, options = {}) {
|
|
|
158
154
|
}
|
|
159
155
|
const blockMeta = blockMap[targetBlock];
|
|
160
156
|
if (!blockMeta) {
|
|
161
|
-
outputError(json, `Block "${targetBlock}" does not exist in the
|
|
157
|
+
outputError(json, `Block "${targetBlock}" does not exist in the registry.`);
|
|
162
158
|
return;
|
|
163
159
|
}
|
|
164
160
|
const adapterContext = blockMeta.adapters?.[envKey] ?? blockMeta.environments?.[envKey];
|
|
165
161
|
if (!adapterContext) {
|
|
166
|
-
outputError(
|
|
167
|
-
json,
|
|
168
|
-
`The block "${targetBlock}" does not support your environment layout: ${envKey}`
|
|
169
|
-
);
|
|
162
|
+
outputError(json, `The block "${targetBlock}" does not support your environment: ${envKey}`);
|
|
170
163
|
return;
|
|
171
164
|
}
|
|
172
165
|
let selectedVariant;
|
|
173
166
|
const variantKeys = Object.keys(adapterContext.variants || {});
|
|
174
167
|
if (variantKeys.length === 0) {
|
|
175
|
-
outputError(
|
|
176
|
-
json,
|
|
177
|
-
`No architecture storage layout variants found for block framework: ${envKey}`
|
|
178
|
-
);
|
|
168
|
+
outputError(json, `No storage variants found for block environment: ${envKey}`);
|
|
179
169
|
return;
|
|
180
170
|
}
|
|
181
171
|
if (yes) {
|
|
@@ -186,7 +176,7 @@ async function addCommand(blockName, options = {}) {
|
|
|
186
176
|
selectedVariant = variantKeys[0];
|
|
187
177
|
} else {
|
|
188
178
|
const selectVariantPrompt = await select({
|
|
189
|
-
message: "
|
|
179
|
+
message: "Select a storage variant:",
|
|
190
180
|
options: variantKeys.map((vKey) => ({
|
|
191
181
|
value: vKey,
|
|
192
182
|
label: vKey.toUpperCase()
|
|
@@ -203,7 +193,7 @@ async function addCommand(blockName, options = {}) {
|
|
|
203
193
|
const targetFolder = path.resolve(rootDir, physicalPath, targetBlock);
|
|
204
194
|
const packageJsonPath = await findUp("package.json", rootDir);
|
|
205
195
|
if (!packageJsonPath) {
|
|
206
|
-
outputError(json, "Could not locate package.json in your current directory
|
|
196
|
+
outputError(json, "Could not locate package.json in your current directory.");
|
|
207
197
|
return;
|
|
208
198
|
}
|
|
209
199
|
const packageJsonDir = dirname(packageJsonPath);
|
|
@@ -212,8 +202,8 @@ async function addCommand(blockName, options = {}) {
|
|
|
212
202
|
const packageJsonContent = await fs.readFile(packageJsonPath, "utf-8");
|
|
213
203
|
packageJson = JSON.parse(packageJsonContent);
|
|
214
204
|
} catch (error) {
|
|
215
|
-
outputError(json, "Failed parsing
|
|
216
|
-
if (!json)
|
|
205
|
+
outputError(json, "Failed parsing package.json.");
|
|
206
|
+
if (!json) log.error(pc.dim(String(error)));
|
|
217
207
|
return;
|
|
218
208
|
}
|
|
219
209
|
const installedDeps = {
|
|
@@ -226,19 +216,16 @@ async function addCommand(blockName, options = {}) {
|
|
|
226
216
|
if (hasMissingDeps) {
|
|
227
217
|
const allMissingNames = [...missingProdDeps, ...missingDevDeps];
|
|
228
218
|
if (!json) {
|
|
229
|
-
|
|
230
|
-
pc.yellow(`
|
|
231
|
-
\u26A0\uFE0F Missing required infrastructure packages: ${allMissingNames.join(", ")}`)
|
|
232
|
-
);
|
|
219
|
+
log.warn(`Missing dependencies: ${pc.cyan(allMissingNames.join(", "))}`);
|
|
233
220
|
}
|
|
234
221
|
const shouldInstallPrompt = yes ? true : await confirm({
|
|
235
|
-
message: "
|
|
222
|
+
message: "Install missing dependencies?",
|
|
236
223
|
initialValue: true
|
|
237
224
|
});
|
|
238
225
|
if (!yes) handleCancel(shouldInstallPrompt);
|
|
239
226
|
if (shouldInstallPrompt) {
|
|
240
227
|
const packageManager = await fs.access(join(packageJsonDir, "pnpm-lock.yaml")).then(() => "pnpm").catch(() => "npm");
|
|
241
|
-
if (!json) s.start(`
|
|
228
|
+
if (!json) s.start(`Installing via ${packageManager}...`);
|
|
242
229
|
try {
|
|
243
230
|
const installTasks = [];
|
|
244
231
|
if (missingProdDeps.length > 0) {
|
|
@@ -252,8 +239,7 @@ async function addCommand(blockName, options = {}) {
|
|
|
252
239
|
);
|
|
253
240
|
}
|
|
254
241
|
for (const installCmd of installTasks) {
|
|
255
|
-
if (!json) s.
|
|
256
|
-
`));
|
|
242
|
+
if (!json) s.message(`Executing: ${installCmd}`);
|
|
257
243
|
await new Promise((resolve, reject) => {
|
|
258
244
|
const child = exec(installCmd, { cwd: packageJsonDir });
|
|
259
245
|
child.stdout?.on("data", (data) => {
|
|
@@ -269,13 +255,12 @@ async function addCommand(blockName, options = {}) {
|
|
|
269
255
|
});
|
|
270
256
|
}
|
|
271
257
|
if (!json) {
|
|
272
|
-
s.
|
|
273
|
-
s.stop();
|
|
258
|
+
s.stop("Dependencies installed.");
|
|
274
259
|
}
|
|
275
260
|
} catch (error) {
|
|
276
261
|
if (!json) {
|
|
277
|
-
s.stop(
|
|
278
|
-
|
|
262
|
+
s.stop("Installation failed.");
|
|
263
|
+
log.error(pc.dim(String(error)));
|
|
279
264
|
} else {
|
|
280
265
|
outputError(json, "Automated dependency installation failed.");
|
|
281
266
|
}
|
|
@@ -317,7 +302,7 @@ async function addCommand(blockName, options = {}) {
|
|
|
317
302
|
}
|
|
318
303
|
if (fileExistsConflict) {
|
|
319
304
|
const overwritePrompt = yes ? true : await confirm({
|
|
320
|
-
message:
|
|
305
|
+
message: `Files for "${targetBlock}" already exist. Overwrite?`,
|
|
321
306
|
initialValue: false
|
|
322
307
|
});
|
|
323
308
|
if (!yes) handleCancel(overwritePrompt);
|
|
@@ -331,7 +316,7 @@ async function addCommand(blockName, options = {}) {
|
|
|
331
316
|
}
|
|
332
317
|
}
|
|
333
318
|
if (!json) {
|
|
334
|
-
s.start(`Downloading
|
|
319
|
+
s.start(`Downloading ${targetBlock}...`);
|
|
335
320
|
}
|
|
336
321
|
try {
|
|
337
322
|
for (const fileMap of filesToDownload) {
|
|
@@ -343,36 +328,143 @@ async function addCommand(blockName, options = {}) {
|
|
|
343
328
|
await fs.mkdir(dirname(localWriteLocation), { recursive: true });
|
|
344
329
|
await fs.writeFile(localWriteLocation, fileContent, "utf-8");
|
|
345
330
|
}
|
|
346
|
-
if (!json) s.stop(
|
|
331
|
+
if (!json) s.stop("Files copied.");
|
|
347
332
|
outputResult(json, {
|
|
348
333
|
success: true,
|
|
349
334
|
block: targetBlock,
|
|
350
335
|
filesWritten: filesToDownload.map((f) => join(targetFolder, f.target)),
|
|
351
336
|
dependenciesInstalled: [...missingProdDeps, ...missingDevDeps],
|
|
352
|
-
message: `
|
|
337
|
+
message: `Block added to ${physicalPath}/${targetBlock}`
|
|
353
338
|
});
|
|
354
339
|
} catch (error) {
|
|
355
340
|
if (!json) {
|
|
356
|
-
s.stop(
|
|
357
|
-
|
|
341
|
+
s.stop("Failed to write files.");
|
|
342
|
+
log.error(pc.dim(String(error)));
|
|
358
343
|
} else {
|
|
359
|
-
outputError(json, "Fatal error occurred while
|
|
344
|
+
outputError(json, "Fatal error occurred while writing block files.");
|
|
360
345
|
}
|
|
361
346
|
}
|
|
362
347
|
}
|
|
363
348
|
|
|
364
349
|
// src/commands/init.ts
|
|
365
|
-
import path2, { join as
|
|
350
|
+
import path2, { join as join7 } from "path";
|
|
366
351
|
import fs2 from "fs/promises";
|
|
352
|
+
import { existsSync as existsSync4 } from "fs";
|
|
353
|
+
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";
|
|
354
|
+
import pc3 from "picocolors";
|
|
355
|
+
|
|
356
|
+
// src/detectors/index.ts
|
|
357
|
+
import { join as join6 } from "path";
|
|
358
|
+
|
|
359
|
+
// src/detectors/readers/package.ts
|
|
360
|
+
import { readFile } from "fs/promises";
|
|
361
|
+
import { join as join2 } from "path";
|
|
362
|
+
async function readPackageJson(cwd) {
|
|
363
|
+
const pkgPath = join2(cwd, "package.json");
|
|
364
|
+
try {
|
|
365
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
366
|
+
return JSON.parse(content);
|
|
367
|
+
} catch {
|
|
368
|
+
throw new Error(`No package.json found at ${pkgPath}. Run blockend from your project root.`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/detectors/readers/tsconfig.ts
|
|
373
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
367
374
|
import { existsSync } from "fs";
|
|
368
|
-
import {
|
|
369
|
-
|
|
375
|
+
import { join as join3 } from "path";
|
|
376
|
+
async function readTsConfig(cwd) {
|
|
377
|
+
const tsconfigPath = join3(cwd, "tsconfig.json");
|
|
378
|
+
if (!existsSync(tsconfigPath)) return null;
|
|
379
|
+
try {
|
|
380
|
+
const content = await readFile2(tsconfigPath, "utf-8");
|
|
381
|
+
const cleanLines = content.split(/\r?\n/).filter((line) => {
|
|
382
|
+
const trimmed = line.trim();
|
|
383
|
+
return !trimmed.startsWith("//") && !trimmed.startsWith("/*");
|
|
384
|
+
}).join("\n").replace(/,(\s*[}\]])/g, "$1");
|
|
385
|
+
return JSON.parse(cleanLines);
|
|
386
|
+
} catch {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/detectors/readers/filesystem.ts
|
|
392
|
+
import { existsSync as existsSync2 } from "fs";
|
|
393
|
+
import { join as join4 } from "path";
|
|
394
|
+
async function inferSrcDir(cwd) {
|
|
395
|
+
const candidates = ["src", "app", "lib"];
|
|
396
|
+
for (const dir of candidates) {
|
|
397
|
+
if (existsSync2(join4(cwd, dir))) {
|
|
398
|
+
return join4(cwd, dir);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return cwd;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/detectors/framework.ts
|
|
405
|
+
function detectFramework(deps) {
|
|
406
|
+
if ("fastify" in deps) return "fastify";
|
|
407
|
+
if ("hono" in deps) return "hono";
|
|
408
|
+
if ("express" in deps) return "express";
|
|
409
|
+
if ("next" in deps) return "next";
|
|
410
|
+
return "none";
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/detectors/runtime.ts
|
|
414
|
+
function detectRuntime(deps) {
|
|
415
|
+
if ("@types/bun" in deps || "bun-types" in deps) return "bun";
|
|
416
|
+
return "node";
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/detectors/package-manager.ts
|
|
420
|
+
import { existsSync as existsSync3 } from "fs";
|
|
421
|
+
import { join as join5 } from "path";
|
|
422
|
+
function detectPackageManager(cwd) {
|
|
423
|
+
if (existsSync3(join5(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
424
|
+
if (existsSync3(join5(cwd, "bun.lockb"))) return "bun";
|
|
425
|
+
if (existsSync3(join5(cwd, "yarn.lock"))) return "yarn";
|
|
426
|
+
return "npm";
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/detectors/index.ts
|
|
430
|
+
async function detectProject(cwd) {
|
|
431
|
+
const [pkg, tsConfig] = await Promise.all([readPackageJson(cwd), readTsConfig(cwd)]);
|
|
432
|
+
const allDeps = {
|
|
433
|
+
...pkg.dependencies,
|
|
434
|
+
...pkg.devDependencies,
|
|
435
|
+
...pkg.peerDependencies
|
|
436
|
+
};
|
|
437
|
+
const srcDir = await inferSrcDir(cwd);
|
|
438
|
+
return {
|
|
439
|
+
root: cwd,
|
|
440
|
+
language: tsConfig !== null ? "typescript" : "javascript",
|
|
441
|
+
runtime: detectRuntime(allDeps),
|
|
442
|
+
framework: detectFramework(allDeps),
|
|
443
|
+
packageManager: detectPackageManager(cwd),
|
|
444
|
+
hasRedis: "ioredis" in allDeps || "redis" in allDeps,
|
|
445
|
+
hasPrisma: "@prisma/client" in allDeps,
|
|
446
|
+
hasDrizzle: "drizzle-orm" in allDeps,
|
|
447
|
+
aliasMap: tsConfig?.compilerOptions?.paths ? flattenTsPaths(tsConfig.compilerOptions.paths) : {},
|
|
448
|
+
srcDir,
|
|
449
|
+
// Default blocks directory: srcDir/lib/blocks
|
|
450
|
+
blocksDir: join6(srcDir, "lib", "blocks")
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function flattenTsPaths(paths) {
|
|
454
|
+
const result = {};
|
|
455
|
+
for (const [alias, targets] of Object.entries(paths)) {
|
|
456
|
+
if (targets[0]) {
|
|
457
|
+
result[alias.replace("/*", "/")] = targets[0].replace("/*", "/");
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
370
462
|
|
|
371
463
|
// src/ui/theme.ts
|
|
372
464
|
import pc2 from "picocolors";
|
|
373
465
|
var theme = {
|
|
374
466
|
brand: {
|
|
375
|
-
primary: pc2.
|
|
467
|
+
primary: pc2.cyan,
|
|
376
468
|
title: pc2.bold,
|
|
377
469
|
logo: pc2.white
|
|
378
470
|
},
|
|
@@ -385,7 +477,7 @@ var theme = {
|
|
|
385
477
|
success: pc2.green,
|
|
386
478
|
warning: pc2.yellow,
|
|
387
479
|
error: pc2.red,
|
|
388
|
-
info: pc2.
|
|
480
|
+
info: pc2.cyan
|
|
389
481
|
},
|
|
390
482
|
emphasis: {
|
|
391
483
|
strong: pc2.bold,
|
|
@@ -396,18 +488,19 @@ var theme = {
|
|
|
396
488
|
// src/ui/format.ts
|
|
397
489
|
var format = {
|
|
398
490
|
title: (text2) => theme.brand.primary(theme.brand.title(text2)),
|
|
399
|
-
success: (text2) => theme.state.success(
|
|
400
|
-
error: (text2) => theme.state.error(
|
|
491
|
+
success: (text2) => theme.state.success(text2),
|
|
492
|
+
error: (text2) => theme.state.error(text2),
|
|
401
493
|
warning: (text2) => theme.state.warning(text2),
|
|
402
494
|
muted: (text2) => theme.text.muted(text2),
|
|
403
|
-
info: (text2) => theme.state.info(text2)
|
|
495
|
+
info: (text2) => theme.state.info(text2),
|
|
496
|
+
highlight: (text2) => theme.brand.primary(text2)
|
|
404
497
|
};
|
|
405
498
|
|
|
406
499
|
// src/commands/init.ts
|
|
407
500
|
async function resolveTsConfigPaths(cwd) {
|
|
408
|
-
const possiblePaths = [
|
|
501
|
+
const possiblePaths = [join7(cwd, "tsconfig.json"), join7(cwd, "jsconfig.json")];
|
|
409
502
|
for (const configPath of possiblePaths) {
|
|
410
|
-
if (
|
|
503
|
+
if (existsSync4(configPath)) {
|
|
411
504
|
try {
|
|
412
505
|
const rawContent = await fs2.readFile(configPath, "utf-8");
|
|
413
506
|
const cleanJsonContent = rawContent.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1");
|
|
@@ -425,7 +518,8 @@ function outputInitError(json, message) {
|
|
|
425
518
|
if (json) {
|
|
426
519
|
process.stdout.write(JSON.stringify({ success: false, error: message }) + "\n");
|
|
427
520
|
} else {
|
|
428
|
-
|
|
521
|
+
log2.error(format.error(message));
|
|
522
|
+
process.exit(1);
|
|
429
523
|
}
|
|
430
524
|
}
|
|
431
525
|
function outputInitResult(json, result) {
|
|
@@ -438,39 +532,32 @@ function outputInitResult(json, result) {
|
|
|
438
532
|
async function initCommand(options = {}) {
|
|
439
533
|
const { yes = false, json = false } = options;
|
|
440
534
|
const cwd = process.cwd();
|
|
441
|
-
const configPath =
|
|
535
|
+
const configPath = join7(cwd, "blockend.json");
|
|
442
536
|
if (!json) {
|
|
443
|
-
console.log(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
\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
|
|
448
|
-
\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
|
|
449
|
-
\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
|
|
450
|
-
`);
|
|
451
|
-
intro2(theme.brand.primary(" Blockend \xB7 Intelligent Backend Blocks Setup "));
|
|
452
|
-
}
|
|
453
|
-
if (existsSync(configPath)) {
|
|
537
|
+
console.log("");
|
|
538
|
+
intro2(`${pc3.bgCyan(pc3.black(" blockend "))} ${theme.text.muted("setup")}`);
|
|
539
|
+
}
|
|
540
|
+
if (existsSync4(configPath)) {
|
|
454
541
|
let action;
|
|
455
542
|
if (yes) {
|
|
456
543
|
action = "overwrite";
|
|
457
544
|
} else {
|
|
458
545
|
const actionPrompt = await select2({
|
|
459
|
-
message: "blockend.json already exists. What
|
|
546
|
+
message: "A blockend.json configuration already exists. What would you like to do?",
|
|
460
547
|
options: [
|
|
461
|
-
{ value: "keep", label: "Keep existing
|
|
462
|
-
{ value: "overwrite", label: "Overwrite
|
|
463
|
-
{ value: "regenerate", label: "Delete and regenerate" }
|
|
548
|
+
{ value: "keep", label: "Keep existing (cancel setup)" },
|
|
549
|
+
{ value: "overwrite", label: "Overwrite current configuration" },
|
|
550
|
+
{ value: "regenerate", label: "Delete and regenerate from scratch" }
|
|
464
551
|
]
|
|
465
552
|
});
|
|
466
553
|
if (isCancel2(actionPrompt) || actionPrompt === "keep") {
|
|
467
554
|
if (json) {
|
|
468
555
|
outputInitResult(json, {
|
|
469
556
|
success: false,
|
|
470
|
-
message: "
|
|
557
|
+
message: "Setup cancelled. Existing config preserved."
|
|
471
558
|
});
|
|
472
559
|
} else {
|
|
473
|
-
outro2(format.muted("
|
|
560
|
+
outro2(format.muted("Setup cancelled. Existing config preserved."));
|
|
474
561
|
}
|
|
475
562
|
return;
|
|
476
563
|
}
|
|
@@ -478,22 +565,22 @@ async function initCommand(options = {}) {
|
|
|
478
565
|
}
|
|
479
566
|
if (action === "regenerate") {
|
|
480
567
|
await fs2.unlink(configPath);
|
|
481
|
-
if (!json)
|
|
568
|
+
if (!json) log2.warn("Existing configuration deleted.");
|
|
482
569
|
}
|
|
483
570
|
}
|
|
484
571
|
const s = spinner2();
|
|
485
|
-
if (!json) s.start("Scanning project
|
|
572
|
+
if (!json) s.start("Scanning project architecture...");
|
|
486
573
|
const context = await detectProject(cwd);
|
|
487
|
-
const hasSrcDir =
|
|
574
|
+
const hasSrcDir = existsSync4(join7(cwd, "src"));
|
|
488
575
|
const tsConfig = await resolveTsConfigPaths(cwd);
|
|
489
|
-
if (!json) s.stop(
|
|
576
|
+
if (!json) s.stop("Project scanned.");
|
|
490
577
|
let framework = context.framework;
|
|
491
578
|
if (!framework) {
|
|
492
579
|
if (yes) {
|
|
493
580
|
framework = "express";
|
|
494
581
|
} else {
|
|
495
582
|
const frameworkSelect = await select2({
|
|
496
|
-
message: "
|
|
583
|
+
message: "Which framework does this project use?",
|
|
497
584
|
options: [
|
|
498
585
|
{ value: "express", label: "Express.js" },
|
|
499
586
|
{ value: "fastify", label: "Fastify" },
|
|
@@ -503,32 +590,30 @@ async function initCommand(options = {}) {
|
|
|
503
590
|
});
|
|
504
591
|
if (isCancel2(frameworkSelect)) {
|
|
505
592
|
if (json) {
|
|
506
|
-
outputInitResult(json, { success: false, message: "
|
|
593
|
+
outputInitResult(json, { success: false, message: "Setup cancelled." });
|
|
507
594
|
} else {
|
|
508
|
-
outro2(format.muted("
|
|
595
|
+
outro2(format.muted("Setup cancelled."));
|
|
509
596
|
}
|
|
510
597
|
return;
|
|
511
598
|
}
|
|
512
599
|
framework = frameworkSelect;
|
|
513
600
|
}
|
|
514
601
|
} else if (!json) {
|
|
515
|
-
|
|
516
|
-
`${format.success("\u2714")} Framework environment detected: ${theme.state.info(framework)}`
|
|
517
|
-
);
|
|
602
|
+
log2.info(`Framework detected: ${theme.state.info(framework)}`);
|
|
518
603
|
}
|
|
519
604
|
const defaultDir = hasSrcDir ? "src/blocks" : "blocks";
|
|
520
605
|
let rawPhysicalInput = defaultDir;
|
|
521
606
|
if (!yes) {
|
|
522
607
|
const directoryPrompt = await text({
|
|
523
|
-
message: "
|
|
608
|
+
message: "Where would you like to install blocks?",
|
|
524
609
|
placeholder: defaultDir,
|
|
525
610
|
initialValue: defaultDir,
|
|
526
611
|
validate(value) {
|
|
527
|
-
if (value?.trim().length === 0) return "
|
|
612
|
+
if (value?.trim().length === 0) return "Directory path cannot be empty.";
|
|
528
613
|
}
|
|
529
614
|
});
|
|
530
615
|
if (isCancel2(directoryPrompt)) {
|
|
531
|
-
outro2(format.muted("
|
|
616
|
+
outro2(format.muted("Setup cancelled."));
|
|
532
617
|
return;
|
|
533
618
|
}
|
|
534
619
|
rawPhysicalInput = String(directoryPrompt).trim();
|
|
@@ -571,7 +656,7 @@ async function initCommand(options = {}) {
|
|
|
571
656
|
includeRedis = true;
|
|
572
657
|
} else {
|
|
573
658
|
const redisConfirm = await confirm2({
|
|
574
|
-
message: "Redis detected. Enable Redis-backed
|
|
659
|
+
message: "Redis detected in project. Enable Redis-backed variants?",
|
|
575
660
|
initialValue: true
|
|
576
661
|
});
|
|
577
662
|
if (!isCancel2(redisConfirm)) {
|
|
@@ -591,67 +676,67 @@ async function initCommand(options = {}) {
|
|
|
591
676
|
blocks: finalPath
|
|
592
677
|
}
|
|
593
678
|
};
|
|
594
|
-
if (!json) s.start("
|
|
679
|
+
if (!json) s.start("Writing configuration...");
|
|
595
680
|
try {
|
|
596
681
|
await fs2.writeFile(configPath, JSON.stringify(configPayload, null, 2), "utf-8");
|
|
597
|
-
if (!json) s.stop(
|
|
682
|
+
if (!json) s.stop("Configuration saved.");
|
|
598
683
|
outputInitResult(json, {
|
|
599
684
|
success: true,
|
|
600
|
-
message: "
|
|
685
|
+
message: "Blockend initialized successfully! Run: npx blockend add <block>",
|
|
601
686
|
config: configPayload
|
|
602
687
|
});
|
|
603
688
|
} catch {
|
|
604
689
|
if (!json) {
|
|
605
|
-
s.stop(
|
|
690
|
+
s.stop("Failed");
|
|
691
|
+
outputInitError(false, "Failed to write configuration file.");
|
|
606
692
|
} else {
|
|
607
|
-
outputInitError(
|
|
693
|
+
outputInitError(true, "Failed to write architectural layout configuration map.");
|
|
608
694
|
}
|
|
609
695
|
}
|
|
610
696
|
}
|
|
611
697
|
|
|
612
698
|
// src/commands/detect.ts
|
|
613
|
-
import { detectProject as detectProject2 } from "@blockend/detector";
|
|
614
699
|
import { outro as outro3 } from "@clack/prompts";
|
|
615
|
-
import
|
|
700
|
+
import pc4 from "picocolors";
|
|
616
701
|
async function detectCommand(options = {}) {
|
|
617
702
|
const { json = false } = options;
|
|
618
703
|
try {
|
|
619
|
-
const context = await
|
|
704
|
+
const context = await detectProject(process.cwd());
|
|
620
705
|
if (json) {
|
|
621
706
|
process.stdout.write(JSON.stringify(context, null, 2) + "\n");
|
|
622
707
|
return;
|
|
623
708
|
}
|
|
624
709
|
console.log();
|
|
625
|
-
console.log(
|
|
710
|
+
console.log(pc4.bold(" Detected project configuration:"));
|
|
626
711
|
console.log();
|
|
627
|
-
console.log(` Framework: ${
|
|
628
|
-
console.log(` Language: ${
|
|
629
|
-
console.log(` Package manager: ${
|
|
630
|
-
console.log(` Source dir: ${
|
|
712
|
+
console.log(` Framework: ${pc4.cyan(context.framework)}`);
|
|
713
|
+
console.log(` Language: ${pc4.cyan(context.language)}`);
|
|
714
|
+
console.log(` Package manager: ${pc4.cyan(context.packageManager)}`);
|
|
715
|
+
console.log(` Source dir: ${pc4.dim(context.srcDir)}`);
|
|
631
716
|
console.log(
|
|
632
|
-
` Redis: ${context.hasRedis ?
|
|
717
|
+
` Redis: ${context.hasRedis ? pc4.green("detected") : pc4.dim("not found")}`
|
|
633
718
|
);
|
|
634
719
|
console.log(
|
|
635
|
-
` Prisma: ${context.hasPrisma ?
|
|
720
|
+
` Prisma: ${context.hasPrisma ? pc4.green("detected") : pc4.dim("not found")}`
|
|
636
721
|
);
|
|
637
722
|
console.log(
|
|
638
|
-
` Drizzle: ${context.hasDrizzle ?
|
|
723
|
+
` Drizzle: ${context.hasDrizzle ? pc4.green("detected") : pc4.dim("not found")}`
|
|
639
724
|
);
|
|
640
725
|
console.log();
|
|
641
726
|
} catch (error) {
|
|
642
727
|
if (json) {
|
|
643
728
|
process.stdout.write(JSON.stringify({ success: false, error: String(error) }) + "\n");
|
|
644
729
|
} else {
|
|
645
|
-
outro3(
|
|
730
|
+
outro3(pc4.red(`\u2716 Detection failed: ${String(error)}`));
|
|
646
731
|
}
|
|
647
732
|
process.exit(1);
|
|
648
733
|
}
|
|
649
734
|
}
|
|
650
735
|
|
|
651
736
|
// src/commands/list.ts
|
|
652
|
-
import { dirname as dirname2, join as
|
|
737
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
653
738
|
import fs3 from "fs/promises";
|
|
654
|
-
import
|
|
739
|
+
import pc5 from "picocolors";
|
|
655
740
|
import { outro as outro4, spinner as spinner3 } from "@clack/prompts";
|
|
656
741
|
var REPO_OWNER2 = "codewithnuh";
|
|
657
742
|
var REPO_NAME2 = "blockend";
|
|
@@ -660,7 +745,7 @@ var MANIFEST_URL2 = `https://raw.githubusercontent.com/${REPO_OWNER2}/${REPO_NAM
|
|
|
660
745
|
async function findUp2(filename, startDir) {
|
|
661
746
|
let dir = startDir;
|
|
662
747
|
while (true) {
|
|
663
|
-
const checkPath =
|
|
748
|
+
const checkPath = join8(dir, filename);
|
|
664
749
|
try {
|
|
665
750
|
await fs3.access(checkPath);
|
|
666
751
|
return checkPath;
|
|
@@ -682,7 +767,7 @@ async function listCommand(options = {}) {
|
|
|
682
767
|
JSON.stringify({ success: false, error: "blockend.json not found." }) + "\n"
|
|
683
768
|
);
|
|
684
769
|
} else {
|
|
685
|
-
outro4(
|
|
770
|
+
outro4(pc5.red("\u2716 blockend.json not found. Run 'npx blockend init' first."));
|
|
686
771
|
}
|
|
687
772
|
return;
|
|
688
773
|
}
|
|
@@ -696,7 +781,7 @@ async function listCommand(options = {}) {
|
|
|
696
781
|
JSON.stringify({ success: false, error: "Failed to parse configuration matrix." }) + "\n"
|
|
697
782
|
);
|
|
698
783
|
} else {
|
|
699
|
-
outro4(
|
|
784
|
+
outro4(pc5.red("\u2716 Failed to parse blockend.json layout configuration."));
|
|
700
785
|
}
|
|
701
786
|
return;
|
|
702
787
|
}
|
|
@@ -710,14 +795,14 @@ async function listCommand(options = {}) {
|
|
|
710
795
|
const response = await fetch(MANIFEST_URL2);
|
|
711
796
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
712
797
|
registry = await response.json();
|
|
713
|
-
if (!json) s.stop(
|
|
798
|
+
if (!json) s.stop(pc5.green("\u2714 Synced available block registries."));
|
|
714
799
|
} catch {
|
|
715
800
|
if (json) {
|
|
716
801
|
process.stdout.write(
|
|
717
802
|
JSON.stringify({ success: false, error: "Network sync failure." }) + "\n"
|
|
718
803
|
);
|
|
719
804
|
} else {
|
|
720
|
-
s.stop(
|
|
805
|
+
s.stop(pc5.red("\u2716 Failed to fetch the component registry from GitHub network paths."));
|
|
721
806
|
}
|
|
722
807
|
return;
|
|
723
808
|
}
|
|
@@ -746,15 +831,15 @@ async function listCommand(options = {}) {
|
|
|
746
831
|
);
|
|
747
832
|
} else {
|
|
748
833
|
console.log(`
|
|
749
|
-
Available backend blocks for ${
|
|
834
|
+
Available backend blocks for ${pc5.magenta(envKey)}:`);
|
|
750
835
|
if (blocksForEnv.length === 0) {
|
|
751
|
-
console.log(
|
|
836
|
+
console.log(pc5.dim(" No blocks found for this framework."));
|
|
752
837
|
} else {
|
|
753
838
|
blocksForEnv.forEach((b) => {
|
|
754
839
|
console.log(`
|
|
755
|
-
${
|
|
756
|
-
console.log(` ${
|
|
757
|
-
console.log(` Storage Variants: ${b.variants.map((v) =>
|
|
840
|
+
${pc5.cyan(b.name)}`);
|
|
841
|
+
console.log(` ${pc5.dim(b.description)}`);
|
|
842
|
+
console.log(` Storage Variants: ${b.variants.map((v) => pc5.yellow(v)).join(", ")}`);
|
|
758
843
|
});
|
|
759
844
|
console.log();
|
|
760
845
|
}
|
|
@@ -762,9 +847,9 @@ Available backend blocks for ${pc4.magenta(envKey)}:`);
|
|
|
762
847
|
}
|
|
763
848
|
|
|
764
849
|
// src/commands/mcp.ts
|
|
765
|
-
import path3, { join as
|
|
850
|
+
import path3, { join as join9 } from "path";
|
|
766
851
|
import fs4 from "fs/promises";
|
|
767
|
-
import
|
|
852
|
+
import pc6 from "picocolors";
|
|
768
853
|
import { outro as outro5, spinner as spinner4, select as select3, confirm as confirm3 } from "@clack/prompts";
|
|
769
854
|
var CLIENT_MATRIX = {
|
|
770
855
|
claude: {
|
|
@@ -887,22 +972,22 @@ async function mcpInitCommand(options) {
|
|
|
887
972
|
}
|
|
888
973
|
const meta = CLIENT_MATRIX[chosenClient];
|
|
889
974
|
if (!meta) {
|
|
890
|
-
outro5(
|
|
975
|
+
outro5(pc6.red(`\u2716 Unsupported client identifier profile: ${options.client}`));
|
|
891
976
|
return;
|
|
892
977
|
}
|
|
893
|
-
const absoluteConfigTarget =
|
|
978
|
+
const absoluteConfigTarget = join9(cwd, meta.relativePath);
|
|
894
979
|
if (options.dryRun) {
|
|
895
980
|
console.log(
|
|
896
|
-
|
|
897
|
-
\u2139 Dry-run mode active. Intended path location: ${
|
|
981
|
+
pc6.cyan(`
|
|
982
|
+
\u2139 Dry-run mode active. Intended path location: ${pc6.dim(meta.relativePath)}`)
|
|
898
983
|
);
|
|
899
984
|
if (meta.format === "json") {
|
|
900
985
|
const jsonConfig = meta.isCustomSchema ? { "mcp.mcpServers": { blockend: BLOCKEND_SERVER } } : { mcpServers: { blockend: BLOCKEND_SERVER } };
|
|
901
|
-
console.log(
|
|
986
|
+
console.log(pc6.gray(JSON.stringify(jsonConfig, null, 2)));
|
|
902
987
|
} else {
|
|
903
|
-
console.log(
|
|
988
|
+
console.log(pc6.gray(generateToml(BLOCKEND_SERVER)));
|
|
904
989
|
}
|
|
905
|
-
outro5(
|
|
990
|
+
outro5(pc6.green("\u2714 Dry run evaluation complete."));
|
|
906
991
|
return;
|
|
907
992
|
}
|
|
908
993
|
let fileConflict = false;
|
|
@@ -917,12 +1002,12 @@ async function mcpInitCommand(options) {
|
|
|
917
1002
|
initialValue: false
|
|
918
1003
|
});
|
|
919
1004
|
if (!shouldOverwrite || typeof shouldOverwrite === "symbol") {
|
|
920
|
-
outro5(
|
|
1005
|
+
outro5(pc6.yellow("\u2139 Operations halted. Project layout revisions preserved."));
|
|
921
1006
|
return;
|
|
922
1007
|
}
|
|
923
1008
|
}
|
|
924
1009
|
const s = spinner4();
|
|
925
|
-
s.start(`Writing localized project configurations inside ${
|
|
1010
|
+
s.start(`Writing localized project configurations inside ${pc6.dim(meta.relativePath)}...`);
|
|
926
1011
|
try {
|
|
927
1012
|
await fs4.mkdir(path3.dirname(absoluteConfigTarget), { recursive: true });
|
|
928
1013
|
if (meta.format === "json") {
|
|
@@ -947,11 +1032,11 @@ async function mcpInitCommand(options) {
|
|
|
947
1032
|
await fs4.writeFile(absoluteConfigTarget, generateToml(BLOCKEND_SERVER), "utf-8");
|
|
948
1033
|
}
|
|
949
1034
|
s.stop(
|
|
950
|
-
|
|
1035
|
+
pc6.green(`\u2714 Local workspace integration fully complete! File target: ${meta.relativePath}`)
|
|
951
1036
|
);
|
|
952
1037
|
} catch (error) {
|
|
953
|
-
s.stop(
|
|
954
|
-
console.error(
|
|
1038
|
+
s.stop(pc6.red("\u2716 Fatal crash reading or creating local configuration maps."));
|
|
1039
|
+
console.error(pc6.dim(String(error)));
|
|
955
1040
|
}
|
|
956
1041
|
}
|
|
957
1042
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blockend-cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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",
|
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
"dist",
|
|
15
15
|
"README.md"
|
|
16
16
|
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup src/index.ts --format esm --dts --clean --out-dir dist",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"prepublishOnly": "pnpm build"
|
|
23
|
+
},
|
|
17
24
|
"keywords": [
|
|
18
25
|
"blockend",
|
|
19
26
|
"backend",
|
|
@@ -59,6 +66,7 @@
|
|
|
59
66
|
"engines": {
|
|
60
67
|
"node": ">=20"
|
|
61
68
|
},
|
|
69
|
+
"packageManager": "pnpm@10.33.2",
|
|
62
70
|
"dependencies": {
|
|
63
71
|
"@clack/prompts": "^1.5.1",
|
|
64
72
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -66,18 +74,11 @@
|
|
|
66
74
|
"commander": "^15.0.0",
|
|
67
75
|
"execa": "^9.6.1",
|
|
68
76
|
"picocolors": "^1.1.1",
|
|
69
|
-
"zod": "^4.4.3"
|
|
70
|
-
"@blockend/detector": "0.1.0"
|
|
77
|
+
"zod": "^4.4.3"
|
|
71
78
|
},
|
|
72
79
|
"devDependencies": {
|
|
73
80
|
"@types/node": "^25.9.3",
|
|
74
81
|
"tsup": "^8.5.1",
|
|
75
82
|
"vitest": "^1.6.1"
|
|
76
|
-
},
|
|
77
|
-
"scripts": {
|
|
78
|
-
"build": "tsup src/index.ts --format esm --dts --clean --out-dir dist",
|
|
79
|
-
"typecheck": "tsc --noEmit",
|
|
80
|
-
"test": "vitest run",
|
|
81
|
-
"test:watch": "vitest"
|
|
82
83
|
}
|
|
83
|
-
}
|
|
84
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Noor ul Hassan
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|