larats 1.1.0 → 1.1.1

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
@@ -18,8 +18,8 @@ When codebases grow large, developers often struggle to understand the architect
18
18
 
19
19
  1. 🔍 **Architecture Scanner & Visualizer (`scan`)**: Instantly scans your repository, categorizes layers (Models, Controllers, Services, Actions, Filament Resources, Routes, etc.), and generates a clean `ARCHITECTURE.md` with an interactive **Mermaid.js flowchart** and directory tree.
20
20
  2. 🏗️ **Contextual Module Scaffolder (`scaffold` / `make`)**: Quickly generates cohesive feature modules with clean, production-ready boilerplate code tailored to your exact stack (Laravel or TypeScript).
21
- 3. 🔒 **100% Local & Secure**: No data ever leaves your computer. Sensitive files (`.env`, private keys, vendor folders) are strictly excluded.
22
-
21
+ 3. 🤖 **AI Architecture Context Packager (`ai`)**: Exports a token-efficient system prompt teaching Cursor AI, Claude, Copilot, or ChatGPT your exact layered boundaries (`--copy`, `--cursor`, `--claude`).
22
+ 4. 🔒 **100% Local & Secure**: No data ever leaves your computer. Sensitive files (`.env`, private keys, vendor folders) are strictly excluded.
23
23
  ---
24
24
 
25
25
  ## 🚀 Quick Start
@@ -36,6 +36,9 @@ npx larats scan
36
36
  # Open interactive Mermaid diagram in your browser
37
37
  npx larats view
38
38
 
39
+ # Export architecture context rules for AI (Cursor, Claude, Copilot)
40
+ npx larats ai --copy
41
+
39
42
  # Or scaffold a new module directly
40
43
  npx larats scaffold Payment
41
44
  ```
@@ -118,6 +121,13 @@ Create new features in seconds with consistent boilerplate:
118
121
  ### 3. 🛡️ Anti-Overwrite Protection
119
122
  LaraTS will **NEVER** silently overwrite your existing code. If a target file already exists, LaraTS skips it and alerts you. You can only overwrite files by explicitly providing `--force`.
120
123
 
124
+
125
+ ### 4. 🤖 AI Architecture Context Packager (`ai`)
126
+ Teach AI coding assistants (Cursor, Claude Code, GitHub Copilot, ChatGPT) your exact project architecture rules so they never write misplaced code again:
127
+ - **Copy to Clipboard**: `larats ai --copy` — Copies clean, compressed architecture instructions (< 500 tokens) ready to paste into ChatGPT or Claude.
128
+ - **Cursor AI Rules**: `larats ai --cursor` — Injects architecture boundaries and directory maps directly into `.cursorrules`.
129
+ - **Claude Code Rules**: `larats ai --claude` — Injects architecture conventions into `CLAUDE.md`.
130
+ *(Safe merge: uses `<!-- larats-rules:start -->` tags so existing user instructions are never overwritten).*
121
131
  ---
122
132
 
123
133
  ## 🔒 Strict Security & Safety Guarantees
@@ -142,6 +152,7 @@ USAGE:
142
152
  COMMANDS:
143
153
  scan Scan repository & generate ARCHITECTURE.md
144
154
  view, preview Open interactive architecture diagram in local browser
155
+ ai Export architecture context prompt for AI (Cursor, Claude, Copilot)
145
156
  scaffold, make [name] Scaffold a new module or feature with clean boilerplate
146
157
  info Show detected project type & architectural flavor
147
158
  help Display help message
@@ -149,6 +160,9 @@ COMMANDS:
149
160
  OPTIONS:
150
161
  -o, --output <file> Output path for scan (default: ARCHITECTURE.md)
151
162
  --view Open local browser viewer immediately after scanning
163
+ --copy Copy AI architecture context prompt to clipboard
164
+ --cursor Inject architecture rules into .cursorrules
165
+ --claude Inject architecture rules into CLAUDE.md
152
166
  -t, --tree Print directory tree in terminal
153
167
  --html Also export standalone ARCHITECTURE.html
154
168
  -m, --modular Enforce modular directory structure
package/dist/index.js CHANGED
@@ -1091,6 +1091,135 @@ function generateArchitectureHtml(scanResult) {
1091
1091
  </html>`;
