11import { execSync } from "node:child_process" ;
22import path from "node:path" ;
33import { Command } from "commander" ;
4+ import { SEPARATOR_LENGTH_CHARS } from "./constants.js" ;
45import type { ScanOptions } from "./types.js" ;
56import { handleError } from "./utils/handle-error.js" ;
67import { highlighter } from "./utils/highlighter.js" ;
7- import { logger } from "./utils/logger.js" ;
8+ import { logger , startLoggerCapture , stopLoggerCapture } from "./utils/logger.js" ;
89import { scan } from "./scan.js" ;
910import { selectProjects } from "./utils/select-projects.js" ;
1011import { prompts } from "./utils/prompts.js" ;
1112import { maybePromptSkillInstall } from "./utils/skill-prompt.js" ;
1213import { maybeInstallGlobally } from "./utils/global-install.js" ;
14+ import { copyToClipboard } from "./utils/copy-to-clipboard.js" ;
1315
1416const VERSION = process . env . VERSION ?? "0.0.0" ;
1517
@@ -19,6 +21,7 @@ interface CliFlags {
1921 verbose : boolean ;
2022 score : boolean ;
2123 fix : boolean ;
24+ prompt : boolean ;
2225 yes : boolean ;
2326 project ?: string ;
2427}
@@ -38,10 +41,17 @@ const program = new Command()
3841 . option ( "-y, --yes" , "skip prompts, scan all workspace projects" )
3942 . option ( "--project <name>" , "select workspace project (comma-separated for multiple)" )
4043 . option ( "--fix" , "open Ami to auto-fix all issues" )
44+ . option ( "--prompt" , "copy latest scan output to clipboard" )
4145 . action ( async ( directory : string , flags : CliFlags ) => {
46+ const isScoreOnly = flags . score && ! flags . prompt ;
47+ const shouldCopyPromptOutput = flags . prompt ;
48+
49+ if ( shouldCopyPromptOutput ) {
50+ startLoggerCapture ( ) ;
51+ }
52+
4253 try {
4354 const resolvedDirectory = path . resolve ( directory ) ;
44- const isScoreOnly = flags . score ;
4555
4656 if ( ! isScoreOnly ) {
4757 logger . log ( `react-doctor v${ VERSION } ` ) ;
@@ -51,7 +61,7 @@ const program = new Command()
5161 const scanOptions : ScanOptions = {
5262 lint : flags . lint ,
5363 deadCode : flags . deadCode ,
54- verbose : Boolean ( flags . verbose ) ,
64+ verbose : flags . prompt || Boolean ( flags . verbose ) ,
5565 scoreOnly : isScoreOnly ,
5666 } ;
5767
@@ -87,14 +97,19 @@ const program = new Command()
8797 openAmiToFix ( resolvedDirectory ) ;
8898 }
8999
90- if ( ! isScoreOnly ) {
100+ if ( ! isScoreOnly && ! flags . prompt ) {
91101 await maybePromptSkillInstall ( shouldSkipPrompts ) ;
92102 if ( ! shouldSkipPrompts && ! flags . fix ) {
93103 await maybePromptAmiFix ( resolvedDirectory ) ;
94104 }
95105 }
96106 } catch ( error ) {
97- handleError ( error ) ;
107+ handleError ( error , { shouldExit : ! shouldCopyPromptOutput } ) ;
108+ } finally {
109+ if ( shouldCopyPromptOutput ) {
110+ const capturedOutput = stopLoggerCapture ( ) ;
111+ copyPromptToClipboard ( capturedOutput , ! isScoreOnly ) ;
112+ }
98113 }
99114 } )
100115 . addHelpText (
@@ -106,8 +121,10 @@ ${highlighter.dim("Learn more:")}
106121 ) ;
107122
108123const AMI_INSTALL_URL = "https://ami.dev/install.sh" ;
109- const AMI_FIX_PROMPT =
110- "Run npx -y react-doctor@latest . --verbose, read every diagnostic, then fix all issues one by one. After fixing, re-run react-doctor to verify the score improved." ;
124+ const FIX_PROMPT =
125+ "Fix all issues reported in the react-doctor diagnostics below, one by one. After applying fixes, run `react-dcotor` again to verify the results improved." ;
126+ const REACT_DOCTOR_OUTPUT_LABEL = "react-doctor output" ;
127+ const SCAN_SUMMARY_SEPARATOR = "─" . repeat ( SEPARATOR_LENGTH_CHARS ) ;
111128
112129const isAmiInstalled = ( ) : boolean => {
113130 try {
@@ -140,7 +157,7 @@ const openAmiToFix = (directory: string): void => {
140157 logger . log ( "Opening Ami to fix react-doctor issues..." ) ;
141158
142159 const encodedDirectory = encodeURIComponent ( resolvedDirectory ) ;
143- const encodedPrompt = encodeURIComponent ( AMI_FIX_PROMPT ) ;
160+ const encodedPrompt = encodeURIComponent ( FIX_PROMPT ) ;
144161 const deeplink = `ami://open-project?cwd=${ encodedDirectory } &prompt=${ encodedPrompt } &mode=agent` ;
145162
146163 try {
@@ -153,6 +170,35 @@ const openAmiToFix = (directory: string): void => {
153170 }
154171} ;
155172
173+ const buildPromptWithOutput = ( reactDoctorOutput : string ) : string => {
174+ const summaryStartIndex = reactDoctorOutput . indexOf ( SCAN_SUMMARY_SEPARATOR ) ;
175+ const diagnosticsOutput =
176+ summaryStartIndex === - 1
177+ ? reactDoctorOutput
178+ : reactDoctorOutput . slice ( 0 , summaryStartIndex ) . trimEnd ( ) ;
179+ const normalizedReactDoctorOutput = diagnosticsOutput . trim ( ) ;
180+ const outputContent =
181+ normalizedReactDoctorOutput . length > 0 ? normalizedReactDoctorOutput : "No output captured." ;
182+ return `${ FIX_PROMPT } \n\n${ REACT_DOCTOR_OUTPUT_LABEL } :\n\`\`\`\n${ outputContent } \n\`\`\`` ;
183+ } ;
184+
185+ const copyPromptToClipboard = ( reactDoctorOutput : string , shouldLogResult : boolean ) : void => {
186+ const promptWithOutput = buildPromptWithOutput ( reactDoctorOutput ) ;
187+ const didCopyPromptToClipboard = copyToClipboard ( promptWithOutput ) ;
188+
189+ if ( ! shouldLogResult ) {
190+ return ;
191+ }
192+
193+ if ( didCopyPromptToClipboard ) {
194+ logger . success ( "Copied latest scan output to clipboard" ) ;
195+ return ;
196+ }
197+
198+ logger . warn ( "Could not copy prompt to clipboard automatically. Use this prompt:" ) ;
199+ logger . info ( promptWithOutput ) ;
200+ } ;
201+
156202const maybePromptAmiFix = async ( directory : string ) : Promise < void > => {
157203 logger . break ( ) ;
158204 logger . log ( `Fix these issues with ${ highlighter . info ( "Ami" ) } ?` ) ;
0 commit comments