create-fornix 0.0.4 → 0.0.5
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 +558 -220
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -8,9 +8,9 @@ import { defineCommand as defineCommand8 } from "citty";
|
|
|
8
8
|
|
|
9
9
|
// src/cli/commands/create.ts
|
|
10
10
|
import { defineCommand } from "citty";
|
|
11
|
-
import { resolve, basename as basename2 } from "path";
|
|
12
|
-
import { mkdirSync as
|
|
13
|
-
import { join as
|
|
11
|
+
import { resolve as resolve2, basename as basename2 } from "path";
|
|
12
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13
|
+
import { join as join6 } from "path";
|
|
14
14
|
import * as p2 from "@clack/prompts";
|
|
15
15
|
import pc3 from "picocolors";
|
|
16
16
|
|
|
@@ -1768,7 +1768,6 @@ export function getStaticPaths() { return [{ params: { slug: '1' } }]; }
|
|
|
1768
1768
|
"cta-newsletter": { "cta-newsletter.astro": "<section>CTA</section>\n" },
|
|
1769
1769
|
"header-sticky": { "header-sticky.astro": "<header>Sticky</header>\n" }
|
|
1770
1770
|
};
|
|
1771
|
-
var FIXTURE_DEFAULT_CONTENT = {};
|
|
1772
1771
|
function loadAllPalettes() {
|
|
1773
1772
|
if (BUILTIN_PALETTES.length > 0) {
|
|
1774
1773
|
return [...BUILTIN_PALETTES];
|
|
@@ -1802,6 +1801,202 @@ function findRegistryPalettesDir() {
|
|
|
1802
1801
|
}
|
|
1803
1802
|
}
|
|
1804
1803
|
|
|
1804
|
+
// src/registry/registry-fetcher.ts
|
|
1805
|
+
import { existsSync, readFileSync as readFileSync2, mkdirSync, writeFileSync, statSync } from "fs";
|
|
1806
|
+
import { join as join2 } from "path";
|
|
1807
|
+
import { homedir } from "os";
|
|
1808
|
+
var DEFAULT_CONFIG = {
|
|
1809
|
+
registryUrl: process.env.FORNIX_REGISTRY_URL || "https://raw.githubusercontent.com/kamsqe/fornix/main/packages/fornix-registry/registry.json",
|
|
1810
|
+
cacheDir: join2(homedir(), ".cache", "fornix"),
|
|
1811
|
+
force: process.env.FORNIX_NO_CACHE === "true",
|
|
1812
|
+
maxCacheAge: 24 * 60 * 60 * 1e3
|
|
1813
|
+
// 24 hours
|
|
1814
|
+
};
|
|
1815
|
+
async function fetchRegistryIndex(config = {}) {
|
|
1816
|
+
if (process.env.FORNIX_E2E_MOCK === "true") {
|
|
1817
|
+
return ok({ blocks: FIXTURE_MANIFESTS, palettes: BUILTIN_PALETTES });
|
|
1818
|
+
}
|
|
1819
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
1820
|
+
const cacheFile = join2(cfg.cacheDir, "registry.json");
|
|
1821
|
+
const mapDataToIndex = (data) => {
|
|
1822
|
+
const blocksArray = Array.isArray(data.blocks) ? data.blocks : [];
|
|
1823
|
+
const blocksRecord = {};
|
|
1824
|
+
for (const block of blocksArray) {
|
|
1825
|
+
if (block.name) {
|
|
1826
|
+
blocksRecord[block.name] = block;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
return {
|
|
1830
|
+
blocks: blocksRecord,
|
|
1831
|
+
palettes: Array.isArray(data.palettes) ? data.palettes : []
|
|
1832
|
+
};
|
|
1833
|
+
};
|
|
1834
|
+
if (!cfg.force && isCacheValid(cacheFile, cfg.maxCacheAge)) {
|
|
1835
|
+
const cached = loadFromCache(cacheFile);
|
|
1836
|
+
if (cached) return ok(mapDataToIndex(cached));
|
|
1837
|
+
}
|
|
1838
|
+
try {
|
|
1839
|
+
const response = await fetch(cfg.registryUrl);
|
|
1840
|
+
if (!response.ok) {
|
|
1841
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1842
|
+
}
|
|
1843
|
+
const data = await response.json();
|
|
1844
|
+
mkdirSync(cfg.cacheDir, { recursive: true });
|
|
1845
|
+
writeFileSync(cacheFile, JSON.stringify(data, null, 2));
|
|
1846
|
+
return ok(mapDataToIndex(data));
|
|
1847
|
+
} catch (error) {
|
|
1848
|
+
const cached = loadFromCache(cacheFile);
|
|
1849
|
+
if (cached) {
|
|
1850
|
+
return ok(mapDataToIndex(cached));
|
|
1851
|
+
}
|
|
1852
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1853
|
+
return err({
|
|
1854
|
+
kind: "RegistryFetchError",
|
|
1855
|
+
message: `Failed to fetch registry from ${cfg.registryUrl}: ${message}`
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
function isCacheValid(cachePath, maxAge) {
|
|
1860
|
+
if (!existsSync(cachePath)) {
|
|
1861
|
+
return false;
|
|
1862
|
+
}
|
|
1863
|
+
try {
|
|
1864
|
+
const stat = statSync(cachePath);
|
|
1865
|
+
const age = Date.now() - stat.mtimeMs;
|
|
1866
|
+
return age < maxAge;
|
|
1867
|
+
} catch {
|
|
1868
|
+
return false;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
function loadFromCache(cachePath) {
|
|
1872
|
+
try {
|
|
1873
|
+
if (!existsSync(cachePath)) return null;
|
|
1874
|
+
const raw = readFileSync2(cachePath, "utf-8");
|
|
1875
|
+
return JSON.parse(raw);
|
|
1876
|
+
} catch {
|
|
1877
|
+
return null;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// src/registry/block-fetcher.ts
|
|
1882
|
+
import { downloadTemplate } from "giget";
|
|
1883
|
+
import {
|
|
1884
|
+
existsSync as existsSync2,
|
|
1885
|
+
readFileSync as readFileSync3,
|
|
1886
|
+
readdirSync as readdirSync2,
|
|
1887
|
+
mkdirSync as mkdirSync2,
|
|
1888
|
+
statSync as statSync2
|
|
1889
|
+
} from "fs";
|
|
1890
|
+
import { join as join3 } from "path";
|
|
1891
|
+
import { homedir as homedir2 } from "os";
|
|
1892
|
+
var DEFAULT_CONFIG2 = {
|
|
1893
|
+
repo: "kamsqe/fornix",
|
|
1894
|
+
ref: "main",
|
|
1895
|
+
cacheDir: join3(homedir2(), ".cache", "fornix", "blocks"),
|
|
1896
|
+
blocksPrefix: "packages/fornix-blocks/blocks",
|
|
1897
|
+
force: process.env.FORNIX_NO_CACHE === "true",
|
|
1898
|
+
maxCacheAge: 24 * 60 * 60 * 1e3
|
|
1899
|
+
// 24 hours
|
|
1900
|
+
};
|
|
1901
|
+
async function fetchBlock(blockName, config = {}) {
|
|
1902
|
+
if (process.env.FORNIX_E2E_MOCK === "true") {
|
|
1903
|
+
const manifest2 = FIXTURE_MANIFESTS[blockName];
|
|
1904
|
+
const files = FIXTURE_BLOCK_SOURCES[blockName];
|
|
1905
|
+
if (!manifest2 || !files) {
|
|
1906
|
+
return err({
|
|
1907
|
+
kind: "FetchError",
|
|
1908
|
+
blockName,
|
|
1909
|
+
message: `Mock block not found: ${blockName}`
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
return ok({ manifest: manifest2, files, fromCache: true });
|
|
1913
|
+
}
|
|
1914
|
+
const cfg = { ...DEFAULT_CONFIG2, ...config };
|
|
1915
|
+
const blockCacheDir = join3(cfg.cacheDir, blockName);
|
|
1916
|
+
if (!cfg.force && isCacheValid2(blockCacheDir, cfg.maxCacheAge)) {
|
|
1917
|
+
return loadFromCache2(blockName, blockCacheDir);
|
|
1918
|
+
}
|
|
1919
|
+
try {
|
|
1920
|
+
const source = `gh:${cfg.repo}/${cfg.blocksPrefix}/${blockName}#${cfg.ref}`;
|
|
1921
|
+
mkdirSync2(cfg.cacheDir, { recursive: true });
|
|
1922
|
+
await downloadTemplate(source, {
|
|
1923
|
+
dir: blockCacheDir,
|
|
1924
|
+
force: true
|
|
1925
|
+
// overwrite cache
|
|
1926
|
+
});
|
|
1927
|
+
return loadFromCache2(blockName, blockCacheDir);
|
|
1928
|
+
} catch (error) {
|
|
1929
|
+
if (existsSync2(join3(blockCacheDir, "block.json"))) {
|
|
1930
|
+
return loadFromCache2(blockName, blockCacheDir);
|
|
1931
|
+
}
|
|
1932
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1933
|
+
return err({
|
|
1934
|
+
kind: "FetchError",
|
|
1935
|
+
blockName,
|
|
1936
|
+
message: `Failed to fetch block '${blockName}': ${message}`
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
async function fetchBlocks(blockNames, config = {}) {
|
|
1941
|
+
return Promise.all(blockNames.map((name) => fetchBlock(name, config)));
|
|
1942
|
+
}
|
|
1943
|
+
function isCacheValid2(blockCacheDir, maxAge) {
|
|
1944
|
+
const manifestPath = join3(blockCacheDir, "block.json");
|
|
1945
|
+
if (!existsSync2(manifestPath)) {
|
|
1946
|
+
return false;
|
|
1947
|
+
}
|
|
1948
|
+
try {
|
|
1949
|
+
const stat = statSync2(manifestPath);
|
|
1950
|
+
const age = Date.now() - stat.mtimeMs;
|
|
1951
|
+
return age < maxAge;
|
|
1952
|
+
} catch {
|
|
1953
|
+
return false;
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
function loadFromCache2(blockName, blockCacheDir) {
|
|
1957
|
+
try {
|
|
1958
|
+
const manifestPath = join3(blockCacheDir, "block.json");
|
|
1959
|
+
if (!existsSync2(manifestPath)) {
|
|
1960
|
+
return err({
|
|
1961
|
+
kind: "FetchError",
|
|
1962
|
+
blockName,
|
|
1963
|
+
message: `Cache miss: no block.json found in ${blockCacheDir}`
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
const raw = readFileSync3(manifestPath, "utf-8");
|
|
1967
|
+
const parsed = JSON.parse(raw);
|
|
1968
|
+
const result = BlockManifestSchema.safeParse(parsed);
|
|
1969
|
+
if (!result.success) {
|
|
1970
|
+
const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
1971
|
+
return err({
|
|
1972
|
+
kind: "FetchError",
|
|
1973
|
+
blockName,
|
|
1974
|
+
message: `Invalid manifest in cache: ${issues}`
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
const files = {};
|
|
1978
|
+
const entries = readdirSync2(blockCacheDir);
|
|
1979
|
+
for (const entry of entries) {
|
|
1980
|
+
const fullPath = join3(blockCacheDir, entry);
|
|
1981
|
+
if (statSync2(fullPath).isFile() && entry !== "block.json") {
|
|
1982
|
+
files[entry] = readFileSync3(fullPath, "utf-8");
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
return ok({
|
|
1986
|
+
manifest: result.data,
|
|
1987
|
+
files,
|
|
1988
|
+
fromCache: true
|
|
1989
|
+
});
|
|
1990
|
+
} catch (error) {
|
|
1991
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1992
|
+
return err({
|
|
1993
|
+
kind: "FetchError",
|
|
1994
|
+
blockName,
|
|
1995
|
+
message: `Failed to load block from cache: ${message}`
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
1805
2000
|
// src/prompts/manual-flow.ts
|
|
1806
2001
|
import * as p from "@clack/prompts";
|
|
1807
2002
|
import pc from "picocolors";
|
|
@@ -1992,8 +2187,8 @@ function buildSummary(config, blockNames, palette) {
|
|
|
1992
2187
|
|
|
1993
2188
|
// src/scaffold/post-scaffold.ts
|
|
1994
2189
|
import { execSync } from "child_process";
|
|
1995
|
-
import { writeFileSync } from "fs";
|
|
1996
|
-
import { join as
|
|
2190
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
2191
|
+
import { join as join4, basename } from "path";
|
|
1997
2192
|
import pc2 from "picocolors";
|
|
1998
2193
|
function runPostScaffold(input, callbacks) {
|
|
1999
2194
|
const log = callbacks?.onLog ?? ((msg) => console.log(msg));
|
|
@@ -2159,7 +2354,7 @@ function generateClaudeMd(projectDir, config, blockNames) {
|
|
|
2159
2354
|
lines.push("```");
|
|
2160
2355
|
lines.push("");
|
|
2161
2356
|
const content = lines.join("\n");
|
|
2162
|
-
|
|
2357
|
+
writeFileSync2(join4(projectDir, "CLAUDE.md"), content, "utf-8");
|
|
2163
2358
|
}
|
|
2164
2359
|
|
|
2165
2360
|
// src/ai/prompt-builder.ts
|
|
@@ -2187,7 +2382,8 @@ Your job:
|
|
|
2187
2382
|
|
|
2188
2383
|
You MUST only select blocks that exist in the catalog below. Never invent block names.`;
|
|
2189
2384
|
}
|
|
2190
|
-
function buildBlocksCatalog(
|
|
2385
|
+
function buildBlocksCatalog(blocksRecord) {
|
|
2386
|
+
const blocks = Object.values(blocksRecord);
|
|
2191
2387
|
const lines = ["# Available Blocks\n"];
|
|
2192
2388
|
const grouped = /* @__PURE__ */ new Map();
|
|
2193
2389
|
for (const block of blocks) {
|
|
@@ -2267,7 +2463,8 @@ function buildPalettesCatalog(palettes) {
|
|
|
2267
2463
|
}
|
|
2268
2464
|
return lines.join("\n");
|
|
2269
2465
|
}
|
|
2270
|
-
function buildConstraintRules(
|
|
2466
|
+
function buildConstraintRules(blocksRecord) {
|
|
2467
|
+
const blocks = Object.values(blocksRecord);
|
|
2271
2468
|
const lines = ["# Constraint Rules\n"];
|
|
2272
2469
|
lines.push("## Render Modes");
|
|
2273
2470
|
lines.push("- `static` \u2014 Pre-rendered HTML, no server. Best for blogs, docs, landing pages.");
|
|
@@ -2993,8 +3190,8 @@ function createCloudflareProvider(opts = {}) {
|
|
|
2993
3190
|
}
|
|
2994
3191
|
|
|
2995
3192
|
// src/ai/providers/mock-provider.ts
|
|
2996
|
-
import { readFileSync as
|
|
2997
|
-
import { join as
|
|
3193
|
+
import { readFileSync as readFileSync4, existsSync as existsSync3 } from "fs";
|
|
3194
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
2998
3195
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2999
3196
|
var FIXTURE_ENTRIES = [
|
|
3000
3197
|
{
|
|
@@ -3034,9 +3231,9 @@ function createMockProvider() {
|
|
|
3034
3231
|
});
|
|
3035
3232
|
}
|
|
3036
3233
|
const fixtureDir = resolveFixtureDir();
|
|
3037
|
-
const filePath =
|
|
3234
|
+
const filePath = join5(fixtureDir, match.filename);
|
|
3038
3235
|
try {
|
|
3039
|
-
const raw =
|
|
3236
|
+
const raw = readFileSync4(filePath, "utf-8");
|
|
3040
3237
|
const parsed = JSON.parse(raw);
|
|
3041
3238
|
const validated = IntentSchema.parse(parsed);
|
|
3042
3239
|
return ok(validated);
|
|
@@ -3053,10 +3250,10 @@ function createMockProvider() {
|
|
|
3053
3250
|
}
|
|
3054
3251
|
function resolveFixtureDir() {
|
|
3055
3252
|
const thisDir = dirname2(fileURLToPath2(import.meta.url));
|
|
3056
|
-
const fromSource =
|
|
3057
|
-
if (
|
|
3058
|
-
const fromDist =
|
|
3059
|
-
if (
|
|
3253
|
+
const fromSource = join5(thisDir, "..", "..", "..", "tests", "fixtures", "ai-responses");
|
|
3254
|
+
if (existsSync3(fromSource)) return fromSource;
|
|
3255
|
+
const fromDist = join5(thisDir, "..", "tests", "fixtures", "ai-responses");
|
|
3256
|
+
if (existsSync3(fromDist)) return fromDist;
|
|
3060
3257
|
return fromSource;
|
|
3061
3258
|
}
|
|
3062
3259
|
|
|
@@ -3363,30 +3560,37 @@ var createCommand = defineCommand({
|
|
|
3363
3560
|
}
|
|
3364
3561
|
},
|
|
3365
3562
|
async run({ args: args2 }) {
|
|
3366
|
-
const
|
|
3563
|
+
const registryResult = await fetchRegistryIndex();
|
|
3564
|
+
if (!isOk(registryResult)) {
|
|
3565
|
+
console.error(pc3.red(`\u2716 Failed to fetch block registry: ${registryResult.error.message}`));
|
|
3566
|
+
process.exitCode = 1;
|
|
3567
|
+
return;
|
|
3568
|
+
}
|
|
3569
|
+
const manifests = registryResult.value.blocks;
|
|
3570
|
+
const allPalettes = registryResult.value.palettes.length > 0 ? registryResult.value.palettes : loadAllPalettes();
|
|
3367
3571
|
const hasExplicitFlags = !!(args2.render || args2.deploy || args2.blocks || args2.database || args2.css || args2.locales || args2.palette || args2.recipe);
|
|
3368
3572
|
if (args2.manual && !args2.yes) {
|
|
3369
|
-
const defaultProjectName = args2.dir ? basename2(
|
|
3573
|
+
const defaultProjectName = args2.dir ? basename2(resolve2(args2.dir)) : "my-project";
|
|
3370
3574
|
const config = await runManualFlow({
|
|
3371
3575
|
defaultProjectName,
|
|
3372
|
-
manifests
|
|
3576
|
+
manifests,
|
|
3373
3577
|
allPalettes
|
|
3374
3578
|
});
|
|
3375
3579
|
if (!config) {
|
|
3376
3580
|
process.exitCode = 0;
|
|
3377
3581
|
return;
|
|
3378
3582
|
}
|
|
3379
|
-
const projectDir = args2.dir ?
|
|
3583
|
+
const projectDir = args2.dir ? resolve2(args2.dir) : resolve2(config.projectDir);
|
|
3380
3584
|
const finalConfig = { ...config, projectDir };
|
|
3381
|
-
return runScaffold(finalConfig, allPalettes, args2["dry-run"] ?? false, args2.verbose ?? false, !(args2.install ?? true), !(args2.git ?? true));
|
|
3585
|
+
return runScaffold(finalConfig, manifests, allPalettes, args2["dry-run"] ?? false, args2.verbose ?? false, !(args2.install ?? true), !(args2.git ?? true));
|
|
3382
3586
|
}
|
|
3383
3587
|
if (args2.manual || hasExplicitFlags) {
|
|
3384
|
-
return runFlagDrivenMode(args2, allPalettes);
|
|
3588
|
+
return runFlagDrivenMode(args2, manifests, allPalettes);
|
|
3385
3589
|
}
|
|
3386
|
-
return runAIMode(args2, allPalettes);
|
|
3590
|
+
return runAIMode(args2, manifests, allPalettes);
|
|
3387
3591
|
}
|
|
3388
3592
|
});
|
|
3389
|
-
async function runAIMode(args2, allPalettes) {
|
|
3593
|
+
async function runAIMode(args2, manifests, allPalettes) {
|
|
3390
3594
|
const providerName = parseProviderName(args2.provider);
|
|
3391
3595
|
if (args2.provider && !providerName) {
|
|
3392
3596
|
console.error(pc3.red(`\u2716 Unknown provider: ${String(args2.provider)}`));
|
|
@@ -3410,10 +3614,10 @@ async function runAIMode(args2, allPalettes) {
|
|
|
3410
3614
|
return;
|
|
3411
3615
|
}
|
|
3412
3616
|
const registry = {
|
|
3413
|
-
blocks: Object.values(
|
|
3617
|
+
blocks: Object.values(manifests),
|
|
3414
3618
|
palettes: [...allPalettes]
|
|
3415
3619
|
};
|
|
3416
|
-
const projectDir =
|
|
3620
|
+
const projectDir = resolve2(String(args2.dir ?? "."));
|
|
3417
3621
|
const projectName = basename2(projectDir);
|
|
3418
3622
|
const nameResult = validateProjectName(projectName);
|
|
3419
3623
|
if (!nameResult.ok) {
|
|
@@ -3448,6 +3652,7 @@ async function runAIMode(args2, allPalettes) {
|
|
|
3448
3652
|
}
|
|
3449
3653
|
return runScaffold(
|
|
3450
3654
|
config,
|
|
3655
|
+
manifests,
|
|
3451
3656
|
allPalettes,
|
|
3452
3657
|
(args2["dry-run"] ?? false) === true,
|
|
3453
3658
|
(args2.verbose ?? false) === true,
|
|
@@ -3455,8 +3660,8 @@ async function runAIMode(args2, allPalettes) {
|
|
|
3455
3660
|
!((args2.git ?? true) === true)
|
|
3456
3661
|
);
|
|
3457
3662
|
}
|
|
3458
|
-
function runFlagDrivenMode(args2, allPalettes) {
|
|
3459
|
-
const projectDir =
|
|
3663
|
+
function runFlagDrivenMode(args2, manifests, allPalettes) {
|
|
3664
|
+
const projectDir = resolve2(String(args2.dir ?? "."));
|
|
3460
3665
|
const projectName = basename2(projectDir);
|
|
3461
3666
|
const nameResult = validateProjectName(projectName);
|
|
3462
3667
|
if (!nameResult.ok) {
|
|
@@ -3539,6 +3744,7 @@ function runFlagDrivenMode(args2, allPalettes) {
|
|
|
3539
3744
|
};
|
|
3540
3745
|
return runScaffold(
|
|
3541
3746
|
config,
|
|
3747
|
+
manifests,
|
|
3542
3748
|
allPalettes,
|
|
3543
3749
|
(args2["dry-run"] ?? false) === true,
|
|
3544
3750
|
(args2.verbose ?? false) === true,
|
|
@@ -3546,12 +3752,36 @@ function runFlagDrivenMode(args2, allPalettes) {
|
|
|
3546
3752
|
!((args2.git ?? true) === true)
|
|
3547
3753
|
);
|
|
3548
3754
|
}
|
|
3549
|
-
function runScaffold(config, allPalettes, dryRun, verbose, skipInstall, skipGit) {
|
|
3755
|
+
async function runScaffold(config, manifests, allPalettes, dryRun, verbose, skipInstall, skipGit) {
|
|
3756
|
+
const spinner2 = p2.spinner();
|
|
3757
|
+
spinner2.start("Fetching blocks from registry...");
|
|
3758
|
+
const blockNames = config.blocks.map((b) => b.name);
|
|
3759
|
+
const blockResults = await fetchBlocks(blockNames);
|
|
3760
|
+
const blockSources = {};
|
|
3761
|
+
const blockDefaultContent = {};
|
|
3762
|
+
for (const result2 of blockResults) {
|
|
3763
|
+
if (!isOk(result2)) {
|
|
3764
|
+
spinner2.stop(`Failed to fetch block '${result2.error.blockName}'`, 1);
|
|
3765
|
+
console.error(pc3.red(`\u2716 ${result2.error.message}`));
|
|
3766
|
+
process.exitCode = 1;
|
|
3767
|
+
return;
|
|
3768
|
+
}
|
|
3769
|
+
const { manifest: manifest2, files: files2 } = result2.value;
|
|
3770
|
+
blockSources[manifest2.name] = files2;
|
|
3771
|
+
try {
|
|
3772
|
+
if (files2["default-content.json"]) {
|
|
3773
|
+
blockDefaultContent[manifest2.name] = JSON.parse(files2["default-content.json"]);
|
|
3774
|
+
}
|
|
3775
|
+
} catch (e) {
|
|
3776
|
+
console.error(pc3.yellow(`\u26A0 Warning: Failed to parse default-content.json for block '${manifest2.name}'`));
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
spinner2.stop("Blocks fetched successfully.");
|
|
3550
3780
|
const input = {
|
|
3551
3781
|
config,
|
|
3552
|
-
manifests
|
|
3553
|
-
blockSources:
|
|
3554
|
-
blockDefaultContent:
|
|
3782
|
+
manifests,
|
|
3783
|
+
blockSources: Object.freeze(blockSources),
|
|
3784
|
+
blockDefaultContent: Object.freeze(blockDefaultContent),
|
|
3555
3785
|
allPalettes
|
|
3556
3786
|
};
|
|
3557
3787
|
const result = scaffold(input);
|
|
@@ -3585,10 +3815,10 @@ function runScaffold(config, allPalettes, dryRun, verbose, skipInstall, skipGit)
|
|
|
3585
3815
|
const files = result.value.files;
|
|
3586
3816
|
let filesWritten = 0;
|
|
3587
3817
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
3588
|
-
const fullPath =
|
|
3589
|
-
const parentDir =
|
|
3590
|
-
|
|
3591
|
-
|
|
3818
|
+
const fullPath = join6(config.projectDir, relativePath);
|
|
3819
|
+
const parentDir = join6(fullPath, "..");
|
|
3820
|
+
mkdirSync4(parentDir, { recursive: true });
|
|
3821
|
+
writeFileSync3(fullPath, content, "utf-8");
|
|
3592
3822
|
filesWritten++;
|
|
3593
3823
|
if (verbose) {
|
|
3594
3824
|
console.log(pc3.dim(` created ${relativePath}`));
|
|
@@ -3677,8 +3907,8 @@ function showNoProviderGuide() {
|
|
|
3677
3907
|
// src/cli/commands/add.ts
|
|
3678
3908
|
import { defineCommand as defineCommand2 } from "citty";
|
|
3679
3909
|
import pc4 from "picocolors";
|
|
3680
|
-
import { readFileSync as
|
|
3681
|
-
import { join as
|
|
3910
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync5 } from "fs";
|
|
3911
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
3682
3912
|
var addCommand = defineCommand2({
|
|
3683
3913
|
meta: {
|
|
3684
3914
|
name: "add",
|
|
@@ -3706,28 +3936,36 @@ var addCommand = defineCommand2({
|
|
|
3706
3936
|
default: false
|
|
3707
3937
|
}
|
|
3708
3938
|
},
|
|
3709
|
-
run({ args: args2 }) {
|
|
3939
|
+
async run({ args: args2 }) {
|
|
3710
3940
|
const typedArgs = args2;
|
|
3711
3941
|
const cwd = process.cwd();
|
|
3712
|
-
const manifestPath =
|
|
3713
|
-
if (!
|
|
3942
|
+
const manifestPath = join7(cwd, "fornix.json");
|
|
3943
|
+
if (!existsSync4(manifestPath)) {
|
|
3714
3944
|
console.error(
|
|
3715
3945
|
pc4.red("\u2717 No fornix.json found. Are you in a Fornix project?")
|
|
3716
3946
|
);
|
|
3717
3947
|
process.exit(1);
|
|
3718
3948
|
}
|
|
3719
|
-
const manifestRaw =
|
|
3949
|
+
const manifestRaw = readFileSync5(manifestPath, "utf-8");
|
|
3720
3950
|
const manifest2 = JSON.parse(manifestRaw);
|
|
3951
|
+
const registryResult = await fetchRegistryIndex();
|
|
3952
|
+
if (!isOk(registryResult)) {
|
|
3953
|
+
console.error(pc4.red(`\u2716 Failed to fetch block registry: ${registryResult.error.message}`));
|
|
3954
|
+
process.exitCode = 1;
|
|
3955
|
+
return;
|
|
3956
|
+
}
|
|
3957
|
+
const manifests = registryResult.value.blocks;
|
|
3721
3958
|
const blockName = typedArgs.block;
|
|
3722
|
-
const blockManifest =
|
|
3959
|
+
const blockManifest = manifests[blockName];
|
|
3723
3960
|
if (!blockManifest) {
|
|
3724
3961
|
console.error(pc4.red(`\u2717 Block '${blockName}' not found in registry.`));
|
|
3725
3962
|
console.error(
|
|
3726
3963
|
pc4.dim(
|
|
3727
|
-
` Available: ${Object.keys(
|
|
3964
|
+
` Available: ${Object.keys(manifests).join(", ")}`
|
|
3728
3965
|
)
|
|
3729
3966
|
);
|
|
3730
|
-
process.
|
|
3967
|
+
process.exitCode = 1;
|
|
3968
|
+
return;
|
|
3731
3969
|
}
|
|
3732
3970
|
const installedNames = new Set(manifest2.blocks.map((b) => b.name));
|
|
3733
3971
|
if (installedNames.has(blockName)) {
|
|
@@ -3736,26 +3974,32 @@ var addCommand = defineCommand2({
|
|
|
3736
3974
|
);
|
|
3737
3975
|
return;
|
|
3738
3976
|
}
|
|
3739
|
-
const blocksToAdd = resolveDependencies2(blockName, installedNames);
|
|
3977
|
+
const blocksToAdd = resolveDependencies2(blockName, installedNames, manifests);
|
|
3740
3978
|
for (const name of blocksToAdd) {
|
|
3741
|
-
const m =
|
|
3979
|
+
const m = manifests[name];
|
|
3742
3980
|
if (m?.requiredMode && manifest2.renderMode !== m.requiredMode) {
|
|
3743
3981
|
console.error(
|
|
3744
3982
|
pc4.red(
|
|
3745
3983
|
`\u2717 Block '${name}' requires '${m.requiredMode}' mode, but project uses '${manifest2.renderMode}'.`
|
|
3746
3984
|
)
|
|
3747
3985
|
);
|
|
3748
|
-
process.
|
|
3986
|
+
process.exitCode = 1;
|
|
3987
|
+
return;
|
|
3749
3988
|
}
|
|
3750
3989
|
}
|
|
3990
|
+
console.log(pc4.dim("Fetching blocks..."));
|
|
3991
|
+
const blockResults = await fetchBlocks(blocksToAdd);
|
|
3751
3992
|
const filesToWrite = [];
|
|
3752
|
-
for (
|
|
3753
|
-
const
|
|
3754
|
-
const
|
|
3755
|
-
if (!
|
|
3756
|
-
console.error(pc4.red(`\u2717
|
|
3757
|
-
process.
|
|
3993
|
+
for (let i = 0; i < blocksToAdd.length; i++) {
|
|
3994
|
+
const name = blocksToAdd[i];
|
|
3995
|
+
const result = blockResults[i];
|
|
3996
|
+
if (!result || !isOk(result)) {
|
|
3997
|
+
console.error(pc4.red(`\u2717 Failed to fetch files for block '${name}'.`));
|
|
3998
|
+
process.exitCode = 1;
|
|
3999
|
+
return;
|
|
3758
4000
|
}
|
|
4001
|
+
const bManifest = result.value.manifest;
|
|
4002
|
+
const sources = result.value.files;
|
|
3759
4003
|
for (const file of bManifest.files) {
|
|
3760
4004
|
const content = sources[file.source];
|
|
3761
4005
|
if (content === void 0) {
|
|
@@ -3764,10 +4008,11 @@ var addCommand = defineCommand2({
|
|
|
3764
4008
|
`\u2717 Source file '${file.source}' not found for block '${name}'.`
|
|
3765
4009
|
)
|
|
3766
4010
|
);
|
|
3767
|
-
process.
|
|
4011
|
+
process.exitCode = 1;
|
|
4012
|
+
return;
|
|
3768
4013
|
}
|
|
3769
4014
|
filesToWrite.push({
|
|
3770
|
-
path:
|
|
4015
|
+
path: join7(cwd, file.destination),
|
|
3771
4016
|
content
|
|
3772
4017
|
});
|
|
3773
4018
|
}
|
|
@@ -3788,15 +4033,15 @@ var addCommand = defineCommand2({
|
|
|
3788
4033
|
return;
|
|
3789
4034
|
}
|
|
3790
4035
|
for (const file of filesToWrite) {
|
|
3791
|
-
|
|
3792
|
-
|
|
4036
|
+
mkdirSync5(dirname3(file.path), { recursive: true });
|
|
4037
|
+
writeFileSync4(file.path, file.content);
|
|
3793
4038
|
if (typedArgs.verbose) {
|
|
3794
4039
|
console.log(` ${pc4.dim("\u2192")} ${file.path}`);
|
|
3795
4040
|
}
|
|
3796
4041
|
}
|
|
3797
4042
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3798
4043
|
for (const name of blocksToAdd) {
|
|
3799
|
-
const bManifest =
|
|
4044
|
+
const bManifest = manifests[name];
|
|
3800
4045
|
if (!bManifest) continue;
|
|
3801
4046
|
manifest2.blocks.push({
|
|
3802
4047
|
name,
|
|
@@ -3805,7 +4050,7 @@ var addCommand = defineCommand2({
|
|
|
3805
4050
|
installedAt: now
|
|
3806
4051
|
});
|
|
3807
4052
|
}
|
|
3808
|
-
|
|
4053
|
+
writeFileSync4(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
3809
4054
|
console.log();
|
|
3810
4055
|
for (const name of blocksToAdd) {
|
|
3811
4056
|
const isDep = name !== blockName;
|
|
@@ -3826,13 +4071,13 @@ var addCommand = defineCommand2({
|
|
|
3826
4071
|
console.log();
|
|
3827
4072
|
}
|
|
3828
4073
|
});
|
|
3829
|
-
function resolveDependencies2(blockName, installedNames) {
|
|
4074
|
+
function resolveDependencies2(blockName, installedNames, manifests) {
|
|
3830
4075
|
const result = [];
|
|
3831
4076
|
const visited = /* @__PURE__ */ new Set();
|
|
3832
4077
|
function walk(name) {
|
|
3833
4078
|
if (visited.has(name) || installedNames.has(name)) return;
|
|
3834
4079
|
visited.add(name);
|
|
3835
|
-
const manifest2 =
|
|
4080
|
+
const manifest2 = manifests[name];
|
|
3836
4081
|
if (!manifest2) return;
|
|
3837
4082
|
for (const dep of manifest2.requires) {
|
|
3838
4083
|
walk(dep);
|
|
@@ -3846,8 +4091,8 @@ function resolveDependencies2(blockName, installedNames) {
|
|
|
3846
4091
|
// src/cli/commands/remove.ts
|
|
3847
4092
|
import { defineCommand as defineCommand3 } from "citty";
|
|
3848
4093
|
import pc5 from "picocolors";
|
|
3849
|
-
import { readFileSync as
|
|
3850
|
-
import { join as
|
|
4094
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync5, unlinkSync, readdirSync as readdirSync3, rmdirSync } from "fs";
|
|
4095
|
+
import { join as join8, dirname as dirname4 } from "path";
|
|
3851
4096
|
var removeCommand = defineCommand3({
|
|
3852
4097
|
meta: {
|
|
3853
4098
|
name: "remove",
|
|
@@ -3875,18 +4120,25 @@ var removeCommand = defineCommand3({
|
|
|
3875
4120
|
default: false
|
|
3876
4121
|
}
|
|
3877
4122
|
},
|
|
3878
|
-
run({ args: args2 }) {
|
|
4123
|
+
async run({ args: args2 }) {
|
|
3879
4124
|
const typedArgs = args2;
|
|
3880
4125
|
const cwd = process.cwd();
|
|
3881
|
-
const manifestPath =
|
|
3882
|
-
if (!
|
|
4126
|
+
const manifestPath = join8(cwd, "fornix.json");
|
|
4127
|
+
if (!existsSync5(manifestPath)) {
|
|
3883
4128
|
console.error(
|
|
3884
4129
|
pc5.red("\u2717 No fornix.json found. Are you in a Fornix project?")
|
|
3885
4130
|
);
|
|
3886
4131
|
process.exit(1);
|
|
3887
4132
|
}
|
|
3888
|
-
const manifestRaw =
|
|
4133
|
+
const manifestRaw = readFileSync6(manifestPath, "utf-8");
|
|
3889
4134
|
const manifest2 = JSON.parse(manifestRaw);
|
|
4135
|
+
const registryResult = await fetchRegistryIndex();
|
|
4136
|
+
if (!isOk(registryResult)) {
|
|
4137
|
+
console.error(pc5.red(`\u2716 Failed to fetch block registry: ${registryResult.error.message}`));
|
|
4138
|
+
process.exitCode = 1;
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4141
|
+
const manifests = registryResult.value.blocks;
|
|
3890
4142
|
const blockName = typedArgs.block;
|
|
3891
4143
|
const installedNames = new Set(manifest2.blocks.map((b) => b.name));
|
|
3892
4144
|
if (!installedNames.has(blockName)) {
|
|
@@ -3895,7 +4147,7 @@ var removeCommand = defineCommand3({
|
|
|
3895
4147
|
);
|
|
3896
4148
|
return;
|
|
3897
4149
|
}
|
|
3898
|
-
const dependents = findDependents(blockName, installedNames);
|
|
4150
|
+
const dependents = findDependents(blockName, installedNames, manifests);
|
|
3899
4151
|
if (dependents.length > 0 && !typedArgs.force) {
|
|
3900
4152
|
console.log(
|
|
3901
4153
|
pc5.yellow(
|
|
@@ -3907,12 +4159,12 @@ var removeCommand = defineCommand3({
|
|
|
3907
4159
|
);
|
|
3908
4160
|
return;
|
|
3909
4161
|
}
|
|
3910
|
-
const blockManifest =
|
|
4162
|
+
const blockManifest = manifests[blockName];
|
|
3911
4163
|
const filesToRemove = [];
|
|
3912
4164
|
if (blockManifest) {
|
|
3913
4165
|
for (const file of blockManifest.files) {
|
|
3914
|
-
const filePath =
|
|
3915
|
-
if (
|
|
4166
|
+
const filePath = join8(cwd, file.destination);
|
|
4167
|
+
if (existsSync5(filePath)) {
|
|
3916
4168
|
filesToRemove.push(filePath);
|
|
3917
4169
|
}
|
|
3918
4170
|
}
|
|
@@ -3934,7 +4186,7 @@ var removeCommand = defineCommand3({
|
|
|
3934
4186
|
tryRemoveEmptyDir(dirname4(filePath), cwd);
|
|
3935
4187
|
}
|
|
3936
4188
|
manifest2.blocks = manifest2.blocks.filter((b) => b.name !== blockName);
|
|
3937
|
-
|
|
4189
|
+
writeFileSync5(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
3938
4190
|
console.log();
|
|
3939
4191
|
console.log(` ${pc5.red("-")} ${pc5.bold(blockName)} removed`);
|
|
3940
4192
|
if (dependents.length > 0) {
|
|
@@ -3953,10 +4205,10 @@ var removeCommand = defineCommand3({
|
|
|
3953
4205
|
console.log();
|
|
3954
4206
|
}
|
|
3955
4207
|
});
|
|
3956
|
-
function findDependents(blockName, installedNames) {
|
|
4208
|
+
function findDependents(blockName, installedNames, manifests) {
|
|
3957
4209
|
const dependents = [];
|
|
3958
4210
|
for (const name of installedNames) {
|
|
3959
|
-
const manifest2 =
|
|
4211
|
+
const manifest2 = manifests[name];
|
|
3960
4212
|
if (manifest2 && manifest2.requires.includes(blockName)) {
|
|
3961
4213
|
dependents.push(name);
|
|
3962
4214
|
}
|
|
@@ -3966,7 +4218,7 @@ function findDependents(blockName, installedNames) {
|
|
|
3966
4218
|
function tryRemoveEmptyDir(dirPath, rootPath) {
|
|
3967
4219
|
if (dirPath === rootPath || !dirPath.startsWith(rootPath)) return;
|
|
3968
4220
|
try {
|
|
3969
|
-
const entries =
|
|
4221
|
+
const entries = readdirSync3(dirPath);
|
|
3970
4222
|
if (entries.length === 0) {
|
|
3971
4223
|
rmdirSync(dirPath);
|
|
3972
4224
|
tryRemoveEmptyDir(dirname4(dirPath), rootPath);
|
|
@@ -4003,9 +4255,16 @@ var listCommand = defineCommand4({
|
|
|
4003
4255
|
default: false
|
|
4004
4256
|
}
|
|
4005
4257
|
},
|
|
4006
|
-
run({ args: args2 }) {
|
|
4258
|
+
async run({ args: args2 }) {
|
|
4007
4259
|
const typedArgs = args2;
|
|
4008
|
-
const
|
|
4260
|
+
const registryResult = await fetchRegistryIndex();
|
|
4261
|
+
if (!isOk(registryResult)) {
|
|
4262
|
+
console.error(pc6.red(`\u2716 Failed to fetch block registry: ${registryResult.error.message}`));
|
|
4263
|
+
process.exitCode = 1;
|
|
4264
|
+
return;
|
|
4265
|
+
}
|
|
4266
|
+
const manifests = registryResult.value.blocks;
|
|
4267
|
+
const blocks = getFilteredBlocks(typedArgs, manifests);
|
|
4009
4268
|
if (blocks.length === 0) {
|
|
4010
4269
|
console.log(pc6.yellow("No blocks found matching your filters."));
|
|
4011
4270
|
return;
|
|
@@ -4017,8 +4276,8 @@ var listCommand = defineCommand4({
|
|
|
4017
4276
|
}
|
|
4018
4277
|
}
|
|
4019
4278
|
});
|
|
4020
|
-
function getFilteredBlocks(args2) {
|
|
4021
|
-
let blocks = Object.values(
|
|
4279
|
+
function getFilteredBlocks(args2, manifests) {
|
|
4280
|
+
let blocks = Object.values(manifests);
|
|
4022
4281
|
if (args2.type) {
|
|
4023
4282
|
const filterType = args2.type.toLowerCase();
|
|
4024
4283
|
blocks = blocks.filter((b) => b.type === filterType);
|
|
@@ -4099,8 +4358,8 @@ function printFormatted(blocks, verbose) {
|
|
|
4099
4358
|
// src/cli/commands/status.ts
|
|
4100
4359
|
import { defineCommand as defineCommand5 } from "citty";
|
|
4101
4360
|
import pc7 from "picocolors";
|
|
4102
|
-
import { readFileSync as
|
|
4103
|
-
import { join as
|
|
4361
|
+
import { readFileSync as readFileSync7, existsSync as existsSync6 } from "fs";
|
|
4362
|
+
import { join as join9 } from "path";
|
|
4104
4363
|
var statusCommand = defineCommand5({
|
|
4105
4364
|
meta: {
|
|
4106
4365
|
name: "status",
|
|
@@ -4121,8 +4380,8 @@ var statusCommand = defineCommand5({
|
|
|
4121
4380
|
run({ args: args2 }) {
|
|
4122
4381
|
const typedArgs = args2;
|
|
4123
4382
|
const cwd = process.cwd();
|
|
4124
|
-
const manifestPath =
|
|
4125
|
-
if (!
|
|
4383
|
+
const manifestPath = join9(cwd, "fornix.json");
|
|
4384
|
+
if (!existsSync6(manifestPath)) {
|
|
4126
4385
|
console.error(
|
|
4127
4386
|
pc7.red("\u2717 No fornix.json found in the current directory.")
|
|
4128
4387
|
);
|
|
@@ -4135,7 +4394,7 @@ var statusCommand = defineCommand5({
|
|
|
4135
4394
|
}
|
|
4136
4395
|
let manifest2;
|
|
4137
4396
|
try {
|
|
4138
|
-
const raw =
|
|
4397
|
+
const raw = readFileSync7(manifestPath, "utf-8");
|
|
4139
4398
|
manifest2 = JSON.parse(raw);
|
|
4140
4399
|
} catch {
|
|
4141
4400
|
console.error(pc7.red("\u2717 Failed to parse fornix.json."));
|
|
@@ -4216,8 +4475,8 @@ function printStatus(manifest2, verbose) {
|
|
|
4216
4475
|
// src/cli/commands/doctor.ts
|
|
4217
4476
|
import { defineCommand as defineCommand6 } from "citty";
|
|
4218
4477
|
import pc8 from "picocolors";
|
|
4219
|
-
import { readFileSync as
|
|
4220
|
-
import { join as
|
|
4478
|
+
import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
|
|
4479
|
+
import { join as join10 } from "path";
|
|
4221
4480
|
var doctorCommand = defineCommand6({
|
|
4222
4481
|
meta: {
|
|
4223
4482
|
name: "doctor",
|
|
@@ -4230,9 +4489,9 @@ var doctorCommand = defineCommand6({
|
|
|
4230
4489
|
default: false
|
|
4231
4490
|
}
|
|
4232
4491
|
},
|
|
4233
|
-
run({ args: args2 }) {
|
|
4492
|
+
async run({ args: args2 }) {
|
|
4234
4493
|
const cwd = process.cwd();
|
|
4235
|
-
const manifestPath =
|
|
4494
|
+
const manifestPath = join10(cwd, "fornix.json");
|
|
4236
4495
|
let hasErrors = false;
|
|
4237
4496
|
const errors = [];
|
|
4238
4497
|
function reportError(msg) {
|
|
@@ -4242,7 +4501,7 @@ var doctorCommand = defineCommand6({
|
|
|
4242
4501
|
console.error(pc8.red(`\u2717 ${msg}`));
|
|
4243
4502
|
}
|
|
4244
4503
|
}
|
|
4245
|
-
if (!
|
|
4504
|
+
if (!existsSync7(manifestPath)) {
|
|
4246
4505
|
reportError("No fornix.json found in the current directory.");
|
|
4247
4506
|
if (args2.json) {
|
|
4248
4507
|
console.log(JSON.stringify({ healthy: false, errors }));
|
|
@@ -4251,7 +4510,7 @@ var doctorCommand = defineCommand6({
|
|
|
4251
4510
|
}
|
|
4252
4511
|
let manifest2;
|
|
4253
4512
|
try {
|
|
4254
|
-
const raw =
|
|
4513
|
+
const raw = readFileSync8(manifestPath, "utf-8");
|
|
4255
4514
|
manifest2 = JSON.parse(raw);
|
|
4256
4515
|
} catch {
|
|
4257
4516
|
reportError("Failed to parse fornix.json.");
|
|
@@ -4260,24 +4519,34 @@ var doctorCommand = defineCommand6({
|
|
|
4260
4519
|
}
|
|
4261
4520
|
process.exit(1);
|
|
4262
4521
|
}
|
|
4522
|
+
const registryResult = await fetchRegistryIndex();
|
|
4523
|
+
if (!isOk(registryResult)) {
|
|
4524
|
+
reportError(`Failed to fetch block registry: ${registryResult.error.message}`);
|
|
4525
|
+
if (args2.json) {
|
|
4526
|
+
console.log(JSON.stringify({ healthy: false, errors }));
|
|
4527
|
+
}
|
|
4528
|
+
process.exitCode = 1;
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
const manifests = registryResult.value.blocks;
|
|
4263
4532
|
const isMultiLocale = manifest2.locales && manifest2.locales.length >= 2;
|
|
4264
4533
|
const locales = isMultiLocale ? manifest2.locales : [""];
|
|
4265
4534
|
const installedBlocks = new Set(manifest2.blocks.map((b) => b.name));
|
|
4266
4535
|
for (const block of manifest2.blocks) {
|
|
4267
|
-
const bManifest =
|
|
4536
|
+
const bManifest = manifests[block.name];
|
|
4268
4537
|
if (bManifest) {
|
|
4269
4538
|
for (const file of bManifest.files) {
|
|
4270
|
-
const filePath =
|
|
4271
|
-
if (!
|
|
4539
|
+
const filePath = join10(cwd, file.destination);
|
|
4540
|
+
if (!existsSync7(filePath)) {
|
|
4272
4541
|
reportError(`Missing expected file for installed block '${block.name}': ${file.destination}`);
|
|
4273
4542
|
}
|
|
4274
4543
|
}
|
|
4275
4544
|
}
|
|
4276
4545
|
}
|
|
4277
|
-
for (const [name, bManifest] of Object.entries(
|
|
4546
|
+
for (const [name, bManifest] of Object.entries(manifests)) {
|
|
4278
4547
|
if (!installedBlocks.has(name)) {
|
|
4279
4548
|
const foundOrphaned = bManifest.files.some((file) => {
|
|
4280
|
-
return
|
|
4549
|
+
return existsSync7(join10(cwd, file.destination));
|
|
4281
4550
|
});
|
|
4282
4551
|
if (foundOrphaned) {
|
|
4283
4552
|
reportError(`Orphaned block files detected for '${name}'. The block is not in fornix.json.`);
|
|
@@ -4287,7 +4556,7 @@ var doctorCommand = defineCommand6({
|
|
|
4287
4556
|
let requiresContentConfig = false;
|
|
4288
4557
|
const missingContentFiles = [];
|
|
4289
4558
|
for (const block of manifest2.blocks) {
|
|
4290
|
-
const bManifest =
|
|
4559
|
+
const bManifest = manifests[block.name];
|
|
4291
4560
|
if (!bManifest) continue;
|
|
4292
4561
|
const hasContentSlots = bManifest.ai?.contentSlots && Object.keys(bManifest.ai.contentSlots).length > 0;
|
|
4293
4562
|
const hasCollections = bManifest.collections && bManifest.collections.length > 0;
|
|
@@ -4301,14 +4570,14 @@ var doctorCommand = defineCommand6({
|
|
|
4301
4570
|
if (locale !== "") {
|
|
4302
4571
|
pathFragment = `src/content/${locale}/${subdirectory}/${bManifest.name}.json`;
|
|
4303
4572
|
}
|
|
4304
|
-
if (!
|
|
4573
|
+
if (!existsSync7(join10(cwd, pathFragment))) {
|
|
4305
4574
|
missingContentFiles.push(pathFragment);
|
|
4306
4575
|
}
|
|
4307
4576
|
}
|
|
4308
4577
|
}
|
|
4309
4578
|
}
|
|
4310
4579
|
if (requiresContentConfig) {
|
|
4311
|
-
if (!
|
|
4580
|
+
if (!existsSync7(join10(cwd, "src/content/config.ts"))) {
|
|
4312
4581
|
reportError("Missing expected file: src/content/config.ts");
|
|
4313
4582
|
}
|
|
4314
4583
|
}
|
|
@@ -4361,57 +4630,71 @@ import {
|
|
|
4361
4630
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
4362
4631
|
|
|
4363
4632
|
// src/mcp/tools/list-blocks.ts
|
|
4364
|
-
function
|
|
4365
|
-
|
|
4366
|
-
if (
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
if (input.search) {
|
|
4375
|
-
const searchTerm = input.search.toLowerCase();
|
|
4376
|
-
blocks = blocks.filter(
|
|
4377
|
-
(block) => block.name.includes(searchTerm) || block.description.toLowerCase().includes(searchTerm) || block.tags.some((tag) => tag.includes(searchTerm))
|
|
4378
|
-
);
|
|
4633
|
+
async function listBlocksHandler(args2) {
|
|
4634
|
+
const result = await fetchRegistryIndex();
|
|
4635
|
+
if (!isOk(result)) {
|
|
4636
|
+
throw new Error(`Failed to fetch block registry: ${result.error.message}`);
|
|
4637
|
+
}
|
|
4638
|
+
const manifests = result.value.blocks;
|
|
4639
|
+
const typedArgs = args2;
|
|
4640
|
+
let blocks = Object.values(manifests);
|
|
4641
|
+
if (typedArgs?.type) {
|
|
4642
|
+
blocks = blocks.filter((b) => b.type === typedArgs.type);
|
|
4379
4643
|
}
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4644
|
+
if (typedArgs?.category) {
|
|
4645
|
+
blocks = blocks.filter((b) => b.category === typedArgs.category);
|
|
4646
|
+
}
|
|
4647
|
+
return {
|
|
4648
|
+
content: [
|
|
4649
|
+
{
|
|
4650
|
+
type: "text",
|
|
4651
|
+
text: JSON.stringify(
|
|
4652
|
+
blocks.map((b) => ({
|
|
4653
|
+
name: b.name,
|
|
4654
|
+
description: b.description,
|
|
4655
|
+
type: b.type,
|
|
4656
|
+
category: b.category,
|
|
4657
|
+
requiredMode: b.requiredMode,
|
|
4658
|
+
requires: b.requires,
|
|
4659
|
+
conflicts: b.conflicts,
|
|
4660
|
+
envVars: b.envVars
|
|
4661
|
+
})),
|
|
4662
|
+
null,
|
|
4663
|
+
2
|
|
4664
|
+
)
|
|
4665
|
+
}
|
|
4666
|
+
]
|
|
4667
|
+
};
|
|
4390
4668
|
}
|
|
4391
4669
|
|
|
4392
4670
|
// src/mcp/tools/add-block.ts
|
|
4393
|
-
import { readFileSync as
|
|
4394
|
-
import { join as
|
|
4395
|
-
function addBlock2(input) {
|
|
4671
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
4672
|
+
import { join as join11, dirname as dirname5 } from "path";
|
|
4673
|
+
async function addBlock2(input) {
|
|
4396
4674
|
const { name, variant = "default", projectDirectory } = input;
|
|
4397
|
-
const manifestPath =
|
|
4398
|
-
if (!
|
|
4675
|
+
const manifestPath = join11(projectDirectory, "fornix.json");
|
|
4676
|
+
if (!existsSync8(manifestPath)) {
|
|
4399
4677
|
return err(
|
|
4400
4678
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
4401
4679
|
);
|
|
4402
4680
|
}
|
|
4403
4681
|
let manifest2;
|
|
4404
4682
|
try {
|
|
4405
|
-
const raw =
|
|
4683
|
+
const raw = readFileSync9(manifestPath, "utf-8");
|
|
4406
4684
|
manifest2 = JSON.parse(raw);
|
|
4407
4685
|
} catch {
|
|
4408
4686
|
return err(new Error("Failed to parse fornix.json."));
|
|
4409
4687
|
}
|
|
4410
|
-
const
|
|
4688
|
+
const registryResult = await fetchRegistryIndex();
|
|
4689
|
+
if (!isOk(registryResult)) {
|
|
4690
|
+
return err(new Error(`Failed to fetch block registry: ${registryResult.error.message}`));
|
|
4691
|
+
}
|
|
4692
|
+
const manifests = registryResult.value.blocks;
|
|
4693
|
+
const blockManifest = manifests[name];
|
|
4411
4694
|
if (!blockManifest) {
|
|
4412
4695
|
return err(
|
|
4413
4696
|
new Error(
|
|
4414
|
-
`Block '${name}' not found in registry. Available: ${Object.keys(
|
|
4697
|
+
`Block '${name}' not found in registry. Available: ${Object.keys(manifests).join(", ")}`
|
|
4415
4698
|
)
|
|
4416
4699
|
);
|
|
4417
4700
|
}
|
|
@@ -4419,9 +4702,9 @@ function addBlock2(input) {
|
|
|
4419
4702
|
if (installedNames.has(name)) {
|
|
4420
4703
|
return ok({ addedBlocks: [], filesCreated: 0 });
|
|
4421
4704
|
}
|
|
4422
|
-
const blocksToAdd = resolveDependencies3(name, installedNames);
|
|
4705
|
+
const blocksToAdd = resolveDependencies3(name, installedNames, manifests);
|
|
4423
4706
|
for (const blockName of blocksToAdd) {
|
|
4424
|
-
const dependencyManifest =
|
|
4707
|
+
const dependencyManifest = manifests[blockName];
|
|
4425
4708
|
if (dependencyManifest?.requiredMode && manifest2.renderMode !== dependencyManifest.requiredMode) {
|
|
4426
4709
|
return err(
|
|
4427
4710
|
new Error(
|
|
@@ -4431,14 +4714,15 @@ function addBlock2(input) {
|
|
|
4431
4714
|
}
|
|
4432
4715
|
}
|
|
4433
4716
|
let filesCreated = 0;
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
const
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4717
|
+
const blockResults = await fetchBlocks(blocksToAdd);
|
|
4718
|
+
for (let i = 0; i < blocksToAdd.length; i++) {
|
|
4719
|
+
const blockName = blocksToAdd[i];
|
|
4720
|
+
const result = blockResults[i];
|
|
4721
|
+
if (!result || !isOk(result)) {
|
|
4722
|
+
return err(new Error(`Failed to fetch files for block '${blockName}'.`));
|
|
4723
|
+
}
|
|
4724
|
+
const blockDef = result.value.manifest;
|
|
4725
|
+
const sources = result.value.files;
|
|
4442
4726
|
for (const file of blockDef.files) {
|
|
4443
4727
|
const content = sources[file.source];
|
|
4444
4728
|
if (content === void 0) {
|
|
@@ -4448,15 +4732,15 @@ function addBlock2(input) {
|
|
|
4448
4732
|
)
|
|
4449
4733
|
);
|
|
4450
4734
|
}
|
|
4451
|
-
const filePath =
|
|
4452
|
-
|
|
4453
|
-
|
|
4735
|
+
const filePath = join11(projectDirectory, file.destination);
|
|
4736
|
+
mkdirSync6(dirname5(filePath), { recursive: true });
|
|
4737
|
+
writeFileSync6(filePath, content);
|
|
4454
4738
|
filesCreated++;
|
|
4455
4739
|
}
|
|
4456
4740
|
}
|
|
4457
4741
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4458
4742
|
for (const blockName of blocksToAdd) {
|
|
4459
|
-
const blockDef =
|
|
4743
|
+
const blockDef = manifests[blockName];
|
|
4460
4744
|
if (!blockDef) continue;
|
|
4461
4745
|
manifest2.blocks.push({
|
|
4462
4746
|
name: blockName,
|
|
@@ -4465,16 +4749,16 @@ function addBlock2(input) {
|
|
|
4465
4749
|
installedAt: now
|
|
4466
4750
|
});
|
|
4467
4751
|
}
|
|
4468
|
-
|
|
4752
|
+
writeFileSync6(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4469
4753
|
return ok({ addedBlocks: blocksToAdd, filesCreated });
|
|
4470
4754
|
}
|
|
4471
|
-
function resolveDependencies3(blockName, installedNames) {
|
|
4755
|
+
function resolveDependencies3(blockName, installedNames, manifests) {
|
|
4472
4756
|
const result = [];
|
|
4473
4757
|
const visited = /* @__PURE__ */ new Set();
|
|
4474
4758
|
function walk(currentName) {
|
|
4475
4759
|
if (visited.has(currentName) || installedNames.has(currentName)) return;
|
|
4476
4760
|
visited.add(currentName);
|
|
4477
|
-
const manifest2 =
|
|
4761
|
+
const manifest2 = manifests[currentName];
|
|
4478
4762
|
if (!manifest2) return;
|
|
4479
4763
|
for (const dependency of manifest2.requires) {
|
|
4480
4764
|
walk(dependency);
|
|
@@ -4487,34 +4771,39 @@ function resolveDependencies3(blockName, installedNames) {
|
|
|
4487
4771
|
|
|
4488
4772
|
// src/mcp/tools/remove-block.ts
|
|
4489
4773
|
import {
|
|
4490
|
-
readFileSync as
|
|
4491
|
-
writeFileSync as
|
|
4492
|
-
existsSync as
|
|
4774
|
+
readFileSync as readFileSync10,
|
|
4775
|
+
writeFileSync as writeFileSync7,
|
|
4776
|
+
existsSync as existsSync9,
|
|
4493
4777
|
unlinkSync as unlinkSync2,
|
|
4494
|
-
readdirSync as
|
|
4778
|
+
readdirSync as readdirSync4,
|
|
4495
4779
|
rmdirSync as rmdirSync2
|
|
4496
4780
|
} from "fs";
|
|
4497
|
-
import { join as
|
|
4498
|
-
function removeBlock(input) {
|
|
4781
|
+
import { join as join12, dirname as dirname6 } from "path";
|
|
4782
|
+
async function removeBlock(input) {
|
|
4499
4783
|
const { name, force = false, projectDirectory } = input;
|
|
4500
|
-
const manifestPath =
|
|
4501
|
-
if (!
|
|
4784
|
+
const manifestPath = join12(projectDirectory, "fornix.json");
|
|
4785
|
+
if (!existsSync9(manifestPath)) {
|
|
4502
4786
|
return err(
|
|
4503
4787
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
4504
4788
|
);
|
|
4505
4789
|
}
|
|
4506
4790
|
let manifest2;
|
|
4507
4791
|
try {
|
|
4508
|
-
const raw =
|
|
4792
|
+
const raw = readFileSync10(manifestPath, "utf-8");
|
|
4509
4793
|
manifest2 = JSON.parse(raw);
|
|
4510
4794
|
} catch {
|
|
4511
4795
|
return err(new Error("Failed to parse fornix.json."));
|
|
4512
4796
|
}
|
|
4797
|
+
const registryResult = await fetchRegistryIndex();
|
|
4798
|
+
if (!isOk(registryResult)) {
|
|
4799
|
+
return err(new Error(`Failed to fetch block registry: ${registryResult.error.message}`));
|
|
4800
|
+
}
|
|
4801
|
+
const manifests = registryResult.value.blocks;
|
|
4513
4802
|
const installedNames = new Set(manifest2.blocks.map((block) => block.name));
|
|
4514
4803
|
if (!installedNames.has(name)) {
|
|
4515
4804
|
return err(new Error(`Block '${name}' is not installed.`));
|
|
4516
4805
|
}
|
|
4517
|
-
const dependents = findDependents2(name, installedNames);
|
|
4806
|
+
const dependents = findDependents2(name, installedNames, manifests);
|
|
4518
4807
|
if (dependents.length > 0 && !force) {
|
|
4519
4808
|
return err(
|
|
4520
4809
|
new Error(
|
|
@@ -4522,12 +4811,12 @@ function removeBlock(input) {
|
|
|
4522
4811
|
)
|
|
4523
4812
|
);
|
|
4524
4813
|
}
|
|
4525
|
-
const blockManifest =
|
|
4814
|
+
const blockManifest = manifests[name];
|
|
4526
4815
|
const filesToRemove = [];
|
|
4527
4816
|
if (blockManifest) {
|
|
4528
4817
|
for (const file of blockManifest.files) {
|
|
4529
|
-
const filePath =
|
|
4530
|
-
if (
|
|
4818
|
+
const filePath = join12(projectDirectory, file.destination);
|
|
4819
|
+
if (existsSync9(filePath)) {
|
|
4531
4820
|
filesToRemove.push(filePath);
|
|
4532
4821
|
}
|
|
4533
4822
|
}
|
|
@@ -4537,17 +4826,17 @@ function removeBlock(input) {
|
|
|
4537
4826
|
tryRemoveEmptyDirectory(dirname6(filePath), projectDirectory);
|
|
4538
4827
|
}
|
|
4539
4828
|
manifest2.blocks = manifest2.blocks.filter((block) => block.name !== name);
|
|
4540
|
-
|
|
4829
|
+
writeFileSync7(manifestPath, JSON.stringify(manifest2, null, 2) + "\n");
|
|
4541
4830
|
return ok({
|
|
4542
4831
|
removedBlock: name,
|
|
4543
4832
|
filesRemoved: filesToRemove.length,
|
|
4544
4833
|
dependentsWarning: dependents
|
|
4545
4834
|
});
|
|
4546
4835
|
}
|
|
4547
|
-
function findDependents2(blockName, installedNames) {
|
|
4836
|
+
function findDependents2(blockName, installedNames, manifests) {
|
|
4548
4837
|
const dependents = [];
|
|
4549
4838
|
for (const installedName of installedNames) {
|
|
4550
|
-
const manifest2 =
|
|
4839
|
+
const manifest2 = manifests[installedName];
|
|
4551
4840
|
if (manifest2 && manifest2.requires.includes(blockName)) {
|
|
4552
4841
|
dependents.push(installedName);
|
|
4553
4842
|
}
|
|
@@ -4557,7 +4846,7 @@ function findDependents2(blockName, installedNames) {
|
|
|
4557
4846
|
function tryRemoveEmptyDirectory(directoryPath, rootPath) {
|
|
4558
4847
|
if (directoryPath === rootPath || !directoryPath.startsWith(rootPath)) return;
|
|
4559
4848
|
try {
|
|
4560
|
-
const entries =
|
|
4849
|
+
const entries = readdirSync4(directoryPath);
|
|
4561
4850
|
if (entries.length === 0) {
|
|
4562
4851
|
rmdirSync2(directoryPath);
|
|
4563
4852
|
tryRemoveEmptyDirectory(dirname6(directoryPath), rootPath);
|
|
@@ -4567,33 +4856,51 @@ function tryRemoveEmptyDirectory(directoryPath, rootPath) {
|
|
|
4567
4856
|
}
|
|
4568
4857
|
|
|
4569
4858
|
// src/mcp/tools/get-content-schema.ts
|
|
4570
|
-
function
|
|
4571
|
-
const
|
|
4572
|
-
|
|
4859
|
+
async function getContentSchemaHandler(args2) {
|
|
4860
|
+
const typedArgs = args2;
|
|
4861
|
+
if (!typedArgs?.block) {
|
|
4862
|
+
throw new Error("Missing required argument: block");
|
|
4863
|
+
}
|
|
4864
|
+
const result = await fetchRegistryIndex();
|
|
4865
|
+
if (!isOk(result)) {
|
|
4866
|
+
throw new Error(`Failed to fetch block registry: ${result.error.message}`);
|
|
4867
|
+
}
|
|
4868
|
+
const manifests = result.value.blocks;
|
|
4869
|
+
const manifest2 = manifests[typedArgs.block];
|
|
4573
4870
|
if (!manifest2) {
|
|
4574
|
-
|
|
4575
|
-
new Error(`Collection '${collection}' not found in registry.`)
|
|
4576
|
-
);
|
|
4871
|
+
throw new Error(`Block '${typedArgs.block}' not found in registry`);
|
|
4577
4872
|
}
|
|
4578
|
-
const
|
|
4579
|
-
if (
|
|
4580
|
-
return
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4873
|
+
const slots = manifest2.ai?.contentSlots ?? {};
|
|
4874
|
+
if (Object.keys(slots).length === 0) {
|
|
4875
|
+
return {
|
|
4876
|
+
content: [
|
|
4877
|
+
{
|
|
4878
|
+
type: "text",
|
|
4879
|
+
text: `Block '${typedArgs.block}' does not have any AI-configurable content slots.`
|
|
4880
|
+
}
|
|
4881
|
+
]
|
|
4882
|
+
};
|
|
4585
4883
|
}
|
|
4586
|
-
return
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4884
|
+
return {
|
|
4885
|
+
content: [
|
|
4886
|
+
{
|
|
4887
|
+
type: "text",
|
|
4888
|
+
text: JSON.stringify(slots, null, 2)
|
|
4889
|
+
}
|
|
4890
|
+
]
|
|
4891
|
+
};
|
|
4590
4892
|
}
|
|
4591
4893
|
|
|
4592
4894
|
// src/mcp/tools/validate-content.ts
|
|
4593
4895
|
import { z as z5 } from "zod";
|
|
4594
|
-
function validateContent(input) {
|
|
4896
|
+
async function validateContent(input) {
|
|
4595
4897
|
const { collection, data } = input;
|
|
4596
|
-
const
|
|
4898
|
+
const registryResult = await fetchRegistryIndex();
|
|
4899
|
+
if (!isOk(registryResult)) {
|
|
4900
|
+
return err(new Error(`Failed to fetch block registry: ${registryResult.error.message}`));
|
|
4901
|
+
}
|
|
4902
|
+
const manifests = registryResult.value.blocks;
|
|
4903
|
+
const manifest2 = manifests[collection];
|
|
4597
4904
|
if (!manifest2) {
|
|
4598
4905
|
return err(
|
|
4599
4906
|
new Error(
|
|
@@ -4641,17 +4948,17 @@ function zodTypeForSlot2(slotType) {
|
|
|
4641
4948
|
}
|
|
4642
4949
|
|
|
4643
4950
|
// src/mcp/tools/get-project-status.ts
|
|
4644
|
-
import { readFileSync as
|
|
4645
|
-
import { join as
|
|
4951
|
+
import { readFileSync as readFileSync11, existsSync as existsSync10 } from "fs";
|
|
4952
|
+
import { join as join13 } from "path";
|
|
4646
4953
|
function getProjectStatus(input) {
|
|
4647
|
-
const manifestPath =
|
|
4648
|
-
if (!
|
|
4954
|
+
const manifestPath = join13(input.projectDirectory, "fornix.json");
|
|
4955
|
+
if (!existsSync10(manifestPath)) {
|
|
4649
4956
|
return err(
|
|
4650
4957
|
new Error("No fornix.json found. Not a Fornix project directory.")
|
|
4651
4958
|
);
|
|
4652
4959
|
}
|
|
4653
4960
|
try {
|
|
4654
|
-
const raw =
|
|
4961
|
+
const raw = readFileSync11(manifestPath, "utf-8");
|
|
4655
4962
|
const manifest2 = JSON.parse(raw);
|
|
4656
4963
|
const blocks = manifest2.blocks;
|
|
4657
4964
|
return ok({
|
|
@@ -4673,8 +4980,8 @@ function getProjectStatus(input) {
|
|
|
4673
4980
|
}
|
|
4674
4981
|
|
|
4675
4982
|
// src/mcp/tools/scaffold-project.ts
|
|
4676
|
-
import { mkdirSync as
|
|
4677
|
-
import { join as
|
|
4983
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
4984
|
+
import { join as join14, basename as basename3 } from "path";
|
|
4678
4985
|
var DEFAULT_COLORS2 = {
|
|
4679
4986
|
primary: "#6366f1",
|
|
4680
4987
|
secondary: "#818cf8",
|
|
@@ -4682,7 +4989,7 @@ var DEFAULT_COLORS2 = {
|
|
|
4682
4989
|
background: "#0f172a",
|
|
4683
4990
|
foreground: "#f8fafc"
|
|
4684
4991
|
};
|
|
4685
|
-
function scaffoldProject(input) {
|
|
4992
|
+
async function scaffoldProject(input) {
|
|
4686
4993
|
const {
|
|
4687
4994
|
projectDirectory,
|
|
4688
4995
|
renderMode = "static",
|
|
@@ -4690,9 +4997,14 @@ function scaffoldProject(input) {
|
|
|
4690
4997
|
blocks = [],
|
|
4691
4998
|
locales = ["en"]
|
|
4692
4999
|
} = input;
|
|
5000
|
+
const registryResult = await fetchRegistryIndex();
|
|
5001
|
+
if (!isOk(registryResult)) {
|
|
5002
|
+
return err(new Error(`Failed to fetch block registry: ${registryResult.error.message}`));
|
|
5003
|
+
}
|
|
5004
|
+
const manifests = registryResult.value.blocks;
|
|
5005
|
+
const allPalettes = registryResult.value.palettes.length > 0 ? registryResult.value.palettes : loadAllPalettes();
|
|
4693
5006
|
const projectName = basename3(projectDirectory);
|
|
4694
5007
|
const blockSelections = blocks.map((name) => ({ name, variant: "default" }));
|
|
4695
|
-
const allPalettes = loadAllPalettes();
|
|
4696
5008
|
const config = {
|
|
4697
5009
|
projectName,
|
|
4698
5010
|
projectDir: projectDirectory,
|
|
@@ -4710,11 +5022,27 @@ function scaffoldProject(input) {
|
|
|
4710
5022
|
themeSwitcher: false,
|
|
4711
5023
|
createdWith: "mcp"
|
|
4712
5024
|
};
|
|
5025
|
+
const blockResults = await fetchBlocks([...blocks]);
|
|
5026
|
+
const blockSources = {};
|
|
5027
|
+
const blockDefaultContent = {};
|
|
5028
|
+
for (const result2 of blockResults) {
|
|
5029
|
+
if (!isOk(result2)) {
|
|
5030
|
+
return err(new Error(`Failed to fetch block '${result2.error.blockName}': ${result2.error.message}`));
|
|
5031
|
+
}
|
|
5032
|
+
const { manifest: manifest2, files: files2 } = result2.value;
|
|
5033
|
+
blockSources[manifest2.name] = files2;
|
|
5034
|
+
try {
|
|
5035
|
+
if (files2["default-content.json"]) {
|
|
5036
|
+
blockDefaultContent[manifest2.name] = JSON.parse(files2["default-content.json"]);
|
|
5037
|
+
}
|
|
5038
|
+
} catch {
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
4713
5041
|
const scaffoldInput = {
|
|
4714
5042
|
config,
|
|
4715
|
-
manifests
|
|
4716
|
-
blockSources
|
|
4717
|
-
blockDefaultContent
|
|
5043
|
+
manifests,
|
|
5044
|
+
blockSources,
|
|
5045
|
+
blockDefaultContent,
|
|
4718
5046
|
allPalettes
|
|
4719
5047
|
};
|
|
4720
5048
|
const result = scaffold(scaffoldInput);
|
|
@@ -4724,10 +5052,10 @@ function scaffoldProject(input) {
|
|
|
4724
5052
|
const files = result.value.files;
|
|
4725
5053
|
let filesCreated = 0;
|
|
4726
5054
|
for (const [relativePath, content] of Object.entries(files)) {
|
|
4727
|
-
const fullPath =
|
|
4728
|
-
const parentDirectory =
|
|
4729
|
-
|
|
4730
|
-
|
|
5055
|
+
const fullPath = join14(projectDirectory, relativePath);
|
|
5056
|
+
const parentDirectory = join14(fullPath, "..");
|
|
5057
|
+
mkdirSync7(parentDirectory, { recursive: true });
|
|
5058
|
+
writeFileSync8(fullPath, content, "utf-8");
|
|
4731
5059
|
filesCreated++;
|
|
4732
5060
|
}
|
|
4733
5061
|
return ok({
|
|
@@ -4924,7 +5252,11 @@ var FornixMCPServer = class {
|
|
|
4924
5252
|
async (request) => {
|
|
4925
5253
|
const uri = request.params.uri;
|
|
4926
5254
|
if (uri === "fornix://registry") {
|
|
4927
|
-
const
|
|
5255
|
+
const registryResult = await fetchRegistryIndex();
|
|
5256
|
+
if (!isOk(registryResult)) {
|
|
5257
|
+
throw new Error(`Failed to fetch block registry: ${registryResult.error.message}`);
|
|
5258
|
+
}
|
|
5259
|
+
const blocks = Object.values(registryResult.value.blocks).map((manifest2) => ({
|
|
4928
5260
|
name: manifest2.name,
|
|
4929
5261
|
type: manifest2.type,
|
|
4930
5262
|
category: manifest2.category,
|
|
@@ -4967,16 +5299,19 @@ var FornixMCPServer = class {
|
|
|
4967
5299
|
async executeTool(toolName, args2) {
|
|
4968
5300
|
switch (toolName) {
|
|
4969
5301
|
case "list_blocks": {
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
5302
|
+
try {
|
|
5303
|
+
const response = await listBlocksHandler({
|
|
5304
|
+
type: args2.type,
|
|
5305
|
+
category: args2.category,
|
|
5306
|
+
search: args2.search
|
|
5307
|
+
});
|
|
5308
|
+
return ok(response.content[0].text);
|
|
5309
|
+
} catch (e) {
|
|
5310
|
+
return err(e instanceof Error ? e : new Error(String(e)));
|
|
5311
|
+
}
|
|
4977
5312
|
}
|
|
4978
5313
|
case "add_block": {
|
|
4979
|
-
const result = addBlock2({
|
|
5314
|
+
const result = await addBlock2({
|
|
4980
5315
|
name: args2.name,
|
|
4981
5316
|
variant: args2.variant,
|
|
4982
5317
|
projectDirectory: args2.projectDirectory
|
|
@@ -4985,7 +5320,7 @@ var FornixMCPServer = class {
|
|
|
4985
5320
|
return ok(JSON.stringify(result.value, null, 2));
|
|
4986
5321
|
}
|
|
4987
5322
|
case "remove_block": {
|
|
4988
|
-
const result = removeBlock({
|
|
5323
|
+
const result = await removeBlock({
|
|
4989
5324
|
name: args2.name,
|
|
4990
5325
|
force: args2.force,
|
|
4991
5326
|
projectDirectory: args2.projectDirectory
|
|
@@ -4994,14 +5329,17 @@ var FornixMCPServer = class {
|
|
|
4994
5329
|
return ok(JSON.stringify(result.value, null, 2));
|
|
4995
5330
|
}
|
|
4996
5331
|
case "get_content_schema": {
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5332
|
+
try {
|
|
5333
|
+
const response = await getContentSchemaHandler({
|
|
5334
|
+
block: args2.collection
|
|
5335
|
+
});
|
|
5336
|
+
return ok(response.content[0].text);
|
|
5337
|
+
} catch (e) {
|
|
5338
|
+
return err(e instanceof Error ? e : new Error(String(e)));
|
|
5339
|
+
}
|
|
5002
5340
|
}
|
|
5003
5341
|
case "update_content": {
|
|
5004
|
-
const validationResult = validateContent({
|
|
5342
|
+
const validationResult = await validateContent({
|
|
5005
5343
|
collection: args2.collection,
|
|
5006
5344
|
data: args2.data
|
|
5007
5345
|
});
|
|
@@ -5023,7 +5361,7 @@ var FornixMCPServer = class {
|
|
|
5023
5361
|
);
|
|
5024
5362
|
}
|
|
5025
5363
|
case "validate_content": {
|
|
5026
|
-
const result = validateContent({
|
|
5364
|
+
const result = await validateContent({
|
|
5027
5365
|
collection: args2.collection,
|
|
5028
5366
|
data: args2.data
|
|
5029
5367
|
});
|
|
@@ -5038,7 +5376,7 @@ var FornixMCPServer = class {
|
|
|
5038
5376
|
return ok(JSON.stringify(result.value, null, 2));
|
|
5039
5377
|
}
|
|
5040
5378
|
case "scaffold_project": {
|
|
5041
|
-
const result = scaffoldProject({
|
|
5379
|
+
const result = await scaffoldProject({
|
|
5042
5380
|
description: args2.description,
|
|
5043
5381
|
projectDirectory: args2.projectDirectory,
|
|
5044
5382
|
renderMode: args2.renderMode,
|