rafters 0.0.84 → 0.0.85
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 +201 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -526,6 +526,10 @@ function generateThemeBlock(groups) {
|
|
|
526
526
|
if (token.lineHeight) {
|
|
527
527
|
lines.push(` --${token.name}--line-height: ${token.lineHeight};`);
|
|
528
528
|
}
|
|
529
|
+
if (token.name.startsWith("line-height-")) {
|
|
530
|
+
const key = token.name.replace(/^line-height-/, "");
|
|
531
|
+
lines.push(` --leading-${key}: ${value};`);
|
|
532
|
+
}
|
|
529
533
|
}
|
|
530
534
|
lines.push("");
|
|
531
535
|
}
|
|
@@ -1182,6 +1186,8 @@ function deriveCandidates(themeCSS) {
|
|
|
1182
1186
|
candidates.add(`font-${name.slice(12)}`);
|
|
1183
1187
|
} else if (name.startsWith("ease-")) {
|
|
1184
1188
|
candidates.add(name);
|
|
1189
|
+
} else if (name.startsWith("leading-")) {
|
|
1190
|
+
candidates.add(name);
|
|
1185
1191
|
} else if (name.startsWith("animate-")) {
|
|
1186
1192
|
candidates.add(name);
|
|
1187
1193
|
} else if (name.startsWith("text-") && !name.includes("--")) {
|
|
@@ -27811,17 +27817,26 @@ var TokenGraph = class {
|
|
|
27811
27817
|
this.snapshot = /* @__PURE__ */ new Map();
|
|
27812
27818
|
for (const [k, v] of this.nodes) this.snapshot.set(k, structuredClone(v));
|
|
27813
27819
|
}
|
|
27820
|
+
// The caller (set/bind) takes a snapshot before invoking this. If any
|
|
27821
|
+
// dependent's transform throws partway through, we roll the whole graph
|
|
27822
|
+
// back to that snapshot and re-throw -- cascade is all-or-nothing, never
|
|
27823
|
+
// half-applied.
|
|
27814
27824
|
cascadeFrom(changed) {
|
|
27815
|
-
|
|
27816
|
-
|
|
27817
|
-
|
|
27818
|
-
|
|
27819
|
-
const
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
|
|
27823
|
-
|
|
27824
|
-
|
|
27825
|
+
try {
|
|
27826
|
+
const dependents = this.collectDependents(changed);
|
|
27827
|
+
if (dependents.size === 0) return;
|
|
27828
|
+
const ordered = this.topoSort(dependents);
|
|
27829
|
+
for (const name of ordered) {
|
|
27830
|
+
const node = this.nodes.get(name);
|
|
27831
|
+
if (!node || node.userOverride || !node.binding) continue;
|
|
27832
|
+
const plugin = this.requirePlugin(node.binding.plugin);
|
|
27833
|
+
const value = plugin.transform(node.binding.input, (n2) => this.get(n2));
|
|
27834
|
+
plugin.outputSchema.parse(value);
|
|
27835
|
+
this.nodes.set(name, { ...node, value });
|
|
27836
|
+
}
|
|
27837
|
+
} catch (error47) {
|
|
27838
|
+
this.undo();
|
|
27839
|
+
throw error47;
|
|
27825
27840
|
}
|
|
27826
27841
|
}
|
|
27827
27842
|
collectDependents(changed) {
|
|
@@ -27941,8 +27956,15 @@ function senseShadcnCss(css) {
|
|
|
27941
27956
|
|
|
27942
27957
|
// ../design-tokens/src/outputs.ts
|
|
27943
27958
|
import { existsSync, realpathSync } from "fs";
|
|
27944
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
27959
|
+
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
27945
27960
|
import { isAbsolute, join as join2, resolve } from "path";
|
|
27961
|
+
var KNOWN_OUTPUT_FILENAMES = [
|
|
27962
|
+
"rafters.css",
|
|
27963
|
+
"rafters.ts",
|
|
27964
|
+
"rafters.json",
|
|
27965
|
+
"rafters.standalone.css",
|
|
27966
|
+
"rafters.documentation.css"
|
|
27967
|
+
];
|
|
27946
27968
|
function fieldToPaths(field) {
|
|
27947
27969
|
if (!field) return [];
|
|
27948
27970
|
if (typeof field === "string") return [field];
|
|
@@ -28007,6 +28029,16 @@ async function regenerateOutputs(registry2, input, hooks2 = {}) {
|
|
|
28007
28029
|
await writeFile(join2(outputDir2, "rafters.documentation.css"), doc);
|
|
28008
28030
|
written.push("rafters.documentation.css");
|
|
28009
28031
|
}
|
|
28032
|
+
const writtenSet = new Set(written);
|
|
28033
|
+
for (const filename of KNOWN_OUTPUT_FILENAMES) {
|
|
28034
|
+
if (writtenSet.has(filename)) continue;
|
|
28035
|
+
const filePath = join2(outputDir2, filename);
|
|
28036
|
+
if (!existsSync(filePath)) continue;
|
|
28037
|
+
try {
|
|
28038
|
+
await unlink(filePath);
|
|
28039
|
+
} catch {
|
|
28040
|
+
}
|
|
28041
|
+
}
|
|
28010
28042
|
hooks2.notify?.();
|
|
28011
28043
|
return written;
|
|
28012
28044
|
}
|
|
@@ -28040,10 +28072,9 @@ var TokenRegistry = class {
|
|
|
28040
28072
|
...t.binding ? { binding: t.binding } : {}
|
|
28041
28073
|
});
|
|
28042
28074
|
}
|
|
28043
|
-
|
|
28044
|
-
|
|
28045
|
-
|
|
28046
|
-
this.graph.bind(t.name, t.binding.plugin, t.binding.input);
|
|
28075
|
+
const ordered = topoSortPass2(parsed, this.plugins);
|
|
28076
|
+
for (const entry of ordered) {
|
|
28077
|
+
this.graph.bind(entry.name, entry.binding.plugin, entry.binding.input);
|
|
28047
28078
|
}
|
|
28048
28079
|
}
|
|
28049
28080
|
registerPlugin(plugin) {
|
|
@@ -28152,6 +28183,39 @@ function toUserOverrideField(override, baseValue) {
|
|
|
28152
28183
|
if (override.kind) result.kind = override.kind;
|
|
28153
28184
|
return result;
|
|
28154
28185
|
}
|
|
28186
|
+
function topoSortPass2(parsed, plugins) {
|
|
28187
|
+
const entries = [];
|
|
28188
|
+
for (const t of parsed) {
|
|
28189
|
+
if (!t.binding || t.userOverride) continue;
|
|
28190
|
+
entries.push({ name: t.name, binding: t.binding });
|
|
28191
|
+
}
|
|
28192
|
+
const entryByName = /* @__PURE__ */ new Map();
|
|
28193
|
+
for (const e of entries) entryByName.set(e.name, e);
|
|
28194
|
+
const sorted = [];
|
|
28195
|
+
const visited = /* @__PURE__ */ new Set();
|
|
28196
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
28197
|
+
const visit = (name, path) => {
|
|
28198
|
+
if (visited.has(name)) return;
|
|
28199
|
+
if (visiting.has(name)) {
|
|
28200
|
+
throw new CircularDependencyError([...path, name]);
|
|
28201
|
+
}
|
|
28202
|
+
visiting.add(name);
|
|
28203
|
+
const entry = entryByName.get(name);
|
|
28204
|
+
if (entry) {
|
|
28205
|
+
const plugin = plugins.get(entry.binding.plugin);
|
|
28206
|
+
if (plugin) {
|
|
28207
|
+
for (const dep of plugin.dependsOn(entry.binding.input)) {
|
|
28208
|
+
if (entryByName.has(dep)) visit(dep, [...path, name]);
|
|
28209
|
+
}
|
|
28210
|
+
}
|
|
28211
|
+
}
|
|
28212
|
+
visiting.delete(name);
|
|
28213
|
+
visited.add(name);
|
|
28214
|
+
if (entry) sorted.push(entry);
|
|
28215
|
+
};
|
|
28216
|
+
for (const e of entries) visit(e.name, []);
|
|
28217
|
+
return sorted;
|
|
28218
|
+
}
|
|
28155
28219
|
|
|
28156
28220
|
// ../design-tokens/src/persistence.ts
|
|
28157
28221
|
function loadRegistryFromDir(dir, plugins = []) {
|
|
@@ -31742,6 +31806,39 @@ var WORKSPACE_PARAM = {
|
|
|
31742
31806
|
description: "Workspace name (directory basename). Required when the MCP session has multiple workspaces and none matches cwd. Call rafters_workspaces to list options."
|
|
31743
31807
|
}
|
|
31744
31808
|
};
|
|
31809
|
+
var GENERATE_LEADING_FILLER = [
|
|
31810
|
+
"give me a ",
|
|
31811
|
+
"give me an ",
|
|
31812
|
+
"give me ",
|
|
31813
|
+
"i need a ",
|
|
31814
|
+
"i need an ",
|
|
31815
|
+
"i need ",
|
|
31816
|
+
"i want a ",
|
|
31817
|
+
"i want an ",
|
|
31818
|
+
"i want ",
|
|
31819
|
+
"create a ",
|
|
31820
|
+
"create an ",
|
|
31821
|
+
"create ",
|
|
31822
|
+
"make me a ",
|
|
31823
|
+
"make me an ",
|
|
31824
|
+
"make a ",
|
|
31825
|
+
"make an ",
|
|
31826
|
+
"make ",
|
|
31827
|
+
"a ",
|
|
31828
|
+
"an ",
|
|
31829
|
+
"the "
|
|
31830
|
+
];
|
|
31831
|
+
function normalizeGenerateQuery(intent) {
|
|
31832
|
+
const lowered = intent.trim().toLowerCase();
|
|
31833
|
+
let candidate = lowered;
|
|
31834
|
+
for (const filler of GENERATE_LEADING_FILLER) {
|
|
31835
|
+
if (candidate.startsWith(filler)) {
|
|
31836
|
+
candidate = candidate.slice(filler.length);
|
|
31837
|
+
break;
|
|
31838
|
+
}
|
|
31839
|
+
}
|
|
31840
|
+
return candidate.trim().replace(/[.,!?]+$/, "");
|
|
31841
|
+
}
|
|
31745
31842
|
var TOOL_DEFINITIONS = [
|
|
31746
31843
|
{
|
|
31747
31844
|
name: "rafters_workspaces",
|
|
@@ -31799,12 +31896,12 @@ var TOOL_DEFINITIONS = [
|
|
|
31799
31896
|
},
|
|
31800
31897
|
{
|
|
31801
31898
|
name: "rafters_generate",
|
|
31802
|
-
description:
|
|
31899
|
+
description: 'Resolve a prose query to ONE registry component and return its verbatim, target-correct snippet with open content slots. A bare component name (e.g. "button", "give me a modal") resolves directly; a semantic question (e.g. "what do I use when it needs to be above everything") falls back to the intent door. Returns { component, target, snippet, slots } where snippet is the registry facet verbatim and each slot is left for the caller to fill. v1 serves single components only -- no parameterization, no composites, no writes.',
|
|
31803
31900
|
inputSchema: {
|
|
31804
31901
|
type: "object",
|
|
31805
31902
|
properties: {
|
|
31806
31903
|
...WORKSPACE_PARAM,
|
|
31807
|
-
intent: { type: "string", description: "
|
|
31904
|
+
intent: { type: "string", description: "A component name or a natural-language request" }
|
|
31808
31905
|
},
|
|
31809
31906
|
required: ["intent"]
|
|
31810
31907
|
}
|
|
@@ -31862,6 +31959,11 @@ var TOOL_DEFINITIONS = [
|
|
|
31862
31959
|
}
|
|
31863
31960
|
];
|
|
31864
31961
|
var DEPRECATED_MSG = "use rafters_describe instead";
|
|
31962
|
+
var OverlayInstalledSchema = external_exports.object({
|
|
31963
|
+
components: external_exports.array(external_exports.string()).optional(),
|
|
31964
|
+
primitives: external_exports.array(external_exports.string()).optional(),
|
|
31965
|
+
composites: external_exports.array(external_exports.string()).optional()
|
|
31966
|
+
});
|
|
31865
31967
|
var RaftersToolHandler = class {
|
|
31866
31968
|
workspaces;
|
|
31867
31969
|
defaultWorkspace;
|
|
@@ -32109,8 +32211,22 @@ var RaftersToolHandler = class {
|
|
|
32109
32211
|
*/
|
|
32110
32212
|
async overlayContext(workspace) {
|
|
32111
32213
|
const config2 = workspace ? await this.readConfig(workspace.root) : null;
|
|
32112
|
-
const
|
|
32113
|
-
|
|
32214
|
+
const parsedInstalled = OverlayInstalledSchema.safeParse(config2?.installed ?? {});
|
|
32215
|
+
if (!parsedInstalled.success) {
|
|
32216
|
+
const issue2 = parsedInstalled.error.issues[0];
|
|
32217
|
+
const field = issue2 && issue2.path.length > 0 ? `installed.${issue2.path.join(".")}` : "installed";
|
|
32218
|
+
const configPath2 = workspace ? getRaftersPaths(workspace.root).config : "config.rafters.json";
|
|
32219
|
+
return {
|
|
32220
|
+
configError: `malformed config at ${configPath2}: ${field} ${issue2?.message ?? "is invalid"}`
|
|
32221
|
+
};
|
|
32222
|
+
}
|
|
32223
|
+
const base = buildInstalledSet({
|
|
32224
|
+
installed: {
|
|
32225
|
+
components: parsedInstalled.data.components ?? [],
|
|
32226
|
+
composites: parsedInstalled.data.composites ?? []
|
|
32227
|
+
}
|
|
32228
|
+
});
|
|
32229
|
+
const components = /* @__PURE__ */ new Set([...base.components, ...parsedInstalled.data.primitives ?? []]);
|
|
32114
32230
|
const parsedTarget = ComponentTargetSchema.safeParse(config2?.componentTarget);
|
|
32115
32231
|
return {
|
|
32116
32232
|
target: parsedTarget.success ? parsedTarget.data : void 0,
|
|
@@ -32139,23 +32255,79 @@ var RaftersToolHandler = class {
|
|
|
32139
32255
|
return this.jsonResult(matchIntent(address, graph));
|
|
32140
32256
|
}
|
|
32141
32257
|
const ctx = await this.overlayContext(resolved);
|
|
32258
|
+
if ("configError" in ctx) {
|
|
32259
|
+
return this.errorResult(ctx.configError);
|
|
32260
|
+
}
|
|
32142
32261
|
return this.jsonResult(describeWithOverlay(address, graph, ctx));
|
|
32143
32262
|
}
|
|
32144
32263
|
/**
|
|
32145
|
-
* The `rafters_generate`
|
|
32146
|
-
*
|
|
32147
|
-
*
|
|
32148
|
-
*
|
|
32149
|
-
*
|
|
32264
|
+
* The `rafters_generate` handler. A prose query resolves -- first through a
|
|
32265
|
+
* light, deterministic direct-name lookup (tier b), then through the existing
|
|
32266
|
+
* intent door (tier c) -- to a single registry component, and the tool returns
|
|
32267
|
+
* that component's authoritative, target-correct snippet verbatim with its
|
|
32268
|
+
* content slots marked open for the caller. Both tiers re-resolve the winning
|
|
32269
|
+
* id through `describe(id, graph, ctx.target)`, so the response always carries
|
|
32270
|
+
* the workspace target's facet, never the intent door's untargeted fields.
|
|
32271
|
+
* v1 serves components only: composites, parameterization, composition,
|
|
32272
|
+
* writes, and MCP-side validation are all out of scope.
|
|
32150
32273
|
*/
|
|
32151
32274
|
async handleGenerate(intent, workspaceName) {
|
|
32152
32275
|
const resolved = this.resolve(workspaceName);
|
|
32153
32276
|
if (workspaceName && !resolved) {
|
|
32154
32277
|
return this.workspaceRequiredError();
|
|
32155
32278
|
}
|
|
32279
|
+
let graph;
|
|
32280
|
+
try {
|
|
32281
|
+
graph = await this.ensureGraph(resolved);
|
|
32282
|
+
} catch (err) {
|
|
32283
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
32284
|
+
return this.errorResult(`failed to build intel graph: ${message}`);
|
|
32285
|
+
}
|
|
32286
|
+
const ctx = await this.overlayContext(resolved);
|
|
32287
|
+
if ("configError" in ctx) {
|
|
32288
|
+
return this.errorResult(ctx.configError);
|
|
32289
|
+
}
|
|
32290
|
+
const candidate = normalizeGenerateQuery(intent);
|
|
32291
|
+
const directHit = graph.nodes.get(candidate);
|
|
32292
|
+
let nodeId;
|
|
32293
|
+
if (directHit && directHit.kind === "component") {
|
|
32294
|
+
nodeId = candidate;
|
|
32295
|
+
} else {
|
|
32296
|
+
const match = matchIntent(intent, graph);
|
|
32297
|
+
if ("use" in match) nodeId = match.use.id;
|
|
32298
|
+
}
|
|
32299
|
+
if (nodeId === void 0) {
|
|
32300
|
+
return this.errorResult("no registry component matches this query");
|
|
32301
|
+
}
|
|
32302
|
+
const result = describe(nodeId, graph, ctx.target);
|
|
32303
|
+
if (result === null || Array.isArray(result) || !("children" in result)) {
|
|
32304
|
+
return this.errorResult("no registry component matches this query");
|
|
32305
|
+
}
|
|
32306
|
+
const node = result;
|
|
32307
|
+
if (node.kind !== "component") {
|
|
32308
|
+
return this.errorResult(
|
|
32309
|
+
`${node.id} resolved, but is a composite -- rafters_generate v1 returns single components only`
|
|
32310
|
+
);
|
|
32311
|
+
}
|
|
32312
|
+
if (ctx.target === void 0) {
|
|
32313
|
+
return this.errorResult(
|
|
32314
|
+
`${node.id} resolved, but no componentTarget is configured for this workspace -- generate cannot choose a snippet without one`
|
|
32315
|
+
);
|
|
32316
|
+
}
|
|
32317
|
+
if (node.snippet === void 0) {
|
|
32318
|
+
return this.errorResult(
|
|
32319
|
+
`${node.id} resolved, but has no ${ctx.target} facet -- nothing to generate for this target`
|
|
32320
|
+
);
|
|
32321
|
+
}
|
|
32156
32322
|
return this.jsonResult({
|
|
32157
|
-
|
|
32158
|
-
|
|
32323
|
+
component: node.id,
|
|
32324
|
+
target: ctx.target,
|
|
32325
|
+
snippet: node.snippet,
|
|
32326
|
+
slots: (node.slots ?? []).map((slot) => ({
|
|
32327
|
+
slot,
|
|
32328
|
+
ownedBy: "caller",
|
|
32329
|
+
status: "open"
|
|
32330
|
+
}))
|
|
32159
32331
|
});
|
|
32160
32332
|
}
|
|
32161
32333
|
/**
|
|
@@ -32290,6 +32462,9 @@ var RaftersToolHandler = class {
|
|
|
32290
32462
|
});
|
|
32291
32463
|
}
|
|
32292
32464
|
const ctx = await this.overlayContext(resolved);
|
|
32465
|
+
if ("configError" in ctx) {
|
|
32466
|
+
return this.errorResult(ctx.configError, { deprecated: DEPRECATED_MSG });
|
|
32467
|
+
}
|
|
32293
32468
|
const out = describeWithOverlay(id, graph, ctx);
|
|
32294
32469
|
return this.jsonResult(this.withDeprecated(out));
|
|
32295
32470
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rafters",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.85",
|
|
4
4
|
"description": "Design Intelligence CLI. Scaffold tokens, import existing shadcn/Tailwind v4 sources, add components, and serve an MCP server so AI agents read decisions instead of guessing.",
|
|
5
5
|
"homepage": "https://rafters.studio",
|
|
6
6
|
"license": "MIT",
|