rafters 0.0.85 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2253 -2144
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -80,9 +80,9 @@ var require_escape_html = __commonJS({
|
|
|
80
80
|
import { Command } from "commander";
|
|
81
81
|
|
|
82
82
|
// src/commands/add.ts
|
|
83
|
-
import { existsSync as
|
|
84
|
-
import { access, mkdir as
|
|
85
|
-
import { dirname, join as
|
|
83
|
+
import { existsSync as existsSync3 } from "fs";
|
|
84
|
+
import { access, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
85
|
+
import { dirname, join as join7 } from "path";
|
|
86
86
|
|
|
87
87
|
// ../design-tokens/src/exporters/dtcg.ts
|
|
88
88
|
function categoryToDTCGType(category, token) {
|
|
@@ -1021,7 +1021,7 @@ ${sourceDirectives}
|
|
|
1021
1021
|
${themeBody}`;
|
|
1022
1022
|
const { execFileSync } = await import("child_process");
|
|
1023
1023
|
const { mkdtempSync, writeFileSync: writeFileSync2, readFileSync: readFileSync4, rmSync } = await import("fs");
|
|
1024
|
-
const { join:
|
|
1024
|
+
const { join: join16, dirname: dirname4 } = await import("path");
|
|
1025
1025
|
const { createRequire: createRequire2 } = await import("module");
|
|
1026
1026
|
const require2 = createRequire2(import.meta.url);
|
|
1027
1027
|
let pkgDir;
|
|
@@ -1031,10 +1031,10 @@ ${themeBody}`;
|
|
|
1031
1031
|
} catch {
|
|
1032
1032
|
throw new Error("Failed to resolve @tailwindcss/cli");
|
|
1033
1033
|
}
|
|
1034
|
-
const binPath =
|
|
1035
|
-
const tempDir = mkdtempSync(
|
|
1036
|
-
const tempInput =
|
|
1037
|
-
const tempOutput =
|
|
1034
|
+
const binPath = join16(pkgDir, "dist", "index.mjs");
|
|
1035
|
+
const tempDir = mkdtempSync(join16(pkgDir, ".tmp-compile-"));
|
|
1036
|
+
const tempInput = join16(tempDir, "input.css");
|
|
1037
|
+
const tempOutput = join16(tempDir, "output.css");
|
|
1038
1038
|
try {
|
|
1039
1039
|
writeFileSync2(tempInput, input);
|
|
1040
1040
|
const args = [binPath, "-i", tempInput, "-o", tempOutput];
|
|
@@ -1218,7 +1218,7 @@ async function registryToDocumentation(registry2, options = {}) {
|
|
|
1218
1218
|
const themeBody = registryToTailwind(registry2, { includeImport: false });
|
|
1219
1219
|
const candidates = deriveCandidates(themeBody);
|
|
1220
1220
|
const { mkdtempSync, writeFileSync: writeFileSync2, rmSync } = await import("fs");
|
|
1221
|
-
const { join:
|
|
1221
|
+
const { join: join16, dirname: dirname4 } = await import("path");
|
|
1222
1222
|
const { createRequire: createRequire2 } = await import("module");
|
|
1223
1223
|
const require2 = createRequire2(import.meta.url);
|
|
1224
1224
|
let pkgDir;
|
|
@@ -1230,8 +1230,8 @@ async function registryToDocumentation(registry2, options = {}) {
|
|
|
1230
1230
|
"Failed to resolve @tailwindcss/cli -- install it to generate documentation CSS"
|
|
1231
1231
|
);
|
|
1232
1232
|
}
|
|
1233
|
-
const tempDir = mkdtempSync(
|
|
1234
|
-
const candidateFile =
|
|
1233
|
+
const tempDir = mkdtempSync(join16(pkgDir, ".tmp-doc-compile-"));
|
|
1234
|
+
const candidateFile = join16(tempDir, "candidates.txt");
|
|
1235
1235
|
writeFileSync2(candidateFile, candidates.join("\n"));
|
|
1236
1236
|
const sourceDirectives = [
|
|
1237
1237
|
`@source "${candidateFile}";`,
|
|
@@ -1240,12 +1240,12 @@ async function registryToDocumentation(registry2, options = {}) {
|
|
|
1240
1240
|
const input = `@import "tailwindcss" source(none);
|
|
1241
1241
|
${sourceDirectives}
|
|
1242
1242
|
${themeBody}`;
|
|
1243
|
-
const tempInput =
|
|
1244
|
-
const tempOutput =
|
|
1243
|
+
const tempInput = join16(tempDir, "input.css");
|
|
1244
|
+
const tempOutput = join16(tempDir, "output.css");
|
|
1245
1245
|
try {
|
|
1246
1246
|
writeFileSync2(tempInput, input);
|
|
1247
1247
|
const { execFileSync } = await import("child_process");
|
|
1248
|
-
const binPath =
|
|
1248
|
+
const binPath = join16(pkgDir, "dist", "index.mjs");
|
|
1249
1249
|
const args = [binPath, "-i", tempInput, "-o", tempOutput];
|
|
1250
1250
|
if (minify) args.push("--minify");
|
|
1251
1251
|
execFileSync("node", args, { stdio: "pipe", timeout: 6e4, cwd: pkgDir });
|
|
@@ -6319,7 +6319,7 @@ function $constructor(name, initializer3, params) {
|
|
|
6319
6319
|
Object.defineProperty(_, "name", { value: name });
|
|
6320
6320
|
return _;
|
|
6321
6321
|
}
|
|
6322
|
-
var $brand = Symbol("zod_brand");
|
|
6322
|
+
var $brand = /* @__PURE__ */ Symbol("zod_brand");
|
|
6323
6323
|
var $ZodAsyncError = class extends Error {
|
|
6324
6324
|
constructor() {
|
|
6325
6325
|
super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`);
|
|
@@ -6464,7 +6464,7 @@ function floatSafeRemainder(val, step) {
|
|
|
6464
6464
|
const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
|
|
6465
6465
|
return valInt % stepInt / 10 ** decCount;
|
|
6466
6466
|
}
|
|
6467
|
-
var EVALUATING = Symbol("evaluating");
|
|
6467
|
+
var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
|
|
6468
6468
|
function defineLazy(object2, key, getter) {
|
|
6469
6469
|
let value = void 0;
|
|
6470
6470
|
Object.defineProperty(object2, key, {
|
|
@@ -15446,8 +15446,8 @@ function yo_default() {
|
|
|
15446
15446
|
}
|
|
15447
15447
|
|
|
15448
15448
|
// ../../node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/registries.js
|
|
15449
|
-
var $output = Symbol("ZodOutput");
|
|
15450
|
-
var $input = Symbol("ZodInput");
|
|
15449
|
+
var $output = /* @__PURE__ */ Symbol("ZodOutput");
|
|
15450
|
+
var $input = /* @__PURE__ */ Symbol("ZodInput");
|
|
15451
15451
|
var $ZodRegistry = class {
|
|
15452
15452
|
constructor() {
|
|
15453
15453
|
this._map = /* @__PURE__ */ new WeakMap();
|
|
@@ -21728,9 +21728,6 @@ var RESERVED_FILL_SEGMENTS = ["to", "via", "from"];
|
|
|
21728
21728
|
var SEPARATOR = `-${RESERVED_FILL_SEGMENTS[0]}-`;
|
|
21729
21729
|
|
|
21730
21730
|
// ../shared/src/types.ts
|
|
21731
|
-
var COMPUTED = Symbol.for(
|
|
21732
|
-
"rafters.computed"
|
|
21733
|
-
);
|
|
21734
21731
|
var OKLCHSchema = external_exports.object({
|
|
21735
21732
|
/** Lightness: 0 (black) to 1 (white) */
|
|
21736
21733
|
l: external_exports.number().min(0).max(1),
|
|
@@ -29559,101 +29556,7 @@ function isPathField(value) {
|
|
|
29559
29556
|
return typeof value === "string" || Array.isArray(value);
|
|
29560
29557
|
}
|
|
29561
29558
|
|
|
29562
|
-
// src/
|
|
29563
|
-
import { existsSync as existsSync3 } from "fs";
|
|
29564
|
-
import { copyFile, mkdir as mkdir2, readFile as readFile3, rm, writeFile as writeFile2 } from "fs/promises";
|
|
29565
|
-
import { createRequire } from "module";
|
|
29566
|
-
import { join as join7, relative as relative2 } from "path";
|
|
29567
|
-
import { checkbox, confirm, select } from "@inquirer/prompts";
|
|
29568
|
-
var REGISTRY_PLUGINS = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
|
|
29569
|
-
async function backupCss(cssPath) {
|
|
29570
|
-
const backupPath = cssPath.replace(/\.css$/, ".backup.css");
|
|
29571
|
-
await copyFile(cssPath, backupPath);
|
|
29572
|
-
return backupPath;
|
|
29573
|
-
}
|
|
29574
|
-
function cleanSourceCssBlocks(content) {
|
|
29575
|
-
function parseBlocks(src) {
|
|
29576
|
-
const blocks = [];
|
|
29577
|
-
let depth = 0;
|
|
29578
|
-
const headerStack = [];
|
|
29579
|
-
let segmentStart = 0;
|
|
29580
|
-
for (let i = 0; i < src.length; i++) {
|
|
29581
|
-
const ch = src.charAt(i);
|
|
29582
|
-
if (ch === "{") {
|
|
29583
|
-
headerStack.push(segmentStart);
|
|
29584
|
-
depth++;
|
|
29585
|
-
if (depth >= 1) {
|
|
29586
|
-
blocks.push({
|
|
29587
|
-
headerStart: segmentStart,
|
|
29588
|
-
bodyStart: i + 1,
|
|
29589
|
-
end: -1,
|
|
29590
|
-
header: src.slice(segmentStart, i).trim()
|
|
29591
|
-
});
|
|
29592
|
-
}
|
|
29593
|
-
segmentStart = i + 1;
|
|
29594
|
-
} else if (ch === "}") {
|
|
29595
|
-
depth--;
|
|
29596
|
-
const open = blocks.slice().reverse().find((b2) => b2.end === -1);
|
|
29597
|
-
if (open) open.end = i + 1;
|
|
29598
|
-
headerStack.pop();
|
|
29599
|
-
segmentStart = i + 1;
|
|
29600
|
-
} else if (ch === ";") {
|
|
29601
|
-
segmentStart = i + 1;
|
|
29602
|
-
}
|
|
29603
|
-
}
|
|
29604
|
-
return blocks.filter((b2) => b2.end !== -1);
|
|
29605
|
-
}
|
|
29606
|
-
function isContentEmpty(body) {
|
|
29607
|
-
return body.replace(/\/\*[\s\S]*?\*\//g, "").trim().length === 0;
|
|
29608
|
-
}
|
|
29609
|
-
let out = content;
|
|
29610
|
-
for (let pass = 0; pass < 10; pass++) {
|
|
29611
|
-
const blocks = parseBlocks(out);
|
|
29612
|
-
let edited = false;
|
|
29613
|
-
for (const block of blocks.sort((a2, b2) => b2.headerStart - a2.headerStart)) {
|
|
29614
|
-
const header = block.header;
|
|
29615
|
-
if (/^@theme\s+inline$/.test(header)) {
|
|
29616
|
-
out = out.slice(0, block.headerStart) + out.slice(block.end);
|
|
29617
|
-
edited = true;
|
|
29618
|
-
break;
|
|
29619
|
-
}
|
|
29620
|
-
if (header === ":root" || /(^|,)\s*:root\s*$/.test(header)) {
|
|
29621
|
-
const body = out.slice(block.bodyStart, block.end - 1);
|
|
29622
|
-
const cleanedBody = body.replace(/--[\w-]+\s*:[^;{}]*;?/g, "");
|
|
29623
|
-
if (isContentEmpty(cleanedBody)) {
|
|
29624
|
-
out = out.slice(0, block.headerStart) + out.slice(block.end);
|
|
29625
|
-
edited = true;
|
|
29626
|
-
break;
|
|
29627
|
-
}
|
|
29628
|
-
if (cleanedBody !== body) {
|
|
29629
|
-
out = out.slice(0, block.bodyStart) + cleanedBody + out.slice(block.end - 1);
|
|
29630
|
-
edited = true;
|
|
29631
|
-
break;
|
|
29632
|
-
}
|
|
29633
|
-
continue;
|
|
29634
|
-
}
|
|
29635
|
-
if (header.startsWith("@media") || header.startsWith("@supports")) {
|
|
29636
|
-
const body = out.slice(block.bodyStart, block.end - 1);
|
|
29637
|
-
if (isContentEmpty(body)) {
|
|
29638
|
-
out = out.slice(0, block.headerStart) + out.slice(block.end);
|
|
29639
|
-
edited = true;
|
|
29640
|
-
break;
|
|
29641
|
-
}
|
|
29642
|
-
}
|
|
29643
|
-
}
|
|
29644
|
-
if (!edited) break;
|
|
29645
|
-
}
|
|
29646
|
-
return out.replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
29647
|
-
}
|
|
29648
|
-
async function stripImportedDeclarations(cwd, cssPath, importedNames) {
|
|
29649
|
-
if (importedNames.length === 0) return;
|
|
29650
|
-
const fullPath = join7(cwd, cssPath);
|
|
29651
|
-
const content = await readFile3(fullPath, "utf-8");
|
|
29652
|
-
const pattern = new RegExp(importedNames.map((n2) => `^\\s*--${n2}[^;]*;\\s*$`).join("|"), "gm");
|
|
29653
|
-
const cleaned = cleanSourceCssBlocks(content.replace(pattern, ""));
|
|
29654
|
-
const collapsed = cleaned.replace(/\n{3,}/g, "\n\n");
|
|
29655
|
-
await writeFile2(fullPath, collapsed);
|
|
29656
|
-
}
|
|
29559
|
+
// src/config/rafters-config.ts
|
|
29657
29560
|
function migrateConfig(raw) {
|
|
29658
29561
|
if ("shadcn" in raw && !("source" in raw)) {
|
|
29659
29562
|
if (raw.shadcn === true) {
|
|
@@ -29663,2058 +29566,1247 @@ function migrateConfig(raw) {
|
|
|
29663
29566
|
}
|
|
29664
29567
|
return raw;
|
|
29665
29568
|
}
|
|
29666
|
-
async function updateMainCss(cwd, cssPath, themePath) {
|
|
29667
|
-
const fullCssPath = join7(cwd, cssPath);
|
|
29668
|
-
const cssContent = await readFile3(fullCssPath, "utf-8");
|
|
29669
|
-
const cssDir = join7(cwd, cssPath, "..");
|
|
29670
|
-
const themeFullPath = join7(cwd, themePath);
|
|
29671
|
-
const relativeThemePath = relative2(cssDir, themeFullPath);
|
|
29672
|
-
if (cssContent.includes(".rafters/output/rafters.css")) {
|
|
29673
|
-
log({ event: "init:css_already_imported", cssPath });
|
|
29674
|
-
return;
|
|
29675
|
-
}
|
|
29676
|
-
await backupCss(fullCssPath);
|
|
29677
|
-
let newContent;
|
|
29678
|
-
if (cssContent.includes('@import "tailwindcss"')) {
|
|
29679
|
-
newContent = cssContent.replace('@import "tailwindcss";', `@import "${relativeThemePath}";`);
|
|
29680
|
-
} else if (cssContent.includes("@import 'tailwindcss'")) {
|
|
29681
|
-
newContent = cssContent.replace("@import 'tailwindcss';", `@import "${relativeThemePath}";`);
|
|
29682
|
-
} else {
|
|
29683
|
-
newContent = `@import "${relativeThemePath}";
|
|
29684
29569
|
|
|
29685
|
-
|
|
29570
|
+
// src/commands/add.ts
|
|
29571
|
+
var REGISTRY_PLUGINS = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
|
|
29572
|
+
async function regenerateAfterInstall(cwd, config2) {
|
|
29573
|
+
const paths = getRaftersPaths(cwd);
|
|
29574
|
+
let registry2;
|
|
29575
|
+
try {
|
|
29576
|
+
registry2 = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS);
|
|
29577
|
+
} catch {
|
|
29578
|
+
return;
|
|
29686
29579
|
}
|
|
29687
|
-
|
|
29688
|
-
|
|
29689
|
-
|
|
29690
|
-
|
|
29691
|
-
|
|
29692
|
-
|
|
29693
|
-
|
|
29694
|
-
|
|
29695
|
-
|
|
29696
|
-
}
|
|
29697
|
-
|
|
29698
|
-
|
|
29699
|
-
|
|
29700
|
-
const fallback = framework === detectedFramework ? projectCssPath : findCssPath(cwd, framework);
|
|
29701
|
-
emit({
|
|
29702
|
-
event: "init:shadcn_css_missing",
|
|
29703
|
-
configuredPath: shadcnCssPath,
|
|
29704
|
-
fallbackPath: fallback,
|
|
29705
|
-
message: "components.json points at a CSS file that does not exist. Falling back to the framework canonical location."
|
|
29580
|
+
if (registry2.size() === 0) return;
|
|
29581
|
+
try {
|
|
29582
|
+
await regenerateOutputs(registry2, {
|
|
29583
|
+
outputDir: paths.output,
|
|
29584
|
+
exports: config2.exports,
|
|
29585
|
+
contentSources: resolveContentSources(cwd, config2),
|
|
29586
|
+
darkMode: config2.darkMode ?? "class",
|
|
29587
|
+
includeImport: config2.source !== "shadcn"
|
|
29588
|
+
});
|
|
29589
|
+
} catch (err) {
|
|
29590
|
+
log({
|
|
29591
|
+
event: "add:regen-failed",
|
|
29592
|
+
message: `Output regeneration failed: ${err instanceof Error ? err.message : String(err)}`
|
|
29706
29593
|
});
|
|
29707
|
-
return fallback;
|
|
29708
|
-
}
|
|
29709
|
-
return framework === detectedFramework ? projectCssPath : findCssPath(cwd, framework);
|
|
29710
|
-
}
|
|
29711
|
-
function slugifyFontName(name) {
|
|
29712
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
29713
|
-
}
|
|
29714
|
-
function isFontStackValue(value) {
|
|
29715
|
-
if (typeof value !== "string") return false;
|
|
29716
|
-
if (value.startsWith("var(")) return false;
|
|
29717
|
-
if (/[,'"]/.test(value)) return true;
|
|
29718
|
-
return /\b(sans-serif|serif|monospace|cursive|fantasy|system-ui|ui-(?:sans-serif|serif|monospace|rounded))\b/.test(
|
|
29719
|
-
value
|
|
29720
|
-
);
|
|
29721
|
-
}
|
|
29722
|
-
async function resolveFramework(detected, flag, agentMode) {
|
|
29723
|
-
if (flag) {
|
|
29724
|
-
if (!isSelectableFramework(flag)) {
|
|
29725
|
-
throw new Error(
|
|
29726
|
-
`Unknown --framework "${flag}". Valid values: ${SELECTABLE_FRAMEWORKS.join(", ")}.`
|
|
29727
|
-
);
|
|
29728
|
-
}
|
|
29729
|
-
return flag;
|
|
29730
29594
|
}
|
|
29731
|
-
if (detected !== "unknown") return detected;
|
|
29732
|
-
if (agentMode || !isInteractive()) return "unknown";
|
|
29733
|
-
const picked = await select({
|
|
29734
|
-
message: "Couldn't auto-detect your framework. Which one is this?",
|
|
29735
|
-
choices: SELECTABLE_FRAMEWORKS.map((value) => ({
|
|
29736
|
-
name: FRAMEWORK_SPECS[value].label ?? value,
|
|
29737
|
-
value
|
|
29738
|
-
}))
|
|
29739
|
-
});
|
|
29740
|
-
return picked;
|
|
29741
29595
|
}
|
|
29742
|
-
async function
|
|
29743
|
-
|
|
29744
|
-
|
|
29596
|
+
async function isInitialized(cwd) {
|
|
29597
|
+
const paths = getRaftersPaths(cwd);
|
|
29598
|
+
try {
|
|
29599
|
+
await access(paths.root);
|
|
29600
|
+
return true;
|
|
29601
|
+
} catch {
|
|
29602
|
+
return false;
|
|
29745
29603
|
}
|
|
29746
|
-
const choices = EXPORT_CHOICES.map((choice) => ({
|
|
29747
|
-
name: choice.name,
|
|
29748
|
-
value: choice.value,
|
|
29749
|
-
checked: existingConfig ? existingConfig[choice.value] : choice.checked
|
|
29750
|
-
}));
|
|
29751
|
-
const allChoices = [
|
|
29752
|
-
...choices,
|
|
29753
|
-
...FUTURE_EXPORTS.map((choice) => ({
|
|
29754
|
-
name: `${choice.name} (${choice.disabled})`,
|
|
29755
|
-
value: choice.value,
|
|
29756
|
-
checked: false,
|
|
29757
|
-
disabled: true
|
|
29758
|
-
}))
|
|
29759
|
-
];
|
|
29760
|
-
const selections = await checkbox({
|
|
29761
|
-
message: "What would you like to export?",
|
|
29762
|
-
choices: allChoices,
|
|
29763
|
-
required: true
|
|
29764
|
-
});
|
|
29765
|
-
return selectionsToConfig(selections);
|
|
29766
29604
|
}
|
|
29767
|
-
function
|
|
29768
|
-
const
|
|
29605
|
+
async function loadConfig(cwd) {
|
|
29606
|
+
const paths = getRaftersPaths(cwd);
|
|
29769
29607
|
try {
|
|
29770
|
-
|
|
29771
|
-
return
|
|
29608
|
+
const content = await readFile3(paths.config, "utf-8");
|
|
29609
|
+
return migrateConfig(
|
|
29610
|
+
JSON.parse(content)
|
|
29611
|
+
);
|
|
29772
29612
|
} catch (err) {
|
|
29773
|
-
if (
|
|
29774
|
-
|
|
29613
|
+
if (existsSync3(paths.config)) {
|
|
29614
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
29615
|
+
log({
|
|
29616
|
+
event: "add:warning",
|
|
29617
|
+
message: `Failed to load config: ${message}`
|
|
29618
|
+
});
|
|
29775
29619
|
}
|
|
29776
|
-
|
|
29620
|
+
return null;
|
|
29777
29621
|
}
|
|
29778
29622
|
}
|
|
29779
|
-
|
|
29780
|
-
|
|
29781
|
-
throw new Error(
|
|
29782
|
-
"Standalone CSS export requires @tailwindcss/cli. Install it as a dev dependency in your project."
|
|
29783
|
-
);
|
|
29784
|
-
}
|
|
29785
|
-
const shouldInstall = await confirm({
|
|
29786
|
-
message: "Standalone CSS requires @tailwindcss/cli. Install it now?",
|
|
29787
|
-
default: true
|
|
29788
|
-
});
|
|
29789
|
-
if (!shouldInstall) {
|
|
29790
|
-
throw new Error("Standalone CSS export requires @tailwindcss/cli.");
|
|
29791
|
-
}
|
|
29792
|
-
await updateDependencies([], ["@tailwindcss/cli"], { cwd });
|
|
29793
|
-
if (!isTailwindCliInstalled()) {
|
|
29794
|
-
throw new Error(
|
|
29795
|
-
"@tailwindcss/cli was installed but cannot be resolved. Try installing at the workspace root."
|
|
29796
|
-
);
|
|
29797
|
-
}
|
|
29623
|
+
function resolveRegistryUrl(options, config2) {
|
|
29624
|
+
return options.registryUrl ?? config2?.registryUrl;
|
|
29798
29625
|
}
|
|
29799
|
-
async function
|
|
29800
|
-
const
|
|
29801
|
-
|
|
29802
|
-
log({ event: "init:prompting_exports" });
|
|
29803
|
-
await ensureTailwindCli(cwd);
|
|
29804
|
-
}
|
|
29805
|
-
if (exports.compiled) {
|
|
29806
|
-
log({ event: "init:compiling_css" });
|
|
29807
|
-
}
|
|
29808
|
-
return regenerateOutputs(registry2, {
|
|
29809
|
-
outputDir: paths.output,
|
|
29810
|
-
exports,
|
|
29811
|
-
contentSources,
|
|
29812
|
-
darkMode,
|
|
29813
|
-
includeImport: source !== "shadcn"
|
|
29814
|
-
});
|
|
29626
|
+
async function saveConfig(cwd, config2) {
|
|
29627
|
+
const paths = getRaftersPaths(cwd);
|
|
29628
|
+
await writeFile2(paths.config, JSON.stringify(config2, null, 2));
|
|
29815
29629
|
}
|
|
29816
|
-
|
|
29817
|
-
|
|
29818
|
-
|
|
29630
|
+
function getInstalledNames(config2) {
|
|
29631
|
+
if (!config2?.installed) return [];
|
|
29632
|
+
const names = /* @__PURE__ */ new Set([
|
|
29633
|
+
...config2.installed.components,
|
|
29634
|
+
...config2.installed.primitives,
|
|
29635
|
+
...config2.installed.composites ?? [],
|
|
29636
|
+
...config2.installed.rules ?? [],
|
|
29637
|
+
...config2.installed.substrate ?? []
|
|
29638
|
+
]);
|
|
29639
|
+
return [...names].sort();
|
|
29640
|
+
}
|
|
29641
|
+
async function discoverUntrackedNames(cwd, config2, client, tracked) {
|
|
29642
|
+
let index = null;
|
|
29819
29643
|
try {
|
|
29820
|
-
|
|
29821
|
-
|
|
29822
|
-
|
|
29823
|
-
|
|
29824
|
-
|
|
29644
|
+
index = await client.fetchIndex();
|
|
29645
|
+
} catch (err) {
|
|
29646
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
29647
|
+
log({
|
|
29648
|
+
event: "add:warning",
|
|
29649
|
+
message: `Could not read the registry index to reconcile on-disk components (${message}). Updating tracked components only.`
|
|
29650
|
+
});
|
|
29825
29651
|
}
|
|
29826
|
-
|
|
29827
|
-
|
|
29828
|
-
|
|
29829
|
-
|
|
29830
|
-
|
|
29831
|
-
|
|
29832
|
-
|
|
29652
|
+
const { untracked } = buildUpdateCandidates(tracked, index, readInstallRoots(cwd, config2));
|
|
29653
|
+
return untracked;
|
|
29654
|
+
}
|
|
29655
|
+
function getComponentTarget(config2) {
|
|
29656
|
+
return resolveComponentTarget(config2);
|
|
29657
|
+
}
|
|
29658
|
+
var SHARED_EXTENSIONS = /* @__PURE__ */ new Set([".behavior.ts", ".classes.ts"]);
|
|
29659
|
+
function isSharedFile(path) {
|
|
29660
|
+
for (const ext of SHARED_EXTENSIONS) {
|
|
29661
|
+
if (path.endsWith(ext)) return true;
|
|
29833
29662
|
}
|
|
29834
|
-
|
|
29835
|
-
|
|
29836
|
-
|
|
29837
|
-
|
|
29838
|
-
|
|
29663
|
+
return false;
|
|
29664
|
+
}
|
|
29665
|
+
var REACT_FALLBACK_TARGETS = /* @__PURE__ */ new Set(["astro", "vue", "svelte"]);
|
|
29666
|
+
function selectFilesForFramework(files, target) {
|
|
29667
|
+
const preferredExt = targetToExtension(target);
|
|
29668
|
+
const shared = files.filter((f) => isSharedFile(f.path));
|
|
29669
|
+
const matched = files.filter((f) => f.path.endsWith(preferredExt));
|
|
29670
|
+
if (matched.length > 0) {
|
|
29671
|
+
return { files: [...matched, ...shared], fallback: false };
|
|
29839
29672
|
}
|
|
29840
|
-
|
|
29841
|
-
|
|
29842
|
-
|
|
29843
|
-
|
|
29844
|
-
namespaces
|
|
29845
|
-
});
|
|
29846
|
-
let exports;
|
|
29847
|
-
if (isAgentMode2) {
|
|
29848
|
-
exports = existingConfig?.exports ?? DEFAULT_EXPORTS;
|
|
29849
|
-
log({ event: "init:exports_default", exports });
|
|
29850
|
-
} else {
|
|
29851
|
-
if (isInteractive()) {
|
|
29852
|
-
log({ event: "init:prompting_exports" });
|
|
29673
|
+
if (REACT_FALLBACK_TARGETS.has(target)) {
|
|
29674
|
+
const fallbackFiles = files.filter((f) => f.path.endsWith(".tsx"));
|
|
29675
|
+
if (fallbackFiles.length > 0) {
|
|
29676
|
+
return { files: [...fallbackFiles, ...shared], fallback: true };
|
|
29853
29677
|
}
|
|
29854
|
-
exports = await promptExportFormats(existingConfig?.exports);
|
|
29855
|
-
log({ event: "init:exports_selected", exports });
|
|
29856
29678
|
}
|
|
29857
|
-
|
|
29858
|
-
|
|
29859
|
-
cwd,
|
|
29860
|
-
paths,
|
|
29861
|
-
registry2,
|
|
29862
|
-
exports,
|
|
29863
|
-
source,
|
|
29864
|
-
"class",
|
|
29865
|
-
existingConfig
|
|
29866
|
-
);
|
|
29867
|
-
if (existingConfig) {
|
|
29868
|
-
existingConfig.exports = exports;
|
|
29869
|
-
await writeFile2(paths.config, JSON.stringify(existingConfig, null, 2));
|
|
29870
|
-
} else {
|
|
29871
|
-
const frameworkPaths = FRAMEWORK_SPECS[framework].components;
|
|
29872
|
-
const newConfig = {
|
|
29873
|
-
framework,
|
|
29874
|
-
componentsPath: frameworkPaths.components,
|
|
29875
|
-
primitivesPath: frameworkPaths.primitives,
|
|
29876
|
-
compositesPath: frameworkPaths.composites,
|
|
29877
|
-
rulesPath: frameworkPaths.rules,
|
|
29878
|
-
cssPath: null,
|
|
29879
|
-
...source ? { source } : {},
|
|
29880
|
-
intent: "efficient",
|
|
29881
|
-
fonts: { path: null, imports: [] },
|
|
29882
|
-
exports,
|
|
29883
|
-
installed: { components: [], primitives: [], composites: [], rules: [] }
|
|
29884
|
-
};
|
|
29885
|
-
await writeFile2(paths.config, JSON.stringify(newConfig, null, 2));
|
|
29679
|
+
if (target !== "react" && !REACT_FALLBACK_TARGETS.has(target)) {
|
|
29680
|
+
return { files: shared, fallback: false };
|
|
29886
29681
|
}
|
|
29887
|
-
|
|
29888
|
-
|
|
29889
|
-
|
|
29890
|
-
|
|
29682
|
+
return { files, fallback: false };
|
|
29683
|
+
}
|
|
29684
|
+
var TARGET_GATED_COMPOSITE_FILES = [{ suffix: "Composite.astro", target: "astro" }];
|
|
29685
|
+
function selectCompositeFiles(files, target) {
|
|
29686
|
+
return files.filter((file2) => {
|
|
29687
|
+
const gate = TARGET_GATED_COMPOSITE_FILES.find((g2) => file2.path.endsWith(g2.suffix));
|
|
29688
|
+
return gate ? gate.target === target : true;
|
|
29891
29689
|
});
|
|
29892
29690
|
}
|
|
29893
|
-
|
|
29894
|
-
|
|
29895
|
-
|
|
29896
|
-
|
|
29897
|
-
|
|
29898
|
-
|
|
29899
|
-
|
|
29900
|
-
|
|
29901
|
-
|
|
29902
|
-
|
|
29903
|
-
|
|
29904
|
-
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
|
|
29909
|
-
|
|
29910
|
-
|
|
29911
|
-
let existingTokens = [];
|
|
29912
|
-
try {
|
|
29913
|
-
existingTokens = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS).list();
|
|
29914
|
-
} catch {
|
|
29691
|
+
var FOLDER_NAMES = /* @__PURE__ */ new Set(["composites"]);
|
|
29692
|
+
function isAlreadyInstalled(config2, item) {
|
|
29693
|
+
if (!config2?.installed) return false;
|
|
29694
|
+
const { components, primitives, composites: composites2, rules, substrate } = config2.installed;
|
|
29695
|
+
const bucketByType = {
|
|
29696
|
+
ui: components,
|
|
29697
|
+
primitive: primitives,
|
|
29698
|
+
composite: composites2 ?? [],
|
|
29699
|
+
rule: rules ?? [],
|
|
29700
|
+
substrate: substrate ?? []
|
|
29701
|
+
};
|
|
29702
|
+
return bucketByType[item.type].includes(item.name);
|
|
29703
|
+
}
|
|
29704
|
+
function isInstalledOnDisk(config2, item, cwd) {
|
|
29705
|
+
if (!isAlreadyInstalled(config2, item)) return false;
|
|
29706
|
+
const filesToCheck = item.type === "ui" ? selectFilesForFramework(item.files, getComponentTarget(config2)).files : item.files;
|
|
29707
|
+
for (const file2 of filesToCheck) {
|
|
29708
|
+
if (fileExists(cwd, transformPath(file2.path, config2, cwd))) return true;
|
|
29915
29709
|
}
|
|
29916
|
-
|
|
29917
|
-
|
|
29918
|
-
|
|
29919
|
-
|
|
29920
|
-
|
|
29921
|
-
|
|
29922
|
-
|
|
29923
|
-
|
|
29924
|
-
|
|
29925
|
-
namespace: t.namespace
|
|
29926
|
-
}))
|
|
29710
|
+
return false;
|
|
29711
|
+
}
|
|
29712
|
+
function trackInstalled(config2, items) {
|
|
29713
|
+
if (!config2.installed) {
|
|
29714
|
+
config2.installed = {
|
|
29715
|
+
components: [],
|
|
29716
|
+
primitives: [],
|
|
29717
|
+
composites: [],
|
|
29718
|
+
rules: []
|
|
29927
29719
|
};
|
|
29928
|
-
await mkdir2(paths.output, { recursive: true });
|
|
29929
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
29930
|
-
const backupPath = join7(paths.output, `reset-${timestamp}.json`);
|
|
29931
|
-
await writeFile2(backupPath, JSON.stringify(backup, null, 2));
|
|
29932
|
-
log({
|
|
29933
|
-
event: "init:reset_backup",
|
|
29934
|
-
path: backupPath,
|
|
29935
|
-
overrideCount: overriddenTokens.length
|
|
29936
|
-
});
|
|
29937
29720
|
}
|
|
29938
|
-
|
|
29939
|
-
if (
|
|
29940
|
-
|
|
29941
|
-
|
|
29942
|
-
|
|
29943
|
-
|
|
29944
|
-
|
|
29721
|
+
const installed = config2.installed;
|
|
29722
|
+
if (!installed.composites) installed.composites = [];
|
|
29723
|
+
if (!installed.rules) installed.rules = [];
|
|
29724
|
+
if (!installed.substrate) installed.substrate = [];
|
|
29725
|
+
const bucketByType = {
|
|
29726
|
+
ui: installed.components,
|
|
29727
|
+
primitive: installed.primitives,
|
|
29728
|
+
composite: installed.composites,
|
|
29729
|
+
rule: installed.rules,
|
|
29730
|
+
substrate: installed.substrate
|
|
29731
|
+
};
|
|
29732
|
+
for (const item of items) {
|
|
29733
|
+
const bucket = bucketByType[item.type];
|
|
29734
|
+
if (!bucket.includes(item.name)) bucket.push(item.name);
|
|
29735
|
+
}
|
|
29736
|
+
installed.components.sort();
|
|
29737
|
+
installed.primitives.sort();
|
|
29738
|
+
installed.composites.sort();
|
|
29739
|
+
installed.rules.sort();
|
|
29740
|
+
installed.substrate.sort();
|
|
29741
|
+
}
|
|
29742
|
+
function rootFor(field, cwd, fallback) {
|
|
29743
|
+
return field === void 0 ? fallback : resolveRoot(field, cwd, fallback);
|
|
29744
|
+
}
|
|
29745
|
+
function transformPath(registryPath, config2, cwd = process.cwd()) {
|
|
29746
|
+
if (!config2) return registryPath;
|
|
29747
|
+
const replacements = [
|
|
29748
|
+
["components/ui/", config2.componentsPath, "components/ui"],
|
|
29749
|
+
["lib/primitives/", config2.primitivesPath, "lib/primitives"],
|
|
29750
|
+
["composites/", config2.compositesPath, "composites"],
|
|
29751
|
+
["rules/", config2.rulesPath, "lib/rules"]
|
|
29752
|
+
];
|
|
29753
|
+
for (const [prefix, field, fallback] of replacements) {
|
|
29754
|
+
if (registryPath.startsWith(prefix)) {
|
|
29755
|
+
return registryPath.replace(prefix, `${rootFor(field, cwd, fallback)}/`);
|
|
29945
29756
|
}
|
|
29946
|
-
exports = await promptExportFormats(existingConfig?.exports);
|
|
29947
|
-
log({ event: "init:exports_selected", exports });
|
|
29948
29757
|
}
|
|
29949
|
-
|
|
29950
|
-
|
|
29951
|
-
|
|
29952
|
-
|
|
29953
|
-
|
|
29954
|
-
|
|
29955
|
-
|
|
29956
|
-
|
|
29957
|
-
|
|
29958
|
-
|
|
29959
|
-
|
|
29960
|
-
|
|
29961
|
-
|
|
29962
|
-
|
|
29963
|
-
|
|
29964
|
-
|
|
29965
|
-
|
|
29966
|
-
const
|
|
29967
|
-
|
|
29968
|
-
|
|
29969
|
-
|
|
29970
|
-
|
|
29971
|
-
|
|
29972
|
-
"
|
|
29973
|
-
|
|
29974
|
-
);
|
|
29975
|
-
|
|
29976
|
-
|
|
29977
|
-
|
|
29978
|
-
|
|
29979
|
-
const
|
|
29980
|
-
|
|
29981
|
-
|
|
29982
|
-
|
|
29983
|
-
|
|
29984
|
-
compositesPath: frameworkPaths.composites,
|
|
29985
|
-
rulesPath: frameworkPaths.rules,
|
|
29986
|
-
cssPath: null,
|
|
29987
|
-
...source ? { source } : {},
|
|
29988
|
-
intent: "efficient",
|
|
29989
|
-
fonts: { path: null, imports: [] },
|
|
29990
|
-
exports,
|
|
29991
|
-
installed: { components: [], primitives: [], composites: [], rules: [] }
|
|
29992
|
-
};
|
|
29993
|
-
await writeFile2(paths.config, JSON.stringify(newConfig, null, 2));
|
|
29758
|
+
return registryPath;
|
|
29759
|
+
}
|
|
29760
|
+
function substrateProjectPath(registryPath, config2, cwd = process.cwd()) {
|
|
29761
|
+
const componentsResolved = rootFor(config2?.componentsPath, cwd, "components/ui");
|
|
29762
|
+
const sourceRoot = componentsResolved.replace(/\/?components\/ui$/, "");
|
|
29763
|
+
return sourceRoot ? join7(sourceRoot, registryPath) : registryPath;
|
|
29764
|
+
}
|
|
29765
|
+
function fileExists(cwd, relativePath) {
|
|
29766
|
+
return existsSync3(join7(cwd, relativePath));
|
|
29767
|
+
}
|
|
29768
|
+
function transformFileContent(content, config2, fileType = "component", cwd = process.cwd(), opts = {}) {
|
|
29769
|
+
const { substrateKinds = [], installPath } = opts;
|
|
29770
|
+
let transformed = content;
|
|
29771
|
+
const componentsPath = rootFor(config2?.componentsPath, cwd, "components/ui");
|
|
29772
|
+
const primitivesPath = rootFor(config2?.primitivesPath, cwd, "lib/primitives");
|
|
29773
|
+
const stripSourceRoot = (p2) => p2.replace(/^(src|app)\//, "");
|
|
29774
|
+
const aliasComponents = stripSourceRoot(componentsPath);
|
|
29775
|
+
const aliasPrimitives = stripSourceRoot(primitivesPath);
|
|
29776
|
+
transformed = transformed.replace(
|
|
29777
|
+
/from\s+['"]\.\.\/\.\.\/primitives\/([^'"]+)['"]/g,
|
|
29778
|
+
`from '@/${aliasPrimitives}/$1'`
|
|
29779
|
+
);
|
|
29780
|
+
transformed = transformed.replace(
|
|
29781
|
+
/from\s+['"]\.\.\/primitives\/([^'"]+)['"]/g,
|
|
29782
|
+
`from '@/${aliasPrimitives}/$1'`
|
|
29783
|
+
);
|
|
29784
|
+
const substrateOwnDir = fileType === "substrate" && installPath ? stripSourceRoot(dirname(installPath)) : null;
|
|
29785
|
+
const aliasSibling = fileType === "primitive" ? aliasPrimitives : substrateOwnDir ?? aliasComponents;
|
|
29786
|
+
transformed = transformed.replace(/from\s+['"]\.\/([^'"]+)['"]/g, `from '@/${aliasSibling}/$1'`);
|
|
29787
|
+
if (substrateKinds.length > 0) {
|
|
29788
|
+
const kindAlternation = substrateKinds.join("|");
|
|
29789
|
+
transformed = transformed.replace(
|
|
29790
|
+
new RegExp(`from\\s+['"](?:\\.\\./){1,2}(${kindAlternation})/([^'"]+)['"]`, "g"),
|
|
29791
|
+
"from '@/$1/$2'"
|
|
29792
|
+
);
|
|
29994
29793
|
}
|
|
29995
|
-
|
|
29996
|
-
|
|
29997
|
-
|
|
29998
|
-
|
|
29999
|
-
|
|
29794
|
+
transformed = transformed.replace(
|
|
29795
|
+
/from\s+['"]\.\.\/([^/'"]+)\/\1([^'"]*)['"]/g,
|
|
29796
|
+
`from '@/${aliasComponents}/$1$2'`
|
|
29797
|
+
);
|
|
29798
|
+
transformed = transformed.replace(
|
|
29799
|
+
/from\s+['"]\.\.\/([^'"]+)['"]/g,
|
|
29800
|
+
`from '@/${aliasComponents}/$1'`
|
|
29801
|
+
);
|
|
29802
|
+
return transformed;
|
|
30000
29803
|
}
|
|
30001
|
-
async function
|
|
30002
|
-
|
|
30003
|
-
|
|
30004
|
-
|
|
30005
|
-
|
|
30006
|
-
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
|
|
30014
|
-
|
|
30015
|
-
|
|
30016
|
-
|
|
30017
|
-
|
|
30018
|
-
|
|
30019
|
-
|
|
30020
|
-
|
|
30021
|
-
|
|
30022
|
-
|
|
30023
|
-
|
|
29804
|
+
async function installItem(cwd, item, options, config2, substrateKinds = []) {
|
|
29805
|
+
const installedFiles = [];
|
|
29806
|
+
let skipped = false;
|
|
29807
|
+
let filesToInstall = item.files;
|
|
29808
|
+
if (item.type === "ui") {
|
|
29809
|
+
const target = getComponentTarget(config2);
|
|
29810
|
+
const selection = selectFilesForFramework(item.files, target);
|
|
29811
|
+
filesToInstall = selection.files;
|
|
29812
|
+
if (selection.fallback) {
|
|
29813
|
+
log({
|
|
29814
|
+
event: "add:fallback",
|
|
29815
|
+
component: item.name,
|
|
29816
|
+
target,
|
|
29817
|
+
message: `No ${targetToExtension(target)} version available for ${item.name}. Installing React version.`
|
|
29818
|
+
});
|
|
29819
|
+
}
|
|
29820
|
+
const hasComponentFile = selection.files.some((f) => !isSharedFile(f.path));
|
|
29821
|
+
if (!hasComponentFile) {
|
|
29822
|
+
throw new Error(
|
|
29823
|
+
`No ${targetToExtension(target)} version available for "${item.name}" and no compatible fallback exists.`
|
|
29824
|
+
);
|
|
29825
|
+
}
|
|
29826
|
+
} else if (item.type === "composite") {
|
|
29827
|
+
filesToInstall = selectCompositeFiles(item.files, getComponentTarget(config2));
|
|
30024
29828
|
}
|
|
30025
|
-
|
|
30026
|
-
|
|
29829
|
+
for (const file2 of filesToInstall) {
|
|
29830
|
+
const projectPath2 = item.type === "substrate" ? substrateProjectPath(file2.path, config2, cwd) : transformPath(file2.path, config2, cwd);
|
|
29831
|
+
const targetPath = join7(cwd, projectPath2);
|
|
29832
|
+
if (fileExists(cwd, projectPath2)) {
|
|
29833
|
+
if (!options.overwrite) {
|
|
29834
|
+
log({
|
|
29835
|
+
event: "add:skip",
|
|
29836
|
+
component: item.name,
|
|
29837
|
+
file: projectPath2,
|
|
29838
|
+
reason: "exists"
|
|
29839
|
+
});
|
|
29840
|
+
skipped = true;
|
|
29841
|
+
continue;
|
|
29842
|
+
}
|
|
29843
|
+
}
|
|
29844
|
+
await mkdir2(dirname(targetPath), { recursive: true });
|
|
29845
|
+
const fileType = item.type === "primitive" ? "primitive" : item.type === "substrate" ? "substrate" : "component";
|
|
29846
|
+
const transformedContent = transformFileContent(file2.content, config2, fileType, cwd, {
|
|
29847
|
+
substrateKinds,
|
|
29848
|
+
installPath: file2.path
|
|
29849
|
+
});
|
|
29850
|
+
await writeFile2(targetPath, transformedContent, "utf-8");
|
|
29851
|
+
installedFiles.push(projectPath2);
|
|
30027
29852
|
}
|
|
30028
|
-
|
|
30029
|
-
|
|
30030
|
-
|
|
29853
|
+
return {
|
|
29854
|
+
installed: installedFiles.length > 0,
|
|
29855
|
+
skipped,
|
|
29856
|
+
files: installedFiles
|
|
29857
|
+
};
|
|
29858
|
+
}
|
|
29859
|
+
async function add(componentArgs, options) {
|
|
29860
|
+
setAgentMode(options.agent ?? false);
|
|
29861
|
+
let components = componentArgs;
|
|
29862
|
+
const cwd = process.cwd();
|
|
29863
|
+
const config2 = await loadConfig(cwd);
|
|
29864
|
+
const client = new RegistryClient(resolveRegistryUrl(options, config2));
|
|
29865
|
+
let folder;
|
|
29866
|
+
const firstArg = components[0];
|
|
29867
|
+
if (firstArg && FOLDER_NAMES.has(firstArg)) {
|
|
29868
|
+
folder = firstArg;
|
|
29869
|
+
components = components.slice(1);
|
|
30031
29870
|
}
|
|
30032
|
-
if (
|
|
30033
|
-
|
|
29871
|
+
if (options.list) {
|
|
29872
|
+
const availableComponents = await client.listComponents();
|
|
29873
|
+
const availableComposites = await client.listComposites();
|
|
29874
|
+
if (options.agent) {
|
|
29875
|
+
log({
|
|
29876
|
+
event: "add:list",
|
|
29877
|
+
components: availableComponents,
|
|
29878
|
+
composites: availableComposites
|
|
29879
|
+
});
|
|
29880
|
+
} else {
|
|
29881
|
+
console.log("Available components:\n");
|
|
29882
|
+
for (const comp of availableComponents) {
|
|
29883
|
+
console.log(` ${comp.name} ${comp.description ?? ""}`);
|
|
29884
|
+
}
|
|
29885
|
+
if (availableComposites.length > 0) {
|
|
29886
|
+
console.log("\nAvailable composites:\n");
|
|
29887
|
+
for (const comp of availableComposites) {
|
|
29888
|
+
console.log(` ${comp.name} ${comp.description ?? ""}`);
|
|
29889
|
+
}
|
|
29890
|
+
}
|
|
29891
|
+
}
|
|
30034
29892
|
return;
|
|
30035
29893
|
}
|
|
30036
|
-
|
|
30037
|
-
|
|
30038
|
-
|
|
30039
|
-
|
|
30040
|
-
}
|
|
30041
|
-
if (raftersExists && options.rebuild) {
|
|
30042
|
-
await regenerateFromExisting(cwd, paths, source, isAgentMode2, framework);
|
|
29894
|
+
const initialized = await isInitialized(cwd);
|
|
29895
|
+
if (!initialized) {
|
|
29896
|
+
error46("Project not initialized. Run `rafters init` first.");
|
|
29897
|
+
process.exitCode = 1;
|
|
30043
29898
|
return;
|
|
30044
29899
|
}
|
|
30045
|
-
|
|
30046
|
-
|
|
30047
|
-
|
|
30048
|
-
|
|
30049
|
-
|
|
30050
|
-
|
|
30051
|
-
|
|
29900
|
+
if (options.update) {
|
|
29901
|
+
options.overwrite = true;
|
|
29902
|
+
}
|
|
29903
|
+
let untrackedNames = [];
|
|
29904
|
+
if (options.updateAll) {
|
|
29905
|
+
options.overwrite = true;
|
|
29906
|
+
if (!config2) {
|
|
29907
|
+
error46("No rafters config found. Run 'rafters init' first.");
|
|
29908
|
+
process.exitCode = 1;
|
|
29909
|
+
return;
|
|
30052
29910
|
}
|
|
30053
|
-
|
|
30054
|
-
|
|
29911
|
+
const trackedNames = getInstalledNames(config2);
|
|
29912
|
+
untrackedNames = await discoverUntrackedNames(cwd, config2, client, trackedNames);
|
|
29913
|
+
const candidates = [.../* @__PURE__ */ new Set([...trackedNames, ...untrackedNames])].sort();
|
|
29914
|
+
if (candidates.length === 0) {
|
|
29915
|
+
error46("No installed components found. Use 'rafters add <component>' to install first.");
|
|
29916
|
+
process.exitCode = 1;
|
|
29917
|
+
return;
|
|
29918
|
+
}
|
|
29919
|
+
if (untrackedNames.length > 0) {
|
|
29920
|
+
log({
|
|
29921
|
+
event: "add:untracked",
|
|
29922
|
+
components: untrackedNames,
|
|
29923
|
+
message: `Found ${untrackedNames.length} component(s) on disk the config never tracked: ${untrackedNames.join(", ")}. Refreshing and tracking them.`
|
|
29924
|
+
});
|
|
29925
|
+
}
|
|
29926
|
+
components = candidates;
|
|
30055
29927
|
}
|
|
30056
|
-
|
|
30057
|
-
|
|
30058
|
-
|
|
29928
|
+
if (folder === "composites" && components.length === 0) {
|
|
29929
|
+
components = ["composites"];
|
|
29930
|
+
}
|
|
29931
|
+
if (components.length === 0) {
|
|
29932
|
+
error46("No components specified. Usage: rafters add <component...>");
|
|
29933
|
+
process.exitCode = 1;
|
|
29934
|
+
return;
|
|
29935
|
+
}
|
|
29936
|
+
log({
|
|
29937
|
+
event: "add:start",
|
|
30059
29938
|
cwd,
|
|
30060
|
-
|
|
30061
|
-
|
|
30062
|
-
|
|
30063
|
-
|
|
30064
|
-
|
|
30065
|
-
)
|
|
30066
|
-
if (sourceCssPathForBase !== null) {
|
|
29939
|
+
components,
|
|
29940
|
+
overwrite: options.overwrite ?? false
|
|
29941
|
+
});
|
|
29942
|
+
const allItems = [];
|
|
29943
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29944
|
+
for (const itemName of components) {
|
|
30067
29945
|
try {
|
|
30068
|
-
|
|
30069
|
-
|
|
30070
|
-
|
|
30071
|
-
|
|
30072
|
-
|
|
30073
|
-
|
|
30074
|
-
|
|
30075
|
-
|
|
30076
|
-
|
|
30077
|
-
|
|
30078
|
-
|
|
30079
|
-
event: "init:import_radius_applied",
|
|
30080
|
-
eventKey: "baseRadius"
|
|
30081
|
-
},
|
|
30082
|
-
{
|
|
30083
|
-
detect: () => adapter.detectFontSize(cssForBase).base,
|
|
30084
|
-
field: "baseFontSizeOverride",
|
|
30085
|
-
event: "init:import_font_size_applied",
|
|
30086
|
-
eventKey: "baseFontSize"
|
|
30087
|
-
},
|
|
30088
|
-
{
|
|
30089
|
-
detect: () => adapter.detectFocusRing(cssForBase).width,
|
|
30090
|
-
field: "focusRingWidthOverride",
|
|
30091
|
-
event: "init:import_focus_ring_applied",
|
|
30092
|
-
eventKey: "focusRingWidth"
|
|
29946
|
+
if (folder === "composites") {
|
|
29947
|
+
if (!seen.has(itemName)) {
|
|
29948
|
+
const composite = await client.fetchComposite(itemName);
|
|
29949
|
+
seen.add(itemName);
|
|
29950
|
+
allItems.push(composite);
|
|
29951
|
+
for (const dep of composite.primitives) {
|
|
29952
|
+
if (!seen.has(dep)) {
|
|
29953
|
+
const depItems = await client.resolveDependencies(dep, seen);
|
|
29954
|
+
allItems.push(...depItems);
|
|
29955
|
+
}
|
|
29956
|
+
}
|
|
30093
29957
|
}
|
|
30094
|
-
|
|
30095
|
-
|
|
30096
|
-
|
|
30097
|
-
// policy note; designers can `rafters set motion-duration-base ...`
|
|
30098
|
-
// after init to override explicitly.
|
|
30099
|
-
];
|
|
30100
|
-
for (const slot of baseSlots) {
|
|
30101
|
-
const value = slot.detect();
|
|
30102
|
-
if (value === void 0) continue;
|
|
30103
|
-
baseConfig[slot.field] = value;
|
|
30104
|
-
log({
|
|
30105
|
-
event: slot.event,
|
|
30106
|
-
cssPath: sourceCssPathForBase,
|
|
30107
|
-
[slot.eventKey]: value
|
|
30108
|
-
});
|
|
29958
|
+
} else {
|
|
29959
|
+
const items = await client.resolveDependencies(itemName, seen);
|
|
29960
|
+
allItems.push(...items);
|
|
30109
29961
|
}
|
|
30110
29962
|
} catch (err) {
|
|
30111
|
-
if (
|
|
29963
|
+
if (err instanceof Error) {
|
|
29964
|
+
error46(err.message);
|
|
29965
|
+
} else {
|
|
29966
|
+
error46(`Failed to fetch "${itemName}"`);
|
|
29967
|
+
}
|
|
29968
|
+
process.exitCode = 1;
|
|
29969
|
+
return;
|
|
30112
29970
|
}
|
|
30113
29971
|
}
|
|
30114
|
-
const
|
|
30115
|
-
|
|
30116
|
-
|
|
30117
|
-
|
|
30118
|
-
|
|
30119
|
-
|
|
30120
|
-
|
|
30121
|
-
|
|
30122
|
-
|
|
30123
|
-
const
|
|
30124
|
-
const
|
|
30125
|
-
|
|
30126
|
-
|
|
30127
|
-
|
|
30128
|
-
|
|
30129
|
-
|
|
30130
|
-
|
|
30131
|
-
|
|
30132
|
-
|
|
30133
|
-
|
|
30134
|
-
|
|
30135
|
-
|
|
30136
|
-
project.cssPath,
|
|
30137
|
-
shadcn?.tailwind?.css,
|
|
30138
|
-
log
|
|
30139
|
-
);
|
|
30140
|
-
if (source !== "shadcn" && exports.tailwind) {
|
|
30141
|
-
if (detectedCssPath) {
|
|
30142
|
-
await updateMainCss(cwd, detectedCssPath, ".rafters/output/rafters.css");
|
|
30143
|
-
} else {
|
|
29972
|
+
const substrateKinds = [
|
|
29973
|
+
...new Set(
|
|
29974
|
+
allItems.filter((item) => item.type === "substrate").map((item) => item.files[0]?.path.split("/")[0]).filter((segment) => Boolean(segment))
|
|
29975
|
+
)
|
|
29976
|
+
];
|
|
29977
|
+
const written = [];
|
|
29978
|
+
const skipped = [];
|
|
29979
|
+
const failed = [];
|
|
29980
|
+
const installedItems = [];
|
|
29981
|
+
const filteredItems = [];
|
|
29982
|
+
const target = getComponentTarget(config2);
|
|
29983
|
+
for (const item of allItems) {
|
|
29984
|
+
if (!options.overwrite && isAlreadyInstalled(config2, item)) {
|
|
29985
|
+
if (isInstalledOnDisk(config2, item, cwd)) {
|
|
29986
|
+
log({
|
|
29987
|
+
event: "add:skip",
|
|
29988
|
+
component: item.name,
|
|
29989
|
+
reason: "already installed"
|
|
29990
|
+
});
|
|
29991
|
+
skipped.push(item.name);
|
|
29992
|
+
continue;
|
|
29993
|
+
}
|
|
30144
29994
|
log({
|
|
30145
|
-
event: "
|
|
30146
|
-
|
|
30147
|
-
|
|
29995
|
+
event: "add:warning",
|
|
29996
|
+
component: item.name,
|
|
29997
|
+
message: "Config tracks this as installed but no files found on disk -- reinstalling."
|
|
30148
29998
|
});
|
|
30149
29999
|
}
|
|
30150
|
-
}
|
|
30151
|
-
let componentTarget = frameworkToTarget(framework);
|
|
30152
|
-
if (framework === "astro" && astroHasReact && isInteractive() && !isAgentMode2) {
|
|
30153
|
-
componentTarget = await select({
|
|
30154
|
-
message: "This Astro project has React integration. Install components as:",
|
|
30155
|
-
choices: [
|
|
30156
|
-
{
|
|
30157
|
-
name: "Astro components (zero client JS, server-rendered)",
|
|
30158
|
-
value: "astro"
|
|
30159
|
-
},
|
|
30160
|
-
{
|
|
30161
|
-
name: "React components (client islands with client:load)",
|
|
30162
|
-
value: "react"
|
|
30163
|
-
}
|
|
30164
|
-
]
|
|
30165
|
-
});
|
|
30166
|
-
}
|
|
30167
|
-
const frameworkPaths = FRAMEWORK_SPECS[framework].components;
|
|
30168
|
-
const config2 = {
|
|
30169
|
-
framework,
|
|
30170
|
-
...source ? { source } : {},
|
|
30171
|
-
componentTarget,
|
|
30172
|
-
componentsPath: frameworkPaths.components,
|
|
30173
|
-
primitivesPath: frameworkPaths.primitives,
|
|
30174
|
-
compositesPath: frameworkPaths.composites,
|
|
30175
|
-
rulesPath: frameworkPaths.rules,
|
|
30176
|
-
cssPath: detectedCssPath,
|
|
30177
|
-
exports,
|
|
30178
|
-
intent: "efficient",
|
|
30179
|
-
fonts: { path: null, imports: [] },
|
|
30180
|
-
installed: {
|
|
30181
|
-
components: [],
|
|
30182
|
-
primitives: [],
|
|
30183
|
-
composites: [],
|
|
30184
|
-
rules: []
|
|
30185
|
-
}
|
|
30186
|
-
};
|
|
30187
|
-
await writeFile2(paths.config, JSON.stringify(config2, null, 2));
|
|
30188
|
-
if (detectedCssPath) {
|
|
30189
|
-
let sourceCss = null;
|
|
30190
30000
|
try {
|
|
30191
|
-
|
|
30001
|
+
const result = await installItem(cwd, item, options, config2, substrateKinds);
|
|
30002
|
+
if (result.installed) {
|
|
30003
|
+
written.push(item.name);
|
|
30004
|
+
installedItems.push(item);
|
|
30005
|
+
if (item.type === "ui") {
|
|
30006
|
+
const selection = selectFilesForFramework(item.files, target);
|
|
30007
|
+
filteredItems.push({ ...item, files: selection.files });
|
|
30008
|
+
} else {
|
|
30009
|
+
filteredItems.push(item);
|
|
30010
|
+
}
|
|
30011
|
+
log({
|
|
30012
|
+
event: "add:installed",
|
|
30013
|
+
component: item.name,
|
|
30014
|
+
type: item.type,
|
|
30015
|
+
files: result.files
|
|
30016
|
+
});
|
|
30017
|
+
}
|
|
30018
|
+
if (result.skipped && !result.installed) {
|
|
30019
|
+
skipped.push(item.name);
|
|
30020
|
+
installedItems.push(item);
|
|
30021
|
+
}
|
|
30192
30022
|
} catch (err) {
|
|
30193
|
-
|
|
30194
|
-
|
|
30195
|
-
if (sourceCss !== null) {
|
|
30196
|
-
const summary = senseShadcnCss(sourceCss);
|
|
30197
|
-
if (summary.totalDeclarations > 0) {
|
|
30023
|
+
failed.push(item.name);
|
|
30024
|
+
if (err instanceof Error) {
|
|
30198
30025
|
log({
|
|
30199
|
-
event: "
|
|
30200
|
-
|
|
30201
|
-
|
|
30026
|
+
event: "add:warning",
|
|
30027
|
+
component: item.name,
|
|
30028
|
+
message: err.message
|
|
30202
30029
|
});
|
|
30203
|
-
const classification = classifyDeclarations(extractShadcnRoot(sourceCss));
|
|
30204
|
-
const colorDecls = adapter.detectColors(sourceCss);
|
|
30205
|
-
const nonColorDecls = [
|
|
30206
|
-
...classification.byNamespace.typography,
|
|
30207
|
-
...classification.byNamespace.spacing,
|
|
30208
|
-
...classification.byNamespace.radius,
|
|
30209
|
-
...classification.byNamespace.shadow
|
|
30210
|
-
];
|
|
30211
|
-
const toImportColors = [];
|
|
30212
|
-
for (const color of colorDecls) {
|
|
30213
|
-
const accept = isAgentMode2 ? true : await confirm({
|
|
30214
|
-
message: `Import --${color.name}: ${color.value}?`,
|
|
30215
|
-
default: true
|
|
30216
|
-
});
|
|
30217
|
-
if (accept) toImportColors.push(color);
|
|
30218
|
-
}
|
|
30219
|
-
const toImportNonColors = [];
|
|
30220
|
-
for (const decl of nonColorDecls) {
|
|
30221
|
-
const accept = isAgentMode2 ? true : await confirm({
|
|
30222
|
-
message: `Import --${decl.name}: ${decl.value}?`,
|
|
30223
|
-
default: true
|
|
30224
|
-
});
|
|
30225
|
-
if (accept) toImportNonColors.push(decl);
|
|
30226
|
-
}
|
|
30227
|
-
if (toImportColors.length + toImportNonColors.length > 0) {
|
|
30228
|
-
for (const color of toImportColors) {
|
|
30229
|
-
const familyName = `imported-${color.name}`;
|
|
30230
|
-
const reason = `imported from --${color.name} in ${detectedCssPath}`;
|
|
30231
|
-
for (const t of importColorFamily(familyName, "500", color.oklch)) {
|
|
30232
|
-
registry2.define(t);
|
|
30233
|
-
}
|
|
30234
|
-
if (color.namespace === "semantic") {
|
|
30235
|
-
registry2.set(color.name, { family: familyName, position: "500" }, { reason });
|
|
30236
|
-
}
|
|
30237
|
-
}
|
|
30238
|
-
const skipped = [];
|
|
30239
|
-
for (const decl of toImportNonColors) {
|
|
30240
|
-
const reason = `imported from --${decl.name} in ${detectedCssPath}`;
|
|
30241
|
-
if (registry2.has(decl.name)) {
|
|
30242
|
-
registry2.set(decl.name, decl.value, { reason });
|
|
30243
|
-
} else {
|
|
30244
|
-
skipped.push(decl.name);
|
|
30245
|
-
}
|
|
30246
|
-
}
|
|
30247
|
-
saveRegistryToDir(paths.tokens, registry2);
|
|
30248
|
-
await generateOutputs(cwd, paths, registry2, exports, source);
|
|
30249
|
-
const importedNames = [
|
|
30250
|
-
...toImportColors.map((c4) => c4.name),
|
|
30251
|
-
...toImportNonColors.map((d2) => d2.name)
|
|
30252
|
-
];
|
|
30253
|
-
await stripImportedDeclarations(cwd, detectedCssPath, importedNames);
|
|
30254
|
-
log({
|
|
30255
|
-
event: "init:import_applied",
|
|
30256
|
-
count: toImportColors.length + toImportNonColors.length - skipped.length,
|
|
30257
|
-
cssPath: detectedCssPath,
|
|
30258
|
-
...skipped.length > 0 ? { skipped } : {}
|
|
30259
|
-
});
|
|
30260
|
-
}
|
|
30261
|
-
}
|
|
30262
|
-
{
|
|
30263
|
-
const themeDecls = extractThemeBlocks2(sourceCss);
|
|
30264
|
-
if (themeDecls.length > 0) {
|
|
30265
|
-
const POSITION_SUFFIX = new RegExp(`^(.+)-(${SCALE_POSITIONS.join("|")})$`);
|
|
30266
|
-
const familySeeds = /* @__PURE__ */ new Map();
|
|
30267
|
-
for (const decl of themeDecls) {
|
|
30268
|
-
if (!decl.name.startsWith("color-")) continue;
|
|
30269
|
-
const oklch2 = tryParseColor(decl.value);
|
|
30270
|
-
if (oklch2 === null) continue;
|
|
30271
|
-
const m3 = decl.name.match(POSITION_SUFFIX);
|
|
30272
|
-
const baseName = ((m3 ? m3[1] : decl.name) ?? decl.name).replace(/^color-/, "");
|
|
30273
|
-
if (!baseName) continue;
|
|
30274
|
-
const seedPosition = m3?.[2] ?? "500";
|
|
30275
|
-
const existing = familySeeds.get(baseName);
|
|
30276
|
-
if (!existing || seedPosition === "500") {
|
|
30277
|
-
familySeeds.set(baseName, { seed: oklch2, seedPosition });
|
|
30278
|
-
}
|
|
30279
|
-
}
|
|
30280
|
-
const families = Array.from(familySeeds, ([name, info]) => ({
|
|
30281
|
-
name,
|
|
30282
|
-
...info
|
|
30283
|
-
}));
|
|
30284
|
-
if (families.length > 0) {
|
|
30285
|
-
for (const family of families) {
|
|
30286
|
-
for (const t of importColorFamily(family.name, family.seedPosition, family.seed)) {
|
|
30287
|
-
registry2.define(t);
|
|
30288
|
-
}
|
|
30289
|
-
}
|
|
30290
|
-
const ALL_ROLES = [
|
|
30291
|
-
"primary",
|
|
30292
|
-
"secondary",
|
|
30293
|
-
"tertiary",
|
|
30294
|
-
"accent",
|
|
30295
|
-
"highlight",
|
|
30296
|
-
"neutral",
|
|
30297
|
-
"muted",
|
|
30298
|
-
"success",
|
|
30299
|
-
"warning",
|
|
30300
|
-
"destructive",
|
|
30301
|
-
"info"
|
|
30302
|
-
];
|
|
30303
|
-
const ROLES = ALL_ROLES.filter((r) => {
|
|
30304
|
-
const tok = registry2.get(r);
|
|
30305
|
-
return tok !== void 0 && tok.namespace === "semantic";
|
|
30306
|
-
});
|
|
30307
|
-
const assignments = [];
|
|
30308
|
-
for (const [i, role] of ROLES.entries()) {
|
|
30309
|
-
let familyChoice;
|
|
30310
|
-
if (isAgentMode2) {
|
|
30311
|
-
familyChoice = families[i]?.name ?? null;
|
|
30312
|
-
} else {
|
|
30313
|
-
familyChoice = await select({
|
|
30314
|
-
message: `Which palette is "${role}"?`,
|
|
30315
|
-
choices: [
|
|
30316
|
-
...families.map((f) => ({ name: f.name, value: f.name })),
|
|
30317
|
-
{ name: "(skip -- keep rafters default)", value: null }
|
|
30318
|
-
]
|
|
30319
|
-
});
|
|
30320
|
-
}
|
|
30321
|
-
if (familyChoice === null) continue;
|
|
30322
|
-
const family = families.find((f) => f.name === familyChoice);
|
|
30323
|
-
if (!family) continue;
|
|
30324
|
-
const position = isAgentMode2 ? family.seedPosition : await select({
|
|
30325
|
-
message: `Which position in "${familyChoice}" for "${role}"?`,
|
|
30326
|
-
choices: SCALE_POSITIONS.map((p2) => ({
|
|
30327
|
-
name: p2,
|
|
30328
|
-
value: p2
|
|
30329
|
-
})),
|
|
30330
|
-
default: family.seedPosition
|
|
30331
|
-
});
|
|
30332
|
-
registry2.set(
|
|
30333
|
-
role,
|
|
30334
|
-
{ family: familyChoice, position },
|
|
30335
|
-
{
|
|
30336
|
-
reason: `assigned ${familyChoice}@${position} as ${role} during import from ${detectedCssPath}`
|
|
30337
|
-
}
|
|
30338
|
-
);
|
|
30339
|
-
assignments.push({ role, family: familyChoice, position });
|
|
30340
|
-
}
|
|
30341
|
-
saveRegistryToDir(paths.tokens, registry2);
|
|
30342
|
-
await generateOutputs(cwd, paths, registry2, exports, source);
|
|
30343
|
-
const themeImportedNames = families.flatMap(
|
|
30344
|
-
(f) => SCALE_POSITIONS.map((p2) => `color-${f.name}-${p2}`).concat([`color-${f.name}`])
|
|
30345
|
-
);
|
|
30346
|
-
await stripImportedDeclarations(cwd, detectedCssPath, themeImportedNames);
|
|
30347
|
-
log({
|
|
30348
|
-
event: "init:import_palettes_applied",
|
|
30349
|
-
cssPath: detectedCssPath,
|
|
30350
|
-
palettesDefined: families.map((f) => f.name),
|
|
30351
|
-
assignments
|
|
30352
|
-
});
|
|
30353
|
-
}
|
|
30354
|
-
}
|
|
30355
|
-
}
|
|
30356
|
-
}
|
|
30357
|
-
if (sourceCss !== null) {
|
|
30358
|
-
const detectedFonts = adapter.detectFonts(sourceCss);
|
|
30359
|
-
if (detectedFonts.length > 0) {
|
|
30360
|
-
const typographyTokens = registry2.list({ namespace: "typography" });
|
|
30361
|
-
const baseFamilyNames = new Set(
|
|
30362
|
-
typographyTokens.filter((t) => (!t.dependsOn || t.dependsOn.length === 0) && isFontStackValue(t.value)).map((t) => t.name)
|
|
30363
|
-
);
|
|
30364
|
-
const fontRoles = [];
|
|
30365
|
-
for (const tok of typographyTokens) {
|
|
30366
|
-
const base = tok.dependsOn?.[0];
|
|
30367
|
-
if (base === void 0 || !baseFamilyNames.has(base)) continue;
|
|
30368
|
-
if (typeof tok.value !== "string" || !tok.value.includes(`var(--${base})`)) continue;
|
|
30369
|
-
fontRoles.push({ role: tok.name, base });
|
|
30370
|
-
}
|
|
30371
|
-
const roleTokenNames = new Set(fontRoles.map((r) => r.role));
|
|
30372
|
-
const canonicalTokenNames = /* @__PURE__ */ new Set([...baseFamilyNames, ...roleTokenNames]);
|
|
30373
|
-
const definedFonts = [];
|
|
30374
|
-
for (const font of detectedFonts) {
|
|
30375
|
-
if (font.sourceDeclName && canonicalTokenNames.has(font.sourceDeclName)) continue;
|
|
30376
|
-
const tokenName = font.sourceDeclName ?? `font-${slugifyFontName(font.name)}`;
|
|
30377
|
-
if (registry2.has(tokenName)) continue;
|
|
30378
|
-
registry2.define({
|
|
30379
|
-
name: tokenName,
|
|
30380
|
-
namespace: "typography",
|
|
30381
|
-
category: "typography",
|
|
30382
|
-
value: font.stack,
|
|
30383
|
-
description: `Imported from ${detectedCssPath}. ${font.sourceDeclName ? `Declared as --${font.sourceDeclName}.` : "Loaded via @font-face or @import."}`,
|
|
30384
|
-
userOverride: null,
|
|
30385
|
-
containerQueryAware: false
|
|
30386
|
-
});
|
|
30387
|
-
definedFonts.push(tokenName);
|
|
30388
|
-
}
|
|
30389
|
-
const fontAssignments = [];
|
|
30390
|
-
const declaredByBase = /* @__PURE__ */ new Map();
|
|
30391
|
-
for (const font of detectedFonts) {
|
|
30392
|
-
if (font.sourceDeclName && baseFamilyNames.has(font.sourceDeclName)) {
|
|
30393
|
-
if (!declaredByBase.has(font.sourceDeclName)) {
|
|
30394
|
-
declaredByBase.set(font.sourceDeclName, font);
|
|
30395
|
-
}
|
|
30396
|
-
}
|
|
30397
|
-
}
|
|
30398
|
-
for (const { role, base } of fontRoles) {
|
|
30399
|
-
let choice;
|
|
30400
|
-
if (isAgentMode2) {
|
|
30401
|
-
const declared = declaredByBase.get(base);
|
|
30402
|
-
if (declared !== void 0) {
|
|
30403
|
-
choice = declared.name;
|
|
30404
|
-
} else if (/mono/.test(base)) {
|
|
30405
|
-
choice = detectedFonts.find((f) => /mono/i.test(f.name))?.name ?? null;
|
|
30406
|
-
} else {
|
|
30407
|
-
const sansLike = detectedFonts.find((f) => !/mono/i.test(f.name));
|
|
30408
|
-
choice = sansLike?.name ?? null;
|
|
30409
|
-
}
|
|
30410
|
-
} else {
|
|
30411
|
-
const prompt = role.replace(/^font-/, "");
|
|
30412
|
-
choice = await select({
|
|
30413
|
-
message: `Which font is "${prompt}"?`,
|
|
30414
|
-
choices: [
|
|
30415
|
-
...detectedFonts.map((f) => ({ name: f.name, value: f.name })),
|
|
30416
|
-
{ name: "(skip -- keep rafters default)", value: null }
|
|
30417
|
-
]
|
|
30418
|
-
});
|
|
30419
|
-
}
|
|
30420
|
-
if (choice === null) continue;
|
|
30421
|
-
const font = detectedFonts.find((f) => f.name === choice);
|
|
30422
|
-
if (font === void 0) continue;
|
|
30423
|
-
const reason = `assigned ${font.name} as ${role} during import from ${detectedCssPath}`;
|
|
30424
|
-
if (registry2.has(role)) {
|
|
30425
|
-
registry2.set(role, font.stack, { reason });
|
|
30426
|
-
}
|
|
30427
|
-
if (registry2.has(base)) {
|
|
30428
|
-
const current = registry2.get(base);
|
|
30429
|
-
if (current?.value !== font.stack) {
|
|
30430
|
-
registry2.set(base, font.stack, { reason });
|
|
30431
|
-
}
|
|
30432
|
-
}
|
|
30433
|
-
fontAssignments.push({ role, family: font.name });
|
|
30434
|
-
}
|
|
30435
|
-
if (fontAssignments.length > 0 || definedFonts.length > 0) {
|
|
30436
|
-
saveRegistryToDir(paths.tokens, registry2);
|
|
30437
|
-
await generateOutputs(cwd, paths, registry2, exports, source);
|
|
30438
|
-
log({
|
|
30439
|
-
event: "init:import_fonts_applied",
|
|
30440
|
-
cssPath: detectedCssPath,
|
|
30441
|
-
fontsDetected: detectedFonts.map((f) => f.name),
|
|
30442
|
-
assignments: fontAssignments,
|
|
30443
|
-
...definedFonts.length > 0 ? { definedFonts } : {}
|
|
30444
|
-
});
|
|
30445
|
-
}
|
|
30446
30030
|
}
|
|
30447
30031
|
}
|
|
30448
30032
|
}
|
|
30449
|
-
|
|
30450
|
-
|
|
30451
|
-
|
|
30452
|
-
|
|
30453
|
-
|
|
30454
|
-
}
|
|
30455
|
-
|
|
30456
|
-
// src/commands/add.ts
|
|
30457
|
-
var REGISTRY_PLUGINS2 = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
|
|
30458
|
-
async function regenerateAfterInstall(cwd, config2) {
|
|
30459
|
-
const paths = getRaftersPaths(cwd);
|
|
30460
|
-
let registry2;
|
|
30461
|
-
try {
|
|
30462
|
-
registry2 = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS2);
|
|
30463
|
-
} catch {
|
|
30464
|
-
return;
|
|
30465
|
-
}
|
|
30466
|
-
if (registry2.size() === 0) return;
|
|
30033
|
+
const emptyDeps = {
|
|
30034
|
+
installed: [],
|
|
30035
|
+
skipped: [],
|
|
30036
|
+
devInstalled: [],
|
|
30037
|
+
failed: []
|
|
30038
|
+
};
|
|
30039
|
+
let depsResult = emptyDeps;
|
|
30467
30040
|
try {
|
|
30468
|
-
await
|
|
30469
|
-
|
|
30470
|
-
exports: config2.exports,
|
|
30471
|
-
contentSources: resolveContentSources(cwd, config2),
|
|
30472
|
-
darkMode: config2.darkMode ?? "class",
|
|
30473
|
-
includeImport: config2.source !== "shadcn"
|
|
30041
|
+
depsResult = await installRegistryDependencies(filteredItems, cwd, {
|
|
30042
|
+
target
|
|
30474
30043
|
});
|
|
30475
30044
|
} catch (err) {
|
|
30045
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
30476
30046
|
log({
|
|
30477
|
-
event: "add:
|
|
30478
|
-
message: `
|
|
30047
|
+
event: "add:deps:install-failed",
|
|
30048
|
+
message: `Failed to process dependencies: ${message}`,
|
|
30049
|
+
dependencies: [],
|
|
30050
|
+
suggestion: "Check package.json and try installing dependencies manually."
|
|
30479
30051
|
});
|
|
30480
30052
|
}
|
|
30481
|
-
|
|
30482
|
-
|
|
30483
|
-
|
|
30484
|
-
|
|
30485
|
-
|
|
30486
|
-
|
|
30487
|
-
|
|
30488
|
-
return false;
|
|
30053
|
+
if (depsResult.installed.length > 0 || depsResult.skipped.length > 0) {
|
|
30054
|
+
log({
|
|
30055
|
+
event: "add:dependencies",
|
|
30056
|
+
dependencies: depsResult.installed,
|
|
30057
|
+
devDependencies: depsResult.devInstalled,
|
|
30058
|
+
skipped: depsResult.skipped
|
|
30059
|
+
});
|
|
30489
30060
|
}
|
|
30490
|
-
|
|
30491
|
-
|
|
30492
|
-
|
|
30493
|
-
|
|
30494
|
-
|
|
30495
|
-
|
|
30496
|
-
|
|
30497
|
-
|
|
30498
|
-
|
|
30499
|
-
|
|
30500
|
-
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
|
|
30504
|
-
|
|
30505
|
-
|
|
30506
|
-
|
|
30061
|
+
if (installedItems.length > 0 && config2) {
|
|
30062
|
+
trackInstalled(config2, installedItems);
|
|
30063
|
+
await saveConfig(cwd, config2);
|
|
30064
|
+
await regenerateAfterInstall(cwd, config2);
|
|
30065
|
+
} else if (installedItems.length > 0 && !config2) {
|
|
30066
|
+
const newConfig = {
|
|
30067
|
+
framework: "unknown",
|
|
30068
|
+
componentsPath: "components/ui",
|
|
30069
|
+
primitivesPath: "lib/primitives",
|
|
30070
|
+
compositesPath: "composites",
|
|
30071
|
+
rulesPath: "lib/rules",
|
|
30072
|
+
cssPath: null,
|
|
30073
|
+
intent: "efficient",
|
|
30074
|
+
fonts: { path: null, imports: [] },
|
|
30075
|
+
exports: DEFAULT_EXPORTS,
|
|
30076
|
+
installed: { components: [], primitives: [], composites: [], rules: [] }
|
|
30077
|
+
};
|
|
30078
|
+
trackInstalled(newConfig, installedItems);
|
|
30079
|
+
await saveConfig(cwd, newConfig);
|
|
30080
|
+
await regenerateAfterInstall(cwd, newConfig);
|
|
30507
30081
|
}
|
|
30508
|
-
|
|
30509
|
-
|
|
30510
|
-
|
|
30511
|
-
|
|
30512
|
-
|
|
30513
|
-
|
|
30514
|
-
|
|
30515
|
-
|
|
30516
|
-
|
|
30517
|
-
|
|
30518
|
-
|
|
30519
|
-
|
|
30520
|
-
|
|
30521
|
-
|
|
30522
|
-
|
|
30523
|
-
...config2.installed.substrate ?? []
|
|
30524
|
-
]);
|
|
30525
|
-
return [...names].sort();
|
|
30526
|
-
}
|
|
30527
|
-
async function discoverUntrackedNames(cwd, config2, client, tracked) {
|
|
30528
|
-
let index = null;
|
|
30529
|
-
try {
|
|
30530
|
-
index = await client.fetchIndex();
|
|
30531
|
-
} catch (err) {
|
|
30532
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
30082
|
+
log({
|
|
30083
|
+
event: "add:complete",
|
|
30084
|
+
written: written.length,
|
|
30085
|
+
skipped: skipped.length,
|
|
30086
|
+
untracked: untrackedNames.length,
|
|
30087
|
+
failed: failed.length,
|
|
30088
|
+
components: written,
|
|
30089
|
+
skippedComponents: skipped,
|
|
30090
|
+
untrackedComponents: untrackedNames,
|
|
30091
|
+
failedComponents: failed
|
|
30092
|
+
});
|
|
30093
|
+
if (failed.length > 0) {
|
|
30094
|
+
process.exitCode = 1;
|
|
30095
|
+
}
|
|
30096
|
+
if (!options.updateAll && skipped.length > 0 && written.length === 0) {
|
|
30533
30097
|
log({
|
|
30534
|
-
event: "add:
|
|
30535
|
-
message:
|
|
30098
|
+
event: "add:hint",
|
|
30099
|
+
message: "Some components were skipped. Use --update to re-fetch, or --update-all to refresh everything.",
|
|
30100
|
+
skipped
|
|
30536
30101
|
});
|
|
30102
|
+
error46("Component already exists. Use --update to re-fetch from registry.");
|
|
30103
|
+
process.exitCode = 1;
|
|
30537
30104
|
}
|
|
30538
|
-
const { untracked } = buildUpdateCandidates(tracked, index, readInstallRoots(cwd, config2));
|
|
30539
|
-
return untracked;
|
|
30540
30105
|
}
|
|
30541
|
-
|
|
30542
|
-
|
|
30543
|
-
}
|
|
30544
|
-
|
|
30545
|
-
|
|
30546
|
-
|
|
30547
|
-
|
|
30106
|
+
|
|
30107
|
+
// src/commands/agents.ts
|
|
30108
|
+
import { existsSync as existsSync6 } from "fs";
|
|
30109
|
+
import { readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
30110
|
+
import { join as join12 } from "path";
|
|
30111
|
+
|
|
30112
|
+
// src/mcp/tools.ts
|
|
30113
|
+
import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
30114
|
+
import { isAbsolute as isAbsolute3, join as join11 } from "path";
|
|
30115
|
+
|
|
30116
|
+
// ../composites/src/built-in-rules/email.ts
|
|
30117
|
+
var email3 = external_exports.string().email();
|
|
30118
|
+
|
|
30119
|
+
// ../composites/src/built-in-rules/credentials.ts
|
|
30120
|
+
var credentials = external_exports.object({ email: email3, password: external_exports.string().min(8) });
|
|
30121
|
+
|
|
30122
|
+
// ../composites/src/built-in-rules/password.ts
|
|
30123
|
+
var password = external_exports.string().min(8);
|
|
30124
|
+
|
|
30125
|
+
// ../composites/src/built-in-rules/required.ts
|
|
30126
|
+
var required2 = external_exports.string().min(1);
|
|
30127
|
+
|
|
30128
|
+
// ../composites/src/built-in-rules/url.ts
|
|
30129
|
+
var url2 = external_exports.string().url();
|
|
30130
|
+
|
|
30131
|
+
// ../composites/src/manifest.ts
|
|
30132
|
+
var CompositeCategorySchema = external_exports.string().min(1);
|
|
30133
|
+
var AppliedRuleSchema = external_exports.union([
|
|
30134
|
+
external_exports.string(),
|
|
30135
|
+
external_exports.object({
|
|
30136
|
+
name: external_exports.string().min(1),
|
|
30137
|
+
config: external_exports.record(external_exports.string(), external_exports.unknown())
|
|
30138
|
+
})
|
|
30139
|
+
]);
|
|
30140
|
+
var CompositeBlockSchema = external_exports.object({
|
|
30141
|
+
id: external_exports.string().min(1),
|
|
30142
|
+
type: external_exports.string().min(1),
|
|
30143
|
+
/** Signatures this block consumes / produces (rule names) -- the typed I/O edges.
|
|
30144
|
+
* Within a composite, an output named X feeds any block input named X. */
|
|
30145
|
+
input: external_exports.array(external_exports.string()).optional(),
|
|
30146
|
+
output: external_exports.array(external_exports.string()).optional(),
|
|
30147
|
+
content: external_exports.unknown().optional(),
|
|
30148
|
+
children: external_exports.array(external_exports.string()).optional(),
|
|
30149
|
+
parentId: external_exports.string().optional(),
|
|
30150
|
+
meta: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
30151
|
+
rules: external_exports.array(AppliedRuleSchema).optional()
|
|
30152
|
+
});
|
|
30153
|
+
var UsagePatternsSchema2 = external_exports.object({
|
|
30154
|
+
do: external_exports.array(external_exports.string()),
|
|
30155
|
+
never: external_exports.array(external_exports.string())
|
|
30156
|
+
});
|
|
30157
|
+
var CompositeManifestSchema = external_exports.object({
|
|
30158
|
+
id: external_exports.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
30159
|
+
name: external_exports.string().min(1),
|
|
30160
|
+
category: CompositeCategorySchema,
|
|
30161
|
+
description: external_exports.string(),
|
|
30162
|
+
keywords: external_exports.array(external_exports.string()),
|
|
30163
|
+
cognitiveLoad: external_exports.number().int().min(1).max(10),
|
|
30164
|
+
// Designer intent fields (optional for backwards compatibility)
|
|
30165
|
+
solves: external_exports.string().optional(),
|
|
30166
|
+
appliesWhen: external_exports.array(external_exports.string()).optional(),
|
|
30167
|
+
usagePatterns: UsagePatternsSchema2.optional()
|
|
30168
|
+
});
|
|
30169
|
+
var CompositeFileSchema = external_exports.object({
|
|
30170
|
+
manifest: CompositeManifestSchema,
|
|
30171
|
+
input: external_exports.array(external_exports.string()).default([]),
|
|
30172
|
+
output: external_exports.array(external_exports.string()).default([]),
|
|
30173
|
+
blocks: external_exports.array(CompositeBlockSchema).min(1)
|
|
30174
|
+
});
|
|
30175
|
+
|
|
30176
|
+
// ../composites/src/discovery.ts
|
|
30177
|
+
function discoverComposites(entries) {
|
|
30178
|
+
const registry2 = /* @__PURE__ */ new Map();
|
|
30179
|
+
const errors = [];
|
|
30180
|
+
for (const entry of entries) {
|
|
30181
|
+
let parsed;
|
|
30182
|
+
try {
|
|
30183
|
+
parsed = JSON.parse(entry.raw);
|
|
30184
|
+
} catch (e) {
|
|
30185
|
+
errors.push({
|
|
30186
|
+
source: entry.source,
|
|
30187
|
+
error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}`
|
|
30188
|
+
});
|
|
30189
|
+
continue;
|
|
30190
|
+
}
|
|
30191
|
+
const result = CompositeFileSchema.safeParse(parsed);
|
|
30192
|
+
if (!result.success) {
|
|
30193
|
+
errors.push({ source: entry.source, error: result.error.message });
|
|
30194
|
+
continue;
|
|
30195
|
+
}
|
|
30196
|
+
const { id } = result.data.manifest;
|
|
30197
|
+
if (registry2.has(id)) {
|
|
30198
|
+
errors.push({
|
|
30199
|
+
source: entry.source,
|
|
30200
|
+
error: `Duplicate composite id "${id}" (already registered)`
|
|
30201
|
+
});
|
|
30202
|
+
continue;
|
|
30203
|
+
}
|
|
30204
|
+
registry2.set(id, result.data);
|
|
30548
30205
|
}
|
|
30549
|
-
|
|
30206
|
+
reportUnresolvedReferences(registry2, errors);
|
|
30207
|
+
return { registry: registry2, errors };
|
|
30550
30208
|
}
|
|
30551
|
-
var
|
|
30552
|
-
function
|
|
30553
|
-
const
|
|
30554
|
-
|
|
30555
|
-
|
|
30556
|
-
|
|
30557
|
-
|
|
30209
|
+
var COMPOSITE_PREFIX = "composite:";
|
|
30210
|
+
function reportUnresolvedReferences(registry2, errors) {
|
|
30211
|
+
for (const [id, composite] of registry2) {
|
|
30212
|
+
const missing = /* @__PURE__ */ new Set();
|
|
30213
|
+
for (const block of composite.blocks) {
|
|
30214
|
+
if (block.type.startsWith(COMPOSITE_PREFIX)) {
|
|
30215
|
+
const refId = block.type.slice(COMPOSITE_PREFIX.length);
|
|
30216
|
+
if (!registry2.has(refId)) {
|
|
30217
|
+
missing.add(refId);
|
|
30218
|
+
}
|
|
30219
|
+
}
|
|
30220
|
+
}
|
|
30221
|
+
if (missing.size > 0) {
|
|
30222
|
+
errors.push({
|
|
30223
|
+
source: id,
|
|
30224
|
+
error: `Unresolved composite reference(s): ${[...missing].map((m3) => `composite:${m3}`).join(", ")}`
|
|
30225
|
+
});
|
|
30226
|
+
}
|
|
30558
30227
|
}
|
|
30559
|
-
|
|
30560
|
-
|
|
30561
|
-
|
|
30562
|
-
|
|
30228
|
+
}
|
|
30229
|
+
|
|
30230
|
+
// ../ui/src/primitives/typeahead.ts
|
|
30231
|
+
function fuzzyScore(query, target) {
|
|
30232
|
+
if (query.length === 0) return 1;
|
|
30233
|
+
if (query.length > target.length) return 0;
|
|
30234
|
+
const q = query.toLowerCase();
|
|
30235
|
+
const t = target.toLowerCase();
|
|
30236
|
+
let score = 0;
|
|
30237
|
+
let qi = 0;
|
|
30238
|
+
let prevMatchIndex = -2;
|
|
30239
|
+
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
|
30240
|
+
if (t[ti] === q[qi]) {
|
|
30241
|
+
score += 1;
|
|
30242
|
+
if (ti === prevMatchIndex + 1) {
|
|
30243
|
+
score += 2;
|
|
30244
|
+
}
|
|
30245
|
+
if (ti === 0 || t[ti - 1] === " " || t[ti - 1] === "-") {
|
|
30246
|
+
score += 3;
|
|
30247
|
+
}
|
|
30248
|
+
prevMatchIndex = ti;
|
|
30249
|
+
qi++;
|
|
30563
30250
|
}
|
|
30564
30251
|
}
|
|
30565
|
-
|
|
30566
|
-
|
|
30252
|
+
return qi === q.length ? score : 0;
|
|
30253
|
+
}
|
|
30254
|
+
|
|
30255
|
+
// ../composites/src/registry.ts
|
|
30256
|
+
var composites = /* @__PURE__ */ new Map();
|
|
30257
|
+
function register3(composite) {
|
|
30258
|
+
const { id } = composite.manifest;
|
|
30259
|
+
if (composites.has(id)) {
|
|
30260
|
+
throw new Error(`Composite "${id}" is already registered`);
|
|
30567
30261
|
}
|
|
30568
|
-
|
|
30262
|
+
composites.set(id, composite);
|
|
30569
30263
|
}
|
|
30570
|
-
|
|
30571
|
-
|
|
30572
|
-
return files.filter((file2) => {
|
|
30573
|
-
const gate = TARGET_GATED_COMPOSITE_FILES.find((g2) => file2.path.endsWith(g2.suffix));
|
|
30574
|
-
return gate ? gate.target === target : true;
|
|
30575
|
-
});
|
|
30264
|
+
function getAll2() {
|
|
30265
|
+
return Array.from(composites.values());
|
|
30576
30266
|
}
|
|
30577
|
-
|
|
30578
|
-
|
|
30579
|
-
if (!config2?.installed) return false;
|
|
30580
|
-
const { components, primitives, composites: composites2, rules, substrate } = config2.installed;
|
|
30581
|
-
const bucketByType = {
|
|
30582
|
-
ui: components,
|
|
30583
|
-
primitive: primitives,
|
|
30584
|
-
composite: composites2 ?? [],
|
|
30585
|
-
rule: rules ?? [],
|
|
30586
|
-
substrate: substrate ?? []
|
|
30587
|
-
};
|
|
30588
|
-
return bucketByType[item.type].includes(item.name);
|
|
30267
|
+
function getByCategory(category) {
|
|
30268
|
+
return getAll2().filter((c4) => c4.manifest.category === category);
|
|
30589
30269
|
}
|
|
30590
|
-
function
|
|
30591
|
-
if (
|
|
30592
|
-
|
|
30593
|
-
for (const file2 of filesToCheck) {
|
|
30594
|
-
if (fileExists(cwd, transformPath(file2.path, config2, cwd))) return true;
|
|
30270
|
+
function search(query) {
|
|
30271
|
+
if (query.length === 0) {
|
|
30272
|
+
return getAll2();
|
|
30595
30273
|
}
|
|
30596
|
-
return
|
|
30274
|
+
return getAll2().map((c4) => {
|
|
30275
|
+
const { name, keywords } = c4.manifest;
|
|
30276
|
+
const score = Math.max(fuzzyScore(query, name), ...keywords.map((k) => fuzzyScore(query, k)));
|
|
30277
|
+
return { c: c4, score };
|
|
30278
|
+
}).filter((entry) => entry.score > 0).sort((a2, b2) => b2.score - a2.score).map((entry) => entry.c);
|
|
30597
30279
|
}
|
|
30598
|
-
|
|
30599
|
-
|
|
30600
|
-
|
|
30601
|
-
|
|
30602
|
-
|
|
30603
|
-
|
|
30604
|
-
|
|
30605
|
-
|
|
30606
|
-
|
|
30607
|
-
const
|
|
30608
|
-
|
|
30609
|
-
|
|
30610
|
-
|
|
30611
|
-
|
|
30612
|
-
|
|
30613
|
-
primitive: installed.primitives,
|
|
30614
|
-
composite: installed.composites,
|
|
30615
|
-
rule: installed.rules,
|
|
30616
|
-
substrate: installed.substrate
|
|
30617
|
-
};
|
|
30618
|
-
for (const item of items) {
|
|
30619
|
-
const bucket = bucketByType[item.type];
|
|
30620
|
-
if (!bucket.includes(item.name)) bucket.push(item.name);
|
|
30280
|
+
|
|
30281
|
+
// ../composites/src/to-mdx.ts
|
|
30282
|
+
var import_escape_html = __toESM(require_escape_html(), 1);
|
|
30283
|
+
|
|
30284
|
+
// ../composites/src/discovery-node.ts
|
|
30285
|
+
import { readdir, readFile as readFile4 } from "fs/promises";
|
|
30286
|
+
import { join as join8 } from "path";
|
|
30287
|
+
var COMPOSITE_SUFFIX = ".composite.json";
|
|
30288
|
+
async function readDir(directory) {
|
|
30289
|
+
const entries = [];
|
|
30290
|
+
let dirents;
|
|
30291
|
+
try {
|
|
30292
|
+
dirents = await readdir(directory, { withFileTypes: true });
|
|
30293
|
+
} catch {
|
|
30294
|
+
return entries;
|
|
30621
30295
|
}
|
|
30622
|
-
|
|
30623
|
-
|
|
30624
|
-
|
|
30625
|
-
|
|
30626
|
-
|
|
30627
|
-
}
|
|
30628
|
-
|
|
30629
|
-
|
|
30630
|
-
}
|
|
30631
|
-
function transformPath(registryPath, config2, cwd = process.cwd()) {
|
|
30632
|
-
if (!config2) return registryPath;
|
|
30633
|
-
const replacements = [
|
|
30634
|
-
["components/ui/", config2.componentsPath, "components/ui"],
|
|
30635
|
-
["lib/primitives/", config2.primitivesPath, "lib/primitives"],
|
|
30636
|
-
["composites/", config2.compositesPath, "composites"],
|
|
30637
|
-
["rules/", config2.rulesPath, "lib/rules"]
|
|
30638
|
-
];
|
|
30639
|
-
for (const [prefix, field, fallback] of replacements) {
|
|
30640
|
-
if (registryPath.startsWith(prefix)) {
|
|
30641
|
-
return registryPath.replace(prefix, `${rootFor(field, cwd, fallback)}/`);
|
|
30296
|
+
for (const dirent of dirents) {
|
|
30297
|
+
const fullPath = join8(directory, dirent.name);
|
|
30298
|
+
if (dirent.isDirectory()) {
|
|
30299
|
+
entries.push(...await readDir(fullPath));
|
|
30300
|
+
continue;
|
|
30301
|
+
}
|
|
30302
|
+
if (dirent.isFile() && dirent.name.endsWith(COMPOSITE_SUFFIX)) {
|
|
30303
|
+
const raw = await readFile4(fullPath, "utf-8");
|
|
30304
|
+
entries.push({ raw, source: fullPath });
|
|
30642
30305
|
}
|
|
30643
30306
|
}
|
|
30644
|
-
return
|
|
30307
|
+
return entries;
|
|
30645
30308
|
}
|
|
30646
|
-
function
|
|
30647
|
-
|
|
30648
|
-
|
|
30649
|
-
|
|
30309
|
+
function nodeFsAdapter(...directories) {
|
|
30310
|
+
return async () => {
|
|
30311
|
+
const entries = [];
|
|
30312
|
+
for (const directory of directories) {
|
|
30313
|
+
entries.push(...await readDir(directory));
|
|
30314
|
+
}
|
|
30315
|
+
return entries;
|
|
30316
|
+
};
|
|
30650
30317
|
}
|
|
30651
|
-
function
|
|
30652
|
-
|
|
30318
|
+
async function discoverFromDirs(...directories) {
|
|
30319
|
+
const adapter = nodeFsAdapter(...directories);
|
|
30320
|
+
return discoverComposites(await adapter());
|
|
30653
30321
|
}
|
|
30654
|
-
|
|
30655
|
-
|
|
30656
|
-
|
|
30657
|
-
|
|
30658
|
-
|
|
30659
|
-
|
|
30660
|
-
|
|
30661
|
-
|
|
30662
|
-
|
|
30663
|
-
|
|
30664
|
-
|
|
30665
|
-
|
|
30666
|
-
|
|
30667
|
-
|
|
30668
|
-
|
|
30669
|
-
|
|
30670
|
-
|
|
30671
|
-
|
|
30672
|
-
|
|
30673
|
-
|
|
30674
|
-
const kindAlternation = substrateKinds.join("|");
|
|
30675
|
-
transformed = transformed.replace(
|
|
30676
|
-
new RegExp(`from\\s+['"](?:\\.\\./){1,2}(${kindAlternation})/([^'"]+)['"]`, "g"),
|
|
30677
|
-
"from '@/$1/$2'"
|
|
30678
|
-
);
|
|
30322
|
+
|
|
30323
|
+
// src/utils/workspaces.ts
|
|
30324
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
30325
|
+
import { basename, dirname as dirname3, join as join10, resolve as resolve4 } from "path";
|
|
30326
|
+
|
|
30327
|
+
// src/utils/discover.ts
|
|
30328
|
+
import { existsSync as existsSync4 } from "fs";
|
|
30329
|
+
import { dirname as dirname2, join as join9, resolve as resolve3 } from "path";
|
|
30330
|
+
function discoverProjectRoot(startDir) {
|
|
30331
|
+
let current = resolve3(startDir);
|
|
30332
|
+
for (; ; ) {
|
|
30333
|
+
const configPath2 = join9(current, ".rafters", "config.rafters.json");
|
|
30334
|
+
if (existsSync4(configPath2)) {
|
|
30335
|
+
return current;
|
|
30336
|
+
}
|
|
30337
|
+
const parent = dirname2(current);
|
|
30338
|
+
if (parent === current) {
|
|
30339
|
+
return null;
|
|
30340
|
+
}
|
|
30341
|
+
current = parent;
|
|
30679
30342
|
}
|
|
30680
|
-
transformed = transformed.replace(
|
|
30681
|
-
/from\s+['"]\.\.\/([^/'"]+)\/\1([^'"]*)['"]/g,
|
|
30682
|
-
`from '@/${aliasComponents}/$1$2'`
|
|
30683
|
-
);
|
|
30684
|
-
transformed = transformed.replace(
|
|
30685
|
-
/from\s+['"]\.\.\/([^'"]+)['"]/g,
|
|
30686
|
-
`from '@/${aliasComponents}/$1'`
|
|
30687
|
-
);
|
|
30688
|
-
return transformed;
|
|
30689
30343
|
}
|
|
30690
|
-
|
|
30691
|
-
|
|
30692
|
-
|
|
30693
|
-
let
|
|
30694
|
-
|
|
30695
|
-
|
|
30696
|
-
const
|
|
30697
|
-
|
|
30698
|
-
|
|
30699
|
-
|
|
30700
|
-
|
|
30701
|
-
|
|
30702
|
-
target,
|
|
30703
|
-
message: `No ${targetToExtension(target)} version available for ${item.name}. Installing React version.`
|
|
30704
|
-
});
|
|
30344
|
+
|
|
30345
|
+
// src/utils/workspaces.ts
|
|
30346
|
+
function findMonorepoRoot(startDir, boundary) {
|
|
30347
|
+
let current = resolve4(startDir);
|
|
30348
|
+
const stopAt = boundary ? resolve4(boundary) : null;
|
|
30349
|
+
for (; ; ) {
|
|
30350
|
+
const pnpmWorkspace = join10(current, "pnpm-workspace.yaml");
|
|
30351
|
+
if (existsSync5(pnpmWorkspace)) {
|
|
30352
|
+
const patterns = parsePnpmWorkspaceYaml(readFileSync2(pnpmWorkspace, "utf-8"));
|
|
30353
|
+
if (patterns.length > 0) {
|
|
30354
|
+
return { root: current, patterns };
|
|
30355
|
+
}
|
|
30705
30356
|
}
|
|
30706
|
-
const
|
|
30707
|
-
if (
|
|
30708
|
-
|
|
30709
|
-
|
|
30710
|
-
|
|
30357
|
+
const pkgJson = join10(current, "package.json");
|
|
30358
|
+
if (existsSync5(pkgJson)) {
|
|
30359
|
+
const patterns = parsePackageJsonWorkspaces(readFileSync2(pkgJson, "utf-8"));
|
|
30360
|
+
if (patterns.length > 0) {
|
|
30361
|
+
return { root: current, patterns };
|
|
30362
|
+
}
|
|
30711
30363
|
}
|
|
30712
|
-
|
|
30713
|
-
|
|
30364
|
+
if (stopAt && current === stopAt) return null;
|
|
30365
|
+
const parent = dirname3(current);
|
|
30366
|
+
if (parent === current) return null;
|
|
30367
|
+
current = parent;
|
|
30714
30368
|
}
|
|
30715
|
-
|
|
30716
|
-
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30721
|
-
|
|
30722
|
-
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
|
|
30726
|
-
|
|
30369
|
+
}
|
|
30370
|
+
function parsePnpmWorkspaceYaml(content) {
|
|
30371
|
+
const lines = content.split("\n");
|
|
30372
|
+
const patterns = [];
|
|
30373
|
+
let inPackages = false;
|
|
30374
|
+
for (const rawLine of lines) {
|
|
30375
|
+
const line = rawLine.replace(/#.*$/, "").trimEnd();
|
|
30376
|
+
if (!line.trim()) continue;
|
|
30377
|
+
if (/^packages\s*:/.test(line)) {
|
|
30378
|
+
inPackages = true;
|
|
30379
|
+
continue;
|
|
30380
|
+
}
|
|
30381
|
+
if (inPackages) {
|
|
30382
|
+
const itemMatch = line.match(/^\s*-\s*(?:["']?)([^"'\s]+)(?:["']?)\s*$/);
|
|
30383
|
+
if (itemMatch?.[1]) {
|
|
30384
|
+
patterns.push(itemMatch[1]);
|
|
30727
30385
|
continue;
|
|
30728
30386
|
}
|
|
30387
|
+
if (!/^\s/.test(line)) {
|
|
30388
|
+
inPackages = false;
|
|
30389
|
+
}
|
|
30729
30390
|
}
|
|
30730
|
-
await mkdir3(dirname(targetPath), { recursive: true });
|
|
30731
|
-
const fileType = item.type === "primitive" ? "primitive" : item.type === "substrate" ? "substrate" : "component";
|
|
30732
|
-
const transformedContent = transformFileContent(file2.content, config2, fileType, cwd, {
|
|
30733
|
-
substrateKinds,
|
|
30734
|
-
installPath: file2.path
|
|
30735
|
-
});
|
|
30736
|
-
await writeFile3(targetPath, transformedContent, "utf-8");
|
|
30737
|
-
installedFiles.push(projectPath2);
|
|
30738
30391
|
}
|
|
30739
|
-
return
|
|
30740
|
-
installed: installedFiles.length > 0,
|
|
30741
|
-
skipped,
|
|
30742
|
-
files: installedFiles
|
|
30743
|
-
};
|
|
30392
|
+
return patterns;
|
|
30744
30393
|
}
|
|
30745
|
-
|
|
30746
|
-
|
|
30747
|
-
|
|
30748
|
-
|
|
30749
|
-
|
|
30750
|
-
|
|
30751
|
-
|
|
30752
|
-
|
|
30753
|
-
|
|
30754
|
-
|
|
30755
|
-
|
|
30394
|
+
function parsePackageJsonWorkspaces(content) {
|
|
30395
|
+
try {
|
|
30396
|
+
const pkg = JSON.parse(content);
|
|
30397
|
+
const ws = pkg.workspaces;
|
|
30398
|
+
if (Array.isArray(ws)) {
|
|
30399
|
+
return ws.filter((p2) => typeof p2 === "string");
|
|
30400
|
+
}
|
|
30401
|
+
if (ws && typeof ws === "object" && Array.isArray(ws.packages)) {
|
|
30402
|
+
return ws.packages.filter((p2) => typeof p2 === "string");
|
|
30403
|
+
}
|
|
30404
|
+
return [];
|
|
30405
|
+
} catch {
|
|
30406
|
+
return [];
|
|
30756
30407
|
}
|
|
30757
|
-
|
|
30758
|
-
|
|
30759
|
-
|
|
30760
|
-
|
|
30761
|
-
|
|
30762
|
-
|
|
30763
|
-
|
|
30764
|
-
|
|
30765
|
-
|
|
30766
|
-
|
|
30767
|
-
|
|
30768
|
-
|
|
30769
|
-
console.log(` ${comp.name} ${comp.description ?? ""}`);
|
|
30408
|
+
}
|
|
30409
|
+
function expandPattern(monorepoRoot, pattern) {
|
|
30410
|
+
const trimmed = pattern.replace(/\/+$/, "");
|
|
30411
|
+
if (trimmed.endsWith("/*")) {
|
|
30412
|
+
const parentRel = trimmed.slice(0, -2);
|
|
30413
|
+
const parent = join10(monorepoRoot, parentRel);
|
|
30414
|
+
if (!existsSync5(parent)) return [];
|
|
30415
|
+
return readdirSync3(parent).map((entry) => join10(parent, entry)).filter((path) => {
|
|
30416
|
+
try {
|
|
30417
|
+
return statSync2(path).isDirectory();
|
|
30418
|
+
} catch {
|
|
30419
|
+
return false;
|
|
30770
30420
|
}
|
|
30771
|
-
|
|
30772
|
-
|
|
30773
|
-
|
|
30774
|
-
|
|
30775
|
-
|
|
30421
|
+
});
|
|
30422
|
+
}
|
|
30423
|
+
const literal2 = join10(monorepoRoot, trimmed);
|
|
30424
|
+
if (existsSync5(literal2)) {
|
|
30425
|
+
try {
|
|
30426
|
+
if (statSync2(literal2).isDirectory()) return [literal2];
|
|
30427
|
+
} catch {
|
|
30428
|
+
return [];
|
|
30429
|
+
}
|
|
30430
|
+
}
|
|
30431
|
+
return [];
|
|
30432
|
+
}
|
|
30433
|
+
function discoverWorkspaces(startDir = process.cwd(), options = {}) {
|
|
30434
|
+
const layout = findMonorepoRoot(startDir, options.boundary);
|
|
30435
|
+
if (!layout) {
|
|
30436
|
+
const single = discoverProjectRoot(startDir);
|
|
30437
|
+
if (!single) return [];
|
|
30438
|
+
if (options.boundary && !single.startsWith(resolve4(options.boundary))) return [];
|
|
30439
|
+
return [{ name: basename(single), root: single }];
|
|
30440
|
+
}
|
|
30441
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30442
|
+
const workspaces = [];
|
|
30443
|
+
for (const pattern of layout.patterns) {
|
|
30444
|
+
for (const dir of expandPattern(layout.root, pattern)) {
|
|
30445
|
+
if (seen.has(dir)) continue;
|
|
30446
|
+
seen.add(dir);
|
|
30447
|
+
const config2 = join10(dir, ".rafters", "config.rafters.json");
|
|
30448
|
+
if (existsSync5(config2)) {
|
|
30449
|
+
workspaces.push({ name: basename(dir), root: dir });
|
|
30776
30450
|
}
|
|
30777
30451
|
}
|
|
30778
|
-
return;
|
|
30779
30452
|
}
|
|
30780
|
-
const
|
|
30781
|
-
if (!
|
|
30782
|
-
|
|
30783
|
-
process.exitCode = 1;
|
|
30784
|
-
return;
|
|
30453
|
+
const rootConfig = join10(layout.root, ".rafters", "config.rafters.json");
|
|
30454
|
+
if (existsSync5(rootConfig) && !seen.has(layout.root)) {
|
|
30455
|
+
workspaces.unshift({ name: basename(layout.root), root: layout.root });
|
|
30785
30456
|
}
|
|
30786
|
-
|
|
30787
|
-
|
|
30457
|
+
return workspaces;
|
|
30458
|
+
}
|
|
30459
|
+
function pickDefaultWorkspace(workspaces, startDir = process.cwd()) {
|
|
30460
|
+
if (workspaces.length === 0) return null;
|
|
30461
|
+
const cwd = resolve4(startDir);
|
|
30462
|
+
const containing = workspaces.find((ws) => cwd === ws.root || cwd.startsWith(`${ws.root}/`));
|
|
30463
|
+
if (containing) return containing;
|
|
30464
|
+
if (workspaces.length === 1) return workspaces[0] ?? null;
|
|
30465
|
+
return null;
|
|
30466
|
+
}
|
|
30467
|
+
function resolveWorkspace(workspaces, defaultWorkspace, name) {
|
|
30468
|
+
if (name) {
|
|
30469
|
+
return workspaces.find((ws) => ws.name === name) ?? null;
|
|
30788
30470
|
}
|
|
30789
|
-
|
|
30790
|
-
|
|
30791
|
-
|
|
30792
|
-
|
|
30793
|
-
|
|
30794
|
-
|
|
30795
|
-
|
|
30796
|
-
|
|
30797
|
-
|
|
30798
|
-
|
|
30799
|
-
|
|
30800
|
-
|
|
30801
|
-
|
|
30802
|
-
|
|
30803
|
-
|
|
30471
|
+
return defaultWorkspace;
|
|
30472
|
+
}
|
|
30473
|
+
|
|
30474
|
+
// src/mcp/graph.ts
|
|
30475
|
+
var EXPAND_OP = "*";
|
|
30476
|
+
var PROBE_OP = "?";
|
|
30477
|
+
function assembleGraph(items) {
|
|
30478
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
30479
|
+
for (const item of items) {
|
|
30480
|
+
const kind = kindOf(item.type);
|
|
30481
|
+
if (kind === null) continue;
|
|
30482
|
+
const src = item.intelligence;
|
|
30483
|
+
const intel = {
|
|
30484
|
+
dos: src?.usagePatterns?.dos ?? [],
|
|
30485
|
+
nevers: src?.usagePatterns?.nevers ?? []
|
|
30486
|
+
};
|
|
30487
|
+
if (src?.cognitiveLoad !== void 0) intel.cognitiveLoad = src.cognitiveLoad;
|
|
30488
|
+
if (src?.semanticMeaning !== void 0) intel.semanticMeaning = src.semanticMeaning;
|
|
30489
|
+
if (src?.accessibility !== void 0) intel.accessibility = src.accessibility;
|
|
30490
|
+
if (src?.attentionEconomics !== void 0) intel.attentionEconomics = src.attentionEconomics;
|
|
30491
|
+
if (src?.trustBuilding !== void 0) intel.trustBuilding = src.trustBuilding;
|
|
30492
|
+
const gn = {
|
|
30493
|
+
id: item.name,
|
|
30494
|
+
kind,
|
|
30495
|
+
intel,
|
|
30496
|
+
facets: item.facets ?? {},
|
|
30497
|
+
composesWith: item.composites,
|
|
30498
|
+
parts: []
|
|
30499
|
+
};
|
|
30500
|
+
if (item.parent !== void 0) gn.parent = item.parent;
|
|
30501
|
+
nodes.set(item.name, gn);
|
|
30502
|
+
}
|
|
30503
|
+
for (const node of nodes.values()) {
|
|
30504
|
+
if (node.parent === void 0) continue;
|
|
30505
|
+
if (node.parent === node.id) {
|
|
30506
|
+
throw new Error(`graph: node "${node.id}" parent references itself`);
|
|
30804
30507
|
}
|
|
30805
|
-
if (
|
|
30806
|
-
|
|
30807
|
-
event: "add:untracked",
|
|
30808
|
-
components: untrackedNames,
|
|
30809
|
-
message: `Found ${untrackedNames.length} component(s) on disk the config never tracked: ${untrackedNames.join(", ")}. Refreshing and tracking them.`
|
|
30810
|
-
});
|
|
30508
|
+
if (!nodes.has(node.parent)) {
|
|
30509
|
+
throw new Error(`graph: node "${node.id}" parent names unknown id "${node.parent}"`);
|
|
30811
30510
|
}
|
|
30812
|
-
components = candidates;
|
|
30813
|
-
}
|
|
30814
|
-
if (folder === "composites" && components.length === 0) {
|
|
30815
|
-
components = ["composites"];
|
|
30816
|
-
}
|
|
30817
|
-
if (components.length === 0) {
|
|
30818
|
-
error46("No components specified. Usage: rafters add <component...>");
|
|
30819
|
-
process.exitCode = 1;
|
|
30820
|
-
return;
|
|
30821
30511
|
}
|
|
30822
|
-
|
|
30823
|
-
|
|
30824
|
-
|
|
30825
|
-
|
|
30826
|
-
|
|
30827
|
-
|
|
30828
|
-
|
|
30829
|
-
|
|
30830
|
-
|
|
30831
|
-
|
|
30832
|
-
if (folder === "composites") {
|
|
30833
|
-
if (!seen.has(itemName)) {
|
|
30834
|
-
const composite = await client.fetchComposite(itemName);
|
|
30835
|
-
seen.add(itemName);
|
|
30836
|
-
allItems.push(composite);
|
|
30837
|
-
for (const dep of composite.primitives) {
|
|
30838
|
-
if (!seen.has(dep)) {
|
|
30839
|
-
const depItems = await client.resolveDependencies(dep, seen);
|
|
30840
|
-
allItems.push(...depItems);
|
|
30841
|
-
}
|
|
30842
|
-
}
|
|
30843
|
-
}
|
|
30844
|
-
} else {
|
|
30845
|
-
const items = await client.resolveDependencies(itemName, seen);
|
|
30846
|
-
allItems.push(...items);
|
|
30847
|
-
}
|
|
30848
|
-
} catch (err) {
|
|
30849
|
-
if (err instanceof Error) {
|
|
30850
|
-
error46(err.message);
|
|
30851
|
-
} else {
|
|
30852
|
-
error46(`Failed to fetch "${itemName}"`);
|
|
30512
|
+
for (const node of nodes.values()) {
|
|
30513
|
+
if (node.parent === void 0) continue;
|
|
30514
|
+
const visited = /* @__PURE__ */ new Set();
|
|
30515
|
+
visited.add(node.id);
|
|
30516
|
+
let current = node.parent;
|
|
30517
|
+
while (current !== void 0) {
|
|
30518
|
+
if (visited.has(current)) {
|
|
30519
|
+
throw new Error(
|
|
30520
|
+
`graph: node "${node.id}" has a circular parent chain through "${current}"`
|
|
30521
|
+
);
|
|
30853
30522
|
}
|
|
30854
|
-
|
|
30855
|
-
|
|
30523
|
+
visited.add(current);
|
|
30524
|
+
current = nodes.get(current)?.parent;
|
|
30856
30525
|
}
|
|
30857
30526
|
}
|
|
30858
|
-
const
|
|
30859
|
-
|
|
30860
|
-
|
|
30861
|
-
)
|
|
30862
|
-
|
|
30863
|
-
const
|
|
30864
|
-
|
|
30865
|
-
|
|
30866
|
-
|
|
30867
|
-
const filteredItems = [];
|
|
30868
|
-
const target = getComponentTarget(config2);
|
|
30869
|
-
for (const item of allItems) {
|
|
30870
|
-
if (!options.overwrite && isAlreadyInstalled(config2, item)) {
|
|
30871
|
-
if (isInstalledOnDisk(config2, item, cwd)) {
|
|
30872
|
-
log({
|
|
30873
|
-
event: "add:skip",
|
|
30874
|
-
component: item.name,
|
|
30875
|
-
reason: "already installed"
|
|
30876
|
-
});
|
|
30877
|
-
skipped.push(item.name);
|
|
30878
|
-
continue;
|
|
30527
|
+
for (const node of nodes.values()) {
|
|
30528
|
+
if (node.parent === void 0) continue;
|
|
30529
|
+
const parentNode = nodes.get(node.parent);
|
|
30530
|
+
if (parentNode) parentNode.parts.push(node.id);
|
|
30531
|
+
}
|
|
30532
|
+
for (const node of nodes.values()) {
|
|
30533
|
+
for (const target of node.composesWith) {
|
|
30534
|
+
if (!nodes.has(target)) {
|
|
30535
|
+
throw new Error(`graph: node "${node.id}" composesWith unknown id "${target}"`);
|
|
30879
30536
|
}
|
|
30880
|
-
log({
|
|
30881
|
-
event: "add:warning",
|
|
30882
|
-
component: item.name,
|
|
30883
|
-
message: "Config tracks this as installed but no files found on disk -- reinstalling."
|
|
30884
|
-
});
|
|
30885
30537
|
}
|
|
30886
|
-
|
|
30887
|
-
|
|
30888
|
-
|
|
30889
|
-
|
|
30890
|
-
|
|
30891
|
-
|
|
30892
|
-
|
|
30893
|
-
|
|
30894
|
-
|
|
30895
|
-
|
|
30896
|
-
|
|
30897
|
-
|
|
30898
|
-
|
|
30899
|
-
|
|
30900
|
-
|
|
30901
|
-
|
|
30902
|
-
|
|
30903
|
-
|
|
30904
|
-
|
|
30905
|
-
|
|
30906
|
-
|
|
30907
|
-
|
|
30908
|
-
|
|
30909
|
-
|
|
30910
|
-
|
|
30911
|
-
|
|
30912
|
-
|
|
30913
|
-
component: item.name,
|
|
30914
|
-
message: err.message
|
|
30915
|
-
});
|
|
30916
|
-
}
|
|
30538
|
+
}
|
|
30539
|
+
return { nodes };
|
|
30540
|
+
}
|
|
30541
|
+
function kindOf(type2) {
|
|
30542
|
+
if (type2 === "composite") return "composite";
|
|
30543
|
+
if (type2 === "ui" || type2 === "primitive") return "component";
|
|
30544
|
+
return null;
|
|
30545
|
+
}
|
|
30546
|
+
function describe(addr, graph, target) {
|
|
30547
|
+
const parts = addr === "" ? [] : addr.split(".");
|
|
30548
|
+
if (parts.length === 0) {
|
|
30549
|
+
return {
|
|
30550
|
+
kinds: [
|
|
30551
|
+
{ addr: "components", type: "edge" },
|
|
30552
|
+
{ addr: "composites", type: "edge" }
|
|
30553
|
+
],
|
|
30554
|
+
nodeCount: graph.nodes.size
|
|
30555
|
+
};
|
|
30556
|
+
}
|
|
30557
|
+
const [head, ...rest] = parts;
|
|
30558
|
+
if (head === void 0) return { error: `cannot resolve: ${addr}` };
|
|
30559
|
+
if (head === "components" || head === "composites") {
|
|
30560
|
+
if (rest.length > 0) return { error: `cannot expand: ${addr}` };
|
|
30561
|
+
const kind = head === "components" ? "component" : "composite";
|
|
30562
|
+
const roster = [];
|
|
30563
|
+
for (const node2 of graph.nodes.values()) {
|
|
30564
|
+
if (node2.kind === kind) roster.push({ id: node2.id });
|
|
30917
30565
|
}
|
|
30566
|
+
return roster;
|
|
30918
30567
|
}
|
|
30919
|
-
|
|
30920
|
-
|
|
30921
|
-
|
|
30922
|
-
|
|
30923
|
-
|
|
30924
|
-
};
|
|
30925
|
-
let depsResult = emptyDeps;
|
|
30926
|
-
try {
|
|
30927
|
-
depsResult = await installRegistryDependencies(filteredItems, cwd, {
|
|
30928
|
-
target
|
|
30929
|
-
});
|
|
30930
|
-
} catch (err) {
|
|
30931
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
30932
|
-
log({
|
|
30933
|
-
event: "add:deps:install-failed",
|
|
30934
|
-
message: `Failed to process dependencies: ${message}`,
|
|
30935
|
-
dependencies: [],
|
|
30936
|
-
suggestion: "Check package.json and try installing dependencies manually."
|
|
30937
|
-
});
|
|
30568
|
+
if (parts[parts.length - 1] === PROBE_OP) {
|
|
30569
|
+
const baseAddr = parts.slice(0, -1).join(".");
|
|
30570
|
+
const result = describe(baseAddr, graph, target);
|
|
30571
|
+
if (typeof result === "object" && result !== null && "error" in result) return null;
|
|
30572
|
+
return result;
|
|
30938
30573
|
}
|
|
30939
|
-
if (
|
|
30940
|
-
|
|
30941
|
-
event: "add:dependencies",
|
|
30942
|
-
dependencies: depsResult.installed,
|
|
30943
|
-
devDependencies: depsResult.devInstalled,
|
|
30944
|
-
skipped: depsResult.skipped
|
|
30945
|
-
});
|
|
30574
|
+
if (head === EXPAND_OP || head === PROBE_OP) {
|
|
30575
|
+
return { error: `cannot expand at root: use 'components' or 'composites'` };
|
|
30946
30576
|
}
|
|
30947
|
-
|
|
30948
|
-
|
|
30949
|
-
|
|
30950
|
-
|
|
30951
|
-
|
|
30952
|
-
|
|
30953
|
-
|
|
30954
|
-
|
|
30955
|
-
primitivesPath: "lib/primitives",
|
|
30956
|
-
compositesPath: "composites",
|
|
30957
|
-
rulesPath: "lib/rules",
|
|
30958
|
-
cssPath: null,
|
|
30959
|
-
intent: "efficient",
|
|
30960
|
-
fonts: { path: null, imports: [] },
|
|
30961
|
-
exports: DEFAULT_EXPORTS,
|
|
30962
|
-
installed: { components: [], primitives: [], composites: [], rules: [] }
|
|
30963
|
-
};
|
|
30964
|
-
trackInstalled(newConfig, installedItems);
|
|
30965
|
-
await saveConfig(cwd, newConfig);
|
|
30966
|
-
await regenerateAfterInstall(cwd, newConfig);
|
|
30577
|
+
const node = graph.nodes.get(head);
|
|
30578
|
+
if (!node) return { error: `unknown node: ${head}` };
|
|
30579
|
+
if (rest.length === 0) return layer0(node, graph, target);
|
|
30580
|
+
if (rest[rest.length - 1] === EXPAND_OP) {
|
|
30581
|
+
const prefix = rest.slice(0, -1);
|
|
30582
|
+
if (prefix.length === 0) return expandNode(node, target);
|
|
30583
|
+
if (prefix.length === 1 && prefix[0] === "props") return expandProps(node, target);
|
|
30584
|
+
return { error: `cannot expand: ${addr}` };
|
|
30967
30585
|
}
|
|
30968
|
-
|
|
30969
|
-
|
|
30970
|
-
written: written.length,
|
|
30971
|
-
skipped: skipped.length,
|
|
30972
|
-
untracked: untrackedNames.length,
|
|
30973
|
-
failed: failed.length,
|
|
30974
|
-
components: written,
|
|
30975
|
-
skippedComponents: skipped,
|
|
30976
|
-
untrackedComponents: untrackedNames,
|
|
30977
|
-
failedComponents: failed
|
|
30978
|
-
});
|
|
30979
|
-
if (failed.length > 0) {
|
|
30980
|
-
process.exitCode = 1;
|
|
30586
|
+
if (rest.length === 1 && rest[0] === "composesWith") {
|
|
30587
|
+
return resolveEdges(node, graph, target);
|
|
30981
30588
|
}
|
|
30982
|
-
if (
|
|
30983
|
-
|
|
30984
|
-
|
|
30985
|
-
|
|
30986
|
-
|
|
30987
|
-
|
|
30988
|
-
|
|
30989
|
-
process.exitCode = 1;
|
|
30589
|
+
if (rest[0] === "props") {
|
|
30590
|
+
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
30591
|
+
const propName = rest[1];
|
|
30592
|
+
const prop = facet && propName !== void 0 ? facet.props[propName] : void 0;
|
|
30593
|
+
if (!prop) return { error: `cannot resolve: ${addr}` };
|
|
30594
|
+
if (rest.length === 2) return toAgentProp(prop);
|
|
30595
|
+
return { error: `cannot resolve: ${addr}` };
|
|
30990
30596
|
}
|
|
30597
|
+
return { error: `cannot resolve: ${addr}` };
|
|
30991
30598
|
}
|
|
30992
|
-
|
|
30993
|
-
|
|
30994
|
-
|
|
30995
|
-
|
|
30996
|
-
|
|
30997
|
-
// src/mcp/server.ts
|
|
30998
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
30999
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
31000
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
31001
|
-
|
|
31002
|
-
// src/version.ts
|
|
31003
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
31004
|
-
var UNKNOWN_VERSION = "0.0.0-unknown";
|
|
31005
|
-
function readVersion() {
|
|
31006
|
-
try {
|
|
31007
|
-
const parsed = JSON.parse(
|
|
31008
|
-
readFileSync2(new URL("../package.json", import.meta.url), "utf-8")
|
|
31009
|
-
);
|
|
31010
|
-
if (parsed !== null && typeof parsed === "object" && "version" in parsed) {
|
|
31011
|
-
const { version: version2 } = parsed;
|
|
31012
|
-
if (typeof version2 === "string" && version2.length > 0) return version2;
|
|
31013
|
-
}
|
|
31014
|
-
} catch {
|
|
30599
|
+
function toAgentProp(prop) {
|
|
30600
|
+
if (prop.type === "grammar") {
|
|
30601
|
+
const { vocab: _vocab, ...agentView } = prop;
|
|
30602
|
+
return agentView;
|
|
31015
30603
|
}
|
|
31016
|
-
return
|
|
30604
|
+
return prop;
|
|
31017
30605
|
}
|
|
31018
|
-
|
|
31019
|
-
|
|
31020
|
-
|
|
31021
|
-
|
|
31022
|
-
|
|
31023
|
-
|
|
31024
|
-
|
|
31025
|
-
|
|
31026
|
-
|
|
31027
|
-
// ../composites/src/built-in-rules/credentials.ts
|
|
31028
|
-
var credentials = external_exports.object({ email: email3, password: external_exports.string().min(8) });
|
|
31029
|
-
|
|
31030
|
-
// ../composites/src/built-in-rules/password.ts
|
|
31031
|
-
var password = external_exports.string().min(8);
|
|
31032
|
-
|
|
31033
|
-
// ../composites/src/built-in-rules/required.ts
|
|
31034
|
-
var required2 = external_exports.string().min(1);
|
|
31035
|
-
|
|
31036
|
-
// ../composites/src/built-in-rules/url.ts
|
|
31037
|
-
var url2 = external_exports.string().url();
|
|
31038
|
-
|
|
31039
|
-
// ../composites/src/manifest.ts
|
|
31040
|
-
var CompositeCategorySchema = external_exports.string().min(1);
|
|
31041
|
-
var AppliedRuleSchema = external_exports.union([
|
|
31042
|
-
external_exports.string(),
|
|
31043
|
-
external_exports.object({
|
|
31044
|
-
name: external_exports.string().min(1),
|
|
31045
|
-
config: external_exports.record(external_exports.string(), external_exports.unknown())
|
|
31046
|
-
})
|
|
31047
|
-
]);
|
|
31048
|
-
var CompositeBlockSchema = external_exports.object({
|
|
31049
|
-
id: external_exports.string().min(1),
|
|
31050
|
-
type: external_exports.string().min(1),
|
|
31051
|
-
/** Signatures this block consumes / produces (rule names) -- the typed I/O edges.
|
|
31052
|
-
* Within a composite, an output named X feeds any block input named X. */
|
|
31053
|
-
input: external_exports.array(external_exports.string()).optional(),
|
|
31054
|
-
output: external_exports.array(external_exports.string()).optional(),
|
|
31055
|
-
content: external_exports.unknown().optional(),
|
|
31056
|
-
children: external_exports.array(external_exports.string()).optional(),
|
|
31057
|
-
parentId: external_exports.string().optional(),
|
|
31058
|
-
meta: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
31059
|
-
rules: external_exports.array(AppliedRuleSchema).optional()
|
|
31060
|
-
});
|
|
31061
|
-
var UsagePatternsSchema2 = external_exports.object({
|
|
31062
|
-
do: external_exports.array(external_exports.string()),
|
|
31063
|
-
never: external_exports.array(external_exports.string())
|
|
31064
|
-
});
|
|
31065
|
-
var CompositeManifestSchema = external_exports.object({
|
|
31066
|
-
id: external_exports.string().min(1).regex(/^[a-z0-9-]+$/),
|
|
31067
|
-
name: external_exports.string().min(1),
|
|
31068
|
-
category: CompositeCategorySchema,
|
|
31069
|
-
description: external_exports.string(),
|
|
31070
|
-
keywords: external_exports.array(external_exports.string()),
|
|
31071
|
-
cognitiveLoad: external_exports.number().int().min(1).max(10),
|
|
31072
|
-
// Designer intent fields (optional for backwards compatibility)
|
|
31073
|
-
solves: external_exports.string().optional(),
|
|
31074
|
-
appliesWhen: external_exports.array(external_exports.string()).optional(),
|
|
31075
|
-
usagePatterns: UsagePatternsSchema2.optional()
|
|
31076
|
-
});
|
|
31077
|
-
var CompositeFileSchema = external_exports.object({
|
|
31078
|
-
manifest: CompositeManifestSchema,
|
|
31079
|
-
input: external_exports.array(external_exports.string()).default([]),
|
|
31080
|
-
output: external_exports.array(external_exports.string()).default([]),
|
|
31081
|
-
blocks: external_exports.array(CompositeBlockSchema).min(1)
|
|
31082
|
-
});
|
|
31083
|
-
|
|
31084
|
-
// ../composites/src/discovery.ts
|
|
31085
|
-
function discoverComposites(entries) {
|
|
31086
|
-
const registry2 = /* @__PURE__ */ new Map();
|
|
31087
|
-
const errors = [];
|
|
31088
|
-
for (const entry of entries) {
|
|
31089
|
-
let parsed;
|
|
31090
|
-
try {
|
|
31091
|
-
parsed = JSON.parse(entry.raw);
|
|
31092
|
-
} catch (e) {
|
|
31093
|
-
errors.push({
|
|
31094
|
-
source: entry.source,
|
|
31095
|
-
error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}`
|
|
31096
|
-
});
|
|
31097
|
-
continue;
|
|
31098
|
-
}
|
|
31099
|
-
const result = CompositeFileSchema.safeParse(parsed);
|
|
31100
|
-
if (!result.success) {
|
|
31101
|
-
errors.push({ source: entry.source, error: result.error.message });
|
|
31102
|
-
continue;
|
|
30606
|
+
function layer0(node, graph, target) {
|
|
30607
|
+
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
30608
|
+
const children = [];
|
|
30609
|
+
if (facet) {
|
|
30610
|
+
for (const [name, prop] of Object.entries(facet.props)) {
|
|
30611
|
+
const child = { addr: `${node.id}.props.${name}`, type: prop.type };
|
|
30612
|
+
if (prop.type === "deprecated") child.deprecatedFor = prop.deprecatedFor;
|
|
30613
|
+
children.push(child);
|
|
31103
30614
|
}
|
|
31104
|
-
|
|
31105
|
-
|
|
31106
|
-
|
|
31107
|
-
|
|
31108
|
-
|
|
31109
|
-
|
|
31110
|
-
|
|
30615
|
+
}
|
|
30616
|
+
if (node.composesWith.length > 0) {
|
|
30617
|
+
children.push({ addr: `${node.id}.composesWith`, type: "edge" });
|
|
30618
|
+
}
|
|
30619
|
+
for (const partId of node.parts) {
|
|
30620
|
+
children.push({ addr: partId, type: "part" });
|
|
30621
|
+
}
|
|
30622
|
+
const result = { id: node.id, kind: node.kind, intel: node.intel, children };
|
|
30623
|
+
if (facet) {
|
|
30624
|
+
result.snippet = facet.snippet;
|
|
30625
|
+
if (facet.slots !== void 0) result.slots = facet.slots;
|
|
30626
|
+
if (facet.events !== void 0) result.events = facet.events;
|
|
30627
|
+
}
|
|
30628
|
+
if (node.parent !== void 0) {
|
|
30629
|
+
result.parent = node.parent;
|
|
30630
|
+
const parentNode = graph.nodes.get(node.parent);
|
|
30631
|
+
if (parentNode) {
|
|
30632
|
+
const siblings = parentNode.parts.filter((id) => id !== node.id);
|
|
30633
|
+
if (siblings.length > 0) result.siblings = siblings;
|
|
31111
30634
|
}
|
|
31112
|
-
registry2.set(id, result.data);
|
|
31113
30635
|
}
|
|
31114
|
-
|
|
31115
|
-
return { registry: registry2, errors };
|
|
30636
|
+
return result;
|
|
31116
30637
|
}
|
|
31117
|
-
|
|
31118
|
-
|
|
31119
|
-
|
|
31120
|
-
|
|
31121
|
-
|
|
31122
|
-
|
|
31123
|
-
|
|
31124
|
-
|
|
31125
|
-
|
|
31126
|
-
|
|
31127
|
-
|
|
31128
|
-
|
|
31129
|
-
|
|
31130
|
-
|
|
31131
|
-
|
|
31132
|
-
|
|
31133
|
-
|
|
30638
|
+
function resolveEdges(node, graph, target) {
|
|
30639
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30640
|
+
const results = [];
|
|
30641
|
+
for (const edgeId of node.composesWith) {
|
|
30642
|
+
if (seen.has(edgeId)) continue;
|
|
30643
|
+
seen.add(edgeId);
|
|
30644
|
+
const edgeNode = graph.nodes.get(edgeId);
|
|
30645
|
+
if (edgeNode) results.push(layer0(edgeNode, graph, target));
|
|
30646
|
+
}
|
|
30647
|
+
return results;
|
|
30648
|
+
}
|
|
30649
|
+
function expandProps(node, target) {
|
|
30650
|
+
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
30651
|
+
const props = {};
|
|
30652
|
+
if (facet) {
|
|
30653
|
+
for (const [name, prop] of Object.entries(facet.props)) {
|
|
30654
|
+
props[name] = toAgentProp(prop);
|
|
31134
30655
|
}
|
|
31135
30656
|
}
|
|
30657
|
+
return { expanded: true, props };
|
|
31136
30658
|
}
|
|
31137
|
-
|
|
31138
|
-
|
|
31139
|
-
|
|
31140
|
-
if (
|
|
31141
|
-
|
|
31142
|
-
|
|
31143
|
-
const t = target.toLowerCase();
|
|
31144
|
-
let score = 0;
|
|
31145
|
-
let qi = 0;
|
|
31146
|
-
let prevMatchIndex = -2;
|
|
31147
|
-
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
|
31148
|
-
if (t[ti] === q[qi]) {
|
|
31149
|
-
score += 1;
|
|
31150
|
-
if (ti === prevMatchIndex + 1) {
|
|
31151
|
-
score += 2;
|
|
31152
|
-
}
|
|
31153
|
-
if (ti === 0 || t[ti - 1] === " " || t[ti - 1] === "-") {
|
|
31154
|
-
score += 3;
|
|
31155
|
-
}
|
|
31156
|
-
prevMatchIndex = ti;
|
|
31157
|
-
qi++;
|
|
30659
|
+
function expandNode(node, target) {
|
|
30660
|
+
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
30661
|
+
const props = {};
|
|
30662
|
+
if (facet) {
|
|
30663
|
+
for (const [name, prop] of Object.entries(facet.props)) {
|
|
30664
|
+
props[name] = toAgentProp(prop);
|
|
31158
30665
|
}
|
|
31159
30666
|
}
|
|
31160
|
-
|
|
30667
|
+
const result = {
|
|
30668
|
+
id: node.id,
|
|
30669
|
+
kind: node.kind,
|
|
30670
|
+
intel: node.intel,
|
|
30671
|
+
props
|
|
30672
|
+
};
|
|
30673
|
+
if (node.composesWith.length > 0) result.composesWith = node.composesWith;
|
|
30674
|
+
if (node.parts.length > 0) result.parts = node.parts;
|
|
30675
|
+
if (facet) {
|
|
30676
|
+
result.snippet = facet.snippet;
|
|
30677
|
+
if (facet.slots !== void 0) result.slots = facet.slots;
|
|
30678
|
+
if (facet.events !== void 0) result.events = facet.events;
|
|
30679
|
+
}
|
|
30680
|
+
return result;
|
|
31161
30681
|
}
|
|
31162
30682
|
|
|
31163
|
-
//
|
|
31164
|
-
var
|
|
31165
|
-
|
|
31166
|
-
|
|
31167
|
-
|
|
31168
|
-
|
|
30683
|
+
// src/mcp/intent.ts
|
|
30684
|
+
var INTENT_TAGS = {
|
|
30685
|
+
modal: ["attention", "blocking", "above-all", "focus-trap"],
|
|
30686
|
+
alert: ["attention", "inline", "passive"],
|
|
30687
|
+
tooltip: ["hint", "passive", "inline", "hover"]
|
|
30688
|
+
};
|
|
30689
|
+
var INTENT_AXES = [
|
|
30690
|
+
{
|
|
30691
|
+
tag: "above-all",
|
|
30692
|
+
keywords: [
|
|
30693
|
+
"above everything",
|
|
30694
|
+
"above all",
|
|
30695
|
+
"on top of everything",
|
|
30696
|
+
"sits on top",
|
|
30697
|
+
"on top",
|
|
30698
|
+
"over everything",
|
|
30699
|
+
"over all",
|
|
30700
|
+
"topmost",
|
|
30701
|
+
"highest layer"
|
|
30702
|
+
]
|
|
30703
|
+
},
|
|
30704
|
+
{
|
|
30705
|
+
tag: "blocking",
|
|
30706
|
+
keywords: [
|
|
30707
|
+
"above everything",
|
|
30708
|
+
"on top of everything",
|
|
30709
|
+
"over everything",
|
|
30710
|
+
"block",
|
|
30711
|
+
"blocking",
|
|
30712
|
+
"must dismiss",
|
|
30713
|
+
"interrupt",
|
|
30714
|
+
"take over",
|
|
30715
|
+
"stops everything",
|
|
30716
|
+
"requires a response",
|
|
30717
|
+
"can't ignore",
|
|
30718
|
+
"cannot ignore"
|
|
30719
|
+
]
|
|
30720
|
+
},
|
|
30721
|
+
{
|
|
30722
|
+
tag: "inline",
|
|
30723
|
+
keywords: ["inline", "in place", "in the flow", "within the page", "non-blocking"]
|
|
30724
|
+
},
|
|
30725
|
+
{
|
|
30726
|
+
tag: "passive",
|
|
30727
|
+
keywords: ["passive", "non-intrusive", "subtle", "quietly", "in the background"]
|
|
30728
|
+
},
|
|
30729
|
+
{
|
|
30730
|
+
tag: "attention",
|
|
30731
|
+
keywords: ["attention", "notice", "get noticed", "important message"]
|
|
30732
|
+
},
|
|
30733
|
+
{
|
|
30734
|
+
tag: "hint",
|
|
30735
|
+
keywords: ["hint", "a tip", "explain", "extra info", "more info"]
|
|
30736
|
+
},
|
|
30737
|
+
{
|
|
30738
|
+
tag: "hover",
|
|
30739
|
+
keywords: ["hover", "on hover", "mouse over"]
|
|
31169
30740
|
}
|
|
31170
|
-
|
|
31171
|
-
|
|
31172
|
-
|
|
31173
|
-
return Array.from(composites.values());
|
|
30741
|
+
];
|
|
30742
|
+
function isNaturalLanguageQuery(input) {
|
|
30743
|
+
return input.includes(" ");
|
|
31174
30744
|
}
|
|
31175
|
-
function
|
|
31176
|
-
|
|
30745
|
+
function matchIntent(query, graph) {
|
|
30746
|
+
const normalized = query.toLowerCase();
|
|
30747
|
+
const matchedTags = collectMatchedTags(normalized);
|
|
30748
|
+
if (matchedTags.size === 0) return noMatch();
|
|
30749
|
+
const winner = selectUse(matchedTags, graph);
|
|
30750
|
+
if (winner === void 0) return noMatch();
|
|
30751
|
+
const winningTags = winner.tags.filter((tag) => matchedTags.has(tag));
|
|
30752
|
+
const decisive = new Set(winningTags);
|
|
30753
|
+
const nearMiss = selectNearMiss(winner, decisive, matchedTags, graph);
|
|
30754
|
+
if (nearMiss === void 0) return noMatch();
|
|
30755
|
+
const use = describeNode(winner.id, graph);
|
|
30756
|
+
const not = describeNode(nearMiss.id, graph);
|
|
30757
|
+
if (use === void 0 || not === void 0) return noMatch();
|
|
30758
|
+
const divergingTags = nearMiss.tags.filter((tag) => !winner.tags.includes(tag));
|
|
30759
|
+
const because = `${winner.id} is ${winningTags.join("/")}; ${nearMiss.id} is ${divergingTags.join("/")}`;
|
|
30760
|
+
return { use, not, because };
|
|
31177
30761
|
}
|
|
31178
|
-
function
|
|
31179
|
-
|
|
31180
|
-
|
|
30762
|
+
function collectMatchedTags(normalizedQuery) {
|
|
30763
|
+
const matched = /* @__PURE__ */ new Set();
|
|
30764
|
+
for (const axis of INTENT_AXES) {
|
|
30765
|
+
for (const keyword of axis.keywords) {
|
|
30766
|
+
if (normalizedQuery.includes(keyword)) {
|
|
30767
|
+
matched.add(axis.tag);
|
|
30768
|
+
break;
|
|
30769
|
+
}
|
|
30770
|
+
}
|
|
31181
30771
|
}
|
|
31182
|
-
return
|
|
31183
|
-
const { name, keywords } = c4.manifest;
|
|
31184
|
-
const score = Math.max(fuzzyScore(query, name), ...keywords.map((k) => fuzzyScore(query, k)));
|
|
31185
|
-
return { c: c4, score };
|
|
31186
|
-
}).filter((entry) => entry.score > 0).sort((a2, b2) => b2.score - a2.score).map((entry) => entry.c);
|
|
30772
|
+
return matched;
|
|
31187
30773
|
}
|
|
31188
|
-
|
|
31189
|
-
|
|
31190
|
-
|
|
31191
|
-
|
|
31192
|
-
|
|
31193
|
-
|
|
31194
|
-
|
|
31195
|
-
|
|
31196
|
-
|
|
31197
|
-
const entries = [];
|
|
31198
|
-
let dirents;
|
|
31199
|
-
try {
|
|
31200
|
-
dirents = await readdir(directory, { withFileTypes: true });
|
|
31201
|
-
} catch {
|
|
31202
|
-
return entries;
|
|
31203
|
-
}
|
|
31204
|
-
for (const dirent of dirents) {
|
|
31205
|
-
const fullPath = join9(directory, dirent.name);
|
|
31206
|
-
if (dirent.isDirectory()) {
|
|
31207
|
-
entries.push(...await readDir(fullPath));
|
|
31208
|
-
continue;
|
|
31209
|
-
}
|
|
31210
|
-
if (dirent.isFile() && dirent.name.endsWith(COMPOSITE_SUFFIX)) {
|
|
31211
|
-
const raw = await readFile5(fullPath, "utf-8");
|
|
31212
|
-
entries.push({ raw, source: fullPath });
|
|
30774
|
+
function selectUse(matchedTags, graph) {
|
|
30775
|
+
let best;
|
|
30776
|
+
let bestScore = 0;
|
|
30777
|
+
for (const [id, tags] of Object.entries(INTENT_TAGS)) {
|
|
30778
|
+
if (!graph.nodes.has(id)) continue;
|
|
30779
|
+
const score = tags.reduce((sum, tag) => matchedTags.has(tag) ? sum + 1 : sum, 0);
|
|
30780
|
+
if (score > bestScore) {
|
|
30781
|
+
bestScore = score;
|
|
30782
|
+
best = { id, tags };
|
|
31213
30783
|
}
|
|
31214
30784
|
}
|
|
31215
|
-
return
|
|
30785
|
+
return best;
|
|
31216
30786
|
}
|
|
31217
|
-
function
|
|
31218
|
-
|
|
31219
|
-
|
|
31220
|
-
|
|
31221
|
-
|
|
30787
|
+
function selectNearMiss(use, decisive, matchedTags, graph) {
|
|
30788
|
+
let best;
|
|
30789
|
+
let bestOverlap = 0;
|
|
30790
|
+
for (const [id, tags] of Object.entries(INTENT_TAGS)) {
|
|
30791
|
+
if (id === use.id || !graph.nodes.has(id)) continue;
|
|
30792
|
+
if (tags.some((tag) => decisive.has(tag))) continue;
|
|
30793
|
+
const overlap = tags.filter((tag) => use.tags.includes(tag));
|
|
30794
|
+
const sharesOutsideMatched = overlap.some((tag) => !matchedTags.has(tag));
|
|
30795
|
+
if (!sharesOutsideMatched) continue;
|
|
30796
|
+
if (overlap.length > bestOverlap) {
|
|
30797
|
+
bestOverlap = overlap.length;
|
|
30798
|
+
best = { id, tags };
|
|
31222
30799
|
}
|
|
31223
|
-
|
|
31224
|
-
|
|
30800
|
+
}
|
|
30801
|
+
return best;
|
|
31225
30802
|
}
|
|
31226
|
-
|
|
31227
|
-
const
|
|
31228
|
-
|
|
30803
|
+
function describeNode(id, graph) {
|
|
30804
|
+
const result = describe(id, graph);
|
|
30805
|
+
if (result !== null && !Array.isArray(result) && "children" in result) return result;
|
|
30806
|
+
return void 0;
|
|
31229
30807
|
}
|
|
31230
|
-
|
|
31231
|
-
|
|
31232
|
-
import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
31233
|
-
import { basename, dirname as dirname3, join as join11, resolve as resolve4 } from "path";
|
|
31234
|
-
|
|
31235
|
-
// src/utils/discover.ts
|
|
31236
|
-
import { existsSync as existsSync5 } from "fs";
|
|
31237
|
-
import { dirname as dirname2, join as join10, resolve as resolve3 } from "path";
|
|
31238
|
-
function discoverProjectRoot(startDir) {
|
|
31239
|
-
let current = resolve3(startDir);
|
|
31240
|
-
for (; ; ) {
|
|
31241
|
-
const configPath2 = join10(current, ".rafters", "config.rafters.json");
|
|
31242
|
-
if (existsSync5(configPath2)) {
|
|
31243
|
-
return current;
|
|
31244
|
-
}
|
|
31245
|
-
const parent = dirname2(current);
|
|
31246
|
-
if (parent === current) {
|
|
31247
|
-
return null;
|
|
31248
|
-
}
|
|
31249
|
-
current = parent;
|
|
31250
|
-
}
|
|
31251
|
-
}
|
|
31252
|
-
|
|
31253
|
-
// src/utils/workspaces.ts
|
|
31254
|
-
function findMonorepoRoot(startDir, boundary) {
|
|
31255
|
-
let current = resolve4(startDir);
|
|
31256
|
-
const stopAt = boundary ? resolve4(boundary) : null;
|
|
31257
|
-
for (; ; ) {
|
|
31258
|
-
const pnpmWorkspace = join11(current, "pnpm-workspace.yaml");
|
|
31259
|
-
if (existsSync6(pnpmWorkspace)) {
|
|
31260
|
-
const patterns = parsePnpmWorkspaceYaml(readFileSync3(pnpmWorkspace, "utf-8"));
|
|
31261
|
-
if (patterns.length > 0) {
|
|
31262
|
-
return { root: current, patterns };
|
|
31263
|
-
}
|
|
31264
|
-
}
|
|
31265
|
-
const pkgJson = join11(current, "package.json");
|
|
31266
|
-
if (existsSync6(pkgJson)) {
|
|
31267
|
-
const patterns = parsePackageJsonWorkspaces(readFileSync3(pkgJson, "utf-8"));
|
|
31268
|
-
if (patterns.length > 0) {
|
|
31269
|
-
return { root: current, patterns };
|
|
31270
|
-
}
|
|
31271
|
-
}
|
|
31272
|
-
if (stopAt && current === stopAt) return null;
|
|
31273
|
-
const parent = dirname3(current);
|
|
31274
|
-
if (parent === current) return null;
|
|
31275
|
-
current = parent;
|
|
31276
|
-
}
|
|
31277
|
-
}
|
|
31278
|
-
function parsePnpmWorkspaceYaml(content) {
|
|
31279
|
-
const lines = content.split("\n");
|
|
31280
|
-
const patterns = [];
|
|
31281
|
-
let inPackages = false;
|
|
31282
|
-
for (const rawLine of lines) {
|
|
31283
|
-
const line = rawLine.replace(/#.*$/, "").trimEnd();
|
|
31284
|
-
if (!line.trim()) continue;
|
|
31285
|
-
if (/^packages\s*:/.test(line)) {
|
|
31286
|
-
inPackages = true;
|
|
31287
|
-
continue;
|
|
31288
|
-
}
|
|
31289
|
-
if (inPackages) {
|
|
31290
|
-
const itemMatch = line.match(/^\s*-\s*(?:["']?)([^"'\s]+)(?:["']?)\s*$/);
|
|
31291
|
-
if (itemMatch?.[1]) {
|
|
31292
|
-
patterns.push(itemMatch[1]);
|
|
31293
|
-
continue;
|
|
31294
|
-
}
|
|
31295
|
-
if (!/^\s/.test(line)) {
|
|
31296
|
-
inPackages = false;
|
|
31297
|
-
}
|
|
31298
|
-
}
|
|
31299
|
-
}
|
|
31300
|
-
return patterns;
|
|
31301
|
-
}
|
|
31302
|
-
function parsePackageJsonWorkspaces(content) {
|
|
31303
|
-
try {
|
|
31304
|
-
const pkg = JSON.parse(content);
|
|
31305
|
-
const ws = pkg.workspaces;
|
|
31306
|
-
if (Array.isArray(ws)) {
|
|
31307
|
-
return ws.filter((p2) => typeof p2 === "string");
|
|
31308
|
-
}
|
|
31309
|
-
if (ws && typeof ws === "object" && Array.isArray(ws.packages)) {
|
|
31310
|
-
return ws.packages.filter((p2) => typeof p2 === "string");
|
|
31311
|
-
}
|
|
31312
|
-
return [];
|
|
31313
|
-
} catch {
|
|
31314
|
-
return [];
|
|
31315
|
-
}
|
|
31316
|
-
}
|
|
31317
|
-
function expandPattern(monorepoRoot, pattern) {
|
|
31318
|
-
const trimmed = pattern.replace(/\/+$/, "");
|
|
31319
|
-
if (trimmed.endsWith("/*")) {
|
|
31320
|
-
const parentRel = trimmed.slice(0, -2);
|
|
31321
|
-
const parent = join11(monorepoRoot, parentRel);
|
|
31322
|
-
if (!existsSync6(parent)) return [];
|
|
31323
|
-
return readdirSync3(parent).map((entry) => join11(parent, entry)).filter((path) => {
|
|
31324
|
-
try {
|
|
31325
|
-
return statSync2(path).isDirectory();
|
|
31326
|
-
} catch {
|
|
31327
|
-
return false;
|
|
31328
|
-
}
|
|
31329
|
-
});
|
|
31330
|
-
}
|
|
31331
|
-
const literal2 = join11(monorepoRoot, trimmed);
|
|
31332
|
-
if (existsSync6(literal2)) {
|
|
31333
|
-
try {
|
|
31334
|
-
if (statSync2(literal2).isDirectory()) return [literal2];
|
|
31335
|
-
} catch {
|
|
31336
|
-
return [];
|
|
31337
|
-
}
|
|
31338
|
-
}
|
|
31339
|
-
return [];
|
|
31340
|
-
}
|
|
31341
|
-
function discoverWorkspaces(startDir = process.cwd(), options = {}) {
|
|
31342
|
-
const layout = findMonorepoRoot(startDir, options.boundary);
|
|
31343
|
-
if (!layout) {
|
|
31344
|
-
const single = discoverProjectRoot(startDir);
|
|
31345
|
-
if (!single) return [];
|
|
31346
|
-
if (options.boundary && !single.startsWith(resolve4(options.boundary))) return [];
|
|
31347
|
-
return [{ name: basename(single), root: single }];
|
|
31348
|
-
}
|
|
31349
|
-
const seen = /* @__PURE__ */ new Set();
|
|
31350
|
-
const workspaces = [];
|
|
31351
|
-
for (const pattern of layout.patterns) {
|
|
31352
|
-
for (const dir of expandPattern(layout.root, pattern)) {
|
|
31353
|
-
if (seen.has(dir)) continue;
|
|
31354
|
-
seen.add(dir);
|
|
31355
|
-
const config2 = join11(dir, ".rafters", "config.rafters.json");
|
|
31356
|
-
if (existsSync6(config2)) {
|
|
31357
|
-
workspaces.push({ name: basename(dir), root: dir });
|
|
31358
|
-
}
|
|
31359
|
-
}
|
|
31360
|
-
}
|
|
31361
|
-
const rootConfig = join11(layout.root, ".rafters", "config.rafters.json");
|
|
31362
|
-
if (existsSync6(rootConfig) && !seen.has(layout.root)) {
|
|
31363
|
-
workspaces.unshift({ name: basename(layout.root), root: layout.root });
|
|
31364
|
-
}
|
|
31365
|
-
return workspaces;
|
|
31366
|
-
}
|
|
31367
|
-
function pickDefaultWorkspace(workspaces, startDir = process.cwd()) {
|
|
31368
|
-
if (workspaces.length === 0) return null;
|
|
31369
|
-
const cwd = resolve4(startDir);
|
|
31370
|
-
const containing = workspaces.find((ws) => cwd === ws.root || cwd.startsWith(`${ws.root}/`));
|
|
31371
|
-
if (containing) return containing;
|
|
31372
|
-
if (workspaces.length === 1) return workspaces[0] ?? null;
|
|
31373
|
-
return null;
|
|
31374
|
-
}
|
|
31375
|
-
function resolveWorkspace(workspaces, defaultWorkspace, name) {
|
|
31376
|
-
if (name) {
|
|
31377
|
-
return workspaces.find((ws) => ws.name === name) ?? null;
|
|
31378
|
-
}
|
|
31379
|
-
return defaultWorkspace;
|
|
31380
|
-
}
|
|
31381
|
-
|
|
31382
|
-
// src/mcp/graph.ts
|
|
31383
|
-
var EXPAND_OP = "*";
|
|
31384
|
-
var PROBE_OP = "?";
|
|
31385
|
-
function assembleGraph(items) {
|
|
31386
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
31387
|
-
for (const item of items) {
|
|
31388
|
-
const kind = kindOf(item.type);
|
|
31389
|
-
if (kind === null) continue;
|
|
31390
|
-
const src = item.intelligence;
|
|
31391
|
-
const intel = {
|
|
31392
|
-
dos: src?.usagePatterns?.dos ?? [],
|
|
31393
|
-
nevers: src?.usagePatterns?.nevers ?? []
|
|
31394
|
-
};
|
|
31395
|
-
if (src?.cognitiveLoad !== void 0) intel.cognitiveLoad = src.cognitiveLoad;
|
|
31396
|
-
if (src?.semanticMeaning !== void 0) intel.semanticMeaning = src.semanticMeaning;
|
|
31397
|
-
if (src?.accessibility !== void 0) intel.accessibility = src.accessibility;
|
|
31398
|
-
if (src?.attentionEconomics !== void 0) intel.attentionEconomics = src.attentionEconomics;
|
|
31399
|
-
if (src?.trustBuilding !== void 0) intel.trustBuilding = src.trustBuilding;
|
|
31400
|
-
const gn = {
|
|
31401
|
-
id: item.name,
|
|
31402
|
-
kind,
|
|
31403
|
-
intel,
|
|
31404
|
-
facets: item.facets ?? {},
|
|
31405
|
-
composesWith: item.composites,
|
|
31406
|
-
parts: []
|
|
31407
|
-
};
|
|
31408
|
-
if (item.parent !== void 0) gn.parent = item.parent;
|
|
31409
|
-
nodes.set(item.name, gn);
|
|
31410
|
-
}
|
|
31411
|
-
for (const node of nodes.values()) {
|
|
31412
|
-
if (node.parent === void 0) continue;
|
|
31413
|
-
if (node.parent === node.id) {
|
|
31414
|
-
throw new Error(`graph: node "${node.id}" parent references itself`);
|
|
31415
|
-
}
|
|
31416
|
-
if (!nodes.has(node.parent)) {
|
|
31417
|
-
throw new Error(`graph: node "${node.id}" parent names unknown id "${node.parent}"`);
|
|
31418
|
-
}
|
|
31419
|
-
}
|
|
31420
|
-
for (const node of nodes.values()) {
|
|
31421
|
-
if (node.parent === void 0) continue;
|
|
31422
|
-
const visited = /* @__PURE__ */ new Set();
|
|
31423
|
-
visited.add(node.id);
|
|
31424
|
-
let current = node.parent;
|
|
31425
|
-
while (current !== void 0) {
|
|
31426
|
-
if (visited.has(current)) {
|
|
31427
|
-
throw new Error(
|
|
31428
|
-
`graph: node "${node.id}" has a circular parent chain through "${current}"`
|
|
31429
|
-
);
|
|
31430
|
-
}
|
|
31431
|
-
visited.add(current);
|
|
31432
|
-
current = nodes.get(current)?.parent;
|
|
31433
|
-
}
|
|
31434
|
-
}
|
|
31435
|
-
for (const node of nodes.values()) {
|
|
31436
|
-
if (node.parent === void 0) continue;
|
|
31437
|
-
const parentNode = nodes.get(node.parent);
|
|
31438
|
-
if (parentNode) parentNode.parts.push(node.id);
|
|
31439
|
-
}
|
|
31440
|
-
for (const node of nodes.values()) {
|
|
31441
|
-
for (const target of node.composesWith) {
|
|
31442
|
-
if (!nodes.has(target)) {
|
|
31443
|
-
throw new Error(`graph: node "${node.id}" composesWith unknown id "${target}"`);
|
|
31444
|
-
}
|
|
31445
|
-
}
|
|
31446
|
-
}
|
|
31447
|
-
return { nodes };
|
|
31448
|
-
}
|
|
31449
|
-
function kindOf(type2) {
|
|
31450
|
-
if (type2 === "composite") return "composite";
|
|
31451
|
-
if (type2 === "ui" || type2 === "primitive") return "component";
|
|
31452
|
-
return null;
|
|
31453
|
-
}
|
|
31454
|
-
function describe(addr, graph, target) {
|
|
31455
|
-
const parts = addr === "" ? [] : addr.split(".");
|
|
31456
|
-
if (parts.length === 0) {
|
|
31457
|
-
return {
|
|
31458
|
-
kinds: [
|
|
31459
|
-
{ addr: "components", type: "edge" },
|
|
31460
|
-
{ addr: "composites", type: "edge" }
|
|
31461
|
-
],
|
|
31462
|
-
nodeCount: graph.nodes.size
|
|
31463
|
-
};
|
|
31464
|
-
}
|
|
31465
|
-
const [head, ...rest] = parts;
|
|
31466
|
-
if (head === void 0) return { error: `cannot resolve: ${addr}` };
|
|
31467
|
-
if (head === "components" || head === "composites") {
|
|
31468
|
-
if (rest.length > 0) return { error: `cannot expand: ${addr}` };
|
|
31469
|
-
const kind = head === "components" ? "component" : "composite";
|
|
31470
|
-
const roster = [];
|
|
31471
|
-
for (const node2 of graph.nodes.values()) {
|
|
31472
|
-
if (node2.kind === kind) roster.push({ id: node2.id });
|
|
31473
|
-
}
|
|
31474
|
-
return roster;
|
|
31475
|
-
}
|
|
31476
|
-
if (parts[parts.length - 1] === PROBE_OP) {
|
|
31477
|
-
const baseAddr = parts.slice(0, -1).join(".");
|
|
31478
|
-
const result = describe(baseAddr, graph, target);
|
|
31479
|
-
if (typeof result === "object" && result !== null && "error" in result) return null;
|
|
31480
|
-
return result;
|
|
31481
|
-
}
|
|
31482
|
-
if (head === EXPAND_OP || head === PROBE_OP) {
|
|
31483
|
-
return { error: `cannot expand at root: use 'components' or 'composites'` };
|
|
31484
|
-
}
|
|
31485
|
-
const node = graph.nodes.get(head);
|
|
31486
|
-
if (!node) return { error: `unknown node: ${head}` };
|
|
31487
|
-
if (rest.length === 0) return layer0(node, graph, target);
|
|
31488
|
-
if (rest[rest.length - 1] === EXPAND_OP) {
|
|
31489
|
-
const prefix = rest.slice(0, -1);
|
|
31490
|
-
if (prefix.length === 0) return expandNode(node, target);
|
|
31491
|
-
if (prefix.length === 1 && prefix[0] === "props") return expandProps(node, target);
|
|
31492
|
-
return { error: `cannot expand: ${addr}` };
|
|
31493
|
-
}
|
|
31494
|
-
if (rest.length === 1 && rest[0] === "composesWith") {
|
|
31495
|
-
return resolveEdges(node, graph, target);
|
|
31496
|
-
}
|
|
31497
|
-
if (rest[0] === "props") {
|
|
31498
|
-
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
31499
|
-
const propName = rest[1];
|
|
31500
|
-
const prop = facet && propName !== void 0 ? facet.props[propName] : void 0;
|
|
31501
|
-
if (!prop) return { error: `cannot resolve: ${addr}` };
|
|
31502
|
-
if (rest.length === 2) return toAgentProp(prop);
|
|
31503
|
-
return { error: `cannot resolve: ${addr}` };
|
|
31504
|
-
}
|
|
31505
|
-
return { error: `cannot resolve: ${addr}` };
|
|
31506
|
-
}
|
|
31507
|
-
function toAgentProp(prop) {
|
|
31508
|
-
if (prop.type === "grammar") {
|
|
31509
|
-
const { vocab: _vocab, ...agentView } = prop;
|
|
31510
|
-
return agentView;
|
|
31511
|
-
}
|
|
31512
|
-
return prop;
|
|
31513
|
-
}
|
|
31514
|
-
function layer0(node, graph, target) {
|
|
31515
|
-
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
31516
|
-
const children = [];
|
|
31517
|
-
if (facet) {
|
|
31518
|
-
for (const [name, prop] of Object.entries(facet.props)) {
|
|
31519
|
-
const child = { addr: `${node.id}.props.${name}`, type: prop.type };
|
|
31520
|
-
if (prop.type === "deprecated") child.deprecatedFor = prop.deprecatedFor;
|
|
31521
|
-
children.push(child);
|
|
31522
|
-
}
|
|
31523
|
-
}
|
|
31524
|
-
if (node.composesWith.length > 0) {
|
|
31525
|
-
children.push({ addr: `${node.id}.composesWith`, type: "edge" });
|
|
31526
|
-
}
|
|
31527
|
-
for (const partId of node.parts) {
|
|
31528
|
-
children.push({ addr: partId, type: "part" });
|
|
31529
|
-
}
|
|
31530
|
-
const result = { id: node.id, kind: node.kind, intel: node.intel, children };
|
|
31531
|
-
if (facet) {
|
|
31532
|
-
result.snippet = facet.snippet;
|
|
31533
|
-
if (facet.slots !== void 0) result.slots = facet.slots;
|
|
31534
|
-
if (facet.events !== void 0) result.events = facet.events;
|
|
31535
|
-
}
|
|
31536
|
-
if (node.parent !== void 0) {
|
|
31537
|
-
result.parent = node.parent;
|
|
31538
|
-
const parentNode = graph.nodes.get(node.parent);
|
|
31539
|
-
if (parentNode) {
|
|
31540
|
-
const siblings = parentNode.parts.filter((id) => id !== node.id);
|
|
31541
|
-
if (siblings.length > 0) result.siblings = siblings;
|
|
31542
|
-
}
|
|
31543
|
-
}
|
|
31544
|
-
return result;
|
|
31545
|
-
}
|
|
31546
|
-
function resolveEdges(node, graph, target) {
|
|
31547
|
-
const seen = /* @__PURE__ */ new Set();
|
|
31548
|
-
const results = [];
|
|
31549
|
-
for (const edgeId of node.composesWith) {
|
|
31550
|
-
if (seen.has(edgeId)) continue;
|
|
31551
|
-
seen.add(edgeId);
|
|
31552
|
-
const edgeNode = graph.nodes.get(edgeId);
|
|
31553
|
-
if (edgeNode) results.push(layer0(edgeNode, graph, target));
|
|
31554
|
-
}
|
|
31555
|
-
return results;
|
|
31556
|
-
}
|
|
31557
|
-
function expandProps(node, target) {
|
|
31558
|
-
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
31559
|
-
const props = {};
|
|
31560
|
-
if (facet) {
|
|
31561
|
-
for (const [name, prop] of Object.entries(facet.props)) {
|
|
31562
|
-
props[name] = toAgentProp(prop);
|
|
31563
|
-
}
|
|
31564
|
-
}
|
|
31565
|
-
return { expanded: true, props };
|
|
31566
|
-
}
|
|
31567
|
-
function expandNode(node, target) {
|
|
31568
|
-
const facet = target === void 0 ? void 0 : node.facets[target];
|
|
31569
|
-
const props = {};
|
|
31570
|
-
if (facet) {
|
|
31571
|
-
for (const [name, prop] of Object.entries(facet.props)) {
|
|
31572
|
-
props[name] = toAgentProp(prop);
|
|
31573
|
-
}
|
|
31574
|
-
}
|
|
31575
|
-
const result = {
|
|
31576
|
-
id: node.id,
|
|
31577
|
-
kind: node.kind,
|
|
31578
|
-
intel: node.intel,
|
|
31579
|
-
props
|
|
31580
|
-
};
|
|
31581
|
-
if (node.composesWith.length > 0) result.composesWith = node.composesWith;
|
|
31582
|
-
if (node.parts.length > 0) result.parts = node.parts;
|
|
31583
|
-
if (facet) {
|
|
31584
|
-
result.snippet = facet.snippet;
|
|
31585
|
-
if (facet.slots !== void 0) result.slots = facet.slots;
|
|
31586
|
-
if (facet.events !== void 0) result.events = facet.events;
|
|
31587
|
-
}
|
|
31588
|
-
return result;
|
|
31589
|
-
}
|
|
31590
|
-
|
|
31591
|
-
// src/mcp/intent.ts
|
|
31592
|
-
var INTENT_TAGS = {
|
|
31593
|
-
modal: ["attention", "blocking", "above-all", "focus-trap"],
|
|
31594
|
-
alert: ["attention", "inline", "passive"],
|
|
31595
|
-
tooltip: ["hint", "passive", "inline", "hover"]
|
|
31596
|
-
};
|
|
31597
|
-
var INTENT_AXES = [
|
|
31598
|
-
{
|
|
31599
|
-
tag: "above-all",
|
|
31600
|
-
keywords: [
|
|
31601
|
-
"above everything",
|
|
31602
|
-
"above all",
|
|
31603
|
-
"on top of everything",
|
|
31604
|
-
"sits on top",
|
|
31605
|
-
"on top",
|
|
31606
|
-
"over everything",
|
|
31607
|
-
"over all",
|
|
31608
|
-
"topmost",
|
|
31609
|
-
"highest layer"
|
|
31610
|
-
]
|
|
31611
|
-
},
|
|
31612
|
-
{
|
|
31613
|
-
tag: "blocking",
|
|
31614
|
-
keywords: [
|
|
31615
|
-
"above everything",
|
|
31616
|
-
"on top of everything",
|
|
31617
|
-
"over everything",
|
|
31618
|
-
"block",
|
|
31619
|
-
"blocking",
|
|
31620
|
-
"must dismiss",
|
|
31621
|
-
"interrupt",
|
|
31622
|
-
"take over",
|
|
31623
|
-
"stops everything",
|
|
31624
|
-
"requires a response",
|
|
31625
|
-
"can't ignore",
|
|
31626
|
-
"cannot ignore"
|
|
31627
|
-
]
|
|
31628
|
-
},
|
|
31629
|
-
{
|
|
31630
|
-
tag: "inline",
|
|
31631
|
-
keywords: ["inline", "in place", "in the flow", "within the page", "non-blocking"]
|
|
31632
|
-
},
|
|
31633
|
-
{
|
|
31634
|
-
tag: "passive",
|
|
31635
|
-
keywords: ["passive", "non-intrusive", "subtle", "quietly", "in the background"]
|
|
31636
|
-
},
|
|
31637
|
-
{
|
|
31638
|
-
tag: "attention",
|
|
31639
|
-
keywords: ["attention", "notice", "get noticed", "important message"]
|
|
31640
|
-
},
|
|
31641
|
-
{
|
|
31642
|
-
tag: "hint",
|
|
31643
|
-
keywords: ["hint", "a tip", "explain", "extra info", "more info"]
|
|
31644
|
-
},
|
|
31645
|
-
{
|
|
31646
|
-
tag: "hover",
|
|
31647
|
-
keywords: ["hover", "on hover", "mouse over"]
|
|
31648
|
-
}
|
|
31649
|
-
];
|
|
31650
|
-
function isNaturalLanguageQuery(input) {
|
|
31651
|
-
return input.includes(" ");
|
|
31652
|
-
}
|
|
31653
|
-
function matchIntent(query, graph) {
|
|
31654
|
-
const normalized = query.toLowerCase();
|
|
31655
|
-
const matchedTags = collectMatchedTags(normalized);
|
|
31656
|
-
if (matchedTags.size === 0) return noMatch();
|
|
31657
|
-
const winner = selectUse(matchedTags, graph);
|
|
31658
|
-
if (winner === void 0) return noMatch();
|
|
31659
|
-
const winningTags = winner.tags.filter((tag) => matchedTags.has(tag));
|
|
31660
|
-
const decisive = new Set(winningTags);
|
|
31661
|
-
const nearMiss = selectNearMiss(winner, decisive, matchedTags, graph);
|
|
31662
|
-
if (nearMiss === void 0) return noMatch();
|
|
31663
|
-
const use = describeNode(winner.id, graph);
|
|
31664
|
-
const not = describeNode(nearMiss.id, graph);
|
|
31665
|
-
if (use === void 0 || not === void 0) return noMatch();
|
|
31666
|
-
const divergingTags = nearMiss.tags.filter((tag) => !winner.tags.includes(tag));
|
|
31667
|
-
const because = `${winner.id} is ${winningTags.join("/")}; ${nearMiss.id} is ${divergingTags.join("/")}`;
|
|
31668
|
-
return { use, not, because };
|
|
31669
|
-
}
|
|
31670
|
-
function collectMatchedTags(normalizedQuery) {
|
|
31671
|
-
const matched = /* @__PURE__ */ new Set();
|
|
31672
|
-
for (const axis of INTENT_AXES) {
|
|
31673
|
-
for (const keyword of axis.keywords) {
|
|
31674
|
-
if (normalizedQuery.includes(keyword)) {
|
|
31675
|
-
matched.add(axis.tag);
|
|
31676
|
-
break;
|
|
31677
|
-
}
|
|
31678
|
-
}
|
|
31679
|
-
}
|
|
31680
|
-
return matched;
|
|
31681
|
-
}
|
|
31682
|
-
function selectUse(matchedTags, graph) {
|
|
31683
|
-
let best;
|
|
31684
|
-
let bestScore = 0;
|
|
31685
|
-
for (const [id, tags] of Object.entries(INTENT_TAGS)) {
|
|
31686
|
-
if (!graph.nodes.has(id)) continue;
|
|
31687
|
-
const score = tags.reduce((sum, tag) => matchedTags.has(tag) ? sum + 1 : sum, 0);
|
|
31688
|
-
if (score > bestScore) {
|
|
31689
|
-
bestScore = score;
|
|
31690
|
-
best = { id, tags };
|
|
31691
|
-
}
|
|
31692
|
-
}
|
|
31693
|
-
return best;
|
|
31694
|
-
}
|
|
31695
|
-
function selectNearMiss(use, decisive, matchedTags, graph) {
|
|
31696
|
-
let best;
|
|
31697
|
-
let bestOverlap = 0;
|
|
31698
|
-
for (const [id, tags] of Object.entries(INTENT_TAGS)) {
|
|
31699
|
-
if (id === use.id || !graph.nodes.has(id)) continue;
|
|
31700
|
-
if (tags.some((tag) => decisive.has(tag))) continue;
|
|
31701
|
-
const overlap = tags.filter((tag) => use.tags.includes(tag));
|
|
31702
|
-
const sharesOutsideMatched = overlap.some((tag) => !matchedTags.has(tag));
|
|
31703
|
-
if (!sharesOutsideMatched) continue;
|
|
31704
|
-
if (overlap.length > bestOverlap) {
|
|
31705
|
-
bestOverlap = overlap.length;
|
|
31706
|
-
best = { id, tags };
|
|
31707
|
-
}
|
|
31708
|
-
}
|
|
31709
|
-
return best;
|
|
31710
|
-
}
|
|
31711
|
-
function describeNode(id, graph) {
|
|
31712
|
-
const result = describe(id, graph);
|
|
31713
|
-
if (result !== null && !Array.isArray(result) && "children" in result) return result;
|
|
31714
|
-
return void 0;
|
|
31715
|
-
}
|
|
31716
|
-
function noMatch() {
|
|
31717
|
-
return { note: "no route matched; describe(components) or describe(composites) to browse" };
|
|
30808
|
+
function noMatch() {
|
|
30809
|
+
return { note: "no route matched; describe(components) or describe(composites) to browse" };
|
|
31718
30810
|
}
|
|
31719
30811
|
|
|
31720
30812
|
// src/mcp/overlay.ts
|
|
@@ -32130,7 +31222,7 @@ var RaftersToolHandler = class {
|
|
|
32130
31222
|
async ensureCompositesLoaded(workspace) {
|
|
32131
31223
|
if (!this.builtInCompositesLoaded) {
|
|
32132
31224
|
await this.loadCompositesFromDirs(
|
|
32133
|
-
|
|
31225
|
+
join11(process.cwd(), "node_modules/@rafters/composites/src/typography")
|
|
32134
31226
|
);
|
|
32135
31227
|
this.builtInCompositesLoaded = true;
|
|
32136
31228
|
}
|
|
@@ -32148,7 +31240,7 @@ var RaftersToolHandler = class {
|
|
|
32148
31240
|
const paths = getRaftersPaths(workspaceRoot);
|
|
32149
31241
|
try {
|
|
32150
31242
|
return migrateConfig(
|
|
32151
|
-
JSON.parse(await
|
|
31243
|
+
JSON.parse(await readFile5(paths.config, "utf-8"))
|
|
32152
31244
|
);
|
|
32153
31245
|
} catch {
|
|
32154
31246
|
return null;
|
|
@@ -32157,7 +31249,7 @@ var RaftersToolHandler = class {
|
|
|
32157
31249
|
/** Persist a workspace's config back to `.rafters/config.rafters.json`. */
|
|
32158
31250
|
async writeConfig(workspaceRoot, config2) {
|
|
32159
31251
|
const paths = getRaftersPaths(workspaceRoot);
|
|
32160
|
-
await
|
|
31252
|
+
await writeFile3(paths.config, `${JSON.stringify(config2, null, 2)}
|
|
32161
31253
|
`);
|
|
32162
31254
|
}
|
|
32163
31255
|
/**
|
|
@@ -32352,123 +31444,1137 @@ var RaftersToolHandler = class {
|
|
|
32352
31444
|
const paths = getRaftersPaths(workspaceRoot);
|
|
32353
31445
|
const config2 = await this.readConfig(workspaceRoot);
|
|
32354
31446
|
if (!config2?.compositesPath) {
|
|
32355
|
-
return [
|
|
31447
|
+
return [join11(paths.root, "composites")];
|
|
31448
|
+
}
|
|
31449
|
+
return resolveReadSet(config2.compositesPath, workspaceRoot);
|
|
31450
|
+
}
|
|
31451
|
+
async handleComposite(args) {
|
|
31452
|
+
const { id, query, category, workspace } = args;
|
|
31453
|
+
if (id) {
|
|
31454
|
+
return this.describeById(id, workspace);
|
|
31455
|
+
}
|
|
31456
|
+
const resolved = this.resolve(workspace);
|
|
31457
|
+
if (workspace && !resolved) {
|
|
31458
|
+
return this.workspaceRequiredError();
|
|
31459
|
+
}
|
|
31460
|
+
await this.ensureCompositesLoaded(resolved);
|
|
31461
|
+
let composites2;
|
|
31462
|
+
if (query) {
|
|
31463
|
+
composites2 = search(query);
|
|
31464
|
+
} else if (category) {
|
|
31465
|
+
composites2 = getByCategory(category);
|
|
31466
|
+
} else {
|
|
31467
|
+
composites2 = getAll2();
|
|
31468
|
+
}
|
|
31469
|
+
const result = composites2.map((c4) => ({
|
|
31470
|
+
id: c4.manifest.id,
|
|
31471
|
+
name: c4.manifest.name,
|
|
31472
|
+
category: c4.manifest.category,
|
|
31473
|
+
description: c4.manifest.description,
|
|
31474
|
+
cognitiveLoad: c4.manifest.cognitiveLoad,
|
|
31475
|
+
solves: c4.manifest.solves,
|
|
31476
|
+
appliesWhen: c4.manifest.appliesWhen,
|
|
31477
|
+
usagePatterns: c4.manifest.usagePatterns,
|
|
31478
|
+
input: c4.input,
|
|
31479
|
+
output: c4.output,
|
|
31480
|
+
blockCount: c4.blocks.length,
|
|
31481
|
+
// Surface which blocks carry which validation rules. Rules compile to
|
|
31482
|
+
// native HTML5 constraint attributes at build, so agents need to see
|
|
31483
|
+
// them to reason about a composite's behavior. Only blocks with rules
|
|
31484
|
+
// are listed -- the array is empty when nothing is constrained.
|
|
31485
|
+
blockRules: c4.blocks.filter((b2) => b2.rules && b2.rules.length > 0).map((b2) => ({ id: b2.id, type: b2.type, rules: b2.rules }))
|
|
31486
|
+
}));
|
|
31487
|
+
return this.jsonResult({ composites: result, deprecated: DEPRECATED_MSG });
|
|
31488
|
+
}
|
|
31489
|
+
async handlePattern(args) {
|
|
31490
|
+
const { solves, query, workspace } = args;
|
|
31491
|
+
const resolved = this.resolve(workspace);
|
|
31492
|
+
if (workspace && !resolved) {
|
|
31493
|
+
return this.workspaceRequiredError();
|
|
31494
|
+
}
|
|
31495
|
+
await this.ensureCompositesLoaded(resolved);
|
|
31496
|
+
let composites2;
|
|
31497
|
+
if (solves) {
|
|
31498
|
+
const all = getAll2();
|
|
31499
|
+
const solvesLower = solves.toLowerCase();
|
|
31500
|
+
composites2 = all.filter(
|
|
31501
|
+
(c4) => c4.manifest.solves?.toLowerCase().includes(solvesLower) || c4.manifest.appliesWhen?.some((a2) => a2.toLowerCase().includes(solvesLower))
|
|
31502
|
+
);
|
|
31503
|
+
} else if (query) {
|
|
31504
|
+
composites2 = search(query);
|
|
31505
|
+
} else {
|
|
31506
|
+
composites2 = getAll2().filter((c4) => c4.manifest.usagePatterns);
|
|
31507
|
+
}
|
|
31508
|
+
if (composites2.length === 0) {
|
|
31509
|
+
const all = getAll2();
|
|
31510
|
+
const available = all.filter((c4) => c4.manifest.solves || c4.manifest.usagePatterns).map((c4) => ({
|
|
31511
|
+
id: c4.manifest.id,
|
|
31512
|
+
solves: c4.manifest.solves
|
|
31513
|
+
}));
|
|
31514
|
+
return this.errorResult("No patterns found matching query", {
|
|
31515
|
+
available,
|
|
31516
|
+
deprecated: DEPRECATED_MSG
|
|
31517
|
+
});
|
|
31518
|
+
}
|
|
31519
|
+
const patterns = composites2.map((c4) => ({
|
|
31520
|
+
id: c4.manifest.id,
|
|
31521
|
+
name: c4.manifest.name,
|
|
31522
|
+
solves: c4.manifest.solves,
|
|
31523
|
+
appliesWhen: c4.manifest.appliesWhen,
|
|
31524
|
+
cognitiveLoad: c4.manifest.cognitiveLoad,
|
|
31525
|
+
usagePatterns: c4.manifest.usagePatterns
|
|
31526
|
+
}));
|
|
31527
|
+
return this.jsonResult({ patterns, deprecated: DEPRECATED_MSG });
|
|
31528
|
+
}
|
|
31529
|
+
/**
|
|
31530
|
+
* DEPRECATED alias for `rafters_describe`. A component resolves by id through
|
|
31531
|
+
* the overlay exactly as `describe(<id>)` does (#2072's resolver has no
|
|
31532
|
+
* separate component/composite address form -- `describe(<id>)` resolves either
|
|
31533
|
+
* kind), so this is a direct, lossless forward with the deprecated marker added.
|
|
31534
|
+
*/
|
|
31535
|
+
async handleComponent(componentName, workspaceName) {
|
|
31536
|
+
return this.describeById(componentName, workspaceName);
|
|
31537
|
+
}
|
|
31538
|
+
/**
|
|
31539
|
+
* Shared by the deprecated `rafters_component` and `rafters_composite({id})`
|
|
31540
|
+
* paths: resolve one id through the overlay, stamp the deprecated marker.
|
|
31541
|
+
*/
|
|
31542
|
+
async describeById(id, workspaceName) {
|
|
31543
|
+
const resolved = this.resolve(workspaceName);
|
|
31544
|
+
if (workspaceName && !resolved) {
|
|
31545
|
+
return this.workspaceRequiredError();
|
|
31546
|
+
}
|
|
31547
|
+
let graph;
|
|
31548
|
+
try {
|
|
31549
|
+
graph = await this.ensureGraph(resolved);
|
|
31550
|
+
} catch (err) {
|
|
31551
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
31552
|
+
return this.errorResult(`failed to build intel graph: ${message}`, {
|
|
31553
|
+
deprecated: DEPRECATED_MSG
|
|
31554
|
+
});
|
|
31555
|
+
}
|
|
31556
|
+
const ctx = await this.overlayContext(resolved);
|
|
31557
|
+
if ("configError" in ctx) {
|
|
31558
|
+
return this.errorResult(ctx.configError, { deprecated: DEPRECATED_MSG });
|
|
31559
|
+
}
|
|
31560
|
+
const out = describeWithOverlay(id, graph, ctx);
|
|
31561
|
+
return this.jsonResult(this.withDeprecated(out));
|
|
31562
|
+
}
|
|
31563
|
+
};
|
|
31564
|
+
|
|
31565
|
+
// src/mcp/agent-contract.ts
|
|
31566
|
+
function liveTools() {
|
|
31567
|
+
return TOOL_DEFINITIONS.filter((t) => !t.description.startsWith("[DEPRECATED"));
|
|
31568
|
+
}
|
|
31569
|
+
function generateAgentContract() {
|
|
31570
|
+
const roster = liveTools().map((t) => `- \`${t.name}\`: ${t.description}`).join("\n");
|
|
31571
|
+
return `# Rafters agent contract
|
|
31572
|
+
|
|
31573
|
+
You are working in a Rafters-powered frontend project. The design system owns
|
|
31574
|
+
every visual decision -- color, spacing, size, weight, radius, shadow, hierarchy,
|
|
31575
|
+
and layout rhythm are already decided. Your job is to assemble the pre-made piece
|
|
31576
|
+
that fits the content and intent, never to author a visual choice. A write-time
|
|
31577
|
+
hook denies any edit that hand-authors a visual decision.
|
|
31578
|
+
|
|
31579
|
+
## Assembly model -- follow this order every time
|
|
31580
|
+
|
|
31581
|
+
1. Query the design system before writing any UI. You do not know this system
|
|
31582
|
+
from training; read it from the MCP tools below, every time. Read the project's
|
|
31583
|
+
recorded intent from \`.rafters/config.rafters.json\` first.
|
|
31584
|
+
2. Prefer a composite or pattern that solves the whole problem (authentication,
|
|
31585
|
+
data entry, navigation, pricing) over assembling lone components by hand.
|
|
31586
|
+
Render its blocks verbatim -- the manifest is the decision.
|
|
31587
|
+
3. Drop to a single component only for a lone affordance no composite covers.
|
|
31588
|
+
4. Author a custom component only when the served design system genuinely does not
|
|
31589
|
+
ship the affordance -- the last resort, not the first.
|
|
31590
|
+
5. Prose and content (headings, paragraphs, lists, quotes) render as bare native
|
|
31591
|
+
HTML inside \`<Container as="article">\` -- no classes, no imports.
|
|
31592
|
+
|
|
31593
|
+
## Never
|
|
31594
|
+
|
|
31595
|
+
- Never write flex, grid, gap, padding, or margin utilities directly. Container
|
|
31596
|
+
and Grid own all layout.
|
|
31597
|
+
- Never use raw \`<h1>\`/\`<p>\`/\`<span>\` with class attributes for styled text.
|
|
31598
|
+
Use the typography components with token props (size, weight, color).
|
|
31599
|
+
- Never use hex colors, arbitrary values, or \`var()\`. Use semantic color tokens
|
|
31600
|
+
(bg-primary, text-accent).
|
|
31601
|
+
- Never pass \`class\` or \`className\` to a served component. Use token props for
|
|
31602
|
+
overrides.
|
|
31603
|
+
|
|
31604
|
+
If you do not know what to build or how it should look, get a design direction
|
|
31605
|
+
before writing code. The pre-edit hook WILL block violations. Consult the design
|
|
31606
|
+
system BEFORE writing, not after.
|
|
31607
|
+
|
|
31608
|
+
## MCP tools
|
|
31609
|
+
|
|
31610
|
+
Query these before writing UI:
|
|
31611
|
+
|
|
31612
|
+
${roster}
|
|
31613
|
+
|
|
31614
|
+
## Running the rafters MCP server
|
|
31615
|
+
|
|
31616
|
+
Non-Claude-Code hosts wire the server manually. Invoke the \`rafters\` binary that
|
|
31617
|
+
is installed in your project, through your package manager:
|
|
31618
|
+
|
|
31619
|
+
- \`pnpm exec rafters mcp\`
|
|
31620
|
+
- \`npx rafters mcp\`
|
|
31621
|
+
- \`yarn rafters mcp\`
|
|
31622
|
+
|
|
31623
|
+
Run the copy pinned in your own lockfile. Do NOT use \`pnpx rafters@latest\`,
|
|
31624
|
+
\`npx rafters@latest\`, \`pnpm dlx rafters\`, or any \`@latest\` pin: these resolve
|
|
31625
|
+
outside your lockfile and can drift from the version your project installed --
|
|
31626
|
+
the exact staleness that strands you on an old rafters after a new one ships.
|
|
31627
|
+
`;
|
|
31628
|
+
}
|
|
31629
|
+
|
|
31630
|
+
// src/commands/agents.ts
|
|
31631
|
+
var START_MARKER = "<!-- rafters:agent-contract:start -->";
|
|
31632
|
+
var END_MARKER = "<!-- rafters:agent-contract:end -->";
|
|
31633
|
+
function mergeAgentContract(existing, contract) {
|
|
31634
|
+
const block = `${START_MARKER}
|
|
31635
|
+
${contract}
|
|
31636
|
+
${END_MARKER}`;
|
|
31637
|
+
if (existing === null) {
|
|
31638
|
+
return `${block}
|
|
31639
|
+
`;
|
|
31640
|
+
}
|
|
31641
|
+
const startIdx = existing.indexOf(START_MARKER);
|
|
31642
|
+
const endIdx = existing.indexOf(END_MARKER);
|
|
31643
|
+
if (startIdx !== -1 && endIdx !== -1 && startIdx < endIdx) {
|
|
31644
|
+
const before = existing.slice(0, startIdx);
|
|
31645
|
+
const after = existing.slice(endIdx + END_MARKER.length);
|
|
31646
|
+
return `${before}${block}${after}`;
|
|
31647
|
+
}
|
|
31648
|
+
return `${existing.trimEnd()}
|
|
31649
|
+
|
|
31650
|
+
${block}
|
|
31651
|
+
`;
|
|
31652
|
+
}
|
|
31653
|
+
async function agents() {
|
|
31654
|
+
const cwd = process.cwd();
|
|
31655
|
+
const paths = getRaftersPaths(cwd);
|
|
31656
|
+
if (!existsSync6(paths.config)) {
|
|
31657
|
+
throw new Error(
|
|
31658
|
+
`No rafters project found: ${paths.config} is missing. Run \`rafters init\` first.`
|
|
31659
|
+
);
|
|
31660
|
+
}
|
|
31661
|
+
const agentsPath = join12(cwd, "AGENTS.md");
|
|
31662
|
+
const existing = existsSync6(agentsPath) ? await readFile6(agentsPath, "utf-8") : null;
|
|
31663
|
+
const merged = mergeAgentContract(existing, generateAgentContract());
|
|
31664
|
+
await writeFile4(agentsPath, merged);
|
|
31665
|
+
}
|
|
31666
|
+
|
|
31667
|
+
// src/commands/init.ts
|
|
31668
|
+
import { existsSync as existsSync7 } from "fs";
|
|
31669
|
+
import { copyFile, mkdir as mkdir3, readFile as readFile7, rm, writeFile as writeFile5 } from "fs/promises";
|
|
31670
|
+
import { createRequire } from "module";
|
|
31671
|
+
import { join as join13, relative as relative2 } from "path";
|
|
31672
|
+
import { checkbox, confirm, select } from "@inquirer/prompts";
|
|
31673
|
+
var REGISTRY_PLUGINS2 = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
|
|
31674
|
+
async function backupCss(cssPath) {
|
|
31675
|
+
const backupPath = cssPath.replace(/\.css$/, ".backup.css");
|
|
31676
|
+
await copyFile(cssPath, backupPath);
|
|
31677
|
+
return backupPath;
|
|
31678
|
+
}
|
|
31679
|
+
function cleanSourceCssBlocks(content) {
|
|
31680
|
+
function parseBlocks(src) {
|
|
31681
|
+
const blocks = [];
|
|
31682
|
+
let depth = 0;
|
|
31683
|
+
const headerStack = [];
|
|
31684
|
+
let segmentStart = 0;
|
|
31685
|
+
for (let i = 0; i < src.length; i++) {
|
|
31686
|
+
const ch = src.charAt(i);
|
|
31687
|
+
if (ch === "{") {
|
|
31688
|
+
headerStack.push(segmentStart);
|
|
31689
|
+
depth++;
|
|
31690
|
+
if (depth >= 1) {
|
|
31691
|
+
blocks.push({
|
|
31692
|
+
headerStart: segmentStart,
|
|
31693
|
+
bodyStart: i + 1,
|
|
31694
|
+
end: -1,
|
|
31695
|
+
header: src.slice(segmentStart, i).trim()
|
|
31696
|
+
});
|
|
31697
|
+
}
|
|
31698
|
+
segmentStart = i + 1;
|
|
31699
|
+
} else if (ch === "}") {
|
|
31700
|
+
depth--;
|
|
31701
|
+
const open = blocks.slice().reverse().find((b2) => b2.end === -1);
|
|
31702
|
+
if (open) open.end = i + 1;
|
|
31703
|
+
headerStack.pop();
|
|
31704
|
+
segmentStart = i + 1;
|
|
31705
|
+
} else if (ch === ";") {
|
|
31706
|
+
segmentStart = i + 1;
|
|
31707
|
+
}
|
|
31708
|
+
}
|
|
31709
|
+
return blocks.filter((b2) => b2.end !== -1);
|
|
31710
|
+
}
|
|
31711
|
+
function isContentEmpty(body) {
|
|
31712
|
+
return body.replace(/\/\*[\s\S]*?\*\//g, "").trim().length === 0;
|
|
31713
|
+
}
|
|
31714
|
+
let out = content;
|
|
31715
|
+
for (let pass = 0; pass < 10; pass++) {
|
|
31716
|
+
const blocks = parseBlocks(out);
|
|
31717
|
+
let edited = false;
|
|
31718
|
+
for (const block of blocks.sort((a2, b2) => b2.headerStart - a2.headerStart)) {
|
|
31719
|
+
const header = block.header;
|
|
31720
|
+
if (/^@theme\s+inline$/.test(header)) {
|
|
31721
|
+
out = out.slice(0, block.headerStart) + out.slice(block.end);
|
|
31722
|
+
edited = true;
|
|
31723
|
+
break;
|
|
31724
|
+
}
|
|
31725
|
+
if (header === ":root" || /(^|,)\s*:root\s*$/.test(header)) {
|
|
31726
|
+
const body = out.slice(block.bodyStart, block.end - 1);
|
|
31727
|
+
const cleanedBody = body.replace(/--[\w-]+\s*:[^;{}]*;?/g, "");
|
|
31728
|
+
if (isContentEmpty(cleanedBody)) {
|
|
31729
|
+
out = out.slice(0, block.headerStart) + out.slice(block.end);
|
|
31730
|
+
edited = true;
|
|
31731
|
+
break;
|
|
31732
|
+
}
|
|
31733
|
+
if (cleanedBody !== body) {
|
|
31734
|
+
out = out.slice(0, block.bodyStart) + cleanedBody + out.slice(block.end - 1);
|
|
31735
|
+
edited = true;
|
|
31736
|
+
break;
|
|
31737
|
+
}
|
|
31738
|
+
continue;
|
|
31739
|
+
}
|
|
31740
|
+
if (header.startsWith("@media") || header.startsWith("@supports")) {
|
|
31741
|
+
const body = out.slice(block.bodyStart, block.end - 1);
|
|
31742
|
+
if (isContentEmpty(body)) {
|
|
31743
|
+
out = out.slice(0, block.headerStart) + out.slice(block.end);
|
|
31744
|
+
edited = true;
|
|
31745
|
+
break;
|
|
31746
|
+
}
|
|
31747
|
+
}
|
|
31748
|
+
}
|
|
31749
|
+
if (!edited) break;
|
|
31750
|
+
}
|
|
31751
|
+
return out.replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
31752
|
+
}
|
|
31753
|
+
async function stripImportedDeclarations(cwd, cssPath, importedNames) {
|
|
31754
|
+
if (importedNames.length === 0) return;
|
|
31755
|
+
const fullPath = join13(cwd, cssPath);
|
|
31756
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
31757
|
+
const pattern = new RegExp(importedNames.map((n2) => `^\\s*--${n2}[^;]*;\\s*$`).join("|"), "gm");
|
|
31758
|
+
const cleaned = cleanSourceCssBlocks(content.replace(pattern, ""));
|
|
31759
|
+
const collapsed = cleaned.replace(/\n{3,}/g, "\n\n");
|
|
31760
|
+
await writeFile5(fullPath, collapsed);
|
|
31761
|
+
}
|
|
31762
|
+
async function updateMainCss(cwd, cssPath, themePath) {
|
|
31763
|
+
const fullCssPath = join13(cwd, cssPath);
|
|
31764
|
+
const cssContent = await readFile7(fullCssPath, "utf-8");
|
|
31765
|
+
const cssDir = join13(cwd, cssPath, "..");
|
|
31766
|
+
const themeFullPath = join13(cwd, themePath);
|
|
31767
|
+
const relativeThemePath = relative2(cssDir, themeFullPath);
|
|
31768
|
+
if (cssContent.includes(".rafters/output/rafters.css")) {
|
|
31769
|
+
log({ event: "init:css_already_imported", cssPath });
|
|
31770
|
+
return;
|
|
31771
|
+
}
|
|
31772
|
+
await backupCss(fullCssPath);
|
|
31773
|
+
let newContent;
|
|
31774
|
+
if (cssContent.includes('@import "tailwindcss"')) {
|
|
31775
|
+
newContent = cssContent.replace('@import "tailwindcss";', `@import "${relativeThemePath}";`);
|
|
31776
|
+
} else if (cssContent.includes("@import 'tailwindcss'")) {
|
|
31777
|
+
newContent = cssContent.replace("@import 'tailwindcss';", `@import "${relativeThemePath}";`);
|
|
31778
|
+
} else {
|
|
31779
|
+
newContent = `@import "${relativeThemePath}";
|
|
31780
|
+
|
|
31781
|
+
${cssContent}`;
|
|
31782
|
+
}
|
|
31783
|
+
await writeFile5(fullCssPath, newContent);
|
|
31784
|
+
log({
|
|
31785
|
+
event: "init:css_updated",
|
|
31786
|
+
cssPath,
|
|
31787
|
+
themePath: relativeThemePath
|
|
31788
|
+
});
|
|
31789
|
+
}
|
|
31790
|
+
function isInteractive() {
|
|
31791
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
31792
|
+
}
|
|
31793
|
+
function resolveSourceCssPath(cwd, framework, detectedFramework, projectCssPath, shadcnCssPath, emit) {
|
|
31794
|
+
if (shadcnCssPath) {
|
|
31795
|
+
if (existsSync7(join13(cwd, shadcnCssPath))) return shadcnCssPath;
|
|
31796
|
+
const fallback = framework === detectedFramework ? projectCssPath : findCssPath(cwd, framework);
|
|
31797
|
+
emit({
|
|
31798
|
+
event: "init:shadcn_css_missing",
|
|
31799
|
+
configuredPath: shadcnCssPath,
|
|
31800
|
+
fallbackPath: fallback,
|
|
31801
|
+
message: "components.json points at a CSS file that does not exist. Falling back to the framework canonical location."
|
|
31802
|
+
});
|
|
31803
|
+
return fallback;
|
|
31804
|
+
}
|
|
31805
|
+
return framework === detectedFramework ? projectCssPath : findCssPath(cwd, framework);
|
|
31806
|
+
}
|
|
31807
|
+
function slugifyFontName(name) {
|
|
31808
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
31809
|
+
}
|
|
31810
|
+
function isFontStackValue(value) {
|
|
31811
|
+
if (typeof value !== "string") return false;
|
|
31812
|
+
if (value.startsWith("var(")) return false;
|
|
31813
|
+
if (/[,'"]/.test(value)) return true;
|
|
31814
|
+
return /\b(sans-serif|serif|monospace|cursive|fantasy|system-ui|ui-(?:sans-serif|serif|monospace|rounded))\b/.test(
|
|
31815
|
+
value
|
|
31816
|
+
);
|
|
31817
|
+
}
|
|
31818
|
+
async function resolveFramework(detected, flag, agentMode) {
|
|
31819
|
+
if (flag) {
|
|
31820
|
+
if (!isSelectableFramework(flag)) {
|
|
31821
|
+
throw new Error(
|
|
31822
|
+
`Unknown --framework "${flag}". Valid values: ${SELECTABLE_FRAMEWORKS.join(", ")}.`
|
|
31823
|
+
);
|
|
31824
|
+
}
|
|
31825
|
+
return flag;
|
|
31826
|
+
}
|
|
31827
|
+
if (detected !== "unknown") return detected;
|
|
31828
|
+
if (agentMode || !isInteractive()) return "unknown";
|
|
31829
|
+
const picked = await select({
|
|
31830
|
+
message: "Couldn't auto-detect your framework. Which one is this?",
|
|
31831
|
+
choices: SELECTABLE_FRAMEWORKS.map((value) => ({
|
|
31832
|
+
name: FRAMEWORK_SPECS[value].label ?? value,
|
|
31833
|
+
value
|
|
31834
|
+
}))
|
|
31835
|
+
});
|
|
31836
|
+
return picked;
|
|
31837
|
+
}
|
|
31838
|
+
async function promptExportFormats(existingConfig) {
|
|
31839
|
+
if (!isInteractive()) {
|
|
31840
|
+
return existingConfig ?? DEFAULT_EXPORTS;
|
|
31841
|
+
}
|
|
31842
|
+
const choices = EXPORT_CHOICES.map((choice) => ({
|
|
31843
|
+
name: choice.name,
|
|
31844
|
+
value: choice.value,
|
|
31845
|
+
checked: existingConfig ? existingConfig[choice.value] : choice.checked
|
|
31846
|
+
}));
|
|
31847
|
+
const allChoices = [
|
|
31848
|
+
...choices,
|
|
31849
|
+
...FUTURE_EXPORTS.map((choice) => ({
|
|
31850
|
+
name: `${choice.name} (${choice.disabled})`,
|
|
31851
|
+
value: choice.value,
|
|
31852
|
+
checked: false,
|
|
31853
|
+
disabled: true
|
|
31854
|
+
}))
|
|
31855
|
+
];
|
|
31856
|
+
const selections = await checkbox({
|
|
31857
|
+
message: "What would you like to export?",
|
|
31858
|
+
choices: allChoices,
|
|
31859
|
+
required: true
|
|
31860
|
+
});
|
|
31861
|
+
return selectionsToConfig(selections);
|
|
31862
|
+
}
|
|
31863
|
+
function isTailwindCliInstalled() {
|
|
31864
|
+
const require2 = createRequire(import.meta.url);
|
|
31865
|
+
try {
|
|
31866
|
+
require2.resolve("@tailwindcss/cli/package.json");
|
|
31867
|
+
return true;
|
|
31868
|
+
} catch (err) {
|
|
31869
|
+
if (err instanceof Error && "code" in err && err.code === "MODULE_NOT_FOUND") {
|
|
31870
|
+
return false;
|
|
31871
|
+
}
|
|
31872
|
+
throw err;
|
|
31873
|
+
}
|
|
31874
|
+
}
|
|
31875
|
+
async function ensureTailwindCli(cwd) {
|
|
31876
|
+
if (!isInteractive() || isAgentMode()) {
|
|
31877
|
+
throw new Error(
|
|
31878
|
+
"Standalone CSS export requires @tailwindcss/cli. Install it as a dev dependency in your project."
|
|
31879
|
+
);
|
|
31880
|
+
}
|
|
31881
|
+
const shouldInstall = await confirm({
|
|
31882
|
+
message: "Standalone CSS requires @tailwindcss/cli. Install it now?",
|
|
31883
|
+
default: true
|
|
31884
|
+
});
|
|
31885
|
+
if (!shouldInstall) {
|
|
31886
|
+
throw new Error("Standalone CSS export requires @tailwindcss/cli.");
|
|
31887
|
+
}
|
|
31888
|
+
await updateDependencies([], ["@tailwindcss/cli"], { cwd });
|
|
31889
|
+
if (!isTailwindCliInstalled()) {
|
|
31890
|
+
throw new Error(
|
|
31891
|
+
"@tailwindcss/cli was installed but cannot be resolved. Try installing at the workspace root."
|
|
31892
|
+
);
|
|
31893
|
+
}
|
|
31894
|
+
}
|
|
31895
|
+
async function generateOutputs(cwd, paths, registry2, exports, source, darkMode = "class", config2) {
|
|
31896
|
+
const contentSources = config2 ? resolveContentSources(cwd, config2) : [];
|
|
31897
|
+
if (exports.compiled && !isTailwindCliInstalled()) {
|
|
31898
|
+
log({ event: "init:prompting_exports" });
|
|
31899
|
+
await ensureTailwindCli(cwd);
|
|
31900
|
+
}
|
|
31901
|
+
if (exports.compiled) {
|
|
31902
|
+
log({ event: "init:compiling_css" });
|
|
31903
|
+
}
|
|
31904
|
+
return regenerateOutputs(registry2, {
|
|
31905
|
+
outputDir: paths.output,
|
|
31906
|
+
exports,
|
|
31907
|
+
contentSources,
|
|
31908
|
+
darkMode,
|
|
31909
|
+
includeImport: source !== "shadcn"
|
|
31910
|
+
});
|
|
31911
|
+
}
|
|
31912
|
+
async function regenerateFromExisting(cwd, paths, source, isAgentMode2, framework) {
|
|
31913
|
+
log({ event: "init:regenerate", cwd });
|
|
31914
|
+
let existingConfig = null;
|
|
31915
|
+
try {
|
|
31916
|
+
const configContent = await readFile7(paths.config, "utf-8");
|
|
31917
|
+
existingConfig = migrateConfig(
|
|
31918
|
+
JSON.parse(configContent)
|
|
31919
|
+
);
|
|
31920
|
+
} catch {
|
|
31921
|
+
}
|
|
31922
|
+
if (framework !== "unknown" && existingConfig) {
|
|
31923
|
+
const frameworkPaths = FRAMEWORK_SPECS[framework].components;
|
|
31924
|
+
existingConfig.framework = framework;
|
|
31925
|
+
existingConfig.componentsPath = frameworkPaths.components;
|
|
31926
|
+
existingConfig.primitivesPath = frameworkPaths.primitives;
|
|
31927
|
+
existingConfig.compositesPath = frameworkPaths.composites;
|
|
31928
|
+
existingConfig.rulesPath = frameworkPaths.rules;
|
|
31929
|
+
}
|
|
31930
|
+
await rm(join13(paths.tokens, "elevation.rafters.json"), { force: true });
|
|
31931
|
+
await rm(join13(paths.tokens, "fill.rafters.json"), { force: true });
|
|
31932
|
+
const registry2 = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS2);
|
|
31933
|
+
if (registry2.size() === 0) {
|
|
31934
|
+
throw new Error("No tokens found. Cannot regenerate without existing tokens.");
|
|
31935
|
+
}
|
|
31936
|
+
const namespaces = [...new Set(registry2.list().map((t) => t.namespace))];
|
|
31937
|
+
log({
|
|
31938
|
+
event: "init:loaded",
|
|
31939
|
+
tokenCount: registry2.size(),
|
|
31940
|
+
namespaces
|
|
31941
|
+
});
|
|
31942
|
+
let exports;
|
|
31943
|
+
if (isAgentMode2) {
|
|
31944
|
+
exports = existingConfig?.exports ?? DEFAULT_EXPORTS;
|
|
31945
|
+
log({ event: "init:exports_default", exports });
|
|
31946
|
+
} else {
|
|
31947
|
+
if (isInteractive()) {
|
|
31948
|
+
log({ event: "init:prompting_exports" });
|
|
31949
|
+
}
|
|
31950
|
+
exports = await promptExportFormats(existingConfig?.exports);
|
|
31951
|
+
log({ event: "init:exports_selected", exports });
|
|
31952
|
+
}
|
|
31953
|
+
await mkdir3(paths.output, { recursive: true });
|
|
31954
|
+
const outputs = await generateOutputs(
|
|
31955
|
+
cwd,
|
|
31956
|
+
paths,
|
|
31957
|
+
registry2,
|
|
31958
|
+
exports,
|
|
31959
|
+
source,
|
|
31960
|
+
"class",
|
|
31961
|
+
existingConfig
|
|
31962
|
+
);
|
|
31963
|
+
if (existingConfig) {
|
|
31964
|
+
existingConfig.exports = exports;
|
|
31965
|
+
await writeFile5(paths.config, JSON.stringify(existingConfig, null, 2));
|
|
31966
|
+
} else {
|
|
31967
|
+
const frameworkPaths = FRAMEWORK_SPECS[framework].components;
|
|
31968
|
+
const newConfig = {
|
|
31969
|
+
framework,
|
|
31970
|
+
componentsPath: frameworkPaths.components,
|
|
31971
|
+
primitivesPath: frameworkPaths.primitives,
|
|
31972
|
+
compositesPath: frameworkPaths.composites,
|
|
31973
|
+
rulesPath: frameworkPaths.rules,
|
|
31974
|
+
cssPath: null,
|
|
31975
|
+
...source ? { source } : {},
|
|
31976
|
+
intent: "efficient",
|
|
31977
|
+
fonts: { path: null, imports: [] },
|
|
31978
|
+
exports,
|
|
31979
|
+
installed: { components: [], primitives: [], composites: [], rules: [] }
|
|
31980
|
+
};
|
|
31981
|
+
await writeFile5(paths.config, JSON.stringify(newConfig, null, 2));
|
|
31982
|
+
}
|
|
31983
|
+
log({
|
|
31984
|
+
event: "init:complete",
|
|
31985
|
+
outputs,
|
|
31986
|
+
path: paths.output
|
|
31987
|
+
});
|
|
31988
|
+
}
|
|
31989
|
+
async function resetToDefaults(cwd, paths, source, isAgentMode2, framework) {
|
|
31990
|
+
log({ event: "init:reset", cwd });
|
|
31991
|
+
let existingConfig = null;
|
|
31992
|
+
try {
|
|
31993
|
+
const configContent = await readFile7(paths.config, "utf-8");
|
|
31994
|
+
existingConfig = migrateConfig(
|
|
31995
|
+
JSON.parse(configContent)
|
|
31996
|
+
);
|
|
31997
|
+
} catch {
|
|
31998
|
+
}
|
|
31999
|
+
if (framework !== "unknown" && existingConfig) {
|
|
32000
|
+
const frameworkPaths = FRAMEWORK_SPECS[framework].components;
|
|
32001
|
+
existingConfig.framework = framework;
|
|
32002
|
+
existingConfig.componentsPath = frameworkPaths.components;
|
|
32003
|
+
existingConfig.primitivesPath = frameworkPaths.primitives;
|
|
32004
|
+
existingConfig.compositesPath = frameworkPaths.composites;
|
|
32005
|
+
existingConfig.rulesPath = frameworkPaths.rules;
|
|
32006
|
+
}
|
|
32007
|
+
let existingTokens = [];
|
|
32008
|
+
try {
|
|
32009
|
+
existingTokens = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS2).list();
|
|
32010
|
+
} catch {
|
|
32011
|
+
}
|
|
32012
|
+
const overriddenTokens = existingTokens.filter((t) => t.userOverride);
|
|
32013
|
+
if (overriddenTokens.length > 0) {
|
|
32014
|
+
const backup = {
|
|
32015
|
+
resetAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32016
|
+
reason: "rafters init --reset",
|
|
32017
|
+
overrides: overriddenTokens.map((t) => ({
|
|
32018
|
+
name: t.name,
|
|
32019
|
+
value: t.value,
|
|
32020
|
+
userOverride: t.userOverride,
|
|
32021
|
+
namespace: t.namespace
|
|
32022
|
+
}))
|
|
32023
|
+
};
|
|
32024
|
+
await mkdir3(paths.output, { recursive: true });
|
|
32025
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
32026
|
+
const backupPath = join13(paths.output, `reset-${timestamp}.json`);
|
|
32027
|
+
await writeFile5(backupPath, JSON.stringify(backup, null, 2));
|
|
32028
|
+
log({
|
|
32029
|
+
event: "init:reset_backup",
|
|
32030
|
+
path: backupPath,
|
|
32031
|
+
overrideCount: overriddenTokens.length
|
|
32032
|
+
});
|
|
32033
|
+
}
|
|
32034
|
+
let exports;
|
|
32035
|
+
if (isAgentMode2) {
|
|
32036
|
+
exports = existingConfig?.exports ?? DEFAULT_EXPORTS;
|
|
32037
|
+
log({ event: "init:exports_default", exports });
|
|
32038
|
+
} else {
|
|
32039
|
+
if (isInteractive()) {
|
|
32040
|
+
log({ event: "init:prompting_exports" });
|
|
32041
|
+
}
|
|
32042
|
+
exports = await promptExportFormats(existingConfig?.exports);
|
|
32043
|
+
log({ event: "init:exports_selected", exports });
|
|
32044
|
+
}
|
|
32045
|
+
const system = generateBaseSystem({});
|
|
32046
|
+
const registry2 = new TokenRegistry(system.allTokens, REGISTRY_PLUGINS2);
|
|
32047
|
+
log({
|
|
32048
|
+
event: "init:reset_generated",
|
|
32049
|
+
tokenCount: registry2.size()
|
|
32050
|
+
});
|
|
32051
|
+
await rm(paths.tokens, { recursive: true, force: true });
|
|
32052
|
+
await mkdir3(paths.tokens, { recursive: true });
|
|
32053
|
+
saveRegistryToDir(paths.tokens, registry2);
|
|
32054
|
+
const allTokensToSave = registry2.list();
|
|
32055
|
+
const namespaceCount = new Set(allTokensToSave.map((t) => t.namespace)).size;
|
|
32056
|
+
log({
|
|
32057
|
+
event: "init:registry_saved",
|
|
32058
|
+
path: paths.tokens,
|
|
32059
|
+
namespaceCount
|
|
32060
|
+
});
|
|
32061
|
+
await mkdir3(paths.output, { recursive: true });
|
|
32062
|
+
const outputs = await generateOutputs(
|
|
32063
|
+
cwd,
|
|
32064
|
+
paths,
|
|
32065
|
+
registry2,
|
|
32066
|
+
exports,
|
|
32067
|
+
source,
|
|
32068
|
+
"class",
|
|
32069
|
+
existingConfig
|
|
32070
|
+
);
|
|
32071
|
+
if (existingConfig) {
|
|
32072
|
+
existingConfig.exports = exports;
|
|
32073
|
+
await writeFile5(paths.config, JSON.stringify(existingConfig, null, 2));
|
|
32074
|
+
} else {
|
|
32075
|
+
const frameworkPaths = FRAMEWORK_SPECS[framework].components;
|
|
32076
|
+
const newConfig = {
|
|
32077
|
+
framework,
|
|
32078
|
+
componentsPath: frameworkPaths.components,
|
|
32079
|
+
primitivesPath: frameworkPaths.primitives,
|
|
32080
|
+
compositesPath: frameworkPaths.composites,
|
|
32081
|
+
rulesPath: frameworkPaths.rules,
|
|
32082
|
+
cssPath: null,
|
|
32083
|
+
...source ? { source } : {},
|
|
32084
|
+
intent: "efficient",
|
|
32085
|
+
fonts: { path: null, imports: [] },
|
|
32086
|
+
exports,
|
|
32087
|
+
installed: { components: [], primitives: [], composites: [], rules: [] }
|
|
32088
|
+
};
|
|
32089
|
+
await writeFile5(paths.config, JSON.stringify(newConfig, null, 2));
|
|
32090
|
+
}
|
|
32091
|
+
log({
|
|
32092
|
+
event: "init:complete",
|
|
32093
|
+
outputs,
|
|
32094
|
+
path: paths.output
|
|
32095
|
+
});
|
|
32096
|
+
}
|
|
32097
|
+
async function init(options) {
|
|
32098
|
+
setAgentMode(options.agent ?? false);
|
|
32099
|
+
const isAgentMode2 = options.agent ?? false;
|
|
32100
|
+
const cwd = process.cwd();
|
|
32101
|
+
const paths = getRaftersPaths(cwd);
|
|
32102
|
+
log({ event: "init:start", cwd });
|
|
32103
|
+
const project = await detectProject(cwd);
|
|
32104
|
+
const { framework: detectedFramework, shadcn, tailwindVersion, astroHasReact } = project;
|
|
32105
|
+
const source = shadcn ? "shadcn" : void 0;
|
|
32106
|
+
log({
|
|
32107
|
+
event: "init:detected",
|
|
32108
|
+
framework: detectedFramework,
|
|
32109
|
+
tailwindVersion,
|
|
32110
|
+
hasShadcn: !!shadcn
|
|
32111
|
+
});
|
|
32112
|
+
const framework = await resolveFramework(detectedFramework, options.framework, isAgentMode2);
|
|
32113
|
+
if (framework !== detectedFramework) {
|
|
32114
|
+
log({
|
|
32115
|
+
event: "init:framework_resolved",
|
|
32116
|
+
detected: detectedFramework,
|
|
32117
|
+
resolved: framework,
|
|
32118
|
+
source: options.framework ? "flag" : "prompt"
|
|
32119
|
+
});
|
|
32120
|
+
}
|
|
32121
|
+
if (isTailwindV3(tailwindVersion)) {
|
|
32122
|
+
throw new Error("Tailwind v3 detected. Rafters requires Tailwind v4.");
|
|
32123
|
+
}
|
|
32124
|
+
const raftersExists = existsSync7(paths.root);
|
|
32125
|
+
if (options.reset && !raftersExists) {
|
|
32126
|
+
throw new Error("Nothing to reset. No .rafters/ directory found.");
|
|
32127
|
+
}
|
|
32128
|
+
if (raftersExists && options.reset) {
|
|
32129
|
+
await resetToDefaults(cwd, paths, source, isAgentMode2, framework);
|
|
32130
|
+
return;
|
|
32131
|
+
}
|
|
32132
|
+
if (raftersExists && !options.rebuild) {
|
|
32133
|
+
throw new Error(
|
|
32134
|
+
".rafters/ directory already exists. Use --rebuild to regenerate output files, or --reset to start from defaults."
|
|
32135
|
+
);
|
|
32136
|
+
}
|
|
32137
|
+
if (raftersExists && options.rebuild) {
|
|
32138
|
+
await regenerateFromExisting(cwd, paths, source, isAgentMode2, framework);
|
|
32139
|
+
return;
|
|
32140
|
+
}
|
|
32141
|
+
let exports;
|
|
32142
|
+
if (isAgentMode2) {
|
|
32143
|
+
exports = DEFAULT_EXPORTS;
|
|
32144
|
+
log({ event: "init:exports_default", exports });
|
|
32145
|
+
} else {
|
|
32146
|
+
if (isInteractive()) {
|
|
32147
|
+
log({ event: "init:prompting_exports" });
|
|
32356
32148
|
}
|
|
32357
|
-
|
|
32149
|
+
exports = await promptExportFormats();
|
|
32150
|
+
log({ event: "init:exports_selected", exports });
|
|
32358
32151
|
}
|
|
32359
|
-
|
|
32360
|
-
|
|
32361
|
-
|
|
32362
|
-
|
|
32363
|
-
|
|
32364
|
-
|
|
32365
|
-
|
|
32366
|
-
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
|
|
32370
|
-
|
|
32371
|
-
|
|
32372
|
-
|
|
32373
|
-
|
|
32374
|
-
|
|
32375
|
-
|
|
32152
|
+
const adapter = getAdapter(source ?? "tailwind");
|
|
32153
|
+
const baseConfig = {};
|
|
32154
|
+
const sourceCssPathForBase = resolveSourceCssPath(
|
|
32155
|
+
cwd,
|
|
32156
|
+
framework,
|
|
32157
|
+
detectedFramework,
|
|
32158
|
+
project.cssPath,
|
|
32159
|
+
shadcn?.tailwind?.css,
|
|
32160
|
+
log
|
|
32161
|
+
);
|
|
32162
|
+
if (sourceCssPathForBase !== null) {
|
|
32163
|
+
try {
|
|
32164
|
+
const cssForBase = await readFile7(join13(cwd, sourceCssPathForBase), "utf-8");
|
|
32165
|
+
const baseSlots = [
|
|
32166
|
+
{
|
|
32167
|
+
detect: () => adapter.detectSpacing(cssForBase).base,
|
|
32168
|
+
field: "baseSpacingUnit",
|
|
32169
|
+
event: "init:import_spacing_applied",
|
|
32170
|
+
eventKey: "baseSpacingUnit"
|
|
32171
|
+
},
|
|
32172
|
+
{
|
|
32173
|
+
detect: () => adapter.detectRadius(cssForBase).base,
|
|
32174
|
+
field: "baseRadiusOverride",
|
|
32175
|
+
event: "init:import_radius_applied",
|
|
32176
|
+
eventKey: "baseRadius"
|
|
32177
|
+
},
|
|
32178
|
+
{
|
|
32179
|
+
detect: () => adapter.detectFontSize(cssForBase).base,
|
|
32180
|
+
field: "baseFontSizeOverride",
|
|
32181
|
+
event: "init:import_font_size_applied",
|
|
32182
|
+
eventKey: "baseFontSize"
|
|
32183
|
+
},
|
|
32184
|
+
{
|
|
32185
|
+
detect: () => adapter.detectFocusRing(cssForBase).width,
|
|
32186
|
+
field: "focusRingWidthOverride",
|
|
32187
|
+
event: "init:import_focus_ring_applied",
|
|
32188
|
+
eventKey: "focusRingWidth"
|
|
32189
|
+
}
|
|
32190
|
+
// Motion duration is intentionally not imported -- rafters' motion
|
|
32191
|
+
// system is research-backed and most projects' `--duration-base`
|
|
32192
|
+
// values are unresearched defaults. See importers/bases.ts for the
|
|
32193
|
+
// policy note; designers can `rafters set motion-duration-base ...`
|
|
32194
|
+
// after init to override explicitly.
|
|
32195
|
+
];
|
|
32196
|
+
for (const slot of baseSlots) {
|
|
32197
|
+
const value = slot.detect();
|
|
32198
|
+
if (value === void 0) continue;
|
|
32199
|
+
baseConfig[slot.field] = value;
|
|
32200
|
+
log({
|
|
32201
|
+
event: slot.event,
|
|
32202
|
+
cssPath: sourceCssPathForBase,
|
|
32203
|
+
[slot.eventKey]: value
|
|
32204
|
+
});
|
|
32205
|
+
}
|
|
32206
|
+
} catch (err) {
|
|
32207
|
+
if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) throw err;
|
|
32376
32208
|
}
|
|
32377
|
-
const result = composites2.map((c4) => ({
|
|
32378
|
-
id: c4.manifest.id,
|
|
32379
|
-
name: c4.manifest.name,
|
|
32380
|
-
category: c4.manifest.category,
|
|
32381
|
-
description: c4.manifest.description,
|
|
32382
|
-
cognitiveLoad: c4.manifest.cognitiveLoad,
|
|
32383
|
-
solves: c4.manifest.solves,
|
|
32384
|
-
appliesWhen: c4.manifest.appliesWhen,
|
|
32385
|
-
usagePatterns: c4.manifest.usagePatterns,
|
|
32386
|
-
input: c4.input,
|
|
32387
|
-
output: c4.output,
|
|
32388
|
-
blockCount: c4.blocks.length,
|
|
32389
|
-
// Surface which blocks carry which validation rules. Rules compile to
|
|
32390
|
-
// native HTML5 constraint attributes at build, so agents need to see
|
|
32391
|
-
// them to reason about a composite's behavior. Only blocks with rules
|
|
32392
|
-
// are listed -- the array is empty when nothing is constrained.
|
|
32393
|
-
blockRules: c4.blocks.filter((b2) => b2.rules && b2.rules.length > 0).map((b2) => ({ id: b2.id, type: b2.type, rules: b2.rules }))
|
|
32394
|
-
}));
|
|
32395
|
-
return this.jsonResult({ composites: result, deprecated: DEPRECATED_MSG });
|
|
32396
32209
|
}
|
|
32397
|
-
|
|
32398
|
-
|
|
32399
|
-
|
|
32400
|
-
|
|
32401
|
-
|
|
32402
|
-
|
|
32403
|
-
|
|
32404
|
-
|
|
32405
|
-
|
|
32406
|
-
|
|
32407
|
-
|
|
32408
|
-
|
|
32409
|
-
|
|
32410
|
-
|
|
32411
|
-
|
|
32412
|
-
|
|
32210
|
+
const system = generateBaseSystem(baseConfig);
|
|
32211
|
+
const registry2 = new TokenRegistry(system.allTokens, REGISTRY_PLUGINS2);
|
|
32212
|
+
log({
|
|
32213
|
+
event: "init:generated",
|
|
32214
|
+
tokenCount: registry2.size()
|
|
32215
|
+
});
|
|
32216
|
+
await mkdir3(paths.tokens, { recursive: true });
|
|
32217
|
+
await mkdir3(paths.output, { recursive: true });
|
|
32218
|
+
saveRegistryToDir(paths.tokens, registry2);
|
|
32219
|
+
const allTokensToSave = registry2.list();
|
|
32220
|
+
const namespaceCount = new Set(allTokensToSave.map((t) => t.namespace)).size;
|
|
32221
|
+
log({
|
|
32222
|
+
event: "init:registry_saved",
|
|
32223
|
+
path: paths.tokens,
|
|
32224
|
+
namespaceCount
|
|
32225
|
+
});
|
|
32226
|
+
const outputs = await generateOutputs(cwd, paths, registry2, exports, source);
|
|
32227
|
+
let detectedCssPath = null;
|
|
32228
|
+
detectedCssPath = resolveSourceCssPath(
|
|
32229
|
+
cwd,
|
|
32230
|
+
framework,
|
|
32231
|
+
detectedFramework,
|
|
32232
|
+
project.cssPath,
|
|
32233
|
+
shadcn?.tailwind?.css,
|
|
32234
|
+
log
|
|
32235
|
+
);
|
|
32236
|
+
if (source !== "shadcn" && exports.tailwind) {
|
|
32237
|
+
if (detectedCssPath) {
|
|
32238
|
+
await updateMainCss(cwd, detectedCssPath, ".rafters/output/rafters.css");
|
|
32413
32239
|
} else {
|
|
32414
|
-
|
|
32415
|
-
|
|
32416
|
-
|
|
32417
|
-
|
|
32418
|
-
const available = all.filter((c4) => c4.manifest.solves || c4.manifest.usagePatterns).map((c4) => ({
|
|
32419
|
-
id: c4.manifest.id,
|
|
32420
|
-
solves: c4.manifest.solves
|
|
32421
|
-
}));
|
|
32422
|
-
return this.errorResult("No patterns found matching query", {
|
|
32423
|
-
available,
|
|
32424
|
-
deprecated: DEPRECATED_MSG
|
|
32240
|
+
log({
|
|
32241
|
+
event: "init:css_not_found",
|
|
32242
|
+
message: 'No main CSS file found. Add @import ".rafters/output/rafters.css" manually.',
|
|
32243
|
+
searchedLocations: FRAMEWORK_SPECS[framework].cssLocations
|
|
32425
32244
|
});
|
|
32426
32245
|
}
|
|
32427
|
-
const patterns = composites2.map((c4) => ({
|
|
32428
|
-
id: c4.manifest.id,
|
|
32429
|
-
name: c4.manifest.name,
|
|
32430
|
-
solves: c4.manifest.solves,
|
|
32431
|
-
appliesWhen: c4.manifest.appliesWhen,
|
|
32432
|
-
cognitiveLoad: c4.manifest.cognitiveLoad,
|
|
32433
|
-
usagePatterns: c4.manifest.usagePatterns
|
|
32434
|
-
}));
|
|
32435
|
-
return this.jsonResult({ patterns, deprecated: DEPRECATED_MSG });
|
|
32436
32246
|
}
|
|
32437
|
-
|
|
32438
|
-
|
|
32439
|
-
|
|
32440
|
-
|
|
32441
|
-
|
|
32442
|
-
|
|
32443
|
-
|
|
32444
|
-
|
|
32247
|
+
let componentTarget = frameworkToTarget(framework);
|
|
32248
|
+
if (framework === "astro" && astroHasReact && isInteractive() && !isAgentMode2) {
|
|
32249
|
+
componentTarget = await select({
|
|
32250
|
+
message: "This Astro project has React integration. Install components as:",
|
|
32251
|
+
choices: [
|
|
32252
|
+
{
|
|
32253
|
+
name: "Astro components (zero client JS, server-rendered)",
|
|
32254
|
+
value: "astro"
|
|
32255
|
+
},
|
|
32256
|
+
{
|
|
32257
|
+
name: "React components (client islands with client:load)",
|
|
32258
|
+
value: "react"
|
|
32259
|
+
}
|
|
32260
|
+
]
|
|
32261
|
+
});
|
|
32445
32262
|
}
|
|
32446
|
-
|
|
32447
|
-
|
|
32448
|
-
|
|
32449
|
-
|
|
32450
|
-
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
|
|
32263
|
+
const frameworkPaths = FRAMEWORK_SPECS[framework].components;
|
|
32264
|
+
const config2 = {
|
|
32265
|
+
framework,
|
|
32266
|
+
...source ? { source } : {},
|
|
32267
|
+
componentTarget,
|
|
32268
|
+
componentsPath: frameworkPaths.components,
|
|
32269
|
+
primitivesPath: frameworkPaths.primitives,
|
|
32270
|
+
compositesPath: frameworkPaths.composites,
|
|
32271
|
+
rulesPath: frameworkPaths.rules,
|
|
32272
|
+
cssPath: detectedCssPath,
|
|
32273
|
+
exports,
|
|
32274
|
+
intent: "efficient",
|
|
32275
|
+
fonts: { path: null, imports: [] },
|
|
32276
|
+
installed: {
|
|
32277
|
+
components: [],
|
|
32278
|
+
primitives: [],
|
|
32279
|
+
composites: [],
|
|
32280
|
+
rules: []
|
|
32454
32281
|
}
|
|
32455
|
-
|
|
32282
|
+
};
|
|
32283
|
+
await writeFile5(paths.config, JSON.stringify(config2, null, 2));
|
|
32284
|
+
if (detectedCssPath) {
|
|
32285
|
+
let sourceCss = null;
|
|
32456
32286
|
try {
|
|
32457
|
-
|
|
32287
|
+
sourceCss = await readFile7(join13(cwd, detectedCssPath), "utf-8");
|
|
32458
32288
|
} catch (err) {
|
|
32459
|
-
|
|
32460
|
-
return this.errorResult(`failed to build intel graph: ${message}`, {
|
|
32461
|
-
deprecated: DEPRECATED_MSG
|
|
32462
|
-
});
|
|
32289
|
+
if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) throw err;
|
|
32463
32290
|
}
|
|
32464
|
-
|
|
32465
|
-
|
|
32466
|
-
|
|
32291
|
+
if (sourceCss !== null) {
|
|
32292
|
+
const summary = senseShadcnCss(sourceCss);
|
|
32293
|
+
if (summary.totalDeclarations > 0) {
|
|
32294
|
+
log({
|
|
32295
|
+
event: "init:import_sensed",
|
|
32296
|
+
cssPath: detectedCssPath,
|
|
32297
|
+
...summary
|
|
32298
|
+
});
|
|
32299
|
+
const classification = classifyDeclarations(extractShadcnRoot(sourceCss));
|
|
32300
|
+
const colorDecls = adapter.detectColors(sourceCss);
|
|
32301
|
+
const nonColorDecls = [
|
|
32302
|
+
...classification.byNamespace.typography,
|
|
32303
|
+
...classification.byNamespace.spacing,
|
|
32304
|
+
...classification.byNamespace.radius,
|
|
32305
|
+
...classification.byNamespace.shadow
|
|
32306
|
+
];
|
|
32307
|
+
const toImportColors = [];
|
|
32308
|
+
for (const color of colorDecls) {
|
|
32309
|
+
const accept = isAgentMode2 ? true : await confirm({
|
|
32310
|
+
message: `Import --${color.name}: ${color.value}?`,
|
|
32311
|
+
default: true
|
|
32312
|
+
});
|
|
32313
|
+
if (accept) toImportColors.push(color);
|
|
32314
|
+
}
|
|
32315
|
+
const toImportNonColors = [];
|
|
32316
|
+
for (const decl of nonColorDecls) {
|
|
32317
|
+
const accept = isAgentMode2 ? true : await confirm({
|
|
32318
|
+
message: `Import --${decl.name}: ${decl.value}?`,
|
|
32319
|
+
default: true
|
|
32320
|
+
});
|
|
32321
|
+
if (accept) toImportNonColors.push(decl);
|
|
32322
|
+
}
|
|
32323
|
+
if (toImportColors.length + toImportNonColors.length > 0) {
|
|
32324
|
+
for (const color of toImportColors) {
|
|
32325
|
+
const familyName = `imported-${color.name}`;
|
|
32326
|
+
const reason = `imported from --${color.name} in ${detectedCssPath}`;
|
|
32327
|
+
for (const t of importColorFamily(familyName, "500", color.oklch)) {
|
|
32328
|
+
registry2.define(t);
|
|
32329
|
+
}
|
|
32330
|
+
if (color.namespace === "semantic") {
|
|
32331
|
+
registry2.set(color.name, { family: familyName, position: "500" }, { reason });
|
|
32332
|
+
}
|
|
32333
|
+
}
|
|
32334
|
+
const skipped = [];
|
|
32335
|
+
for (const decl of toImportNonColors) {
|
|
32336
|
+
const reason = `imported from --${decl.name} in ${detectedCssPath}`;
|
|
32337
|
+
if (registry2.has(decl.name)) {
|
|
32338
|
+
registry2.set(decl.name, decl.value, { reason });
|
|
32339
|
+
} else {
|
|
32340
|
+
skipped.push(decl.name);
|
|
32341
|
+
}
|
|
32342
|
+
}
|
|
32343
|
+
saveRegistryToDir(paths.tokens, registry2);
|
|
32344
|
+
await generateOutputs(cwd, paths, registry2, exports, source);
|
|
32345
|
+
const importedNames = [
|
|
32346
|
+
...toImportColors.map((c4) => c4.name),
|
|
32347
|
+
...toImportNonColors.map((d2) => d2.name)
|
|
32348
|
+
];
|
|
32349
|
+
await stripImportedDeclarations(cwd, detectedCssPath, importedNames);
|
|
32350
|
+
log({
|
|
32351
|
+
event: "init:import_applied",
|
|
32352
|
+
count: toImportColors.length + toImportNonColors.length - skipped.length,
|
|
32353
|
+
cssPath: detectedCssPath,
|
|
32354
|
+
...skipped.length > 0 ? { skipped } : {}
|
|
32355
|
+
});
|
|
32356
|
+
}
|
|
32357
|
+
}
|
|
32358
|
+
{
|
|
32359
|
+
const themeDecls = extractThemeBlocks2(sourceCss);
|
|
32360
|
+
if (themeDecls.length > 0) {
|
|
32361
|
+
const POSITION_SUFFIX = new RegExp(`^(.+)-(${SCALE_POSITIONS.join("|")})$`);
|
|
32362
|
+
const familySeeds = /* @__PURE__ */ new Map();
|
|
32363
|
+
for (const decl of themeDecls) {
|
|
32364
|
+
if (!decl.name.startsWith("color-")) continue;
|
|
32365
|
+
const oklch2 = tryParseColor(decl.value);
|
|
32366
|
+
if (oklch2 === null) continue;
|
|
32367
|
+
const m3 = decl.name.match(POSITION_SUFFIX);
|
|
32368
|
+
const baseName = ((m3 ? m3[1] : decl.name) ?? decl.name).replace(/^color-/, "");
|
|
32369
|
+
if (!baseName) continue;
|
|
32370
|
+
const seedPosition = m3?.[2] ?? "500";
|
|
32371
|
+
const existing = familySeeds.get(baseName);
|
|
32372
|
+
if (!existing || seedPosition === "500") {
|
|
32373
|
+
familySeeds.set(baseName, { seed: oklch2, seedPosition });
|
|
32374
|
+
}
|
|
32375
|
+
}
|
|
32376
|
+
const families = Array.from(familySeeds, ([name, info]) => ({
|
|
32377
|
+
name,
|
|
32378
|
+
...info
|
|
32379
|
+
}));
|
|
32380
|
+
if (families.length > 0) {
|
|
32381
|
+
for (const family of families) {
|
|
32382
|
+
for (const t of importColorFamily(family.name, family.seedPosition, family.seed)) {
|
|
32383
|
+
registry2.define(t);
|
|
32384
|
+
}
|
|
32385
|
+
}
|
|
32386
|
+
const ALL_ROLES = [
|
|
32387
|
+
"primary",
|
|
32388
|
+
"secondary",
|
|
32389
|
+
"tertiary",
|
|
32390
|
+
"accent",
|
|
32391
|
+
"highlight",
|
|
32392
|
+
"neutral",
|
|
32393
|
+
"muted",
|
|
32394
|
+
"success",
|
|
32395
|
+
"warning",
|
|
32396
|
+
"destructive",
|
|
32397
|
+
"info"
|
|
32398
|
+
];
|
|
32399
|
+
const ROLES = ALL_ROLES.filter((r) => {
|
|
32400
|
+
const tok = registry2.get(r);
|
|
32401
|
+
return tok !== void 0 && tok.namespace === "semantic";
|
|
32402
|
+
});
|
|
32403
|
+
const assignments = [];
|
|
32404
|
+
for (const [i, role] of ROLES.entries()) {
|
|
32405
|
+
let familyChoice;
|
|
32406
|
+
if (isAgentMode2) {
|
|
32407
|
+
familyChoice = families[i]?.name ?? null;
|
|
32408
|
+
} else {
|
|
32409
|
+
familyChoice = await select({
|
|
32410
|
+
message: `Which palette is "${role}"?`,
|
|
32411
|
+
choices: [
|
|
32412
|
+
...families.map((f) => ({ name: f.name, value: f.name })),
|
|
32413
|
+
{ name: "(skip -- keep rafters default)", value: null }
|
|
32414
|
+
]
|
|
32415
|
+
});
|
|
32416
|
+
}
|
|
32417
|
+
if (familyChoice === null) continue;
|
|
32418
|
+
const family = families.find((f) => f.name === familyChoice);
|
|
32419
|
+
if (!family) continue;
|
|
32420
|
+
const position = isAgentMode2 ? family.seedPosition : await select({
|
|
32421
|
+
message: `Which position in "${familyChoice}" for "${role}"?`,
|
|
32422
|
+
choices: SCALE_POSITIONS.map((p2) => ({
|
|
32423
|
+
name: p2,
|
|
32424
|
+
value: p2
|
|
32425
|
+
})),
|
|
32426
|
+
default: family.seedPosition
|
|
32427
|
+
});
|
|
32428
|
+
registry2.set(
|
|
32429
|
+
role,
|
|
32430
|
+
{ family: familyChoice, position },
|
|
32431
|
+
{
|
|
32432
|
+
reason: `assigned ${familyChoice}@${position} as ${role} during import from ${detectedCssPath}`
|
|
32433
|
+
}
|
|
32434
|
+
);
|
|
32435
|
+
assignments.push({ role, family: familyChoice, position });
|
|
32436
|
+
}
|
|
32437
|
+
saveRegistryToDir(paths.tokens, registry2);
|
|
32438
|
+
await generateOutputs(cwd, paths, registry2, exports, source);
|
|
32439
|
+
const themeImportedNames = families.flatMap(
|
|
32440
|
+
(f) => SCALE_POSITIONS.map((p2) => `color-${f.name}-${p2}`).concat([`color-${f.name}`])
|
|
32441
|
+
);
|
|
32442
|
+
await stripImportedDeclarations(cwd, detectedCssPath, themeImportedNames);
|
|
32443
|
+
log({
|
|
32444
|
+
event: "init:import_palettes_applied",
|
|
32445
|
+
cssPath: detectedCssPath,
|
|
32446
|
+
palettesDefined: families.map((f) => f.name),
|
|
32447
|
+
assignments
|
|
32448
|
+
});
|
|
32449
|
+
}
|
|
32450
|
+
}
|
|
32451
|
+
}
|
|
32452
|
+
}
|
|
32453
|
+
if (sourceCss !== null) {
|
|
32454
|
+
const detectedFonts = adapter.detectFonts(sourceCss);
|
|
32455
|
+
if (detectedFonts.length > 0) {
|
|
32456
|
+
const typographyTokens = registry2.list({ namespace: "typography" });
|
|
32457
|
+
const baseFamilyNames = new Set(
|
|
32458
|
+
typographyTokens.filter((t) => (!t.dependsOn || t.dependsOn.length === 0) && isFontStackValue(t.value)).map((t) => t.name)
|
|
32459
|
+
);
|
|
32460
|
+
const fontRoles = [];
|
|
32461
|
+
for (const tok of typographyTokens) {
|
|
32462
|
+
const base = tok.dependsOn?.[0];
|
|
32463
|
+
if (base === void 0 || !baseFamilyNames.has(base)) continue;
|
|
32464
|
+
if (typeof tok.value !== "string" || !tok.value.includes(`var(--${base})`)) continue;
|
|
32465
|
+
fontRoles.push({ role: tok.name, base });
|
|
32466
|
+
}
|
|
32467
|
+
const roleTokenNames = new Set(fontRoles.map((r) => r.role));
|
|
32468
|
+
const canonicalTokenNames = /* @__PURE__ */ new Set([...baseFamilyNames, ...roleTokenNames]);
|
|
32469
|
+
const definedFonts = [];
|
|
32470
|
+
for (const font of detectedFonts) {
|
|
32471
|
+
if (font.sourceDeclName && canonicalTokenNames.has(font.sourceDeclName)) continue;
|
|
32472
|
+
const tokenName = font.sourceDeclName ?? `font-${slugifyFontName(font.name)}`;
|
|
32473
|
+
if (registry2.has(tokenName)) continue;
|
|
32474
|
+
registry2.define({
|
|
32475
|
+
name: tokenName,
|
|
32476
|
+
namespace: "typography",
|
|
32477
|
+
category: "typography",
|
|
32478
|
+
value: font.stack,
|
|
32479
|
+
description: `Imported from ${detectedCssPath}. ${font.sourceDeclName ? `Declared as --${font.sourceDeclName}.` : "Loaded via @font-face or @import."}`,
|
|
32480
|
+
userOverride: null,
|
|
32481
|
+
containerQueryAware: false
|
|
32482
|
+
});
|
|
32483
|
+
definedFonts.push(tokenName);
|
|
32484
|
+
}
|
|
32485
|
+
const fontAssignments = [];
|
|
32486
|
+
const declaredByBase = /* @__PURE__ */ new Map();
|
|
32487
|
+
for (const font of detectedFonts) {
|
|
32488
|
+
if (font.sourceDeclName && baseFamilyNames.has(font.sourceDeclName)) {
|
|
32489
|
+
if (!declaredByBase.has(font.sourceDeclName)) {
|
|
32490
|
+
declaredByBase.set(font.sourceDeclName, font);
|
|
32491
|
+
}
|
|
32492
|
+
}
|
|
32493
|
+
}
|
|
32494
|
+
for (const { role, base } of fontRoles) {
|
|
32495
|
+
let choice;
|
|
32496
|
+
if (isAgentMode2) {
|
|
32497
|
+
const declared = declaredByBase.get(base);
|
|
32498
|
+
if (declared !== void 0) {
|
|
32499
|
+
choice = declared.name;
|
|
32500
|
+
} else if (/mono/.test(base)) {
|
|
32501
|
+
choice = detectedFonts.find((f) => /mono/i.test(f.name))?.name ?? null;
|
|
32502
|
+
} else {
|
|
32503
|
+
const sansLike = detectedFonts.find((f) => !/mono/i.test(f.name));
|
|
32504
|
+
choice = sansLike?.name ?? null;
|
|
32505
|
+
}
|
|
32506
|
+
} else {
|
|
32507
|
+
const prompt = role.replace(/^font-/, "");
|
|
32508
|
+
choice = await select({
|
|
32509
|
+
message: `Which font is "${prompt}"?`,
|
|
32510
|
+
choices: [
|
|
32511
|
+
...detectedFonts.map((f) => ({ name: f.name, value: f.name })),
|
|
32512
|
+
{ name: "(skip -- keep rafters default)", value: null }
|
|
32513
|
+
]
|
|
32514
|
+
});
|
|
32515
|
+
}
|
|
32516
|
+
if (choice === null) continue;
|
|
32517
|
+
const font = detectedFonts.find((f) => f.name === choice);
|
|
32518
|
+
if (font === void 0) continue;
|
|
32519
|
+
const reason = `assigned ${font.name} as ${role} during import from ${detectedCssPath}`;
|
|
32520
|
+
if (registry2.has(role)) {
|
|
32521
|
+
registry2.set(role, font.stack, { reason });
|
|
32522
|
+
}
|
|
32523
|
+
if (registry2.has(base)) {
|
|
32524
|
+
const current = registry2.get(base);
|
|
32525
|
+
if (current?.value !== font.stack) {
|
|
32526
|
+
registry2.set(base, font.stack, { reason });
|
|
32527
|
+
}
|
|
32528
|
+
}
|
|
32529
|
+
fontAssignments.push({ role, family: font.name });
|
|
32530
|
+
}
|
|
32531
|
+
if (fontAssignments.length > 0 || definedFonts.length > 0) {
|
|
32532
|
+
saveRegistryToDir(paths.tokens, registry2);
|
|
32533
|
+
await generateOutputs(cwd, paths, registry2, exports, source);
|
|
32534
|
+
log({
|
|
32535
|
+
event: "init:import_fonts_applied",
|
|
32536
|
+
cssPath: detectedCssPath,
|
|
32537
|
+
fontsDetected: detectedFonts.map((f) => f.name),
|
|
32538
|
+
assignments: fontAssignments,
|
|
32539
|
+
...definedFonts.length > 0 ? { definedFonts } : {}
|
|
32540
|
+
});
|
|
32541
|
+
}
|
|
32542
|
+
}
|
|
32467
32543
|
}
|
|
32468
|
-
const out = describeWithOverlay(id, graph, ctx);
|
|
32469
|
-
return this.jsonResult(this.withDeprecated(out));
|
|
32470
32544
|
}
|
|
32471
|
-
|
|
32545
|
+
log({
|
|
32546
|
+
event: "init:complete",
|
|
32547
|
+
outputs: [...outputs, "config.rafters.json"],
|
|
32548
|
+
path: paths.output
|
|
32549
|
+
});
|
|
32550
|
+
}
|
|
32551
|
+
|
|
32552
|
+
// src/commands/mcp.ts
|
|
32553
|
+
import { existsSync as existsSync8 } from "fs";
|
|
32554
|
+
import { basename as basename2, join as join14, resolve as resolve5 } from "path";
|
|
32555
|
+
|
|
32556
|
+
// src/mcp/server.ts
|
|
32557
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
32558
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
32559
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
32560
|
+
|
|
32561
|
+
// src/version.ts
|
|
32562
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
32563
|
+
var UNKNOWN_VERSION = "0.0.0-unknown";
|
|
32564
|
+
function readVersion() {
|
|
32565
|
+
try {
|
|
32566
|
+
const parsed = JSON.parse(
|
|
32567
|
+
readFileSync3(new URL("../package.json", import.meta.url), "utf-8")
|
|
32568
|
+
);
|
|
32569
|
+
if (parsed !== null && typeof parsed === "object" && "version" in parsed) {
|
|
32570
|
+
const { version: version2 } = parsed;
|
|
32571
|
+
if (typeof version2 === "string" && version2.length > 0) return version2;
|
|
32572
|
+
}
|
|
32573
|
+
} catch {
|
|
32574
|
+
}
|
|
32575
|
+
return UNKNOWN_VERSION;
|
|
32576
|
+
}
|
|
32577
|
+
var VERSION = readVersion();
|
|
32472
32578
|
|
|
32473
32579
|
// src/mcp/server.ts
|
|
32474
32580
|
async function startMcpServer(workspaces, defaultWorkspace) {
|
|
@@ -32518,8 +32624,8 @@ async function mcp(options) {
|
|
|
32518
32624
|
let defaultWorkspace;
|
|
32519
32625
|
if (options.projectRoot) {
|
|
32520
32626
|
const explicit = resolve5(options.projectRoot);
|
|
32521
|
-
const configPath2 =
|
|
32522
|
-
if (!
|
|
32627
|
+
const configPath2 = join14(explicit, ".rafters", "config.rafters.json");
|
|
32628
|
+
if (!existsSync8(configPath2)) {
|
|
32523
32629
|
process.stderr.write(
|
|
32524
32630
|
`--project-root ${explicit} does not contain .rafters/config.rafters.json
|
|
32525
32631
|
`
|
|
@@ -32539,12 +32645,12 @@ async function mcp(options) {
|
|
|
32539
32645
|
}
|
|
32540
32646
|
|
|
32541
32647
|
// src/commands/studio.ts
|
|
32542
|
-
import { existsSync as
|
|
32648
|
+
import { existsSync as existsSync9 } from "fs";
|
|
32543
32649
|
import { resolve as resolve6 } from "path";
|
|
32544
32650
|
|
|
32545
32651
|
// ../studio/src/api/vite-plugin.ts
|
|
32546
|
-
import { readFile as
|
|
32547
|
-
import { isAbsolute as isAbsolute4, join as
|
|
32652
|
+
import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
32653
|
+
import { isAbsolute as isAbsolute4, join as join15 } from "path";
|
|
32548
32654
|
var REGISTRY_PLUGINS3 = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
|
|
32549
32655
|
var STUDIO_REASON_DEFAULT = "studio interactive edit";
|
|
32550
32656
|
var TokenResponseSchema = external_exports.object({
|
|
@@ -32570,8 +32676,8 @@ var ColorBuildOptionsSchema = external_exports.object({
|
|
|
32570
32676
|
states: external_exports.record(external_exports.string(), external_exports.string()).optional()
|
|
32571
32677
|
});
|
|
32572
32678
|
var projectPath = process.env.RAFTERS_PROJECT_PATH || process.cwd();
|
|
32573
|
-
var outputDir =
|
|
32574
|
-
var configPath =
|
|
32679
|
+
var outputDir = join15(projectPath, ".rafters", "output");
|
|
32680
|
+
var configPath = join15(projectPath, ".rafters", "config.rafters.json");
|
|
32575
32681
|
var FontsConfigSchema = external_exports.object({
|
|
32576
32682
|
path: external_exports.union([external_exports.string(), external_exports.null()]).optional(),
|
|
32577
32683
|
imports: external_exports.array(external_exports.string()).optional()
|
|
@@ -32588,7 +32694,7 @@ var RaftersConfigSchema = external_exports.object({
|
|
|
32588
32694
|
var KNOWN_INTENTS = ["efficient"];
|
|
32589
32695
|
async function readRaftersConfig() {
|
|
32590
32696
|
try {
|
|
32591
|
-
const raw = JSON.parse(await
|
|
32697
|
+
const raw = JSON.parse(await readFile8(configPath, "utf8"));
|
|
32592
32698
|
if ("shadcn" in raw && !("source" in raw)) {
|
|
32593
32699
|
if (raw.shadcn === true) raw.source = "shadcn";
|
|
32594
32700
|
delete raw.shadcn;
|
|
@@ -32998,7 +33104,7 @@ function studioApiPlugin() {
|
|
|
32998
33104
|
return {
|
|
32999
33105
|
name: "rafters-studio-api",
|
|
33000
33106
|
async configureServer(server) {
|
|
33001
|
-
const tokensDir =
|
|
33107
|
+
const tokensDir = join15(projectPath, ".rafters", "tokens");
|
|
33002
33108
|
try {
|
|
33003
33109
|
registry2 = loadRegistryFromDir(tokensDir, REGISTRY_PLUGINS3);
|
|
33004
33110
|
initialized = true;
|
|
@@ -33061,11 +33167,11 @@ function studioApiPlugin() {
|
|
|
33061
33167
|
const classDirs = watchConfig ? resolveContentSources(projectPath, watchConfig) : [];
|
|
33062
33168
|
const newDirs = classDirs.filter((d2) => !trackedClassDirs.has(d2));
|
|
33063
33169
|
if (newDirs.length > 0) {
|
|
33064
|
-
server.watcher.add(newDirs.map((dir) =>
|
|
33170
|
+
server.watcher.add(newDirs.map((dir) => join15(dir, "**/*.classes.ts")));
|
|
33065
33171
|
for (const d2 of newDirs) trackedClassDirs.add(d2);
|
|
33066
33172
|
}
|
|
33067
33173
|
};
|
|
33068
|
-
server.watcher.add([
|
|
33174
|
+
server.watcher.add([join15(tokensDir, "*.rafters.json"), configPath]);
|
|
33069
33175
|
await syncWatchTargets();
|
|
33070
33176
|
let debounce = null;
|
|
33071
33177
|
const onChange = (changed) => {
|
|
@@ -33193,7 +33299,7 @@ function studioApiPlugin() {
|
|
|
33193
33299
|
updated.fonts = merged;
|
|
33194
33300
|
}
|
|
33195
33301
|
try {
|
|
33196
|
-
await
|
|
33302
|
+
await writeFile6(configPath, `${JSON.stringify(updated, null, 2)}
|
|
33197
33303
|
`);
|
|
33198
33304
|
reply({ ok: true, config: updated });
|
|
33199
33305
|
} catch (error47) {
|
|
@@ -33351,7 +33457,7 @@ import { createServer } from "vite";
|
|
|
33351
33457
|
async function studio() {
|
|
33352
33458
|
const cwd = process.cwd();
|
|
33353
33459
|
const paths = getRaftersPaths(cwd);
|
|
33354
|
-
if (!
|
|
33460
|
+
if (!existsSync9(paths.root)) {
|
|
33355
33461
|
console.error('No .rafters/ directory found. Run "rafters init" first.');
|
|
33356
33462
|
process.exit(1);
|
|
33357
33463
|
}
|
|
@@ -33386,6 +33492,9 @@ program.command("init").description("Initialize .rafters/ with default tokens an
|
|
|
33386
33492
|
program.command("add").description("Add rafters components to the project").argument("[components...]", "Component names to add").option("--list", "List available components").option("--overwrite", "Overwrite existing component files").option("--update", "Re-fetch named components from registry").option("--update-all", "Re-fetch all installed components from registry").option("--registry-url <url>", "Custom registry URL").option("--agent", "Output JSON for machine consumption").action(withErrorHandler(add));
|
|
33387
33493
|
program.command("mcp").description("Start MCP server for AI agent access (stdio)").option("--project-root <path>", "Explicit project root (skips .rafters/ discovery)").action(mcp);
|
|
33388
33494
|
program.command("studio").description("Open Studio UI for visual token editing").action(studio);
|
|
33495
|
+
program.command("agents").description(
|
|
33496
|
+
"Write or update the rafters contract block in AGENTS.md for non-Claude-Code agent hosts"
|
|
33497
|
+
).action(withErrorHandler(agents));
|
|
33389
33498
|
program.parse();
|
|
33390
33499
|
/*! Bundled license information:
|
|
33391
33500
|
|