rafters 0.0.5 → 0.0.6
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/README.md +130 -0
- package/dist/index.js +218 -56
- package/package.json +2 -1
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# rafters
|
|
2
|
+
|
|
3
|
+
Design Intelligence CLI. Scaffold tokens, add components, and serve an MCP server so AI agents build UIs with designer-level judgment instead of guessing.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx rafters init
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install globally:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add -g rafters
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
### `rafters init`
|
|
20
|
+
|
|
21
|
+
Initialize a project with design tokens. Detects your framework, scaffolds `.rafters/` with a complete token system, and generates output files.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
rafters init # Interactive setup
|
|
25
|
+
rafters init --force # Regenerate output from existing config
|
|
26
|
+
rafters init --agent # JSON output for CI/machine consumption
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Supported frameworks:** Next.js, Vite, Remix, React Router, Astro
|
|
30
|
+
|
|
31
|
+
**Export formats** (configured during init):
|
|
32
|
+
|
|
33
|
+
| Format | File | Default | Description |
|
|
34
|
+
|--------|------|---------|-------------|
|
|
35
|
+
| Tailwind CSS | `rafters.css` | Yes | CSS custom properties with `@theme` |
|
|
36
|
+
| TypeScript | `rafters.ts` | Yes | Type-safe constants with JSDoc intelligence |
|
|
37
|
+
| DTCG JSON | `rafters.json` | No | W3C Design Tokens standard format |
|
|
38
|
+
| Compiled CSS | `rafters.compiled.css` | No | Pre-processed, no build step needed |
|
|
39
|
+
|
|
40
|
+
Automatically detects and migrates existing shadcn/ui color values. Requires Tailwind v4.
|
|
41
|
+
|
|
42
|
+
### `rafters add [components...]`
|
|
43
|
+
|
|
44
|
+
Add components from the Rafters registry to your project.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
rafters add button dialog # Add specific components
|
|
48
|
+
rafters add --list # List all available components
|
|
49
|
+
rafters add --overwrite # Replace existing files
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Components include embedded design intelligence: cognitive load ratings (1-7), accessibility requirements, do/never guidance, and trust-building patterns. Dependencies are resolved automatically.
|
|
53
|
+
|
|
54
|
+
### `rafters studio`
|
|
55
|
+
|
|
56
|
+
Launch Studio for visual token editing.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
rafters studio
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Opens a Vite-powered UI where you design by doing: pick a primary color, explain why, watch the system paint your scale. Every decision is recorded with reasoning so AI agents read intent instead of guessing.
|
|
63
|
+
|
|
64
|
+
### `rafters mcp`
|
|
65
|
+
|
|
66
|
+
Start the MCP server for AI agent access via stdio transport.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
rafters mcp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## MCP Tools
|
|
73
|
+
|
|
74
|
+
Four tools give AI agents complete design system access:
|
|
75
|
+
|
|
76
|
+
### `rafters_vocabulary`
|
|
77
|
+
|
|
78
|
+
System overview. Colors, spacing, typography, components, cognitive loads, and available patterns. Call this first to orient.
|
|
79
|
+
|
|
80
|
+
### `rafters_pattern`
|
|
81
|
+
|
|
82
|
+
Deep guidance for specific UI scenarios. Returns which components to use, which tokens to apply, accessibility requirements, and do/never rules.
|
|
83
|
+
|
|
84
|
+
**Available patterns:** `destructive-action`, `form-validation`, `empty-state`, `loading-state`, `navigation-hierarchy`, `data-table`, `modal-dialog`, `tooltip-guidance`, `card-layout`, `dropdown-actions`
|
|
85
|
+
|
|
86
|
+
### `rafters_component`
|
|
87
|
+
|
|
88
|
+
Full intelligence for a specific component. Cognitive load rating, attention economics, accessibility requirements, trust-building patterns, variants, sizes, and primitives.
|
|
89
|
+
|
|
90
|
+
### `rafters_token`
|
|
91
|
+
|
|
92
|
+
Token dependency graph, derivation rules, and human override context. Returns how a token is computed, what it depends on, and whether a designer manually overrode it (with their reasoning).
|
|
93
|
+
|
|
94
|
+
**Derivation rules:** `calc()`, `state:hover`, `scale:600`, `contrast:auto`, `invert`
|
|
95
|
+
|
|
96
|
+
## How It Works
|
|
97
|
+
|
|
98
|
+
Rafters is a Design Intelligence Protocol. AI agents don't have taste - they guess at colors, spacing, hierarchy. Rafters encodes a designer's judgment into queryable data so AI reads decisions instead of guessing.
|
|
99
|
+
|
|
100
|
+
Three layers:
|
|
101
|
+
|
|
102
|
+
- **What** (Components) - 55 React components with embedded intelligence metadata
|
|
103
|
+
- **Where** (Tokens) - 240+ tokens with dependency graph and human override tracking
|
|
104
|
+
- **Why** (Decisions) - Do/never patterns, cognitive load scores, trust patterns, accessibility requirements
|
|
105
|
+
|
|
106
|
+
The token system uses OKLCH color space, modular scales based on musical ratios, and a dependency engine that automatically derives related values. When a designer overrides a computed value, the system records the reason so AI agents respect the intent.
|
|
107
|
+
|
|
108
|
+
## Project Structure
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
.rafters/
|
|
112
|
+
config.rafters.json # Framework paths and export settings
|
|
113
|
+
tokens/
|
|
114
|
+
color.rafters.json # Color tokens with OKLCH values
|
|
115
|
+
spacing.rafters.json # Spacing scale
|
|
116
|
+
typography.rafters.json # Type scale
|
|
117
|
+
output/
|
|
118
|
+
rafters.css # Tailwind CSS export
|
|
119
|
+
rafters.ts # TypeScript constants
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Requirements
|
|
123
|
+
|
|
124
|
+
- Node.js >= 24.12.0
|
|
125
|
+
- Tailwind CSS v4
|
|
126
|
+
- React >= 19.0.0
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -12730,7 +12730,7 @@ function setAgentMode(agent) {
|
|
|
12730
12730
|
}
|
|
12731
12731
|
function log(event) {
|
|
12732
12732
|
if (context.agent) {
|
|
12733
|
-
console.log(event);
|
|
12733
|
+
console.log(JSON.stringify(event));
|
|
12734
12734
|
return;
|
|
12735
12735
|
}
|
|
12736
12736
|
const eventType = event.event;
|
|
@@ -12748,13 +12748,14 @@ function log(event) {
|
|
|
12748
12748
|
}
|
|
12749
12749
|
context.spinner = ora("Generating tokens...").start();
|
|
12750
12750
|
break;
|
|
12751
|
-
case "init:shadcn_detected":
|
|
12751
|
+
case "init:shadcn_detected": {
|
|
12752
12752
|
context.spinner?.info("Found existing shadcn colors");
|
|
12753
12753
|
console.log(` Backed up: ${event.backupPath}`);
|
|
12754
12754
|
const colors = event.colorsFound;
|
|
12755
12755
|
console.log(` Colors: ${colors.light} light, ${colors.dark} dark`);
|
|
12756
12756
|
context.spinner = ora("Generating tokens...").start();
|
|
12757
12757
|
break;
|
|
12758
|
+
}
|
|
12758
12759
|
case "init:generated":
|
|
12759
12760
|
context.spinner?.succeed(`Generated ${event.tokenCount} tokens`);
|
|
12760
12761
|
context.spinner = ora("Saving registry...").start();
|
|
@@ -12782,7 +12783,18 @@ function log(event) {
|
|
|
12782
12783
|
case "init:colors_imported":
|
|
12783
12784
|
console.log(` Imported ${event.count} existing colors`);
|
|
12784
12785
|
break;
|
|
12785
|
-
case "init:
|
|
12786
|
+
case "init:exports_default":
|
|
12787
|
+
console.log(" Using default exports (agent mode)");
|
|
12788
|
+
break;
|
|
12789
|
+
case "init:exports_selected":
|
|
12790
|
+
context.spinner = ora("Generating outputs...").start();
|
|
12791
|
+
break;
|
|
12792
|
+
case "init:compiling_css":
|
|
12793
|
+
if (context.spinner) {
|
|
12794
|
+
context.spinner.text = "Compiling CSS with Tailwind...";
|
|
12795
|
+
}
|
|
12796
|
+
break;
|
|
12797
|
+
case "init:complete": {
|
|
12786
12798
|
context.spinner?.succeed("Done!");
|
|
12787
12799
|
console.log(`
|
|
12788
12800
|
Output: ${event.path}`);
|
|
@@ -12792,12 +12804,14 @@ function log(event) {
|
|
|
12792
12804
|
}
|
|
12793
12805
|
console.log("");
|
|
12794
12806
|
break;
|
|
12807
|
+
}
|
|
12795
12808
|
// Add events
|
|
12796
|
-
case "add:start":
|
|
12809
|
+
case "add:start": {
|
|
12797
12810
|
const components = event.components;
|
|
12798
12811
|
context.spinner = ora(`Adding ${components.join(", ")}...`).start();
|
|
12799
12812
|
break;
|
|
12800
|
-
|
|
12813
|
+
}
|
|
12814
|
+
case "add:installed": {
|
|
12801
12815
|
context.spinner?.succeed(`Installed ${event.component}`);
|
|
12802
12816
|
const files = event.files;
|
|
12803
12817
|
for (const file2 of files) {
|
|
@@ -12805,6 +12819,7 @@ function log(event) {
|
|
|
12805
12819
|
}
|
|
12806
12820
|
context.spinner = ora("Installing...").start();
|
|
12807
12821
|
break;
|
|
12822
|
+
}
|
|
12808
12823
|
case "add:skip":
|
|
12809
12824
|
context.spinner?.warn(`Skipped ${event.component} (already exists)`);
|
|
12810
12825
|
context.spinner = ora("Installing...").start();
|
|
@@ -12843,7 +12858,7 @@ function log(event) {
|
|
|
12843
12858
|
}
|
|
12844
12859
|
function error46(message) {
|
|
12845
12860
|
if (context.agent) {
|
|
12846
|
-
console.error({ event: "error", message });
|
|
12861
|
+
console.error(JSON.stringify({ event: "error", message }));
|
|
12847
12862
|
return;
|
|
12848
12863
|
}
|
|
12849
12864
|
context.spinner?.fail(message);
|
|
@@ -12932,7 +12947,11 @@ async function loadConfig(cwd) {
|
|
|
12932
12947
|
try {
|
|
12933
12948
|
const content = await readFile(paths.config, "utf-8");
|
|
12934
12949
|
return JSON.parse(content);
|
|
12935
|
-
} catch {
|
|
12950
|
+
} catch (err) {
|
|
12951
|
+
if (existsSync(paths.config)) {
|
|
12952
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12953
|
+
log({ event: "add:warning", message: `Failed to load config: ${message}` });
|
|
12954
|
+
}
|
|
12936
12955
|
return null;
|
|
12937
12956
|
}
|
|
12938
12957
|
}
|
|
@@ -12965,12 +12984,13 @@ function transformFileContent(content, config3) {
|
|
|
12965
12984
|
/from\s+['"]\.\/([^'"]+)['"]/g,
|
|
12966
12985
|
`from '@/${componentsPath}/$1'`
|
|
12967
12986
|
);
|
|
12968
|
-
const libPath = primitivesPath
|
|
12987
|
+
const libPath = dirname(primitivesPath);
|
|
12969
12988
|
transformed = transformed.replace(
|
|
12970
12989
|
/from\s+['"]\.\.\/lib\/([^'"]+)['"]/g,
|
|
12971
12990
|
`from '@/${libPath}/$1'`
|
|
12972
12991
|
);
|
|
12973
|
-
const
|
|
12992
|
+
const componentsMatch = componentsPath.match(/^(.*)components\/ui$/);
|
|
12993
|
+
const hooksPath = componentsMatch ? `${componentsMatch[1]}hooks`.replace(/^\//, "") : "hooks";
|
|
12974
12994
|
transformed = transformed.replace(
|
|
12975
12995
|
/from\s+['"]\.\.\/hooks\/([^'"]+)['"]/g,
|
|
12976
12996
|
`from '@/${hooksPath}/$1'`
|
|
@@ -13123,9 +13143,10 @@ async function add(components, options) {
|
|
|
13123
13143
|
}
|
|
13124
13144
|
|
|
13125
13145
|
// src/commands/init.ts
|
|
13126
|
-
import {
|
|
13146
|
+
import { checkbox } from "@inquirer/prompts";
|
|
13147
|
+
import { existsSync as existsSync3 } from "fs";
|
|
13127
13148
|
import { copyFile, mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
13128
|
-
import { join as
|
|
13149
|
+
import { join as join9, relative } from "path";
|
|
13129
13150
|
|
|
13130
13151
|
// ../design-tokens/src/generation-rules.ts
|
|
13131
13152
|
var GenerationRuleParser = class {
|
|
@@ -24155,7 +24176,7 @@ var require_volume = __commonJS({
|
|
|
24155
24176
|
var Dir_1 = require_Dir();
|
|
24156
24177
|
var resolveCrossPlatform = pathModule.resolve;
|
|
24157
24178
|
var { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY, O_SYMLINK, F_OK, COPYFILE_EXCL, COPYFILE_FICLONE_FORCE } = constants_1.constants;
|
|
24158
|
-
var { sep: sep2, relative: relative2, join:
|
|
24179
|
+
var { sep: sep2, relative: relative2, join: join12, dirname: dirname4 } = pathModule.posix ? pathModule.posix : pathModule;
|
|
24159
24180
|
var kMinPoolSpace = 128;
|
|
24160
24181
|
var EPERM = "EPERM";
|
|
24161
24182
|
var ENOENT2 = "ENOENT";
|
|
@@ -24221,7 +24242,7 @@ var require_volume = __commonJS({
|
|
|
24221
24242
|
function flatten(pathPrefix, node) {
|
|
24222
24243
|
for (const path2 in node) {
|
|
24223
24244
|
const contentOrNode = node[path2];
|
|
24224
|
-
const joinedPath =
|
|
24245
|
+
const joinedPath = join12(pathPrefix, path2);
|
|
24225
24246
|
if (typeof contentOrNode === "string" || contentOrNode instanceof buffer_1.Buffer) {
|
|
24226
24247
|
flatJSON[joinedPath] = contentOrNode;
|
|
24227
24248
|
} else if (typeof contentOrNode === "object" && contentOrNode !== null && Object.keys(contentOrNode).length > 0) {
|
|
@@ -24378,7 +24399,7 @@ var require_volume = __commonJS({
|
|
|
24378
24399
|
return null;
|
|
24379
24400
|
node = curr === null || curr === void 0 ? void 0 : curr.getNode();
|
|
24380
24401
|
if (resolveSymlinks && node.isSymlink()) {
|
|
24381
|
-
const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink :
|
|
24402
|
+
const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink : join12(pathModule.dirname(curr.getPath()), node.symlink);
|
|
24382
24403
|
steps = filenameToSteps(resolvedPath).concat(steps.slice(i + 1));
|
|
24383
24404
|
curr = this.root;
|
|
24384
24405
|
i = 0;
|
|
@@ -49133,7 +49154,7 @@ function tagTokenizer() {
|
|
|
49133
49154
|
|
|
49134
49155
|
// ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/parser/tokenizers/type.js
|
|
49135
49156
|
function typeTokenizer(spacing = "compact") {
|
|
49136
|
-
const
|
|
49157
|
+
const join12 = getJoiner(spacing);
|
|
49137
49158
|
return (spec) => {
|
|
49138
49159
|
let curlies = 0;
|
|
49139
49160
|
let lines = [];
|
|
@@ -49176,7 +49197,7 @@ function typeTokenizer(spacing = "compact") {
|
|
|
49176
49197
|
}
|
|
49177
49198
|
parts[0] = parts[0].slice(1);
|
|
49178
49199
|
parts[parts.length - 1] = parts[parts.length - 1].slice(0, -1);
|
|
49179
|
-
spec.type =
|
|
49200
|
+
spec.type = join12(parts);
|
|
49180
49201
|
return spec;
|
|
49181
49202
|
};
|
|
49182
49203
|
}
|
|
@@ -49274,9 +49295,9 @@ function nameTokenizer() {
|
|
|
49274
49295
|
|
|
49275
49296
|
// ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/parser/tokenizers/description.js
|
|
49276
49297
|
function descriptionTokenizer(spacing = "compact", markers = Markers) {
|
|
49277
|
-
const
|
|
49298
|
+
const join12 = getJoiner2(spacing);
|
|
49278
49299
|
return (spec) => {
|
|
49279
|
-
spec.description =
|
|
49300
|
+
spec.description = join12(spec.source, markers);
|
|
49280
49301
|
return spec;
|
|
49281
49302
|
};
|
|
49282
49303
|
}
|
|
@@ -50132,6 +50153,79 @@ async function detectProject(cwd) {
|
|
|
50132
50153
|
};
|
|
50133
50154
|
}
|
|
50134
50155
|
|
|
50156
|
+
// src/utils/exports.ts
|
|
50157
|
+
import { execFileSync } from "child_process";
|
|
50158
|
+
import { existsSync as existsSync2 } from "fs";
|
|
50159
|
+
import { join as join8 } from "path";
|
|
50160
|
+
var DEFAULT_EXPORTS = {
|
|
50161
|
+
tailwind: true,
|
|
50162
|
+
typescript: true,
|
|
50163
|
+
dtcg: false,
|
|
50164
|
+
compiled: false
|
|
50165
|
+
};
|
|
50166
|
+
var EXPORT_CHOICES = [
|
|
50167
|
+
{
|
|
50168
|
+
name: "Tailwind CSS (web projects)",
|
|
50169
|
+
value: "tailwind",
|
|
50170
|
+
checked: true
|
|
50171
|
+
},
|
|
50172
|
+
{
|
|
50173
|
+
name: "TypeScript (type-safe constants)",
|
|
50174
|
+
value: "typescript",
|
|
50175
|
+
checked: true
|
|
50176
|
+
},
|
|
50177
|
+
{
|
|
50178
|
+
name: "DTCG JSON (Figma Tokens, Style Dictionary)",
|
|
50179
|
+
value: "dtcg",
|
|
50180
|
+
checked: false
|
|
50181
|
+
},
|
|
50182
|
+
{
|
|
50183
|
+
name: "Compiled CSS (documentation, no build step)",
|
|
50184
|
+
value: "compiled",
|
|
50185
|
+
checked: false
|
|
50186
|
+
}
|
|
50187
|
+
];
|
|
50188
|
+
var FUTURE_EXPORTS = [
|
|
50189
|
+
{
|
|
50190
|
+
name: "iOS (Swift/SwiftUI)",
|
|
50191
|
+
value: "tailwind",
|
|
50192
|
+
// placeholder
|
|
50193
|
+
checked: false,
|
|
50194
|
+
disabled: "coming soon"
|
|
50195
|
+
},
|
|
50196
|
+
{
|
|
50197
|
+
name: "Android (Compose)",
|
|
50198
|
+
value: "tailwind",
|
|
50199
|
+
// placeholder
|
|
50200
|
+
checked: false,
|
|
50201
|
+
disabled: "coming soon"
|
|
50202
|
+
}
|
|
50203
|
+
];
|
|
50204
|
+
async function generateCompiledCss(cwd, inputPath, outputPath) {
|
|
50205
|
+
const nodeModulesBin = join8(cwd, "node_modules", ".bin", "tailwindcss");
|
|
50206
|
+
const hasTailwindCli = existsSync2(nodeModulesBin);
|
|
50207
|
+
const args = ["-i", inputPath, "-o", outputPath, "--minify"];
|
|
50208
|
+
if (hasTailwindCli) {
|
|
50209
|
+
execFileSync(nodeModulesBin, args, { cwd, stdio: "pipe" });
|
|
50210
|
+
} else {
|
|
50211
|
+
try {
|
|
50212
|
+
execFileSync("pnpm", ["exec", "tailwindcss", ...args], { cwd, stdio: "pipe" });
|
|
50213
|
+
} catch {
|
|
50214
|
+
throw new Error(
|
|
50215
|
+
"Tailwind CLI not found. Install it with: pnpm add -D @tailwindcss/cli"
|
|
50216
|
+
);
|
|
50217
|
+
}
|
|
50218
|
+
}
|
|
50219
|
+
}
|
|
50220
|
+
function selectionsToConfig(selections) {
|
|
50221
|
+
return {
|
|
50222
|
+
tailwind: selections.includes("tailwind"),
|
|
50223
|
+
typescript: selections.includes("typescript"),
|
|
50224
|
+
dtcg: selections.includes("dtcg"),
|
|
50225
|
+
compiled: selections.includes("compiled")
|
|
50226
|
+
};
|
|
50227
|
+
}
|
|
50228
|
+
|
|
50135
50229
|
// src/commands/init.ts
|
|
50136
50230
|
async function backupCss(cssPath) {
|
|
50137
50231
|
const backupPath = cssPath.replace(/\.css$/, ".backup.css");
|
|
@@ -50157,20 +50251,20 @@ var COMPONENT_PATHS = {
|
|
|
50157
50251
|
async function findMainCssFile(cwd, framework) {
|
|
50158
50252
|
const locations = CSS_LOCATIONS[framework] || CSS_LOCATIONS.unknown;
|
|
50159
50253
|
for (const location of locations) {
|
|
50160
|
-
const fullPath =
|
|
50161
|
-
if (
|
|
50254
|
+
const fullPath = join9(cwd, location);
|
|
50255
|
+
if (existsSync3(fullPath)) {
|
|
50162
50256
|
return location;
|
|
50163
50257
|
}
|
|
50164
50258
|
}
|
|
50165
50259
|
return null;
|
|
50166
50260
|
}
|
|
50167
50261
|
async function updateMainCss(cwd, cssPath, themePath) {
|
|
50168
|
-
const fullCssPath =
|
|
50262
|
+
const fullCssPath = join9(cwd, cssPath);
|
|
50169
50263
|
const cssContent = await readFile4(fullCssPath, "utf-8");
|
|
50170
|
-
const cssDir =
|
|
50171
|
-
const themeFullPath =
|
|
50264
|
+
const cssDir = join9(cwd, cssPath, "..");
|
|
50265
|
+
const themeFullPath = join9(cwd, themePath);
|
|
50172
50266
|
const relativeThemePath = relative(cssDir, themeFullPath);
|
|
50173
|
-
if (cssContent.includes(".rafters/output/
|
|
50267
|
+
if (cssContent.includes(".rafters/output/rafters.css")) {
|
|
50174
50268
|
log({ event: "init:css_already_imported", cssPath });
|
|
50175
50269
|
return;
|
|
50176
50270
|
}
|
|
@@ -50192,8 +50286,66 @@ ${cssContent}`;
|
|
|
50192
50286
|
themePath: relativeThemePath
|
|
50193
50287
|
});
|
|
50194
50288
|
}
|
|
50195
|
-
async function
|
|
50289
|
+
async function promptExportFormats(existingConfig) {
|
|
50290
|
+
const choices = EXPORT_CHOICES.map((choice) => ({
|
|
50291
|
+
name: choice.name,
|
|
50292
|
+
value: choice.value,
|
|
50293
|
+
checked: existingConfig ? existingConfig[choice.value] : choice.checked
|
|
50294
|
+
}));
|
|
50295
|
+
const allChoices = [
|
|
50296
|
+
...choices,
|
|
50297
|
+
...FUTURE_EXPORTS.map((choice) => ({
|
|
50298
|
+
name: `${choice.name} (${choice.disabled})`,
|
|
50299
|
+
value: choice.value,
|
|
50300
|
+
checked: false,
|
|
50301
|
+
disabled: true
|
|
50302
|
+
}))
|
|
50303
|
+
];
|
|
50304
|
+
const selections = await checkbox({
|
|
50305
|
+
message: "What would you like to export?",
|
|
50306
|
+
choices: allChoices,
|
|
50307
|
+
required: true
|
|
50308
|
+
});
|
|
50309
|
+
return selectionsToConfig(selections);
|
|
50310
|
+
}
|
|
50311
|
+
async function generateOutputs(cwd, paths, registry2, exports, shadcn) {
|
|
50312
|
+
const outputs = [];
|
|
50313
|
+
if (exports.tailwind) {
|
|
50314
|
+
const tailwindCss = registryToTailwind(registry2, { includeImport: !shadcn });
|
|
50315
|
+
await writeFile3(join9(paths.output, "rafters.css"), tailwindCss);
|
|
50316
|
+
outputs.push("rafters.css");
|
|
50317
|
+
}
|
|
50318
|
+
if (exports.typescript) {
|
|
50319
|
+
const typescriptSrc = registryToTypeScript(registry2, { includeJSDoc: true });
|
|
50320
|
+
await writeFile3(join9(paths.output, "rafters.ts"), typescriptSrc);
|
|
50321
|
+
outputs.push("rafters.ts");
|
|
50322
|
+
}
|
|
50323
|
+
if (exports.dtcg) {
|
|
50324
|
+
const dtcgJson = toDTCG(registry2.list());
|
|
50325
|
+
await writeFile3(join9(paths.output, "rafters.json"), JSON.stringify(dtcgJson, null, 2));
|
|
50326
|
+
outputs.push("rafters.json");
|
|
50327
|
+
}
|
|
50328
|
+
if (exports.compiled) {
|
|
50329
|
+
if (!exports.tailwind) {
|
|
50330
|
+
const tailwindCss = registryToTailwind(registry2, { includeImport: !shadcn });
|
|
50331
|
+
await writeFile3(join9(paths.output, "rafters.css"), tailwindCss);
|
|
50332
|
+
}
|
|
50333
|
+
const inputPath = join9(paths.output, "rafters.css");
|
|
50334
|
+
const outputPath = join9(paths.output, "rafters.compiled.css");
|
|
50335
|
+
log({ event: "init:compiling_css" });
|
|
50336
|
+
await generateCompiledCss(cwd, inputPath, outputPath);
|
|
50337
|
+
outputs.push("rafters.compiled.css");
|
|
50338
|
+
}
|
|
50339
|
+
return outputs;
|
|
50340
|
+
}
|
|
50341
|
+
async function regenerateFromExisting(cwd, paths, shadcn, isAgentMode) {
|
|
50196
50342
|
log({ event: "init:regenerate", cwd });
|
|
50343
|
+
let existingConfig = null;
|
|
50344
|
+
try {
|
|
50345
|
+
const configContent = await readFile4(paths.config, "utf-8");
|
|
50346
|
+
existingConfig = JSON.parse(configContent);
|
|
50347
|
+
} catch {
|
|
50348
|
+
}
|
|
50197
50349
|
const adapter = new NodePersistenceAdapter(cwd);
|
|
50198
50350
|
const namespaces = await adapter.listNamespaces();
|
|
50199
50351
|
if (namespaces.length === 0) {
|
|
@@ -50210,21 +50362,27 @@ async function regenerateFromExisting(cwd, paths, shadcn) {
|
|
|
50210
50362
|
namespaces
|
|
50211
50363
|
});
|
|
50212
50364
|
const registry2 = new TokenRegistry(allTokens);
|
|
50213
|
-
|
|
50214
|
-
|
|
50215
|
-
|
|
50365
|
+
let exports;
|
|
50366
|
+
if (isAgentMode) {
|
|
50367
|
+
exports = existingConfig?.exports ?? DEFAULT_EXPORTS;
|
|
50368
|
+
} else {
|
|
50369
|
+
exports = await promptExportFormats(existingConfig?.exports);
|
|
50370
|
+
}
|
|
50216
50371
|
await mkdir3(paths.output, { recursive: true });
|
|
50217
|
-
await
|
|
50218
|
-
|
|
50219
|
-
|
|
50372
|
+
const outputs = await generateOutputs(cwd, paths, registry2, exports, shadcn);
|
|
50373
|
+
if (existingConfig) {
|
|
50374
|
+
existingConfig.exports = exports;
|
|
50375
|
+
await writeFile3(paths.config, JSON.stringify(existingConfig, null, 2));
|
|
50376
|
+
}
|
|
50220
50377
|
log({
|
|
50221
50378
|
event: "init:complete",
|
|
50222
|
-
outputs
|
|
50379
|
+
outputs,
|
|
50223
50380
|
path: paths.output
|
|
50224
50381
|
});
|
|
50225
50382
|
}
|
|
50226
50383
|
async function init(options) {
|
|
50227
50384
|
setAgentMode(options.agent ?? false);
|
|
50385
|
+
const isAgentMode = options.agent ?? false;
|
|
50228
50386
|
const cwd = process.cwd();
|
|
50229
50387
|
const paths = getRaftersPaths(cwd);
|
|
50230
50388
|
log({ event: "init:start", cwd });
|
|
@@ -50238,19 +50396,19 @@ async function init(options) {
|
|
|
50238
50396
|
if (isTailwindV3(tailwindVersion)) {
|
|
50239
50397
|
throw new Error("Tailwind v3 detected. Rafters requires Tailwind v4.");
|
|
50240
50398
|
}
|
|
50241
|
-
const raftersExists =
|
|
50399
|
+
const raftersExists = existsSync3(paths.root);
|
|
50242
50400
|
if (raftersExists && !options.force) {
|
|
50243
50401
|
throw new Error(
|
|
50244
50402
|
".rafters/ directory already exists. Use --force to regenerate output files from existing config."
|
|
50245
50403
|
);
|
|
50246
50404
|
}
|
|
50247
50405
|
if (raftersExists && options.force) {
|
|
50248
|
-
await regenerateFromExisting(cwd, paths, shadcn);
|
|
50406
|
+
await regenerateFromExisting(cwd, paths, shadcn, isAgentMode);
|
|
50249
50407
|
return;
|
|
50250
50408
|
}
|
|
50251
50409
|
let existingColors = null;
|
|
50252
50410
|
if (shadcn?.tailwind?.css) {
|
|
50253
|
-
const cssPath =
|
|
50411
|
+
const cssPath = join9(cwd, shadcn.tailwind.css);
|
|
50254
50412
|
try {
|
|
50255
50413
|
const cssContent = await readFile4(cssPath, "utf-8");
|
|
50256
50414
|
existingColors = parseCssVariables(cssContent);
|
|
@@ -50268,6 +50426,14 @@ async function init(options) {
|
|
|
50268
50426
|
log({ event: "init:shadcn_css_error", error: String(err) });
|
|
50269
50427
|
}
|
|
50270
50428
|
}
|
|
50429
|
+
let exports;
|
|
50430
|
+
if (isAgentMode) {
|
|
50431
|
+
exports = DEFAULT_EXPORTS;
|
|
50432
|
+
log({ event: "init:exports_default", exports });
|
|
50433
|
+
} else {
|
|
50434
|
+
exports = await promptExportFormats();
|
|
50435
|
+
log({ event: "init:exports_selected", exports });
|
|
50436
|
+
}
|
|
50271
50437
|
const result = buildColorSystem({
|
|
50272
50438
|
exports: {
|
|
50273
50439
|
tailwind: { includeImport: !shadcn },
|
|
@@ -50331,21 +50497,16 @@ async function init(options) {
|
|
|
50331
50497
|
path: paths.tokens,
|
|
50332
50498
|
namespaceCount: tokensByNamespace.size
|
|
50333
50499
|
});
|
|
50334
|
-
const
|
|
50335
|
-
const typescriptSrc = registryToTypeScript(registry2, { includeJSDoc: true });
|
|
50336
|
-
const dtcgJson = toDTCG(registry2.list());
|
|
50337
|
-
await writeFile3(join8(paths.output, "theme.css"), tailwindCss);
|
|
50338
|
-
await writeFile3(join8(paths.output, "tokens.ts"), typescriptSrc);
|
|
50339
|
-
await writeFile3(join8(paths.output, "tokens.json"), JSON.stringify(dtcgJson, null, 2));
|
|
50500
|
+
const outputs = await generateOutputs(cwd, paths, registry2, exports, shadcn);
|
|
50340
50501
|
let detectedCssPath = null;
|
|
50341
|
-
if (!shadcn) {
|
|
50502
|
+
if (!shadcn && exports.tailwind) {
|
|
50342
50503
|
detectedCssPath = await findMainCssFile(cwd, framework);
|
|
50343
50504
|
if (detectedCssPath) {
|
|
50344
|
-
await updateMainCss(cwd, detectedCssPath, ".rafters/output/
|
|
50505
|
+
await updateMainCss(cwd, detectedCssPath, ".rafters/output/rafters.css");
|
|
50345
50506
|
} else {
|
|
50346
50507
|
log({
|
|
50347
50508
|
event: "init:css_not_found",
|
|
50348
|
-
message: 'No main CSS file found. Add @import ".rafters/output/
|
|
50509
|
+
message: 'No main CSS file found. Add @import ".rafters/output/rafters.css" manually.',
|
|
50349
50510
|
searchedLocations: CSS_LOCATIONS[framework] || CSS_LOCATIONS.unknown
|
|
50350
50511
|
});
|
|
50351
50512
|
}
|
|
@@ -50357,12 +50518,13 @@ async function init(options) {
|
|
|
50357
50518
|
framework,
|
|
50358
50519
|
componentsPath: frameworkPaths.components,
|
|
50359
50520
|
primitivesPath: frameworkPaths.primitives,
|
|
50360
|
-
cssPath: detectedCssPath
|
|
50521
|
+
cssPath: detectedCssPath,
|
|
50522
|
+
exports
|
|
50361
50523
|
};
|
|
50362
50524
|
await writeFile3(paths.config, JSON.stringify(config3, null, 2));
|
|
50363
50525
|
log({
|
|
50364
50526
|
event: "init:complete",
|
|
50365
|
-
outputs: [
|
|
50527
|
+
outputs: [...outputs, "config.rafters.json"],
|
|
50366
50528
|
path: paths.output
|
|
50367
50529
|
});
|
|
50368
50530
|
}
|
|
@@ -50374,7 +50536,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
|
|
|
50374
50536
|
|
|
50375
50537
|
// src/mcp/tools.ts
|
|
50376
50538
|
import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
|
|
50377
|
-
import { basename, join as
|
|
50539
|
+
import { basename, join as join10 } from "path";
|
|
50378
50540
|
var DESIGN_PATTERNS = {
|
|
50379
50541
|
"destructive-action": {
|
|
50380
50542
|
name: "Destructive Action",
|
|
@@ -50924,7 +51086,7 @@ var RaftersToolHandler = class {
|
|
|
50924
51086
|
* Get path to UI components directory
|
|
50925
51087
|
*/
|
|
50926
51088
|
getComponentsPath() {
|
|
50927
|
-
const monorepoPath =
|
|
51089
|
+
const monorepoPath = join10(this.projectRoot, "packages/ui/src/components/ui");
|
|
50928
51090
|
return monorepoPath;
|
|
50929
51091
|
}
|
|
50930
51092
|
/**
|
|
@@ -50932,7 +51094,7 @@ var RaftersToolHandler = class {
|
|
|
50932
51094
|
*/
|
|
50933
51095
|
async loadComponentMetadata(name2) {
|
|
50934
51096
|
const componentsPath = this.getComponentsPath();
|
|
50935
|
-
const filePath =
|
|
51097
|
+
const filePath = join10(componentsPath, `${name2}.tsx`);
|
|
50936
51098
|
try {
|
|
50937
51099
|
const source = await readFile5(filePath, "utf-8");
|
|
50938
51100
|
const intelligence = parseJSDocIntelligence(source);
|
|
@@ -51284,22 +51446,22 @@ async function mcp() {
|
|
|
51284
51446
|
}
|
|
51285
51447
|
|
|
51286
51448
|
// src/commands/studio.ts
|
|
51287
|
-
import { existsSync as
|
|
51288
|
-
import { dirname as dirname3, join as
|
|
51449
|
+
import { existsSync as existsSync4 } from "fs";
|
|
51450
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
51289
51451
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
51290
51452
|
import { execa as execa2 } from "execa";
|
|
51291
51453
|
var __dirname2 = dirname3(fileURLToPath3(import.meta.url));
|
|
51292
51454
|
async function studio() {
|
|
51293
51455
|
const cwd = process.cwd();
|
|
51294
51456
|
const paths = getRaftersPaths(cwd);
|
|
51295
|
-
if (!
|
|
51457
|
+
if (!existsSync4(paths.root)) {
|
|
51296
51458
|
console.error('No .rafters/ directory found. Run "rafters init" first.');
|
|
51297
51459
|
process.exit(1);
|
|
51298
51460
|
}
|
|
51299
|
-
const devStudioPath =
|
|
51300
|
-
const prodStudioPath =
|
|
51301
|
-
const studioPath =
|
|
51302
|
-
if (!
|
|
51461
|
+
const devStudioPath = join11(__dirname2, "..", "..", "..", "studio");
|
|
51462
|
+
const prodStudioPath = join11(__dirname2, "..", "node_modules", "@rafters", "studio");
|
|
51463
|
+
const studioPath = existsSync4(devStudioPath) ? devStudioPath : prodStudioPath;
|
|
51464
|
+
if (!existsSync4(studioPath)) {
|
|
51303
51465
|
console.error("Studio package not found. Please reinstall @rafters/cli.");
|
|
51304
51466
|
process.exit(1);
|
|
51305
51467
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rafters",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "CLI for Rafters design system - scaffold tokens and add components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@antfu/ni": "^28.1.0",
|
|
17
|
+
"@inquirer/prompts": "^8.2.0",
|
|
17
18
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
18
19
|
"commander": "^13.0.0",
|
|
19
20
|
"execa": "^9.6.1",
|