@rootnative/cli 0.0.0-alpha.0 → 0.0.0-alpha.2

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 CHANGED
@@ -305,6 +305,12 @@ export default function App() {
305
305
  }
306
306
  ```
307
307
 
308
+ ## Docs
309
+
310
+ Full docs: https://rootnative.github.io/ui/cli
311
+
312
+ LLM-optimized reference: https://rootnative.github.io/ui/llms-full.txt — or read `node_modules/@rootnative/cli/llms.txt` for the exact installed version.
313
+
308
314
  ## License
309
315
 
310
316
  MIT
package/dist/index.mjs CHANGED
@@ -277,6 +277,10 @@ function transformImports(source, options) {
277
277
  if (importPath.startsWith("@rootnative/core")) {
278
278
  return match;
279
279
  }
280
+ if (importPath.startsWith("../internal/")) {
281
+ const restOfPath = importPath.replace(/^\.\.\/internal\//, "");
282
+ return `${prefix}${quote}./${restOfPath}${quote}`;
283
+ }
280
284
  if (importPath.startsWith("../")) {
281
285
  const targetComponent = extractComponentName(importPath);
282
286
  if (targetComponent && installedComponents.includes(targetComponent)) {
@@ -561,6 +565,7 @@ var TEMPLATE_BINARY_FILES = [
561
565
  "assets/adaptive-icon.png",
562
566
  "assets/favicon.png"
563
567
  ];
568
+ var TEMPLATE_OPTIONAL_TEXT_FILES = ["CLAUDE.md"];
564
569
  function isValidTemplate(value) {
565
570
  return value in TEMPLATE_CONFIGS;
566
571
  }
@@ -589,6 +594,11 @@ async function fetchText(url) {
589
594
  }
590
595
  return res.text();
591
596
  }
597
+ async function fetchTextOptional(url) {
598
+ const res = await fetch(url);
599
+ if (!res.ok) return null;
600
+ return res.text();
601
+ }
592
602
  async function fetchBinary(url) {
593
603
  const res = await fetch(url);
594
604
  if (!res.ok) return null;
@@ -737,6 +747,12 @@ async function createCommand(name, options = {}) {
737
747
  }
738
748
  await fs4.outputFile(path4.join(targetDir, file), content);
739
749
  }
750
+ for (const file of TEMPLATE_OPTIONAL_TEXT_FILES) {
751
+ const content = await fetchTextOptional(`${templateBaseUrl}/${file}`);
752
+ if (content !== null) {
753
+ await fs4.outputFile(path4.join(targetDir, file), content);
754
+ }
755
+ }
740
756
  for (const file of TEMPLATE_BINARY_FILES) {
741
757
  const buffer = await fetchBinary(`${templateBaseUrl}/${file}`);
742
758
  if (buffer) {
@@ -912,6 +928,22 @@ async function doctorCommand(cwd) {
912
928
  }
913
929
  }
914
930
  const nodeModules = path5.resolve(cwd, "node_modules");
931
+ const inertiaPkgPath = path5.join(
932
+ nodeModules,
933
+ "@rootnative",
934
+ "inertia",
935
+ "package.json"
936
+ );
937
+ if (await fs5.pathExists(inertiaPkgPath)) {
938
+ const inertiaPkg = await fs5.readJSON(inertiaPkgPath);
939
+ logCheck("pass", `@rootnative/inertia@${inertiaPkg.version} installed`);
940
+ } else {
941
+ logCheck(
942
+ "fail",
943
+ '@rootnative/inertia not installed (required by all animated components). Run "rootnative upgrade" or install it with react-native-reanimated and react-native-worklets.'
944
+ );
945
+ issues++;
946
+ }
915
947
  const safeAreaInstalled = await fs5.pathExists(
916
948
  path5.join(nodeModules, "react-native-safe-area-context")
917
949
  );
@@ -947,6 +979,41 @@ async function doctorCommand(cwd) {
947
979
  import chalk5 from "chalk";
948
980
  import { execa as execa3 } from "execa";
949
981
  import prompts3 from "prompts";
982
+
983
+ // src/lib/llm-docs.ts
984
+ import path6 from "path";
985
+ import fs6 from "fs-extra";
986
+ var CLAUDE_MD = "CLAUDE.md";
987
+ var POINTER_MARKER = "@rootnative/components/llms.txt";
988
+ var LLM_DOCS_POINTER = `## RootNative UI docs for AI agents
989
+
990
+ This project uses RootNative UI (\`@rootnative/*\`). LLM-optimized docs:
991
+
992
+ - \`node_modules/@rootnative/components/llms.txt\` \u2014 all component props for the exact installed version (works offline)
993
+ - \`node_modules/@rootnative/core/llms.txt\` \u2014 theme system API (\`ThemeProvider\`, \`useTheme\`, \`defineTheme\`)
994
+ - https://rootnative.github.io/ui/llms.txt \u2014 hosted overview (latest release)
995
+ - https://rootnative.github.io/ui/llms-full.txt \u2014 hosted complete API reference (latest release)
996
+
997
+ Prefer the \`node_modules\` copies \u2014 they match the installed version exactly.
998
+ `;
999
+ async function ensureLlmDocsPointer(cwd) {
1000
+ const filePath = path6.join(cwd, CLAUDE_MD);
1001
+ if (!await fs6.pathExists(filePath)) {
1002
+ await fs6.outputFile(filePath, `# CLAUDE.md
1003
+
1004
+ ${LLM_DOCS_POINTER}`);
1005
+ return "created";
1006
+ }
1007
+ const existing = await fs6.readFile(filePath, "utf8");
1008
+ if (existing.includes(POINTER_MARKER)) {
1009
+ return "already-present";
1010
+ }
1011
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
1012
+ await fs6.outputFile(filePath, `${existing}${separator}${LLM_DOCS_POINTER}`);
1013
+ return "appended";
1014
+ }
1015
+
1016
+ // src/commands/init.ts
950
1017
  async function initCommand(cwd, options = {}) {
951
1018
  logger.break();
952
1019
  if (await configExists(cwd)) {
@@ -1029,6 +1096,26 @@ async function initCommand(cwd, options = {}) {
1029
1096
  };
1030
1097
  await writeConfig(cwd, config);
1031
1098
  logger.success("Created rootnative.json");
1099
+ let addLlmDocs = options.yes;
1100
+ if (!options.yes) {
1101
+ const answer = await prompts3({
1102
+ type: "confirm",
1103
+ name: "addLlmDocs",
1104
+ message: "Add RootNative LLM docs pointer to CLAUDE.md (for AI agents)?",
1105
+ initial: true
1106
+ });
1107
+ addLlmDocs = answer.addLlmDocs;
1108
+ }
1109
+ if (addLlmDocs) {
1110
+ const result = await ensureLlmDocsPointer(cwd);
1111
+ if (result === "created") {
1112
+ logger.success("Created CLAUDE.md with LLM docs pointer");
1113
+ } else if (result === "appended") {
1114
+ logger.success("Added LLM docs pointer to CLAUDE.md");
1115
+ } else {
1116
+ logger.info("CLAUDE.md already points to the RootNative LLM docs");
1117
+ }
1118
+ }
1032
1119
  let installCore = options.yes;
1033
1120
  if (!options.yes) {
1034
1121
  const answer = await prompts3({
@@ -1065,9 +1152,9 @@ async function initCommand(cwd, options = {}) {
1065
1152
  }
1066
1153
 
1067
1154
  // src/commands/list.ts
1068
- import path6 from "path";
1155
+ import path7 from "path";
1069
1156
  import chalk6 from "chalk";
1070
- import fs6 from "fs-extra";
1157
+ import fs7 from "fs-extra";
1071
1158
  async function listCommand(cwd) {
1072
1159
  logger.break();
1073
1160
  const config = await readConfig(cwd);
@@ -1093,8 +1180,8 @@ async function listCommand(cwd) {
1093
1180
  ` ${chalk6.dim("-".repeat(70))}`
1094
1181
  );
1095
1182
  for (const component of registryIndex.components) {
1096
- const componentDir = path6.join(componentsDir, component.name);
1097
- const installed = await fs6.pathExists(componentDir);
1183
+ const componentDir = path7.join(componentsDir, component.name);
1184
+ const installed = await fs7.pathExists(componentDir);
1098
1185
  const status = installed ? chalk6.green("installed") : chalk6.dim("-");
1099
1186
  const nameDisplay = installed ? chalk6.green(component.name) : component.name;
1100
1187
  console.log(
@@ -1114,9 +1201,9 @@ function padEnd(str, length) {
1114
1201
  }
1115
1202
 
1116
1203
  // src/commands/update.ts
1117
- import path7 from "path";
1204
+ import path8 from "path";
1118
1205
  import chalk8 from "chalk";
1119
- import fs7 from "fs-extra";
1206
+ import fs8 from "fs-extra";
1120
1207
  import prompts4 from "prompts";
1121
1208
 
1122
1209
  // src/lib/diff.ts
@@ -1266,8 +1353,8 @@ async function updateCommand(componentNames, cwd, options) {
1266
1353
  targetNames = componentNames;
1267
1354
  const missing = [];
1268
1355
  for (const name of targetNames) {
1269
- const dir = path7.join(componentsDir, name);
1270
- if (!await fs7.pathExists(dir)) {
1356
+ const dir = path8.join(componentsDir, name);
1357
+ if (!await fs8.pathExists(dir)) {
1271
1358
  missing.push(name);
1272
1359
  }
1273
1360
  }
@@ -1290,8 +1377,8 @@ async function updateCommand(componentNames, cwd, options) {
1290
1377
  resolveSpinner.succeed("Registry fetched");
1291
1378
  const componentDiffs = [];
1292
1379
  for (const { entry } of resolution.components) {
1293
- const componentDir = path7.join(componentsDir, entry.name);
1294
- if (!await fs7.pathExists(componentDir)) {
1380
+ const componentDir = path8.join(componentsDir, entry.name);
1381
+ if (!await fs8.pathExists(componentDir)) {
1295
1382
  componentDiffs.push({
1296
1383
  name: entry.name,
1297
1384
  diffs: [],
@@ -1301,8 +1388,8 @@ async function updateCommand(componentNames, cwd, options) {
1301
1388
  }
1302
1389
  const diffs = [];
1303
1390
  for (const filePath of entry.files) {
1304
- const fileName = path7.basename(filePath);
1305
- const localPath = path7.join(componentDir, fileName);
1391
+ const fileName = path8.basename(filePath);
1392
+ const localPath = path8.join(componentDir, fileName);
1306
1393
  const remoteContent = await fetchFileContent(config, filePath);
1307
1394
  const transformed = transformImports(remoteContent, {
1308
1395
  config,
@@ -1310,8 +1397,8 @@ async function updateCommand(componentNames, cwd, options) {
1310
1397
  installedComponents: allComponentNames
1311
1398
  });
1312
1399
  let localContent = "";
1313
- if (await fs7.pathExists(localPath)) {
1314
- localContent = await fs7.readFile(localPath, "utf-8");
1400
+ if (await fs8.pathExists(localPath)) {
1401
+ localContent = await fs8.readFile(localPath, "utf-8");
1315
1402
  }
1316
1403
  diffs.push(computeDiff(localContent, transformed, fileName));
1317
1404
  }
@@ -1322,12 +1409,12 @@ async function updateCommand(componentNames, cwd, options) {
1322
1409
  for (const utilName of resolution.utils) {
1323
1410
  const utilEntry = utilsRegistry[utilName];
1324
1411
  if (!utilEntry) continue;
1325
- const fileName = path7.basename(utilEntry.file);
1326
- const localPath = path7.join(libDir, fileName);
1412
+ const fileName = path8.basename(utilEntry.file);
1413
+ const localPath = path8.join(libDir, fileName);
1327
1414
  const remoteContent = await fetchFileContent(config, utilEntry.file);
1328
1415
  let localContent = "";
1329
- if (await fs7.pathExists(localPath)) {
1330
- localContent = await fs7.readFile(localPath, "utf-8");
1416
+ if (await fs8.pathExists(localPath)) {
1417
+ localContent = await fs8.readFile(localPath, "utf-8");
1331
1418
  }
1332
1419
  utilDiffs.push(computeDiff(localContent, remoteContent, `lib/${fileName}`));
1333
1420
  }
@@ -1339,10 +1426,10 @@ async function updateCommand(componentNames, cwd, options) {
1339
1426
  }
1340
1427
  }
1341
1428
  const newBarrel = generateUtilsBarrel(resolution.utils, utilExports);
1342
- const barrelPath = path7.join(libDir, "rootnative-utils.ts");
1429
+ const barrelPath = path8.join(libDir, "rootnative-utils.ts");
1343
1430
  let oldBarrel = "";
1344
- if (await fs7.pathExists(barrelPath)) {
1345
- oldBarrel = await fs7.readFile(barrelPath, "utf-8");
1431
+ if (await fs8.pathExists(barrelPath)) {
1432
+ oldBarrel = await fs8.readFile(barrelPath, "utf-8");
1346
1433
  }
1347
1434
  utilDiffs.push(computeDiff(oldBarrel, newBarrel, "lib/rootnative-utils.ts"));
1348
1435
  const changedComponents = componentDiffs.filter((c) => c.hasChanges);
@@ -1405,8 +1492,8 @@ async function updateCommand(componentNames, cwd, options) {
1405
1492
  const applySpinner = createSpinner("Applying updates...");
1406
1493
  applySpinner.start();
1407
1494
  for (const comp of changedComponents) {
1408
- const componentDir = path7.join(componentsDir, comp.name);
1409
- await fs7.ensureDir(componentDir);
1495
+ const componentDir = path8.join(componentsDir, comp.name);
1496
+ await fs8.ensureDir(componentDir);
1410
1497
  if (comp.diffs.length === 0) {
1411
1498
  const entry = resolution.components.find(
1412
1499
  (c) => c.entry.name === comp.name
@@ -1414,14 +1501,14 @@ async function updateCommand(componentNames, cwd, options) {
1414
1501
  if (!entry) continue;
1415
1502
  for (const filePath of entry.entry.files) {
1416
1503
  const content = await fetchFileContent(config, filePath);
1417
- const fileName = path7.basename(filePath);
1504
+ const fileName = path8.basename(filePath);
1418
1505
  const transformed = transformImports(content, {
1419
1506
  config,
1420
1507
  componentName: comp.name,
1421
1508
  installedComponents: allComponentNames
1422
1509
  });
1423
- await fs7.writeFile(
1424
- path7.join(componentDir, fileName),
1510
+ await fs8.writeFile(
1511
+ path8.join(componentDir, fileName),
1425
1512
  transformed,
1426
1513
  "utf-8"
1427
1514
  );
@@ -1431,23 +1518,23 @@ async function updateCommand(componentNames, cwd, options) {
1431
1518
  for (const diff of comp.diffs) {
1432
1519
  if (!diff.hasChanges) continue;
1433
1520
  const newContent = diff.lines.filter((l) => l.type !== "remove").map((l) => l.content).join("\n");
1434
- await fs7.writeFile(
1435
- path7.join(componentDir, diff.fileName),
1521
+ await fs8.writeFile(
1522
+ path8.join(componentDir, diff.fileName),
1436
1523
  newContent,
1437
1524
  "utf-8"
1438
1525
  );
1439
1526
  }
1440
1527
  }
1441
- await fs7.ensureDir(libDir);
1528
+ await fs8.ensureDir(libDir);
1442
1529
  for (const diff of changedUtils) {
1443
1530
  if (!diff.hasChanges) continue;
1444
1531
  const newContent = diff.lines.filter((l) => l.type !== "remove").map((l) => l.content).join("\n");
1445
- const filePath = path7.join(
1532
+ const filePath = path8.join(
1446
1533
  libDir,
1447
1534
  // diff.fileName is like "lib/color.ts" — strip the "lib/" prefix
1448
1535
  diff.fileName.replace(/^lib\//, "")
1449
1536
  );
1450
- await fs7.writeFile(filePath, newContent, "utf-8");
1537
+ await fs8.writeFile(filePath, newContent, "utf-8");
1451
1538
  }
1452
1539
  applySpinner.succeed("Updates applied");
1453
1540
  logger.break();
@@ -1457,17 +1544,17 @@ async function updateCommand(componentNames, cwd, options) {
1457
1544
  logger.break();
1458
1545
  }
1459
1546
  async function getInstalledComponents(componentsDir) {
1460
- if (!await fs7.pathExists(componentsDir)) {
1547
+ if (!await fs8.pathExists(componentsDir)) {
1461
1548
  return [];
1462
1549
  }
1463
- const entries = await fs7.readdir(componentsDir);
1550
+ const entries = await fs8.readdir(componentsDir);
1464
1551
  const components = [];
1465
1552
  for (const entry of entries) {
1466
- const fullPath = path7.join(componentsDir, entry);
1467
- const stat = await fs7.stat(fullPath);
1553
+ const fullPath = path8.join(componentsDir, entry);
1554
+ const stat = await fs8.stat(fullPath);
1468
1555
  if (stat.isDirectory()) {
1469
- const indexPath = path7.join(fullPath, "index.ts");
1470
- if (await fs7.pathExists(indexPath)) {
1556
+ const indexPath = path8.join(fullPath, "index.ts");
1557
+ if (await fs8.pathExists(indexPath)) {
1471
1558
  components.push(entry);
1472
1559
  }
1473
1560
  }
@@ -1476,10 +1563,10 @@ async function getInstalledComponents(componentsDir) {
1476
1563
  }
1477
1564
 
1478
1565
  // src/commands/upgrade.ts
1479
- import path8 from "path";
1566
+ import path9 from "path";
1480
1567
  import chalk9 from "chalk";
1481
1568
  import { execa as execa4 } from "execa";
1482
- import fs8 from "fs-extra";
1569
+ import fs9 from "fs-extra";
1483
1570
  import prompts5 from "prompts";
1484
1571
  async function fetchLatestFromNpm(packageName) {
1485
1572
  const url = `https://registry.npmjs.org/${packageName}/latest`;
@@ -1492,16 +1579,16 @@ async function fetchLatestFromNpm(packageName) {
1492
1579
  return response.json();
1493
1580
  }
1494
1581
  function getInstalledPackageInfo(cwd, packageName) {
1495
- const pkgPath = path8.resolve(
1582
+ const pkgPath = path9.resolve(
1496
1583
  cwd,
1497
1584
  "node_modules",
1498
1585
  ...packageName.split("/"),
1499
1586
  "package.json"
1500
1587
  );
1501
- if (!fs8.pathExistsSync(pkgPath)) {
1588
+ if (!fs9.pathExistsSync(pkgPath)) {
1502
1589
  return null;
1503
1590
  }
1504
- const pkg = fs8.readJSONSync(pkgPath);
1591
+ const pkg = fs9.readJSONSync(pkgPath);
1505
1592
  return {
1506
1593
  version: pkg.version,
1507
1594
  peerDependencies: pkg.peerDependencies,
@@ -1532,9 +1619,9 @@ function diffPeerDeps(current, latest) {
1532
1619
  return { added, changed, removed };
1533
1620
  }
1534
1621
  function getProjectDeps(cwd) {
1535
- const pkgPath = path8.resolve(cwd, "package.json");
1622
+ const pkgPath = path9.resolve(cwd, "package.json");
1536
1623
  try {
1537
- const pkg = fs8.readJSONSync(pkgPath);
1624
+ const pkg = fs9.readJSONSync(pkgPath);
1538
1625
  return {
1539
1626
  ...pkg.dependencies,
1540
1627
  ...pkg.devDependencies
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @rootnative/cli — CLI for RootNative UI
2
2
 
3
- > Version: 0.0.0-alpha.0
3
+ > Version: 0.0.0-alpha.2
4
4
  > Binary: `rootnative`
5
5
  > Requirements: Node >=18
6
6
 
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@rootnative/cli",
3
- "version": "0.0.0-alpha.0",
3
+ "version": "0.0.0-alpha.2",
4
4
  "description": "CLI for adding RootNative UI components to your React Native project",
5
5
  "private": false,
6
6
  "bin": {
7
7
  "rootnative": "dist/index.mjs"
8
8
  },
9
- "exports": "./dist/index.mjs",
9
+ "exports": {
10
+ "./package.json": "./package.json",
11
+ ".": "./dist/index.mjs"
12
+ },
10
13
  "files": [
11
14
  "dist",
12
15
  "llms.txt"