@idia-ui/cli 0.2.0

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/cli.js ADDED
@@ -0,0 +1,1602 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_commander = require("commander");
28
+ var import_node_path10 = require("path");
29
+
30
+ // src/commands/storybook/add.ts
31
+ var import_node_fs5 = require("fs");
32
+ var import_node_path5 = require("path");
33
+ var import_node_child_process = require("child_process");
34
+
35
+ // src/commands/storybook/constants.ts
36
+ var STORYBOOK_DOCS_VERSION = "0.2.0";
37
+ var STORYBOOK_PRESET_VERSION = "0.2.0";
38
+ var STORYBOOK_CLI_VERSION = "8.4.7";
39
+ var STORYBOOK_DEV_DEPS = [
40
+ "storybook@^8.4.7",
41
+ "@storybook/react-vite@^8.4.7",
42
+ "@storybook/addon-docs@^8.4.7",
43
+ "@storybook/addon-a11y@^8.4.7",
44
+ "@storybook/addon-links@^8.4.7",
45
+ `@idia-ui/storybook-docs@${STORYBOOK_DOCS_VERSION}`,
46
+ `@idia-ui/storybook-preset-react-native-web@${STORYBOOK_PRESET_VERSION}`,
47
+ "vite@^5.4.0",
48
+ "vite-tsconfig-paths@^4.3.0"
49
+ ];
50
+ var STORYBOOK_OWNED_FILES = [
51
+ ".storybook/main.ts",
52
+ ".storybook/preview.tsx",
53
+ "src/stories/Button.stories.tsx"
54
+ ];
55
+
56
+ // src/commands/storybook/templates.ts
57
+ function storybookMainTs() {
58
+ return `import type { StorybookConfig } from '@storybook/react-vite';
59
+ import { idiaStorybookMain } from '@idia-ui/storybook-preset-react-native-web';
60
+
61
+ const config: StorybookConfig = idiaStorybookMain({
62
+ stories: ['../src/stories/**/*.stories.@(ts|tsx)'],
63
+ staticDirs: ['../public'],
64
+ });
65
+
66
+ export default config;
67
+ `;
68
+ }
69
+ function storybookPreviewTsx() {
70
+ return `import React from 'react';
71
+ import type { Preview } from '@storybook/react';
72
+ import { AppProviders } from '../src/providers/AppProviders';
73
+ import '../global.css';
74
+
75
+ const preview: Preview = {
76
+ decorators: [
77
+ (Story) => (
78
+ <AppProviders>
79
+ <Story />
80
+ </AppProviders>
81
+ ),
82
+ ],
83
+ parameters: {
84
+ layout: 'centered',
85
+ controls: { expanded: true },
86
+ },
87
+ };
88
+
89
+ export default preview;
90
+ `;
91
+ }
92
+ function buttonStoriesTsx() {
93
+ return `import type { Meta, StoryObj } from '@storybook/react';
94
+ import { Button } from '@idia-ui/core';
95
+ import { BrainDocSections } from '@idia-ui/storybook-docs';
96
+
97
+ const meta = {
98
+ title: 'Components/Button',
99
+ component: Button,
100
+ tags: ['autodocs'],
101
+ parameters: {
102
+ docs: {
103
+ page: () => (
104
+ <>
105
+ <BrainDocSections
106
+ componentId="button"
107
+ sectionIds={['intent', 'usage', 'variants', 'accessibility']}
108
+ />
109
+ </>
110
+ ),
111
+ },
112
+ },
113
+ args: {
114
+ label: 'Button',
115
+ variant: 'primary',
116
+ },
117
+ } satisfies Meta<typeof Button>;
118
+
119
+ export default meta;
120
+
121
+ type Story = StoryObj<typeof meta>;
122
+
123
+ export const Playground: Story = {};
124
+ `;
125
+ }
126
+
127
+ // src/utils/pm.ts
128
+ var import_node_fs = require("fs");
129
+ var import_node_path = require("path");
130
+ function findLockfileDir(start) {
131
+ let dir = start;
132
+ while (true) {
133
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "pnpm-lock.yaml"))) return dir;
134
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "yarn.lock"))) return dir;
135
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "package-lock.json"))) return dir;
136
+ const parent = (0, import_node_path.dirname)(dir);
137
+ if (parent === dir) return start;
138
+ dir = parent;
139
+ }
140
+ }
141
+ function detectPackageManager(cwd) {
142
+ const root = findLockfileDir(cwd);
143
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(root, "pnpm-lock.yaml"))) return "pnpm";
144
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(root, "yarn.lock"))) return "yarn";
145
+ return "npm";
146
+ }
147
+ function pmAddCommand(pm, packages) {
148
+ const list = packages.join(" ");
149
+ switch (pm) {
150
+ case "pnpm":
151
+ return `pnpm add ${list}`;
152
+ case "yarn":
153
+ return `yarn add ${list}`;
154
+ default:
155
+ return `npm install ${list}`;
156
+ }
157
+ }
158
+
159
+ // src/utils/package-json.ts
160
+ var import_node_fs2 = require("fs");
161
+ var import_node_path2 = require("path");
162
+ function readPackageJson(cwd) {
163
+ try {
164
+ const raw = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(cwd, "package.json"), "utf8");
165
+ return JSON.parse(raw);
166
+ } catch {
167
+ return null;
168
+ }
169
+ }
170
+ function getDepVersion(pkg, name) {
171
+ return pkg.dependencies?.[name] ?? pkg.devDependencies?.[name];
172
+ }
173
+ function readInstalledVersion(cwd, name) {
174
+ try {
175
+ const pkgPath = (0, import_node_path2.join)(cwd, "node_modules", name, "package.json");
176
+ const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf8");
177
+ const parsed = JSON.parse(raw);
178
+ return parsed.version ?? null;
179
+ } catch {
180
+ return null;
181
+ }
182
+ }
183
+ function readPeerRange(cwd, packageName, peerName) {
184
+ try {
185
+ const pkgPath = (0, import_node_path2.join)(cwd, "node_modules", packageName, "package.json");
186
+ const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf8");
187
+ const parsed = JSON.parse(raw);
188
+ return parsed.peerDependencies?.[peerName] ?? null;
189
+ } catch {
190
+ return null;
191
+ }
192
+ }
193
+
194
+ // src/utils/fs.ts
195
+ var import_node_fs3 = require("fs");
196
+ var import_node_path3 = require("path");
197
+ var import_semver = __toESM(require("semver"));
198
+ function satisfiesRange(version, range) {
199
+ const cleaned = version.replace(/^[\^~>=<]*/, "").split(" ")[0] ?? version;
200
+ if (range === "workspace:*" || range.startsWith("file:")) return true;
201
+ const coerced = import_semver.default.coerce(cleaned);
202
+ if (!coerced) return false;
203
+ return import_semver.default.satisfies(coerced, range, { includePrerelease: true });
204
+ }
205
+ function getMinor(version) {
206
+ const coerced = import_semver.default.coerce(version);
207
+ if (!coerced) return null;
208
+ return `${coerced.major}.${coerced.minor}`;
209
+ }
210
+ function readTextFile(cwd, relativePath) {
211
+ try {
212
+ return (0, import_node_fs3.readFileSync)((0, import_node_path3.join)(cwd, relativePath), "utf8");
213
+ } catch {
214
+ return null;
215
+ }
216
+ }
217
+ function fileExists(cwd, relativePath) {
218
+ try {
219
+ return (0, import_node_fs3.existsSync)((0, import_node_path3.join)(cwd, relativePath));
220
+ } catch {
221
+ return false;
222
+ }
223
+ }
224
+ function grepSrcForPattern(cwd, pattern) {
225
+ const filesToScan = [
226
+ "App.tsx",
227
+ "src/providers/AppProviders.tsx"
228
+ ];
229
+ for (const file of filesToScan) {
230
+ const content = readTextFile(cwd, file);
231
+ if (content && pattern.test(content)) return true;
232
+ }
233
+ const srcRoot = (0, import_node_path3.join)(cwd, "src");
234
+ try {
235
+ const walk = (dir) => {
236
+ for (const entry of (0, import_node_fs3.readdirSync)(dir)) {
237
+ const full = (0, import_node_path3.join)(dir, entry);
238
+ const stat = (0, import_node_fs3.statSync)(full);
239
+ if (stat.isDirectory()) {
240
+ if (walk(full)) return true;
241
+ } else if (/\.(tsx?|jsx?)$/.test(entry)) {
242
+ const content = (0, import_node_fs3.readFileSync)(full, "utf8");
243
+ if (pattern.test(content)) return true;
244
+ }
245
+ }
246
+ return false;
247
+ };
248
+ if ((0, import_node_fs3.existsSync)(srcRoot)) return walk(srcRoot);
249
+ } catch {
250
+ }
251
+ return false;
252
+ }
253
+
254
+ // src/utils/manifest.ts
255
+ var import_node_fs4 = require("fs");
256
+ var import_node_path4 = require("path");
257
+ var CLI_VERSION = "0.2.0";
258
+ function readManifest(cwd) {
259
+ try {
260
+ const raw = (0, import_node_fs4.readFileSync)((0, import_node_path4.join)(cwd, ".idia", "manifest.json"), "utf8");
261
+ return JSON.parse(raw);
262
+ } catch {
263
+ return null;
264
+ }
265
+ }
266
+ function writeManifest(cwd, manifest) {
267
+ const manifestPath = (0, import_node_path4.join)(cwd, ".idia", "manifest.json");
268
+ (0, import_node_fs4.mkdirSync)((0, import_node_path4.dirname)(manifestPath), { recursive: true });
269
+ (0, import_node_fs4.writeFileSync)(manifestPath, `${JSON.stringify(manifest, null, 2)}
270
+ `, "utf8");
271
+ }
272
+ function regenerateManifest(cwd, coreVersion) {
273
+ const manifestPath = (0, import_node_path4.join)(cwd, ".idia", "manifest.json");
274
+ const manifest = {
275
+ idiaCli: CLI_VERSION,
276
+ core: coreVersion
277
+ };
278
+ writeManifest(cwd, manifest);
279
+ return { created: true, path: manifestPath };
280
+ }
281
+ function manifestExists(cwd) {
282
+ return fileExists(cwd, ".idia/manifest.json");
283
+ }
284
+ function validateManifestShape(manifest) {
285
+ return typeof manifest.idiaCli === "string" && typeof manifest.core === "string" && /^\d+\.\d+\.\d+/.test(manifest.idiaCli) && /^\d+\.\d+\.\d+/.test(manifest.core);
286
+ }
287
+
288
+ // src/commands/storybook/add.ts
289
+ function writeOwnedFile(cwd, relativePath, content, force) {
290
+ const fullPath = (0, import_node_path5.join)(cwd, relativePath);
291
+ if (fileExists(cwd, relativePath) && !force) {
292
+ const existing = readTextFile(cwd, relativePath);
293
+ if (existing === content) return false;
294
+ throw new Error(
295
+ `Conflict: ${relativePath} exists with different content. Use --repair or --force.`
296
+ );
297
+ }
298
+ (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(fullPath), { recursive: true });
299
+ (0, import_node_fs5.writeFileSync)(fullPath, content, "utf8");
300
+ return true;
301
+ }
302
+ function parseDepSpec(spec) {
303
+ if (spec.startsWith("@")) {
304
+ const at2 = spec.indexOf("@", 1);
305
+ return { name: spec.slice(0, at2), range: spec.slice(at2 + 1) };
306
+ }
307
+ const at = spec.indexOf("@");
308
+ return { name: spec.slice(0, at), range: spec.slice(at + 1) };
309
+ }
310
+ function resolveDepVersion(name, range) {
311
+ if (name.startsWith("@idia-ui/storybook")) {
312
+ return "workspace:*";
313
+ }
314
+ return `^${range.replace(/^\^/, "")}`;
315
+ }
316
+ function addDevDependencies(cwd) {
317
+ const pkg = readPackageJson(cwd);
318
+ if (!pkg) throw new Error("package.json not found");
319
+ pkg.devDependencies ??= {};
320
+ for (const spec of STORYBOOK_DEV_DEPS) {
321
+ const { name, range } = parseDepSpec(spec);
322
+ pkg.devDependencies[name] = resolveDepVersion(name, range);
323
+ }
324
+ pkg.scripts ??= {};
325
+ pkg.scripts.storybook = "storybook dev -p 6006";
326
+ pkg.scripts["build-storybook"] = "storybook build";
327
+ (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(cwd, "package.json"), `${JSON.stringify(pkg, null, 2)}
328
+ `, "utf8");
329
+ }
330
+ function runPmInstall(cwd, pm) {
331
+ const args = pm === "pnpm" ? ["add", "-D", ...STORYBOOK_DEV_DEPS] : pm === "yarn" ? ["add", "-D", ...STORYBOOK_DEV_DEPS] : ["install", "-D", ...STORYBOOK_DEV_DEPS];
332
+ const result = (0, import_node_child_process.spawnSync)(pm, args, {
333
+ cwd,
334
+ stdio: "inherit",
335
+ shell: process.platform === "win32"
336
+ });
337
+ if (result.status !== 0) {
338
+ throw new Error(`${pm} install failed with exit code ${result.status ?? "unknown"}`);
339
+ }
340
+ }
341
+ function resolveCoreVersion(cwd, pkg) {
342
+ const installed = readInstalledVersion(cwd, "@idia-ui/core");
343
+ if (installed) return installed;
344
+ const declared = pkg.dependencies?.["@idia-ui/core"] ?? "";
345
+ if (declared && !declared.startsWith("workspace")) {
346
+ return declared.replace(/^[\^~]/, "");
347
+ }
348
+ return "0.2.0";
349
+ }
350
+ function updateStorybookManifest(cwd, manifest, coreVersion) {
351
+ manifest.idiaCli = CLI_VERSION;
352
+ manifest.core = coreVersion;
353
+ manifest.features ??= {};
354
+ manifest.features.storybook = {
355
+ installed: (/* @__PURE__ */ new Date()).toISOString(),
356
+ storybookDocs: STORYBOOK_DOCS_VERSION,
357
+ storybook: STORYBOOK_CLI_VERSION,
358
+ preset: STORYBOOK_PRESET_VERSION
359
+ };
360
+ writeManifest(cwd, manifest);
361
+ }
362
+ function isStorybookInstalled(cwd) {
363
+ return fileExists(cwd, ".storybook/main.ts") && Boolean(readPackageJson(cwd)?.devDependencies?.["@idia-ui/storybook-docs"]);
364
+ }
365
+ function runAddStorybook(options) {
366
+ const { cwd, repair = false, force = false, skipInstall = false } = options;
367
+ const pm = options.pm ?? detectPackageManager(cwd);
368
+ const warnings = [];
369
+ const filesWritten = [];
370
+ const pkg = readPackageJson(cwd);
371
+ if (!pkg) {
372
+ console.error("package.json not found \u2014 run from your app root");
373
+ process.exit(1);
374
+ }
375
+ let manifest = readManifest(cwd);
376
+ const coreVersion = resolveCoreVersion(cwd, pkg);
377
+ if (!manifest) {
378
+ manifest = { idiaCli: CLI_VERSION, core: coreVersion };
379
+ warnings.push("Created .idia/manifest.json stub");
380
+ } else {
381
+ manifest.core = coreVersion;
382
+ }
383
+ const alreadyInstalled = isStorybookInstalled(cwd);
384
+ if (alreadyInstalled && !repair && !force) {
385
+ console.log("Storybook already installed \u2014 idempotent skip (use --repair to refresh CLI-owned files)");
386
+ updateStorybookManifest(cwd, manifest, coreVersion);
387
+ return { installed: true, alreadyInstalled: true, filesWritten, warnings };
388
+ }
389
+ const ownedContent = {
390
+ ".storybook/main.ts": storybookMainTs(),
391
+ ".storybook/preview.tsx": storybookPreviewTsx(),
392
+ "src/stories/Button.stories.tsx": buttonStoriesTsx()
393
+ };
394
+ for (const relativePath of STORYBOOK_OWNED_FILES) {
395
+ if (repair || force || !fileExists(cwd, relativePath)) {
396
+ const wrote = writeOwnedFile(cwd, relativePath, ownedContent[relativePath], force || repair);
397
+ if (wrote) filesWritten.push(relativePath);
398
+ }
399
+ }
400
+ addDevDependencies(cwd);
401
+ if (!skipInstall) {
402
+ console.log(`Installing Storybook devDependencies via ${pm}\u2026`);
403
+ runPmInstall(cwd, pm);
404
+ } else {
405
+ warnings.push("Skipped package manager install (--skip-install)");
406
+ }
407
+ updateStorybookManifest(cwd, manifest, coreVersion);
408
+ console.log("\n\u2713 Storybook add-on installed");
409
+ console.log(` @idia-ui/storybook-docs@${STORYBOOK_DOCS_VERSION}`);
410
+ console.log(` @idia-ui/storybook-preset-react-native-web@${STORYBOOK_PRESET_VERSION}`);
411
+ console.log(" Run: pnpm storybook (or npm run storybook)");
412
+ return { installed: true, alreadyInstalled, filesWritten, warnings };
413
+ }
414
+
415
+ // src/brain/install.ts
416
+ var import_node_fs8 = require("fs");
417
+ var import_node_path8 = require("path");
418
+
419
+ // src/brain/agents-patch.ts
420
+ var import_node_fs6 = require("fs");
421
+ var import_node_path6 = require("path");
422
+
423
+ // src/brain/constants.ts
424
+ var BRAIN_PACK_NAME = "@idia-ui/brain-pack";
425
+ var BRAIN_PACK_VERSION = "0.2.0";
426
+ var BRAIN_TIER_DISCLAIMER = "@idia-ui/brain-pack is a generated AI retrieval pack for idia-ui usage guidance. It does not imply Tier 1 passive external-agent graduation, GA readiness, or full-catalog efficacy.";
427
+ var BRAIN_DOCTOR_DISCLAIMER = "brain pack is experimental retrieval guidance; it does not imply Tier 1P graduation";
428
+ var AGENTS_BEGIN = "<!-- idia-ui brain:begin -->";
429
+ var AGENTS_END = "<!-- idia-ui brain:end -->";
430
+ var AGENTS_BRAIN_BLOCK = `${AGENTS_BEGIN}
431
+ ## idia-ui design-system brain
432
+
433
+ For idia-ui component usage, design-system documentation, Storybook docs, UI generation, or AI-guided UI work, read:
434
+
435
+ \`.idia/brain/AGENTS.md\`
436
+
437
+ before using idia-ui components.
438
+
439
+ This installed brain pack is read-only generated guidance. Do not edit files under \`.idia/brain/\` directly.
440
+ ${AGENTS_END}`;
441
+
442
+ // src/brain/agents-patch.ts
443
+ function patchRootAgentsMd(cwd, options) {
444
+ const agentsPath = (0, import_node_path6.join)(cwd, "AGENTS.md");
445
+ const existing = readTextFile(cwd, "AGENTS.md");
446
+ if (existing === null) {
447
+ (0, import_node_fs6.writeFileSync)(agentsPath, `${AGENTS_BRAIN_BLOCK}
448
+ `, "utf8");
449
+ return { status: "created" };
450
+ }
451
+ const hasBegin = existing.includes(AGENTS_BEGIN);
452
+ const hasEnd = existing.includes(AGENTS_END);
453
+ if (hasBegin && hasEnd) {
454
+ if (!options.repair && !options.force) {
455
+ return { status: "merged" };
456
+ }
457
+ const updated = existing.replace(
458
+ new RegExp(`${escapeRegExp(AGENTS_BEGIN)}[\\s\\S]*?${escapeRegExp(AGENTS_END)}`),
459
+ AGENTS_BRAIN_BLOCK
460
+ );
461
+ if (updated === existing && !options.force) {
462
+ return { status: "merged" };
463
+ }
464
+ (0, import_node_fs6.writeFileSync)(agentsPath, updated, "utf8");
465
+ return { status: "replaced" };
466
+ }
467
+ if (hasBegin !== hasEnd) {
468
+ if (options.force) {
469
+ const withoutPartial = existing.replace(new RegExp(`${escapeRegExp(AGENTS_BEGIN)}[\\s\\S]*`), "").replace(AGENTS_END, "");
470
+ (0, import_node_fs6.writeFileSync)(
471
+ agentsPath,
472
+ `${withoutPartial.trimEnd()}
473
+
474
+ ${AGENTS_BRAIN_BLOCK}
475
+ `,
476
+ "utf8"
477
+ );
478
+ return { status: "replaced" };
479
+ }
480
+ return writeConflictPatch(cwd, existing);
481
+ }
482
+ (0, import_node_fs6.writeFileSync)(agentsPath, `${existing.trimEnd()}
483
+
484
+ ${AGENTS_BRAIN_BLOCK}
485
+ `, "utf8");
486
+ return { status: "merged" };
487
+ }
488
+ function writeConflictPatch(cwd, existing) {
489
+ const patchDir = (0, import_node_path6.join)(cwd, ".idia", "patches");
490
+ (0, import_node_fs6.mkdirSync)(patchDir, { recursive: true });
491
+ const patchPath = (0, import_node_path6.join)(patchDir, "AGENTS.md.patch");
492
+ const patchBody = [
493
+ "# AGENTS.md brain block patch (idia-ui add brain)",
494
+ "# Apply manually or run: idia-ui add brain --repair --force",
495
+ "",
496
+ AGENTS_BRAIN_BLOCK,
497
+ "",
498
+ "# --- existing AGENTS.md preserved below ---",
499
+ "",
500
+ existing
501
+ ].join("\n");
502
+ (0, import_node_fs6.writeFileSync)(patchPath, patchBody, "utf8");
503
+ return { status: "conflict", patchPath };
504
+ }
505
+ function escapeRegExp(value) {
506
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
507
+ }
508
+
509
+ // src/brain/pack.ts
510
+ var import_node_fs7 = require("fs");
511
+ var import_node_module = require("module");
512
+ var import_node_path7 = require("path");
513
+ function resolveBrainPackRoot(cwd) {
514
+ const local = (0, import_node_path7.join)(cwd, "node_modules", BRAIN_PACK_NAME);
515
+ if ((0, import_node_fs7.existsSync)((0, import_node_path7.join)(local, "pack-manifest.json"))) return local;
516
+ try {
517
+ const require2 = (0, import_node_module.createRequire)((0, import_node_path7.join)(cwd, "package.json"));
518
+ const pkgJson = require2.resolve(`${BRAIN_PACK_NAME}/package.json`);
519
+ return (0, import_node_path7.dirname)(pkgJson);
520
+ } catch {
521
+ return null;
522
+ }
523
+ }
524
+ function readBrainPackManifest(packRoot) {
525
+ try {
526
+ const raw = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(packRoot, "pack-manifest.json"), "utf8");
527
+ return JSON.parse(raw);
528
+ } catch {
529
+ return null;
530
+ }
531
+ }
532
+ function brainPackSpecForApp(cwd) {
533
+ try {
534
+ const pkgPath = (0, import_node_path7.join)(cwd, "package.json");
535
+ const pkg = JSON.parse((0, import_node_fs7.readFileSync)(pkgPath, "utf8"));
536
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
537
+ const usesWorkspace = Object.values(deps).some((v) => v === "workspace:*");
538
+ if (usesWorkspace) return "workspace:*";
539
+ } catch {
540
+ }
541
+ return "^0.2.0";
542
+ }
543
+ function readInstalledPackManifest(cwd, packageName, manifestFile = "pack-manifest.json") {
544
+ try {
545
+ const raw = (0, import_node_fs7.readFileSync)(
546
+ (0, import_node_path7.join)(cwd, "node_modules", packageName, manifestFile),
547
+ "utf8"
548
+ );
549
+ return JSON.parse(raw);
550
+ } catch {
551
+ return null;
552
+ }
553
+ }
554
+
555
+ // src/utils/exec.ts
556
+ var import_node_child_process2 = require("child_process");
557
+ function runPmInstall2(cwd, pm) {
558
+ const result = spawnCommand(pm, installArgs(pm), cwd);
559
+ if (result.status !== 0) {
560
+ throw new Error(`${pm} install failed (exit ${result.status ?? 1})`);
561
+ }
562
+ }
563
+ function runPmAddDev(cwd, pm, packageSpec) {
564
+ const result = spawnCommand(pm, addDevArgs(pm, packageSpec), cwd);
565
+ if (result.status !== 0) {
566
+ throw new Error(`${pm} add failed for ${packageSpec} (exit ${result.status ?? 1})`);
567
+ }
568
+ }
569
+ function installArgs(pm) {
570
+ switch (pm) {
571
+ case "pnpm":
572
+ return ["install"];
573
+ case "yarn":
574
+ return ["install"];
575
+ default:
576
+ return ["install"];
577
+ }
578
+ }
579
+ function addDevArgs(pm, packageSpec) {
580
+ switch (pm) {
581
+ case "pnpm":
582
+ return ["add", "-D", packageSpec];
583
+ case "yarn":
584
+ return ["add", "-D", packageSpec];
585
+ default:
586
+ return ["install", "-D", packageSpec];
587
+ }
588
+ }
589
+ function spawnCommand(pm, args, cwd) {
590
+ const command = pm === "npm" ? "npm" : pm;
591
+ return (0, import_node_child_process2.spawnSync)(command, args, { cwd, stdio: "inherit", shell: process.platform === "win32" });
592
+ }
593
+
594
+ // src/brain/install.ts
595
+ function copyDirRecursive(src, dest, force) {
596
+ (0, import_node_fs8.mkdirSync)(dest, { recursive: true });
597
+ for (const entry of (0, import_node_fs8.readdirSync)(src)) {
598
+ const srcPath = (0, import_node_path8.join)(src, entry);
599
+ const destPath = (0, import_node_path8.join)(dest, entry);
600
+ const stat = (0, import_node_fs8.statSync)(srcPath);
601
+ if (stat.isDirectory()) {
602
+ copyDirRecursive(srcPath, destPath, force);
603
+ } else {
604
+ if (!force && (0, import_node_fs8.existsSync)(destPath)) continue;
605
+ (0, import_node_fs8.cpSync)(srcPath, destPath);
606
+ }
607
+ }
608
+ }
609
+ function ensureBrainPackDependency(cwd, pm, skipInstall) {
610
+ const pkg = readPackageJson(cwd);
611
+ if (!pkg) throw new Error("package.json not found");
612
+ const spec = brainPackSpecForApp(cwd);
613
+ const existing = pkg.devDependencies?.[BRAIN_PACK_NAME];
614
+ if (existing === spec || existing === BRAIN_PACK_VERSION) {
615
+ if (!skipInstall && !(0, import_node_fs8.existsSync)((0, import_node_path8.join)(cwd, "node_modules", BRAIN_PACK_NAME))) {
616
+ runPmInstall2(cwd, pm);
617
+ }
618
+ return;
619
+ }
620
+ if (!skipInstall) {
621
+ runPmAddDev(cwd, pm, `${BRAIN_PACK_NAME}@${spec}`);
622
+ }
623
+ }
624
+ function isBrainInstalled(cwd, manifest) {
625
+ const hasAgents = fileExists(cwd, ".idia/brain/AGENTS.md");
626
+ const hasFeature = Boolean(manifest?.features?.brain?.brainPack);
627
+ return hasAgents && hasFeature;
628
+ }
629
+ function installBrainPack(options) {
630
+ const { cwd, repair = false, force = false, skipInstall = false } = options;
631
+ const pm = options.pm ?? detectPackageManager(cwd);
632
+ const manifest = readManifest(cwd);
633
+ if (isBrainInstalled(cwd, manifest) && !repair && !force) {
634
+ const packRoot2 = resolveBrainPackRoot(cwd);
635
+ if (packRoot2) {
636
+ const packManifest2 = readBrainPackManifest(packRoot2);
637
+ return {
638
+ alreadyInstalled: true,
639
+ packRoot: packRoot2,
640
+ sourceHash: packManifest2?.sourceHash ?? "",
641
+ agentsPatch: { status: "merged" }
642
+ };
643
+ }
644
+ }
645
+ ensureBrainPackDependency(cwd, pm, skipInstall);
646
+ const packRoot = resolveBrainPackRoot(cwd);
647
+ if (!packRoot) {
648
+ throw new Error(
649
+ `${BRAIN_PACK_NAME} not found. Run ${pm} add -D ${BRAIN_PACK_NAME} and retry.`
650
+ );
651
+ }
652
+ const packManifest = readBrainPackManifest(packRoot);
653
+ if (!packManifest?.sourceHash) {
654
+ throw new Error(`${BRAIN_PACK_NAME} pack-manifest.json missing or invalid.`);
655
+ }
656
+ const brainSrc = (0, import_node_path8.join)(packRoot, "brain");
657
+ const rulesSrc = (0, import_node_path8.join)(packRoot, "cursor-rules");
658
+ const brainDest = (0, import_node_path8.join)(cwd, ".idia", "brain");
659
+ const rulesDest = (0, import_node_path8.join)(cwd, ".cursor", "rules");
660
+ copyDirRecursive(brainSrc, brainDest, force || repair);
661
+ (0, import_node_fs8.mkdirSync)(rulesDest, { recursive: true });
662
+ copyDirRecursive(rulesSrc, rulesDest, force || repair);
663
+ const agentsPatch = patchRootAgentsMd(cwd, { repair, force });
664
+ const coreVersion = readInstalledVersion(cwd, "@idia-ui/core") ?? manifest?.core ?? packManifest.core;
665
+ const nextManifest = {
666
+ idiaCli: manifest?.idiaCli ?? CLI_VERSION,
667
+ core: coreVersion,
668
+ features: {
669
+ ...manifest?.features,
670
+ brain: {
671
+ installed: (/* @__PURE__ */ new Date()).toISOString(),
672
+ brainPack: packManifest.brainPack,
673
+ sourceHash: packManifest.sourceHash
674
+ }
675
+ }
676
+ };
677
+ writeManifest(cwd, nextManifest);
678
+ return {
679
+ alreadyInstalled: false,
680
+ packRoot,
681
+ sourceHash: packManifest.sourceHash,
682
+ agentsPatch
683
+ };
684
+ }
685
+ function printBrainInstallSummary(result) {
686
+ if (result.alreadyInstalled) {
687
+ console.log("\u2713 brain pack already installed");
688
+ console.log(` .idia/brain/AGENTS.md`);
689
+ console.log(` sourceHash: ${result.sourceHash || "(unknown)"}`);
690
+ console.log("");
691
+ console.log(BRAIN_TIER_DISCLAIMER);
692
+ return;
693
+ }
694
+ console.log("\u2713 brain pack installed to .idia/brain/");
695
+ console.log("\u2713 cursor rules copied to .cursor/rules/idia-ui-*.mdc");
696
+ switch (result.agentsPatch.status) {
697
+ case "created":
698
+ console.log("\u2713 AGENTS.md created with idia-ui brain block");
699
+ break;
700
+ case "merged":
701
+ console.log("\u2713 AGENTS.md updated with idia-ui brain block");
702
+ break;
703
+ case "replaced":
704
+ console.log("\u2713 AGENTS.md brain block repaired");
705
+ break;
706
+ case "conflict":
707
+ console.warn(
708
+ `\u26A0 AGENTS.md conflict \u2014 wrote suggested patch: ${result.agentsPatch.patchPath}`
709
+ );
710
+ console.warn(" Run idia-ui add brain --repair --force to overwrite.");
711
+ break;
712
+ }
713
+ console.log(` sourceHash: ${result.sourceHash}`);
714
+ console.log("");
715
+ console.log(BRAIN_TIER_DISCLAIMER);
716
+ }
717
+
718
+ // src/commands/add.ts
719
+ function runAddBrain(options) {
720
+ try {
721
+ const result = installBrainPack({
722
+ cwd: options.cwd,
723
+ repair: options.repair,
724
+ force: options.force,
725
+ pm: options.pm
726
+ });
727
+ printBrainInstallSummary(result);
728
+ if (result.agentsPatch.status === "conflict") {
729
+ process.exit(1);
730
+ }
731
+ } catch (error) {
732
+ console.error(error instanceof Error ? error.message : String(error));
733
+ console.error(BRAIN_TIER_DISCLAIMER);
734
+ process.exit(1);
735
+ }
736
+ }
737
+ function runAddUnknown(target) {
738
+ console.error(`Unknown add target: ${target}`);
739
+ console.error("Available: storybook, brain");
740
+ process.exit(1);
741
+ }
742
+
743
+ // src/checks/packs.ts
744
+ var import_node_fs9 = require("fs");
745
+ var import_node_path9 = require("path");
746
+ function readCoverageManifest(cwd) {
747
+ try {
748
+ const raw = (0, import_node_fs9.readFileSync)(
749
+ (0, import_node_path9.join)(cwd, "node_modules/@idia-ui/storybook-docs/fragments/coverage-manifest.json"),
750
+ "utf8"
751
+ );
752
+ return JSON.parse(raw);
753
+ } catch {
754
+ return null;
755
+ }
756
+ }
757
+ function listStoryComponentIds(cwd) {
758
+ const storiesDir = (0, import_node_path9.join)(cwd, "src/stories");
759
+ const ids = /* @__PURE__ */ new Set();
760
+ try {
761
+ for (const file of (0, import_node_fs9.readdirSync)(storiesDir)) {
762
+ const match = file.match(/^([A-Za-z]+)\.stories\.(tsx?|jsx?)$/);
763
+ if (match) {
764
+ ids.add(match[1].toLowerCase());
765
+ }
766
+ }
767
+ } catch {
768
+ return [];
769
+ }
770
+ return [...ids].sort();
771
+ }
772
+ function runStorybookChecks(cwd) {
773
+ const hasPreview = fileExists(cwd, ".storybook/preview.tsx");
774
+ const storybookDocs = fileExists(
775
+ cwd,
776
+ "node_modules/@idia-ui/storybook-docs/package.json"
777
+ );
778
+ if (!hasPreview && !storybookDocs) {
779
+ return [
780
+ {
781
+ id: "S1",
782
+ status: "info",
783
+ message: "Storybook not installed \u2014 skipping S* checks"
784
+ },
785
+ {
786
+ id: "S2",
787
+ status: "skip",
788
+ message: "@idia-ui/storybook-docs not installed"
789
+ },
790
+ {
791
+ id: "S3",
792
+ status: "skip",
793
+ message: "storybook fragment manifest not available"
794
+ }
795
+ ];
796
+ }
797
+ const results = [];
798
+ const preview = readTextFile(cwd, ".storybook/preview.tsx");
799
+ const importsTamagui = preview !== null && (/tamagui\.config/.test(preview) || /AppProviders/.test(preview));
800
+ if (importsTamagui) {
801
+ results.push({
802
+ id: "S1",
803
+ status: "pass",
804
+ message: ".storybook/preview.tsx wires AppProviders (shared token parity)"
805
+ });
806
+ } else {
807
+ results.push({
808
+ id: "S1",
809
+ status: "fail",
810
+ message: ".storybook/preview.tsx missing AppProviders / tamagui.config wiring",
811
+ fixHint: "idia-ui add storybook --repair"
812
+ });
813
+ }
814
+ const docsVersion = readInstalledVersion(cwd, "@idia-ui/storybook-docs");
815
+ const coreVersion = readInstalledVersion(cwd, "@idia-ui/core");
816
+ const docsMinor = docsVersion ? getMinor(docsVersion) : null;
817
+ const coreMinor = coreVersion ? getMinor(coreVersion) : null;
818
+ if (!storybookDocs) {
819
+ results.push({
820
+ id: "S2",
821
+ status: "warn",
822
+ message: "@idia-ui/storybook-docs not installed",
823
+ fixHint: "idia-ui add storybook"
824
+ });
825
+ } else if (docsMinor && coreMinor && docsMinor === coreMinor) {
826
+ results.push({
827
+ id: "S2",
828
+ status: "pass",
829
+ message: `@idia-ui/storybook-docs@${docsVersion} matches @idia-ui/core@${coreVersion} minor`
830
+ });
831
+ } else {
832
+ results.push({
833
+ id: "S2",
834
+ status: "warn",
835
+ message: `storybook-docs minor (${docsMinor ?? "unknown"}) \u2260 core minor (${coreMinor ?? "unknown"})`,
836
+ fixHint: "idia-ui sync storybook"
837
+ });
838
+ }
839
+ const coverage = readCoverageManifest(cwd);
840
+ const coveredIds = coverage?.components ? Object.keys(coverage.components) : [];
841
+ const storyIds = listStoryComponentIds(cwd);
842
+ const uncovered = storyIds.filter((id) => !coveredIds.includes(id));
843
+ if (!coverage) {
844
+ results.push({
845
+ id: "S3",
846
+ status: "warn",
847
+ message: "coverage manifest not readable",
848
+ fixHint: "idia-ui sync storybook"
849
+ });
850
+ } else if (uncovered.length === 0) {
851
+ results.push({
852
+ id: "S3",
853
+ status: "pass",
854
+ message: coveredIds.length > 0 ? `fragment manifest covers ${coveredIds.length} Wave 1 components; story files aligned` : "fragment manifest present; no src/stories/ files to cross-ref"
855
+ });
856
+ if (coveredIds.length > 0 && coveredIds.length < 10) {
857
+ results.push({
858
+ id: "S3-info",
859
+ status: "info",
860
+ message: `storybook-docs pack has doc fragments for ${coveredIds.length} components. Other components render without brain prose.`
861
+ });
862
+ }
863
+ } else {
864
+ results.push({
865
+ id: "S3",
866
+ status: "warn",
867
+ message: `stories without fragments: ${uncovered.join(", ")}`,
868
+ fixHint: "upgrade @idia-ui/storybook-docs or remove uncovered stories"
869
+ });
870
+ }
871
+ const manifest = readManifest(cwd);
872
+ if (storybookDocs && !manifest?.features?.storybook) {
873
+ results.push({
874
+ id: "S-manifest",
875
+ status: "warn",
876
+ message: "manifest features.storybook missing",
877
+ fixHint: "idia-ui sync storybook"
878
+ });
879
+ }
880
+ return results;
881
+ }
882
+ function readPackSourceHash(cwd, packageName) {
883
+ return readInstalledPackManifest(cwd, packageName)?.sourceHash ?? null;
884
+ }
885
+ function runBrainChecks(cwd) {
886
+ const brainPackInstalled = fileExists(
887
+ cwd,
888
+ "node_modules/@idia-ui/brain-pack/package.json"
889
+ );
890
+ const brainAgents = fileExists(cwd, ".idia/brain/AGENTS.md");
891
+ const rootAgents = readTextFile(cwd, "AGENTS.md");
892
+ const hasBrainBlock = rootAgents !== null && /idia-ui brain:begin/.test(rootAgents) && /idia-ui brain:end/.test(rootAgents);
893
+ if (!brainPackInstalled && !brainAgents) {
894
+ return [
895
+ {
896
+ id: "B1",
897
+ status: "warn",
898
+ message: "brain pack not installed",
899
+ fixHint: "Run idia-ui add brain"
900
+ },
901
+ {
902
+ id: "B2",
903
+ status: "skip",
904
+ message: "AGENTS.md brain block not present"
905
+ },
906
+ {
907
+ id: "B3",
908
+ status: "skip",
909
+ message: "brain sourceHash coupling not checked"
910
+ },
911
+ {
912
+ id: "B4",
913
+ status: "skip",
914
+ message: "brain/storybook hash coupling not checked"
915
+ }
916
+ ];
917
+ }
918
+ const results = [];
919
+ const manifest = readManifest(cwd);
920
+ const manifestHash = manifest?.features?.brain?.sourceHash;
921
+ const packRoot = resolveBrainPackRoot(cwd);
922
+ const packManifest = packRoot ? readBrainPackManifest(packRoot) : null;
923
+ const packHash = packManifest?.sourceHash;
924
+ results.push({
925
+ id: "B1",
926
+ status: brainAgents ? "pass" : "warn",
927
+ message: brainAgents ? "\u2713 brain pack installed" : ".idia/brain/AGENTS.md missing",
928
+ fixHint: brainAgents ? void 0 : "idia-ui add brain"
929
+ });
930
+ results.push({
931
+ id: "B2",
932
+ status: hasBrainBlock ? "pass" : "warn",
933
+ message: hasBrainBlock ? "root AGENTS.md contains idia-ui brain block" : "root AGENTS.md missing idia-ui brain:begin/end block",
934
+ fixHint: hasBrainBlock ? void 0 : "idia-ui add brain --repair"
935
+ });
936
+ if (manifestHash && packHash && manifestHash === packHash) {
937
+ results.push({
938
+ id: "B3",
939
+ status: "pass",
940
+ message: "manifest sourceHash matches installed brain pack"
941
+ });
942
+ } else if (manifestHash && packHash) {
943
+ results.push({
944
+ id: "B3",
945
+ status: "warn",
946
+ message: "manifest sourceHash drifted from brain pack pin",
947
+ fixHint: "idia-ui sync brain"
948
+ });
949
+ } else {
950
+ results.push({
951
+ id: "B3",
952
+ status: "warn",
953
+ message: "manifest sourceHash missing or brain pack not resolved",
954
+ fixHint: "idia-ui sync brain"
955
+ });
956
+ }
957
+ const storybookHash = readPackSourceHash(cwd, "@idia-ui/storybook-docs");
958
+ if (!storybookHash) {
959
+ results.push({
960
+ id: "B4",
961
+ status: "skip",
962
+ message: "storybook-docs not installed \u2014 B4 hash coupling skipped"
963
+ });
964
+ } else if (packHash && storybookHash === packHash) {
965
+ results.push({
966
+ id: "B4",
967
+ status: "pass",
968
+ message: "brain pack and storybook-docs share sourceHash (same generate run)"
969
+ });
970
+ } else {
971
+ results.push({
972
+ id: "B4",
973
+ status: "warn",
974
+ message: "brain pack and storybook-docs sourceHash skew",
975
+ fixHint: "idia-ui sync all"
976
+ });
977
+ }
978
+ results.push({
979
+ id: "B-tier",
980
+ status: "info",
981
+ message: `\u26A0 ${BRAIN_DOCTOR_DISCLAIMER}`
982
+ });
983
+ return results;
984
+ }
985
+ function runCustomCheck(cwd) {
986
+ const hasStories = fileExists(cwd, "src/stories");
987
+ if (!hasStories) {
988
+ return [
989
+ {
990
+ id: "C1",
991
+ status: "skip",
992
+ message: "no src/stories/ directory (Storybook not configured)"
993
+ }
994
+ ];
995
+ }
996
+ return [
997
+ {
998
+ id: "C1",
999
+ status: "info",
1000
+ message: "wrapper/story coverage check not implemented (stub)",
1001
+ fixHint: "Add stories for components in src/components/"
1002
+ }
1003
+ ];
1004
+ }
1005
+
1006
+ // src/checks/manifest.ts
1007
+ function runManifestCheck(cwd, fix) {
1008
+ const results = [];
1009
+ let fixed = false;
1010
+ if (!manifestExists(cwd)) {
1011
+ if (fix) {
1012
+ const pkg = readPackageJson(cwd);
1013
+ const coreVersion = readInstalledVersion(cwd, "@idia-ui/core") ?? pkg?.dependencies?.["@idia-ui/core"]?.replace(/^[\^~]/, "") ?? "0.2.0";
1014
+ regenerateManifest(cwd, coreVersion);
1015
+ fixed = true;
1016
+ results.push({
1017
+ id: "M1",
1018
+ status: "pass",
1019
+ message: ".idia/manifest.json regenerated",
1020
+ fixHint: "Created manifest with idiaCli + core versions"
1021
+ });
1022
+ return { results, fixed };
1023
+ }
1024
+ results.push({
1025
+ id: "M1",
1026
+ status: "warn",
1027
+ message: ".idia/manifest.json missing",
1028
+ fixHint: "Run idia-ui doctor --fix or idia-ui sync all after first add-on"
1029
+ });
1030
+ return { results, fixed };
1031
+ }
1032
+ const manifest = readManifest(cwd);
1033
+ if (!manifest || !validateManifestShape(manifest)) {
1034
+ results.push({
1035
+ id: "M1",
1036
+ status: "fail",
1037
+ message: ".idia/manifest.json invalid or unparseable",
1038
+ fixHint: "Run idia-ui doctor --fix to regenerate"
1039
+ });
1040
+ return { results, fixed };
1041
+ }
1042
+ results.push({
1043
+ id: "M1",
1044
+ status: "pass",
1045
+ message: `.idia/manifest.json valid (core ${manifest.core}, cli ${manifest.idiaCli})`
1046
+ });
1047
+ return { results, fixed };
1048
+ }
1049
+
1050
+ // src/checks/navigation.ts
1051
+ function runNavigationChecks(cwd) {
1052
+ const results = [];
1053
+ const providers = readTextFile(cwd, "src/providers/AppProviders.tsx") ?? readTextFile(cwd, "src/providers/AppProviders.ts");
1054
+ if (providers && /NavigationContainer/.test(providers)) {
1055
+ results.push({
1056
+ id: "N1",
1057
+ status: "pass",
1058
+ message: "NavigationContainer in provider tree"
1059
+ });
1060
+ } else {
1061
+ results.push({
1062
+ id: "N1",
1063
+ status: "fail",
1064
+ message: "NavigationContainer not found in AppProviders",
1065
+ fixHint: "Wrap navigation in NavigationContainer per templates/consumer-app"
1066
+ });
1067
+ }
1068
+ const usesIdiaNav = grepSrcForPattern(cwd, /\bLink\b.*@idia-ui\/core/) || grepSrcForPattern(cwd, /NavigationProvider/) || grepSrcForPattern(cwd, /useIdiaNavigation|navigateTo/);
1069
+ if (usesIdiaNav) {
1070
+ const hasNavProvider = providers ? /NavigationProvider/.test(providers) : grepSrcForPattern(cwd, /NavigationProvider/);
1071
+ if (!hasNavProvider) {
1072
+ results.push({
1073
+ id: "N2",
1074
+ status: "warn",
1075
+ message: "idia Link/navigation helpers used but NavigationProvider missing",
1076
+ fixHint: "Wrap navigation ref with NavigationProvider from @idia-ui/core"
1077
+ });
1078
+ } else {
1079
+ results.push({
1080
+ id: "N2",
1081
+ status: "pass",
1082
+ message: "NavigationProvider present"
1083
+ });
1084
+ }
1085
+ } else {
1086
+ results.push({
1087
+ id: "N2",
1088
+ status: "pass",
1089
+ message: "idia Link/navigation helpers not used (NavigationProvider optional)"
1090
+ });
1091
+ }
1092
+ return results;
1093
+ }
1094
+
1095
+ // src/checks/peers.ts
1096
+ var P2_PACKAGES = [
1097
+ "react-native-reanimated",
1098
+ "react-native-gesture-handler",
1099
+ "react-native-screens",
1100
+ "react-native-safe-area-context"
1101
+ ];
1102
+ var BARREL_REQUIRED_PEERS = ["zustand", "@tanstack/react-table"];
1103
+ var BARREL_OPTIONAL_PEERS = [
1104
+ "react-image-crop",
1105
+ "@fullcalendar/core",
1106
+ "@fullcalendar/daygrid",
1107
+ "@fullcalendar/timegrid",
1108
+ "@fullcalendar/interaction",
1109
+ "@fullcalendar/multimonth",
1110
+ "@fullcalendar/list",
1111
+ "@fullcalendar/react"
1112
+ ];
1113
+ function runPeerChecks(cwd) {
1114
+ const pkg = readPackageJson(cwd);
1115
+ if (!pkg) {
1116
+ return [
1117
+ {
1118
+ id: "P0",
1119
+ status: "fail",
1120
+ message: "package.json not found or unreadable",
1121
+ fixHint: "Run idia-ui doctor from your app root"
1122
+ }
1123
+ ];
1124
+ }
1125
+ const pm = detectPackageManager(cwd);
1126
+ const results = [];
1127
+ const tamaguiVersion = readInstalledVersion(cwd, "tamagui") ?? getDepVersion(pkg, "tamagui")?.replace(/^[\^~]/, "") ?? null;
1128
+ const coreRange = readPeerRange(cwd, "@idia-ui/core", "tamagui") ?? readPeerRange(cwd, "@idia-ui/theme", "tamagui") ?? "^1.70.0";
1129
+ if (!tamaguiVersion) {
1130
+ results.push({
1131
+ id: "P1",
1132
+ status: "fail",
1133
+ message: "tamagui not installed",
1134
+ fixHint: pmAddCommand(pm, ["tamagui@^1.144.0"])
1135
+ });
1136
+ } else if (!satisfiesRange(tamaguiVersion, coreRange)) {
1137
+ results.push({
1138
+ id: "P1",
1139
+ status: "fail",
1140
+ message: `tamagui ${tamaguiVersion} outside peer range ${coreRange}`,
1141
+ fixHint: pmAddCommand(pm, [`tamagui@${coreRange}`])
1142
+ });
1143
+ } else {
1144
+ results.push({
1145
+ id: "P1",
1146
+ status: "pass",
1147
+ message: `tamagui ${tamaguiVersion} in peer range`
1148
+ });
1149
+ }
1150
+ const missingP2 = P2_PACKAGES.filter((name) => {
1151
+ const installed = readInstalledVersion(cwd, name);
1152
+ const declared = getDepVersion(pkg, name);
1153
+ return !installed && !declared;
1154
+ });
1155
+ if (missingP2.length > 0) {
1156
+ results.push({
1157
+ id: "P2",
1158
+ status: "fail",
1159
+ message: `missing navigation/gesture peers: ${missingP2.join(", ")}`,
1160
+ fixHint: pmAddCommand(pm, [...missingP2])
1161
+ });
1162
+ } else {
1163
+ results.push({
1164
+ id: "P2",
1165
+ status: "pass",
1166
+ message: "gesture-handler, reanimated, screens, safe-area-context present"
1167
+ });
1168
+ }
1169
+ const idiaPkgs = Object.keys({
1170
+ ...pkg.dependencies,
1171
+ ...pkg.devDependencies
1172
+ }).filter((n) => n.startsWith("@idia-ui/"));
1173
+ const idiaVersions = idiaPkgs.map((name) => {
1174
+ const v = readInstalledVersion(cwd, name) ?? getDepVersion(pkg, name)?.replace(/^[\^~]/, "");
1175
+ return v ? { name, version: v } : null;
1176
+ }).filter((x) => x !== null);
1177
+ const minors = new Set(
1178
+ idiaVersions.map((p) => {
1179
+ const parts = p.version.split(".");
1180
+ return parts.length >= 2 ? `${parts[0]}.${parts[1]}` : null;
1181
+ }).filter((m) => m !== null && !m.includes("workspace"))
1182
+ );
1183
+ if (idiaVersions.length === 0) {
1184
+ results.push({
1185
+ id: "P3",
1186
+ status: "fail",
1187
+ message: "no @idia-ui/* packages in dependencies",
1188
+ fixHint: pmAddCommand(pm, ["@idia-ui/core@latest", "@idia-ui/theme@latest"])
1189
+ });
1190
+ } else if (minors.size > 1) {
1191
+ results.push({
1192
+ id: "P3",
1193
+ status: "fail",
1194
+ message: `@idia-ui/* minor versions misaligned: ${[...minors].join(", ")}`,
1195
+ fixHint: `${pm === "pnpm" ? "pnpm" : pm} update @idia-ui/*`
1196
+ });
1197
+ } else {
1198
+ results.push({
1199
+ id: "P3",
1200
+ status: "pass",
1201
+ message: `@idia-ui/* versions aligned (${idiaVersions.map((p) => `${p.name}@${p.version}`).join(", ")})`
1202
+ });
1203
+ }
1204
+ for (const peer of BARREL_REQUIRED_PEERS) {
1205
+ const installed = readInstalledVersion(cwd, peer);
1206
+ const declared = getDepVersion(pkg, peer);
1207
+ if (!installed && !declared) {
1208
+ results.push({
1209
+ id: "P3",
1210
+ status: "fail",
1211
+ message: `barrel peer ${peer} not installed (required for @idia-ui/core Table graph)`,
1212
+ fixHint: `${pmAddCommand(pm, [peer])} \u2014 see docs/generated/peer-dep-matrix.md`
1213
+ });
1214
+ }
1215
+ }
1216
+ for (const peer of BARREL_OPTIONAL_PEERS) {
1217
+ const installed = readInstalledVersion(cwd, peer);
1218
+ const declared = getDepVersion(pkg, peer);
1219
+ if (!installed && !declared) {
1220
+ results.push({
1221
+ id: "P3",
1222
+ status: "warn",
1223
+ message: `optional peer ${peer} not installed (barrel may require it)`,
1224
+ fixHint: `Install per peer-dep-matrix.md: ${pmAddCommand(pm, [peer])}`
1225
+ });
1226
+ }
1227
+ }
1228
+ return results;
1229
+ }
1230
+
1231
+ // src/checks/runtime.ts
1232
+ var OVERLAY_IMPORT = /from\s+['"]@idia-ui\/core['"][\s\S]*?(Modal|Toast|Dialog|ConfirmationModal)/;
1233
+ function runRuntimeChecks(cwd) {
1234
+ const results = [];
1235
+ if (fileExists(cwd, "tamagui.config.ts")) {
1236
+ results.push({
1237
+ id: "R1",
1238
+ status: "pass",
1239
+ message: "tamagui.config.ts found"
1240
+ });
1241
+ } else {
1242
+ results.push({
1243
+ id: "R1",
1244
+ status: "fail",
1245
+ message: "tamagui.config.ts missing at app root",
1246
+ fixHint: "Restore from templates/consumer-app or re-scaffold"
1247
+ });
1248
+ }
1249
+ const providers = readTextFile(cwd, "src/providers/AppProviders.tsx") ?? readTextFile(cwd, "src/providers/AppProviders.ts");
1250
+ if (!providers) {
1251
+ results.push({
1252
+ id: "R2",
1253
+ status: "fail",
1254
+ message: "AppProviders not found",
1255
+ fixHint: "Add src/providers/AppProviders.tsx per templates/consumer-app"
1256
+ });
1257
+ } else {
1258
+ const hasTamagui = /TamaguiProvider/.test(providers);
1259
+ const hasTheme = /ThemeProvider/.test(providers) && /@idia-ui\/theme/.test(providers);
1260
+ if (hasTamagui && hasTheme) {
1261
+ results.push({
1262
+ id: "R2",
1263
+ status: "pass",
1264
+ message: "AppProviders: TamaguiProvider + ThemeProvider"
1265
+ });
1266
+ } else {
1267
+ results.push({
1268
+ id: "R2",
1269
+ status: "fail",
1270
+ message: "AppProviders missing TamaguiProvider and/or ThemeProvider (@idia-ui/theme)",
1271
+ fixHint: "Compare src/providers/AppProviders.tsx with templates/consumer-app"
1272
+ });
1273
+ }
1274
+ }
1275
+ const usesOverlays = grepSrcForPattern(cwd, OVERLAY_IMPORT) || grepSrcForPattern(cwd, /\b(Modal|Toast|Dialog)\b.*@idia-ui\/core/);
1276
+ if (usesOverlays) {
1277
+ const hasModal = providers ? /ModalProvider/.test(providers) : false;
1278
+ const hasToast = providers ? /ToastProvider/.test(providers) : false;
1279
+ if (!hasModal || !hasToast) {
1280
+ results.push({
1281
+ id: "R3",
1282
+ status: "warn",
1283
+ message: "overlay components imported but ModalProvider/ToastProvider missing",
1284
+ fixHint: "Add ModalProvider and ToastProvider to AppProviders per template"
1285
+ });
1286
+ } else {
1287
+ results.push({
1288
+ id: "R3",
1289
+ status: "pass",
1290
+ message: "overlay providers present"
1291
+ });
1292
+ }
1293
+ } else {
1294
+ results.push({
1295
+ id: "R3",
1296
+ status: "pass",
1297
+ message: "no overlay imports detected (Modal/Toast providers optional)"
1298
+ });
1299
+ }
1300
+ const fontGate = providers && /waitForWebFonts/.test(providers) || grepSrcForPattern(cwd, /expo-font/) || grepSrcForPattern(cwd, /useFonts/);
1301
+ if (fontGate) {
1302
+ results.push({
1303
+ id: "R4",
1304
+ status: "pass",
1305
+ message: "web font gate configured (waitForWebFonts or expo-font)"
1306
+ });
1307
+ } else {
1308
+ results.push({
1309
+ id: "R4",
1310
+ status: "warn",
1311
+ message: "no waitForWebFonts or expo-font pattern detected",
1312
+ fixHint: "Copy font loading pattern from templates/consumer-app AppProviders"
1313
+ });
1314
+ }
1315
+ return results;
1316
+ }
1317
+
1318
+ // src/checks/index.ts
1319
+ function runAllChecks(options) {
1320
+ const { cwd, full, fix } = options;
1321
+ const checks = [];
1322
+ checks.push(...runPeerChecks(cwd));
1323
+ checks.push(...runRuntimeChecks(cwd));
1324
+ checks.push(...runNavigationChecks(cwd));
1325
+ const { results: manifestResults } = runManifestCheck(cwd, fix);
1326
+ checks.push(...manifestResults);
1327
+ if (full) {
1328
+ checks.push(...runStorybookChecks(cwd));
1329
+ checks.push(...runBrainChecks(cwd));
1330
+ checks.push(...runCustomCheck(cwd));
1331
+ }
1332
+ return checks;
1333
+ }
1334
+ function summarizeChecks(checks, strict) {
1335
+ let passed = 0;
1336
+ let warnings = 0;
1337
+ let failures = 0;
1338
+ let skipped = 0;
1339
+ for (const check of checks) {
1340
+ switch (check.status) {
1341
+ case "pass":
1342
+ passed++;
1343
+ break;
1344
+ case "warn":
1345
+ warnings++;
1346
+ break;
1347
+ case "fail":
1348
+ failures++;
1349
+ break;
1350
+ case "skip":
1351
+ case "info":
1352
+ skipped++;
1353
+ break;
1354
+ }
1355
+ }
1356
+ let exitCode = 0;
1357
+ if (failures > 0) exitCode = 1;
1358
+ else if (strict && warnings > 0) exitCode = 1;
1359
+ return { passed, warnings, failures, skipped, exitCode };
1360
+ }
1361
+
1362
+ // src/commands/doctor.ts
1363
+ var STATUS_ICON = {
1364
+ pass: "\u2713",
1365
+ warn: "\u26A0",
1366
+ fail: "\u2717",
1367
+ info: "\u2139",
1368
+ skip: "\u25CB"
1369
+ };
1370
+ function runDoctor(options) {
1371
+ const checks = runAllChecks(options);
1372
+ const summary = summarizeChecks(checks, options.strict);
1373
+ return {
1374
+ checks,
1375
+ summary: {
1376
+ passed: summary.passed,
1377
+ warnings: summary.warnings,
1378
+ failures: summary.failures,
1379
+ skipped: summary.skipped
1380
+ },
1381
+ exitCode: summary.exitCode
1382
+ };
1383
+ }
1384
+ function formatHumanReport(report, full) {
1385
+ const lines = ["idia-ui doctor", ""];
1386
+ for (const check of report.checks) {
1387
+ const icon = STATUS_ICON[check.status] ?? "\xB7";
1388
+ lines.push(`${icon} ${check.id} ${check.message}`);
1389
+ if (check.fixHint && (check.status === "fail" || check.status === "warn")) {
1390
+ lines.push(` \u2192 ${check.fixHint}`);
1391
+ }
1392
+ }
1393
+ lines.push("");
1394
+ const { passed, warnings, failures } = report.summary;
1395
+ const mode = full ? "full" : "quick";
1396
+ lines.push(
1397
+ `${passed} passed, ${warnings} warnings, ${failures} failures (${mode} scan) \u2014 exit ${report.exitCode}`
1398
+ );
1399
+ return lines.join("\n");
1400
+ }
1401
+ function formatJsonReport(report) {
1402
+ return JSON.stringify(
1403
+ {
1404
+ checks: report.checks.map((c) => ({
1405
+ id: c.id,
1406
+ status: c.status,
1407
+ message: c.message,
1408
+ ...c.fixHint ? { fixHint: c.fixHint } : {}
1409
+ })),
1410
+ summary: report.summary,
1411
+ exitCode: report.exitCode
1412
+ },
1413
+ null,
1414
+ 2
1415
+ );
1416
+ }
1417
+
1418
+ // src/commands/storybook/sync.ts
1419
+ var import_node_child_process3 = require("child_process");
1420
+ function isStorybookInstalled2(cwd) {
1421
+ return fileExists(cwd, ".storybook/main.ts");
1422
+ }
1423
+ function runPmUpdate(cwd, pm) {
1424
+ const packages = [
1425
+ `@idia-ui/storybook-docs@${STORYBOOK_DOCS_VERSION}`,
1426
+ `@idia-ui/storybook-preset-react-native-web@${STORYBOOK_PRESET_VERSION}`
1427
+ ];
1428
+ const args = pm === "pnpm" ? ["add", "-D", ...packages] : pm === "yarn" ? ["add", "-D", ...packages] : ["install", "-D", ...packages];
1429
+ const result = (0, import_node_child_process3.spawnSync)(pm, args, {
1430
+ cwd,
1431
+ stdio: "inherit",
1432
+ shell: process.platform === "win32"
1433
+ });
1434
+ if (result.status !== 0) {
1435
+ throw new Error(`${pm} update failed with exit code ${result.status ?? "unknown"}`);
1436
+ }
1437
+ }
1438
+ function runSyncStorybook(options) {
1439
+ const { cwd, skipInstall = false } = options;
1440
+ const pm = options.pm ?? detectPackageManager(cwd);
1441
+ if (!isStorybookInstalled2(cwd)) {
1442
+ console.error("Storybook not installed \u2014 run idia-ui add storybook first");
1443
+ process.exit(1);
1444
+ }
1445
+ const manifest = readManifest(cwd);
1446
+ if (!manifest) {
1447
+ console.error(".idia/manifest.json missing \u2014 run idia-ui doctor --fix");
1448
+ process.exit(1);
1449
+ }
1450
+ const corePkg = readPackageJson(cwd);
1451
+ const coreVersion = readInstalledVersion(cwd, "@idia-ui/core") ?? corePkg?.dependencies?.["@idia-ui/core"]?.replace(/^[\^~]/, "") ?? manifest.core;
1452
+ if (!skipInstall) {
1453
+ console.log(`Syncing storybook packs via ${pm}\u2026`);
1454
+ runPmUpdate(cwd, pm);
1455
+ }
1456
+ manifest.idiaCli = CLI_VERSION;
1457
+ manifest.core = coreVersion;
1458
+ manifest.features ??= {};
1459
+ manifest.features.storybook = {
1460
+ installed: manifest.features.storybook?.installed ?? (/* @__PURE__ */ new Date()).toISOString(),
1461
+ storybookDocs: STORYBOOK_DOCS_VERSION,
1462
+ storybook: STORYBOOK_CLI_VERSION,
1463
+ preset: STORYBOOK_PRESET_VERSION
1464
+ };
1465
+ writeManifest(cwd, manifest);
1466
+ console.log(`\u2713 Synced @idia-ui/storybook-docs@${STORYBOOK_DOCS_VERSION}`);
1467
+ console.log(`\u2713 Synced @idia-ui/storybook-preset-react-native-web@${STORYBOOK_PRESET_VERSION}`);
1468
+ return {
1469
+ synced: true,
1470
+ message: `storybook-docs@${STORYBOOK_DOCS_VERSION} aligned with core@${coreVersion}`
1471
+ };
1472
+ }
1473
+
1474
+ // src/commands/sync.ts
1475
+ function runSyncBrain(options) {
1476
+ try {
1477
+ const result = installBrainPack({
1478
+ cwd: options.cwd,
1479
+ repair: true,
1480
+ force: options.force,
1481
+ pm: options.pm
1482
+ });
1483
+ console.log("\u2713 brain pack synced");
1484
+ printBrainInstallSummary(result);
1485
+ } catch (error) {
1486
+ console.error(error instanceof Error ? error.message : String(error));
1487
+ console.error(BRAIN_TIER_DISCLAIMER);
1488
+ process.exit(1);
1489
+ }
1490
+ }
1491
+ function runSyncStorybook2(options) {
1492
+ runSyncStorybook({
1493
+ cwd: options.cwd,
1494
+ pm: options.pm
1495
+ });
1496
+ }
1497
+ function runSyncAll(options) {
1498
+ const { cwd } = options;
1499
+ const manifest = readManifest(cwd);
1500
+ const hasStorybook = fileExists(cwd, ".storybook/main.ts") || Boolean(manifest?.features?.storybook);
1501
+ const hasBrain = Boolean(manifest?.features?.brain?.brainPack);
1502
+ if (!hasStorybook && !hasBrain) {
1503
+ console.error("No storybook or brain features installed \u2014 run idia-ui add storybook or add brain first");
1504
+ process.exit(1);
1505
+ }
1506
+ if (hasStorybook) {
1507
+ runSyncStorybook({ cwd, pm: options.pm });
1508
+ } else {
1509
+ console.log("\u25CB storybook not installed \u2014 skipping storybook sync");
1510
+ }
1511
+ if (hasBrain) {
1512
+ runSyncBrain(options);
1513
+ } else {
1514
+ console.log("\u25CB brain not installed \u2014 skipping brain sync");
1515
+ console.error(BRAIN_DOCTOR_DISCLAIMER);
1516
+ }
1517
+ console.log("");
1518
+ console.log("\u2713 sync all complete");
1519
+ }
1520
+ function runSync(target, options) {
1521
+ switch (target) {
1522
+ case "brain":
1523
+ runSyncBrain(options);
1524
+ break;
1525
+ case "storybook":
1526
+ runSyncStorybook2(options);
1527
+ break;
1528
+ case "all":
1529
+ runSyncAll(options);
1530
+ break;
1531
+ default:
1532
+ console.error(`Unknown sync target: ${target}`);
1533
+ console.error("Available: storybook | brain | all");
1534
+ process.exit(1);
1535
+ }
1536
+ }
1537
+
1538
+ // src/cli.ts
1539
+ var program = new import_commander.Command();
1540
+ program.name("idia-ui").description("idia-ui CLI \u2014 doctor and add-on management for consumer apps").version(CLI_VERSION);
1541
+ program.command("doctor").description("Run health checks on an idia consumer app").option("--cwd <path>", "App root directory", process.cwd()).option("--full", "Include Storybook (S*) and brain (B*) checks").option("--fix", "Limited safe repairs (manifest regenerate)").option("--strict", "Treat warnings as failures").option("--json", "Machine-readable JSON output").action((opts) => {
1542
+ const cwd = (0, import_node_path10.resolve)(opts.cwd);
1543
+ const report = runDoctor({
1544
+ cwd,
1545
+ full: Boolean(opts.full),
1546
+ fix: Boolean(opts.fix),
1547
+ strict: Boolean(opts.strict),
1548
+ json: Boolean(opts.json)
1549
+ });
1550
+ if (opts.json) {
1551
+ console.log(formatJsonReport(report));
1552
+ } else {
1553
+ console.log(formatHumanReport(report, Boolean(opts.full)));
1554
+ }
1555
+ process.exit(report.exitCode);
1556
+ });
1557
+ var addCmd = program.command("add").description("Install idia add-on packs");
1558
+ addCmd.command("storybook").description("Install Storybook docs pack").option("--repair", "Refresh CLI-owned Storybook files").option("--force", "Overwrite conflicts (document risk)").option("--skip-install", "Skip package manager install").option("--pm <manager>", "Override package manager (pnpm|npm|yarn)").option("--cwd <path>", "App root directory", process.cwd()).action(
1559
+ (opts) => {
1560
+ runAddStorybook({
1561
+ cwd: (0, import_node_path10.resolve)(opts.cwd),
1562
+ repair: Boolean(opts.repair),
1563
+ force: Boolean(opts.force),
1564
+ skipInstall: Boolean(opts.skipInstall),
1565
+ pm: parsePm(opts.pm)
1566
+ });
1567
+ }
1568
+ );
1569
+ addCmd.command("brain").description("Install brain AI pack (Phase 4 \u2014 experimental)").option("--repair", "Re-apply rules, AGENTS block, and .idia/brain/ from pack").option("--force", "Overwrite conflicts (document risk)").option("--pm <manager>", "Override package manager (pnpm|npm|yarn)").option("--cwd <path>", "App root directory", process.cwd()).action((opts) => {
1570
+ runAddBrain({
1571
+ cwd: (0, import_node_path10.resolve)(opts.cwd),
1572
+ repair: Boolean(opts.repair),
1573
+ force: Boolean(opts.force),
1574
+ pm: parsePm(opts.pm)
1575
+ });
1576
+ });
1577
+ addCmd.argument("[target]", "add-on target").action((target) => {
1578
+ if (!target) {
1579
+ console.error("Usage: idia-ui add <storybook|brain>");
1580
+ process.exit(1);
1581
+ }
1582
+ runAddUnknown(target);
1583
+ });
1584
+ var syncCmd = program.command("sync").description("Sync installed pack versions and manifest");
1585
+ syncCmd.argument("<target>", "storybook | brain | all").option("--force", "Overwrite pack-owned files").option("--pm <manager>", "Override package manager (pnpm|npm|yarn)").option("--cwd <path>", "App root directory", process.cwd()).action((target, opts) => {
1586
+ runSync(target, {
1587
+ cwd: (0, import_node_path10.resolve)(opts.cwd),
1588
+ force: Boolean(opts.force),
1589
+ pm: parsePm(opts.pm)
1590
+ });
1591
+ });
1592
+ program.parse();
1593
+ function parsePm(value) {
1594
+ if (!value) return void 0;
1595
+ const normalized = value.toLowerCase();
1596
+ if (normalized === "pnpm" || normalized === "npm" || normalized === "yarn") {
1597
+ return normalized;
1598
+ }
1599
+ console.error(`Invalid --pm value "${value}". Use pnpm, npm, or yarn.`);
1600
+ process.exit(1);
1601
+ }
1602
+ //# sourceMappingURL=cli.js.map