@@ -15,6 +15,25 @@ const LOG_LEVEL_COLORS: Record<string, string> = {
1515 DEBUG : COLORS . FG . GREEN ,
1616}
1717
18+ type ParsedLogTarget = { type : "workflow" ; name : string } | { type : "rule" ; workflowName : string ; ruleName : string }
19+
20+ /**
21+ * Parse a log target argument into either a workflow or workflow/rule target
22+ * @param arg The argument to parse (e.g., "my-workflow" or "my-workflow/my-rule")
23+ * @returns Parsed target object
24+ */
25+ const parseLogTarget = ( arg : string ) : ParsedLogTarget => {
26+ const slashIndex = arg . indexOf ( "/" )
27+ if ( slashIndex > 0 && slashIndex < arg . length - 1 ) {
28+ return {
29+ type : "rule" ,
30+ workflowName : arg . substring ( 0 , slashIndex ) ,
31+ ruleName : arg . substring ( slashIndex + 1 ) ,
32+ }
33+ }
34+ return { type : "workflow" , name : arg }
35+ }
36+
1837/**
1938 * Print logs for a workflow rule
2039 * @param workflowName Name of the workflow
@@ -47,7 +66,7 @@ const printLogs = (workflowName: string, ruleName: string, logs: RuleLog[]) => {
4766 * Logs command implementation
4867 */
4968export const logsCommand = async (
50- workflowNames : string [ ] ,
69+ targets : string [ ] ,
5170 { host, token, top, watch, all } : { host : string ; token : string ; top : number ; watch ?: number ; all : boolean } ,
5271) : Promise < void > => {
5372 const youtrackService = new YoutrackService ( host , token )
@@ -66,19 +85,50 @@ export const logsCommand = async (
6685
6786 const workflows = serverWorkflows . filter ( ( w ) => isManifestExists ( w . name ) )
6887
69- if ( workflowNames . length === 0 ) {
70- // Validate specified workflows
71- const invalidWorkflows = workflowNames . filter ( ( name ) => ! workflows . some ( ( w ) => w . name === name ) )
88+ // Parse targets into workflow-only and rule-specific targets
89+ const parsedTargets = targets . map ( parseLogTarget )
90+ const workflowTargets = parsedTargets . filter ( ( t ) : t is { type : "workflow" ; name : string } => t . type === "workflow" )
91+ const ruleTargets = parsedTargets . filter (
92+ ( t ) : t is { type : "rule" ; workflowName : string ; ruleName : string } => t . type === "rule" ,
93+ )
94+
95+ // Resolve rule-specific targets directly
96+ const directRules : WorkflowRule [ ] = [ ]
97+ for ( const target of ruleTargets ) {
98+ const workflow = workflows . find ( ( w ) => w . name === target . workflowName )
99+ if ( ! workflow ) {
100+ spinner . fail ( `Workflow '${ target . workflowName } ' not found` )
101+ return
102+ }
103+ const rule = workflow . rules . find ( ( r ) => r . name === target . ruleName )
104+ if ( ! rule ) {
105+ spinner . fail ( `Rule '${ target . ruleName } ' not found in workflow '${ target . workflowName } '` )
106+ return
107+ }
108+ directRules . push ( {
109+ workflowId : workflow . id ,
110+ ruleId : rule . id ,
111+ workflowName : workflow . name ,
112+ ruleName : rule . name ,
113+ } )
114+ }
115+
116+ // Handle workflow-only targets
117+ const workflowNames = workflowTargets . map ( ( t ) => t . name )
72118
119+ // Validate specified workflows
120+ if ( workflowNames . length > 0 ) {
121+ const invalidWorkflows = workflowNames . filter ( ( name ) => ! workflows . some ( ( w ) => w . name === name ) )
73122 if ( invalidWorkflows . length > 0 ) {
74123 spinner . fail ( `Invalid workflow names: ${ invalidWorkflows . join ( ", " ) } ` )
75124 return
76125 }
77126 }
78127
79- // If no workflows specified, show selection menu
80- const workflowsToProcess = workflowNames . length ? workflows . filter ( ( w ) => workflowNames . includes ( w . name ) ) : workflows
81- const workflowRules = workflowsToProcess . reduce (
128+ // Build workflow rules for workflow-only targets (used for --all or prompt)
129+ const workflowsToProcess =
130+ workflowNames . length > 0 ? workflows . filter ( ( w ) => workflowNames . includes ( w . name ) ) : workflows
131+ const workflowRulesForPrompt = workflowsToProcess . reduce (
82132 ( res , workflow ) => {
83133 res . push (
84134 ...workflow . rules . map ( ( r ) => ( {
@@ -96,27 +146,41 @@ export const logsCommand = async (
96146 [ ] as { name : string ; value : WorkflowRule } [ ] ,
97147 )
98148
99- if ( workflowRules . length === 0 ) {
149+ // If we have direct rules and no workflow targets, skip prompt/all logic
150+ const hasWorkflowTargets = workflowNames . length > 0 || targets . length === 0
151+ const needsSelection = hasWorkflowTargets && ! all && workflowRulesForPrompt . length > 0
152+
153+ if ( workflowRulesForPrompt . length === 0 && directRules . length === 0 ) {
100154 spinner . fail ( "No workflows found. Add workflows first." )
101155 return
102156 }
103157
104158 spinner . stop ( )
105159
106- const selectedRules : WorkflowRule [ ] = [ ]
107- if ( all ) {
108- selectedRules . push ( ...workflowRules . map ( ( { value } ) => value ) )
109- } else {
110- const { selectedRules : selectedRulesFromPrompt } = await inquirer . prompt < { selectedRules : WorkflowRule [ ] } > ( [
111- {
112- type : "checkbox" ,
113- name : "selectedRules" ,
114- message : "Select rules to view logs for:" ,
115- choices : workflowRules ,
116- validate : ( input ) => ( input . length > 0 ? true : "Please select at least one rule" ) ,
117- } ,
118- ] )
119- selectedRules . push ( ...selectedRulesFromPrompt )
160+ const selectedRules : WorkflowRule [ ] = [ ...directRules ]
161+
162+ if ( hasWorkflowTargets ) {
163+ if ( all ) {
164+ // Add all rules from workflow targets
165+ selectedRules . push ( ...workflowRulesForPrompt . map ( ( { value } ) => value ) )
166+ } else if ( needsSelection ) {
167+ // Prompt for selection from workflow targets
168+ const { selectedRules : selectedRulesFromPrompt } = await inquirer . prompt < { selectedRules : WorkflowRule [ ] } > ( [
169+ {
170+ type : "checkbox" ,
171+ name : "selectedRules" ,
172+ message : "Select rules to view logs for:" ,
173+ choices : workflowRulesForPrompt ,
174+ validate : ( input ) => ( input . length > 0 || directRules . length > 0 ? true : "Please select at least one rule" ) ,
175+ } ,
176+ ] )
177+ selectedRules . push ( ...selectedRulesFromPrompt )
178+ }
179+ }
180+
181+ if ( selectedRules . length === 0 ) {
182+ spinner . fail ( "No rules selected" )
183+ return
120184 }
121185
122186 spinner . start ( "Fetching logs..." )
0 commit comments