11import OpenAI from "openai" ;
2+ import fetch from "node-fetch" ;
23
34/**
45 * Preprocess Task - Convert one-liner task descriptions to structured user stories
5- *
6+ *
67 * This script takes a natural language task description and converts it to a
7- * structured user story format using OpenAI, making it clearer for code generation.
8+ * structured user story format using OpenAI, then creates a Confluence page
9+ * with the requirements before triggering the main agent.
810 */
911
1012const task = process . env . TASK || "" ;
1113const openaiApiKey = process . env . OPENAI_API_KEY ;
1214const model = process . env . PREPROCESSING_MODEL || "gpt-4o-mini" ;
1315
16+ // Confluence configuration
17+ const confluenceUrl = process . env . CONFLUENCE_URL || "" ;
18+ const confluenceEmail = process . env . CONFLUENCE_EMAIL || "" ;
19+ const confluenceApiToken = process . env . CONFLUENCE_API_TOKEN || "" ;
20+ const confluenceSpaceKey = process . env . CONFLUENCE_SPACE_KEY || "AICODE" ;
21+ const confluenceParentPageId = process . env . CONFLUENCE_PARENT_PAGE_ID || "" ;
22+
1423if ( ! task ) {
1524 console . error ( "[PREPROCESS] TASK environment variable not set" ) ;
1625 process . exit ( 1 ) ;
@@ -33,51 +42,155 @@ Convert the given task description into a structured user story format following
3342Acceptance Criteria:
3443- [Criterion 1]
3544- [Criterion 2]
36- - [Criterion 3]"
45+ - [Criterion 3]
46+
47+ Technical Notes:
48+ - [Any technical considerations]
49+ - [Files likely to be modified]
50+ - [Potential risks or edge cases]"
3751
3852Guidelines:
3953- Identify the user role (administrator, developer, end user, etc.)
4054- Extract the main action/feature requested
4155- Clarify the benefit or goal
4256- Break down the task into specific, testable acceptance criteria
57+ - Add technical notes about implementation details
4358- Maintain all technical context and requirements
4459- Keep it concise but comprehensive
4560- If the task is already well-structured, enhance it rather than completely rewriting
4661
47- Output ONLY the user story, no explanations or markdown formatting.` ;
62+ Output ONLY the user story with technical notes , no explanations or markdown formatting.` ;
4863
4964const userPrompt = `Convert this task description into a structured user story:
5065
5166${ task } `;
5267
68+ /**
69+ * Create a Confluence page with the requirements
70+ * @param {string } title - Page title
71+ * @param {string } content - Page content (user story)
72+ * @returns {Promise<string|null> } Confluence page URL or null if failed
73+ */
74+ async function createConfluencePage ( title , content ) {
75+ if ( ! confluenceUrl || ! confluenceEmail || ! confluenceApiToken ) {
76+ console . log ( "[PREPROCESS] Confluence credentials not configured, skipping page creation" ) ;
77+ return null ;
78+ }
79+
80+ try {
81+ console . log ( `[PREPROCESS] Creating Confluence page in space ${ confluenceSpaceKey } ...` ) ;
82+ if ( confluenceParentPageId ) {
83+ console . log ( `[PREPROCESS] Parent page ID: ${ confluenceParentPageId } ` ) ;
84+ }
85+
86+ // Format content as Confluence storage format (XHTML-based)
87+ const storageContent = `
88+ <h2>Original Task</h2>
89+ <p>${ task . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) . replace ( / \n / g, '<br/>' ) } </p>
90+
91+ <h2>User Story</h2>
92+ <p>${ content . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) . replace ( / \n / g, '<br/>' ) } </p>
93+
94+ <h2>Implementation Status</h2>
95+ <p><ac:structured-macro ac:name="status"><ac:parameter ac:name="colour">Yellow</ac:parameter><ac:parameter ac:name="title">In Progress</ac:parameter></ac:structured-macro></p>
96+
97+ <h2>Related Links</h2>
98+ <p>GitHub Run: <a href="https://github.com/htilly/SlackONOS/actions/runs/${ process . env . GITHUB_RUN_ID || 'unknown' } ">View Workflow</a></p>
99+ ` . trim ( ) ;
100+
101+ const auth = Buffer . from ( `${ confluenceEmail } :${ confluenceApiToken } ` ) . toString ( 'base64' ) ;
102+
103+ // Build the page object
104+ const pageData = {
105+ type : 'page' ,
106+ title : title ,
107+ space : { key : confluenceSpaceKey } ,
108+ body : {
109+ storage : {
110+ value : storageContent ,
111+ representation : 'storage'
112+ }
113+ }
114+ } ;
115+
116+ // Add parent page if specified
117+ if ( confluenceParentPageId ) {
118+ pageData . ancestors = [ { id : confluenceParentPageId } ] ;
119+ }
120+
121+ const response = await fetch ( `${ confluenceUrl } /rest/api/content` , {
122+ method : 'POST' ,
123+ headers : {
124+ 'Authorization' : `Basic ${ auth } ` ,
125+ 'Content-Type' : 'application/json' ,
126+ 'Accept' : 'application/json'
127+ } ,
128+ body : JSON . stringify ( pageData )
129+ } ) ;
130+
131+ if ( ! response . ok ) {
132+ const errorText = await response . text ( ) ;
133+ console . error ( `[PREPROCESS] Failed to create Confluence page: ${ response . status } ${ response . statusText } ` ) ;
134+ console . error ( `[PREPROCESS] Error details: ${ errorText } ` ) ;
135+ return null ;
136+ }
137+
138+ const data = await response . json ( ) ;
139+ const pageUrl = `${ confluenceUrl } /wiki${ data . _links . webui } ` ;
140+ console . log ( `[PREPROCESS] ✅ Confluence page created: ${ pageUrl } ` ) ;
141+ return pageUrl ;
142+
143+ } catch ( error ) {
144+ console . error ( `[PREPROCESS] Error creating Confluence page: ${ error . message } ` ) ;
145+ return null ;
146+ }
147+ }
148+
53149async function preprocessTask ( ) {
54150 try {
55151 console . log ( `[PREPROCESS] Converting task to user story using ${ model } ...` ) ;
56152 console . log ( `[PREPROCESS] Original task: ${ task } ` ) ;
57-
153+
58154 const response = await openai . chat . completions . create ( {
59155 model : model ,
60156 messages : [
61157 { role : "system" , content : systemPrompt } ,
62158 { role : "user" , content : userPrompt }
63159 ] ,
64160 temperature : 0.3 , // Lower temperature for more consistent output
65- max_tokens : 500 , // User stories should be concise
161+ max_tokens : 800 , // Increased for technical notes
66162 } ) ;
67-
163+
68164 const enhancedTask = response . choices [ 0 ] . message . content . trim ( ) ;
69-
165+
70166 console . log ( `[PREPROCESS] Enhanced task (user story):` ) ;
71167 console . log ( enhancedTask ) ;
72-
168+
169+ // Create Confluence page with requirements
170+ const timestamp = new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ;
171+ const requester = process . env . REQUESTER || 'unknown' ;
172+ const pageTitle = `AICODE: ${ task . substring ( 0 , 80 ) } (${ timestamp } )` ;
173+ const confluenceUrl = await createConfluencePage ( pageTitle , enhancedTask ) ;
174+
73175 // Output to stdout for workflow to capture
74176 console . log ( `\n[PREPROCESS] ENHANCED_TASK_START` ) ;
75177 console . log ( enhancedTask ) ;
76178 console . log ( `[PREPROCESS] ENHANCED_TASK_END` ) ;
77-
179+
78180 // Also output as JSON for easier parsing
79- process . stdout . write ( `\nENHANCED_TASK_JSON:${ JSON . stringify ( { original : task , enhanced : enhancedTask } ) } \n` ) ;
80-
181+ const outputData = {
182+ original : task ,
183+ enhanced : enhancedTask ,
184+ confluenceUrl : confluenceUrl || 'N/A' ,
185+ requester : requester ,
186+ timestamp : timestamp
187+ } ;
188+ process . stdout . write ( `\nENHANCED_TASK_JSON:${ JSON . stringify ( outputData ) } \n` ) ;
189+
190+ if ( confluenceUrl ) {
191+ console . log ( `\n[PREPROCESS] 📝 Requirements documented: ${ confluenceUrl } ` ) ;
192+ }
193+
81194 } catch ( error ) {
82195 console . error ( `[PREPROCESS] Error during preprocessing: ${ error . message } ` ) ;
83196 console . warn ( `[PREPROCESS] Falling back to original task` ) ;
0 commit comments