1092
1092
  }
1093
1093
 
1094
+ // src/generator/ai-context.ts
1095
+ var AI_RULES_START_TAG = "<!-- larats-rules:start -->";
1096
+ var AI_RULES_END_TAG = "<!-- larats-rules:end -->";
1097
+ function generateAiContext(scanResult) {
1098
+ const { projectInfo, components, modules } = scanResult;
1099
+ const lines = [];
1100
+ lines.push("# Codebase Architecture Rules");
1101
+ lines.push(`> Project: ${projectInfo.name} | Stack: ${projectInfo.summary}`);
1102
+ lines.push("");
1103
+ lines.push("## Architectural Boundaries & Principles");
1104
+ lines.push("- Maintain strict separation of concerns across active architectural layers.");
1105
+ lines.push("- Keep Controllers thin: handle HTTP requests, invoke Services, and return responses.");
1106
+ lines.push("- Encapsulate all business rules, calculations, and mutations inside Services/Actions.");
1107
+ lines.push("- Isolate data storage and queries to Repositories or Model entities.");
1108
+ lines.push("- Validate all incoming user inputs with dedicated DTOs or Form Requests before reaching domain logic.");
1109
+ lines.push("");
1110
+ lines.push("## Project Structure & Layer Conventions");
1111
+ if (projectInfo.type === "laravel") {
1112
+ const isModular = Boolean(projectInfo.laravel?.isModular);
1113
+ const modDir = projectInfo.laravel?.modulesDir || "app/Modules";
1114
+ if (isModular) {
1115
+ lines.push(`- **Architecture Style**: Modular Domain Architecture`);
1116
+ lines.push(`- **Module Root**: \`${modDir}/<FeatureName>/\``);
1117
+ lines.push(`- **Controllers**: \`${modDir}/<FeatureName>/Controllers/\` (HTTP handlers)`);
1118
+ lines.push(`- **Services / Actions**: \`${modDir}/<FeatureName>/Services/\` or \`Actions/\` (Business logic)`);
1119
+ lines.push(`- **Models**: \`${modDir}/<FeatureName>/Models/\` (Eloquent entities)`);
1120
+ lines.push(`- **Validation**: \`${modDir}/<FeatureName>/Requests/\` (FormRequest classes)`);
1121
+ lines.push(`- **Routes**: \`${modDir}/<FeatureName>/routes.php\` or root \`routes/\``);
1122
+ } else {
1123
+ lines.push(`- **Architecture Style**: Standard Laravel Layered Architecture`);
1124
+ lines.push(`- **Controllers**: \`app/Http/Controllers/\``);
1125
+ lines.push(`- **Services / Actions**: \`app/Services/\` or \`app/Actions/\``);
1126
+ lines.push(`- **Models**: \`app/Models/\``);
1127
+ lines.push(`- **Validation**: \`app/Http/Requests/\``);
1128
+ lines.push(`- **Routes**: \`routes/web.php\`, \`routes/api.php\``);
1129
+ lines.push(`- **Migrations**: \`database/migrations/\``);
1130
+ }
1131
+ if (projectInfo.laravel?.hasFilament) {
1132
+ lines.push(`- **Admin Panel**: Filament Resources live in \`app/Filament/Resources/\``);
1133
+ }
1134
+ } else if (projectInfo.type === "typescript") {
1135
+ const isModular = Boolean(projectInfo.typescript?.isModular);
1136
+ const srcDir = projectInfo.typescript?.srcDir || "src";
1137
+ const orm = projectInfo.typescript?.hasPrisma ? "Prisma" : projectInfo.typescript?.hasTypeORM ? "TypeORM" : projectInfo.typescript?.hasDrizzle ? "Drizzle" : null;
1138
+ if (isModular) {
1139
+ lines.push(`- **Architecture Style**: Feature Module Architecture`);
1140
+ lines.push(`- **Module Root**: \`${srcDir}/modules/<feature>/\``);
1141
+ lines.push(`- **Controllers**: \`${srcDir}/modules/<feature>/<feature>.controller.ts\``);
1142
+ lines.push(`- **Services**: \`${srcDir}/modules/<feature>/<feature>.service.ts\``);
1143
+ lines.push(`- **Repositories**: \`${srcDir}/modules/<feature>/<feature>.repository.ts\``);
1144
+ lines.push(`- **DTOs / Schemas**: \`${srcDir}/modules/<feature>/<feature>.dto.ts\``);
1145
+ lines.push(`- **Routes**: \`${srcDir}/modules/<feature>/<feature>.routes.ts\``);
1146
+ lines.push(`- **Module Index**: \`${srcDir}/modules/<feature>/index.ts\` (barrel export)`);
1147
+ } else {
1148
+ lines.push(`- **Architecture Style**: Layered Architecture`);
1149
+ lines.push(`- **Controllers**: \`${srcDir}/controllers/\``);
1150
+ lines.push(`- **Services**: \`${srcDir}/services/\``);
1151
+ lines.push(`- **Repositories / Data**: \`${srcDir}/repositories/\``);
1152
+ lines.push(`- **DTOs / Schemas**: \`${srcDir}/dtos/\` or \`${srcDir}/schemas/\``);
1153
+ lines.push(`- **Routes**: \`${srcDir}/routes/\``);
1154
+ }
1155
+ if (orm) {
1156
+ lines.push(`- **Database ORM**: ${orm} is used for data persistence.`);
1157
+ }
1158
+ } else {
1159
+ lines.push(`- **Source Code**: \`src/\``);
1160
+ lines.push(`- **Automated Tests**: \`tests/\``);
1161
+ }
1162
+ if (modules.length > 0) {
1163
+ lines.push("");
1164
+ lines.push(`## Active Feature Modules (${modules.length})`);
1165
+ lines.push(`Existing domains: ${modules.map((m) => `\`${m}\``).join(", ")}`);
1166
+ lines.push("When creating new features, follow this established domain partition.");
1167
+ }
1168
+ lines.push("");
1169
+ lines.push("## AI Coding Guardrails");
1170
+ lines.push("1. NEVER place raw queries or database transactions inside Controller files.");
1171
+ lines.push("2. When implementing a new feature, always create cohesive layers: Route -> Controller -> Service -> Repository/Model.");
1172
+ lines.push("3. Keep naming conventions consistent with existing files in the repository.");
1173
+ lines.push("4. Write automated unit/feature tests for new domain services.");
1174
+ return lines.join("\n");
1175
+ }
1176
+ function mergeAiRules(existingContent, newRules) {
1177
+ const block = `${AI_RULES_START_TAG}
1178
+ ${newRules}
1179
+ ${AI_RULES_END_TAG}`;
1180
+ if (!existingContent || existingContent.trim() === "") {
1181
+ return block + "\n";
1182
+ }
1183
+ const regex = new RegExp(`${AI_RULES_START_TAG}[\\s\\S]*?${AI_RULES_END_TAG}`, "m");
1184
+ if (regex.test(existingContent)) {
1185
+ return existingContent.replace(regex, block);
1186
+ }
1187
+ return existingContent.trimEnd() + "\n\n" + block + "\n";
1188
+ }
1189
+
1190
+ // src/utils/clipboard.ts
1191
+ import { spawn } from "child_process";
1192
+ async function copyToClipboard(text2) {
1193
+ const platform = process.platform;
1194
+ let command;
1195
+ let args = [];
1196
+ if (platform === "win32") {
1197
+ command = "clip";
1198
+ } else if (platform === "darwin") {
1199
+ command = "pbcopy";
1200
+ } else {
1201
+ command = process.env.WAYLAND_DISPLAY ? "wl-copy" : "xclip";
1202
+ if (command === "xclip") {
1203
+ args = ["-selection", "clipboard"];
1204
+ }
1205
+ }
1206
+ return new Promise((resolve) => {
1207
+ try {
1208
+ const child = spawn(command, args, { stdio: ["pipe", "ignore", "ignore"] });
1209
+ child.on("error", () => {
1210
+ resolve(false);
1211
+ });
1212
+ child.on("close", (code) => {
1213
+ resolve(code === 0);
1214
+ });
1215
+ child.stdin.write(text2);
1216
+ child.stdin.end();
1217
+ } catch {
1218
+ resolve(false);
1219
+ }
1220
+ });
1221
+ }
1222
+
1094
1223
  // src/generator/scaffolder.ts
1095
1224
  import fs2 from "fs";
1096
1225
  import path3 from "path";
@@ -2032,7 +2161,7 @@ async function scaffoldModule(projectInfo, options) {
2032
2161
  }
2033
2162
 
2034
2163
  // src/index.ts
2035
- var VERSION = true ? "1.1.0" : "1.1.0";
2164
+ var VERSION = true ? "1.1.1" : "1.1.1";
2036
2165
  function showHelp() {
2037
2166
  console.log(`
2038
2167
  ${pc.bold(pc.cyan("LaraTS CLI"))} ${pc.gray(`v${VERSION}`)}
@@ -2045,6 +2174,7 @@ ${pc.bold("USAGE:")}
2045
2174
  ${pc.bold("COMMANDS:")}
2046
2175
  ${pc.green("scan")} Scan repository & generate ARCHITECTURE.md
2047
2176
  ${pc.green("view")}, ${pc.green("preview")} Open interactive architecture diagram in local browser
2177
+ ${pc.green("ai")} Export architecture context prompt for AI (Cursor, Claude, Copilot)
2048
2178
  ${pc.green("scaffold")}, ${pc.green("make")} [name] Scaffold a new module or feature with clean boilerplate
2049
2179
  ${pc.green("info")} Show detected project type & architectural flavor
2050
2180
  ${pc.green("help")} Display this help message
@@ -2052,6 +2182,9 @@ ${pc.bold("COMMANDS:")}
2052
2182
  ${pc.bold("OPTIONS:")}
2053
2183
  ${pc.yellow("-o, --output")} <file> Output path for scan (default: ARCHITECTURE.md)
2054
2184
  ${pc.yellow("--view")} Open local browser viewer immediately after scanning
2185
+ ${pc.yellow("--copy")} Copy AI architecture context prompt to clipboard
2186
+ ${pc.yellow("--cursor")} Inject architecture rules into .cursorrules
2187
+ ${pc.yellow("--claude")} Inject architecture rules into CLAUDE.md
2055
2188
  ${pc.yellow("-t, --tree")} Print directory tree in terminal
2056
2189
  ${pc.yellow("--html")} Also export standalone ARCHITECTURE.html
2057
2190
  ${pc.yellow("-m, --modular")} Enforce modular directory structure
@@ -2072,12 +2205,12 @@ function handleCancel() {
2072
2205
  p.cancel(pc.yellow("Operation cancelled. See you next time! \u{1F44B}"));
2073
2206
  process.exit(0);
2074
2207
  }
2075
- function checkAndPromptStar() {
2208
+ async function checkAndPromptStar(customStatsPath, forceAnswer) {
2076
2209
  try {
2077
2210
  const configDir = path4.join(os2.homedir(), ".larats");
2078
- const statsFile = path4.join(configDir, "stats.json");
2079
- if (!fs3.existsSync(configDir)) {
2080
- fs3.mkdirSync(configDir, { recursive: true });
2211
+ const statsFile = customStatsPath || path4.join(configDir, "stats.json");
2212
+ if (!fs3.existsSync(path4.dirname(statsFile))) {
2213
+ fs3.mkdirSync(path4.dirname(statsFile), { recursive: true });
2081
2214
  }
2082
2215
  let stats = {};
2083
2216
  if (fs3.existsSync(statsFile)) {
@@ -2085,15 +2218,30 @@ function checkAndPromptStar() {
2085
2218
  }
2086
2219
  const runs = (stats.runs || 0) + 1;
2087
2220
  stats.runs = runs;
2088
- fs3.writeFileSync(statsFile, JSON.stringify(stats, null, 2), "utf-8");
2089
- if (runs === 2 || runs === 3) {
2090
- console.log(
2091
- `
2221
+ if (runs >= 3 && !stats.hasPromptedStar) {
2222
+ stats.hasPromptedStar = true;
2223
+ const repoUrl = "https://github.com/SidiqFatuh187/larats-cli";
2224
+ if (process.stdin.isTTY || typeof forceAnswer === "boolean") {
2225
+ const shouldStar = typeof forceAnswer === "boolean" ? forceAnswer : await p.confirm({
2226
+ message: "\u2B50 Enjoying LaraTS? Would you like to support the project with a star on GitHub?",
2227
+ initialValue: true
2228
+ });
2229
+ if (shouldStar && !p.isCancel(shouldStar)) {
2230
+ openViewer(repoUrl);
2231
+ p.log.success(pc.green("Thank you so much for your support! \u2B50 Opening GitHub repository in your browser..."));
2232
+ } else {
2233
+ p.log.message(pc.gray("No problem, enjoy building with LaraTS! \u{1F44D}"));
2234
+ }
2235
+ } else {
2236
+ console.log(
2237
+ `
2092
2238
  ${pc.yellow("\u2B50 Enjoying LaraTS?")} If you find this CLI useful, please consider giving it a star on GitHub:
2093
- ${pc.bold(pc.cyan("\u{1F449} https://github.com/SidiqFatuh187/larats-cli"))}
2239
+ ${pc.bold(pc.cyan("\u{1F449} " + repoUrl))}
2094
2240
  `
2095
- );
2241
+ );
2242
+ }
2096
2243
  }
2244
+ fs3.writeFileSync(statsFile, JSON.stringify(stats, null, 2), "utf-8");
2097
2245
  } catch {
2098
2246
  }
2099
2247
  }
@@ -2109,6 +2257,61 @@ function openViewer(destination) {
2109
2257
  p.log.warn(`Could not open browser automatically. Please open ${pc.cyan(destination)} manually.`);
2110
2258
  }
2111
2259
  }
2260
+ async function runAi(projectInfo, options = {}) {
2261
+ const spinner2 = p.spinner();
2262
+ spinner2.start("Scanning codebase & formulating AI architecture context...");
2263
+ try {
2264
+ const scanResult = await scanProject(projectInfo);
2265
+ spinner2.stop(pc.green(`Architecture context ready (${scanResult.scanDurationMs}ms)`));
2266
+ const aiContext = generateAiContext(scanResult);
2267
+ let handled = false;
2268
+ if (options.cursor) {
2269
+ const cursorPath = path4.resolve(projectInfo.rootPath, ".cursorrules");
2270
+ const existing = fs3.existsSync(cursorPath) ? fs3.readFileSync(cursorPath, "utf-8") : "";
2271
+ const updated = mergeAiRules(existing, aiContext);
2272
+ fs3.writeFileSync(cursorPath, updated, "utf-8");
2273
+ p.log.success(pc.green(`\u2714 Architecture rules injected into ${pc.bold(".cursorrules")}`));
2274
+ handled = true;
2275
+ }
2276
+ if (options.claude) {
2277
+ const claudePath = path4.resolve(projectInfo.rootPath, "CLAUDE.md");
2278
+ const existing = fs3.existsSync(claudePath) ? fs3.readFileSync(claudePath, "utf-8") : "";
2279
+ const updated = mergeAiRules(existing, aiContext);
2280
+ fs3.writeFileSync(claudePath, updated, "utf-8");
2281
+ p.log.success(pc.green(`\u2714 Architecture rules injected into ${pc.bold("CLAUDE.md")}`));
2282
+ handled = true;
2283
+ }
2284
+ if (options.copy) {
2285
+ const copied = await copyToClipboard(aiContext);
2286
+ if (copied) {
2287
+ p.log.success(pc.green("\u2714 Architecture context copied to clipboard! Ready to paste into ChatGPT or Claude."));
2288
+ } else {
2289
+ p.log.warn("Could not access system clipboard. Printing prompt to console instead.");
2290
+ console.log(`
2291
+ ${aiContext}
2292
+ `);
2293
+ }
2294
+ handled = true;
2295
+ }
2296
+ if (!handled) {
2297
+ console.log(`
2298
+ ${pc.bold(pc.cyan("--- AI Codebase Architecture Prompt ---"))}
2299
+ ${aiContext}
2300
+ ${pc.bold(pc.cyan("---------------------------------------"))}
2301
+ `);
2302
+ p.note(
2303
+ `Tip: Run with ${pc.yellow("--copy")} to copy directly to clipboard,
2304
+ or ${pc.yellow("--cursor")} / ${pc.yellow("--claude")} to inject into rules files automatically!`,
2305
+ pc.green("Prompt Ready for AI \u{1F916}")
2306
+ );
2307
+ }
2308
+ p.outro(pc.cyan("Done!"));
2309
+ } catch (err) {
2310
+ spinner2.stop(pc.red("Failed to generate AI context"));
2311
+ p.log.error(err instanceof Error ? err.message : String(err));
2312
+ process.exitCode = 1;
2313
+ }
2314
+ }
2112
2315
  async function runScan(projectInfo, outputPath = "ARCHITECTURE.md", options = {}) {
2113
2316
  const spinner2 = p.spinner();
2114
2317
  spinner2.start("Scanning codebase structure & components (strict ignore list active)...");
@@ -2432,13 +2635,16 @@ async function main() {
2432
2635
  tree: { type: "boolean", short: "t" },
2433
2636
  html: { type: "boolean" },
2434
2637
  version: { type: "boolean", short: "v" },
2638
+ copy: { type: "boolean" },
2639
+ cursor: { type: "boolean" },
2640
+ claude: { type: "boolean" },
2435
2641
  help: { type: "boolean", short: "h" }
2436
2642
  },
2437
2643
  allowPositionals: true,
2438
2644
  strict: true
2439
2645
  });
2440
2646
  } catch (err) {
2441
- console.error(pc.red(`Error: ${err.message}`));
2647
+ console.error(pc.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
2442
2648
  showHelp();
2443
2649
  process.exitCode = 1;
2444
2650
  return;
@@ -2459,6 +2665,7 @@ async function main() {
2459
2665
  scaffold: true,
2460
2666
  make: true,
2461
2667
  info: true,
2668
+ ai: true,
2462
2669
  help: true
2463
2670
  };
2464
2671
  if (command && !VALID_COMMANDS[command]) {
@@ -2486,7 +2693,7 @@ async function main() {
2486
2693
  tree: isTreeFlag,
2487
2694
  html: isHtmlFlag
2488
2695
  });
2489
- checkAndPromptStar();
2696
+ await checkAndPromptStar();
2490
2697
  return;
2491
2698
  }
2492
2699
  if (command === "view" || command === "preview") {
@@ -2506,7 +2713,18 @@ Double-click ${pc.cyan("ARCHITECTURE.html")} anytime to view offline!`,
2506
2713
  pc.green("Opened in Browser! \u{1F310}")
2507
2714
  );
2508
2715
  p.outro(pc.cyan("Done!"));
2509
- checkAndPromptStar();
2716
+ await checkAndPromptStar();
2717
+ return;
2718
+ }
2719
+ if (command === "ai") {
2720
+ p.intro(`${pc.bgCyan(pc.black(" LaraTS "))} ${pc.gray(`v${VERSION}`)} - AI Architecture Context`);
2721
+ p.log.info(`Target: ${pc.green(projectInfo.summary)}`);
2722
+ await runAi(projectInfo, {
2723
+ copy: parsed.values.copy,
2724
+ cursor: parsed.values.cursor,
2725
+ claude: parsed.values.claude
2726
+ });
2727
+ await checkAndPromptStar();
2510
2728
  return;
2511
2729
  }
2512
2730
  if (command === "scaffold" || command === "make") {
@@ -2519,14 +2737,14 @@ Double-click ${pc.cyan("ARCHITECTURE.html")} anytime to view offline!`,
2519
2737
  force: isForceFlag,
2520
2738
  dryRun: isDryRunFlag
2521
2739
  });
2522
- checkAndPromptStar();
2740
+ await checkAndPromptStar();
2523
2741
  return;
2524
2742
  }
2525
2743
  if (command === "info") {
2526
2744
  p.intro(`${pc.bgCyan(pc.black(" LaraTS "))} ${pc.gray(`v${VERSION}`)}`);
2527
2745
  showProjectInfo(projectInfo);
2528
2746
  p.outro(pc.cyan("Done."));
2529
- checkAndPromptStar();
2747
+ await checkAndPromptStar();
2530
2748
  return;
2531
2749
  }
2532
2750
  if (!process.stdin.isTTY) {
@@ -2544,6 +2762,11 @@ Double-click ${pc.cyan("ARCHITECTURE.html")} anytime to view offline!`,
2544
2762
  label: "\u{1F50D} Scan Architecture & Generate ARCHITECTURE.md",
2545
2763
  hint: "Creates component stats, directory tree & Mermaid flowchart"
2546
2764
  },
2765
+ {
2766
+ value: "ai",
2767
+ label: "\u{1F916} Export Architecture Context for AI (Cursor, Claude, Copilot)",
2768
+ hint: "Generate architecture rules for AI coding agents"
2769
+ },
2547
2770
  {
2548
2771
  value: "view",
2549
2772
  label: "\u{1F310} Open Interactive Architecture Viewer in Browser",
@@ -2576,6 +2799,24 @@ Double-click ${pc.cyan("ARCHITECTURE.html")} anytime to view offline!`,
2576
2799
  tree: isTreeFlag,
2577
2800
  html: isHtmlFlag
2578
2801
  });
2802
+ } else if (action === "ai") {
2803
+ const aiTarget = await p.select({
2804
+ message: "How would you like to export the AI architecture context?",
2805
+ options: [
2806
+ { value: "copy", label: "\u{1F4CB} Copy directly to Clipboard", hint: "Ready to paste into ChatGPT or Claude web" },
2807
+ { value: "cursor", label: "\u{1F5B1}\uFE0F Save into .cursorrules", hint: "Inject rules for Cursor AI" },
2808
+ { value: "claude", label: "\u{1F9E0} Save into CLAUDE.md", hint: "Inject rules for Claude Code" },
2809
+ { value: "print", label: "\u{1F5A5}\uFE0F Display prompt in terminal", hint: "View formatted markdown text" }
2810
+ ]
2811
+ });
2812
+ if (p.isCancel(aiTarget)) {
2813
+ handleCancel();
2814
+ }
2815
+ await runAi(projectInfo, {
2816
+ copy: aiTarget === "copy",
2817
+ cursor: aiTarget === "cursor",
2818
+ claude: aiTarget === "claude"
2819
+ });
2579
2820
  } else if (action === "view") {
2580
2821
  const spinner2 = p.spinner();
2581
2822
  spinner2.start("Scanning repository & generating architecture map...");
@@ -2596,7 +2837,7 @@ Double-click ${pc.cyan("ARCHITECTURE.html")} anytime to view offline!`,
2596
2837
  showProjectInfo(projectInfo);
2597
2838
  p.outro(pc.cyan("Done!"));
2598
2839
  }
2599
- checkAndPromptStar();
2840
+ await checkAndPromptStar();
2600
2841
  }
2601
2842
  main().catch((err) => {
2602
2843
  p.log.error(pc.red(`Fatal error: ${err.message || String(err)}`));