s9n-devops-agent 2.0.18-dev.1 → 2.0.18-dev.12

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
@@ -81,6 +81,7 @@ s9n-devops-agent start
81
81
  ### 🌲 Smart Branch Management
82
82
  - **Hierarchy:** `session/task` → `daily/date` → `main`.
83
83
  - **Auto-Merge:** Sessions automatically merge into daily branches, which roll over to main.
84
+ - **Base Branch Selection:** Choose any branch (main, develop, etc.) as the starting point for your session worktree.
84
85
 
85
86
  ### 📋 House Rules System
86
87
  - **Context Injection:** AI agents read `docs/houserules.md` to understand your coding conventions.
@@ -54,21 +54,11 @@ function runShellScript(scriptPath, scriptArgs = []) {
54
54
  switch(command) {
55
55
  case 'start':
56
56
  case 'session':
57
- // Start interactive session manager
58
- // On Windows, always use Node.js coordinator (bash may not be available)
59
- if (process.platform === 'win32') {
60
- console.log('Starting DevOps Agent session manager...\n');
61
- runScript(join(rootDir, 'src', 'session-coordinator.js'), ['start', ...args.slice(1)]);
62
- } else {
63
- // Unix/Mac: use bash script
64
- const sessionScript = join(rootDir, 'start-devops-session.sh');
65
- if (fs.existsSync(sessionScript)) {
66
- runShellScript(sessionScript, args.slice(1));
67
- } else {
68
- console.error('Session script not found. Please ensure the package is properly installed.');
69
- process.exit(1);
70
- }
71
- }
57
+ case 'chat':
58
+ // Unified entry point: Run Kora (Smart Assistant)
59
+ // Kora handles session creation and management conversationally
60
+ console.log('Starting Kora (Smart DevOps Assistant)...');
61
+ runScript(join(rootDir, 'src', 'agent-chat.js'), args.slice(1));
72
62
  break;
73
63
 
74
64
  case 'worker':
@@ -101,6 +91,11 @@ switch(command) {
101
91
  runScript(join(rootDir, 'src', 'session-coordinator.js'), ['create', ...args.slice(1)]);
102
92
  break;
103
93
 
94
+ case 'resume':
95
+ // Resume existing session
96
+ runScript(join(rootDir, 'src', 'session-coordinator.js'), ['resume', ...args.slice(1)]);
97
+ break;
98
+
104
99
  case 'close':
105
100
  // Close session
106
101
  runScript(join(rootDir, 'src', 'close-session.js'), args.slice(1));
@@ -118,7 +113,7 @@ switch(command) {
118
113
  break;
119
114
 
120
115
  case 'chat':
121
- // Run the chat agent
116
+ // Run the chat agent (kept for backward compatibility, now same as start)
122
117
  runScript(join(rootDir, 'src', 'agent-chat.js'), args.slice(1));
123
118
  break;
124
119
 
@@ -207,11 +202,12 @@ For more information, visit: https://github.com/SecondBrainAICo/CS_DevOpsAgent
207
202
  break;
208
203
 
209
204
  default:
210
- // Default to chat if no command provided, or if unknown command
211
- // This replaces the prior UX where help was default
205
+ // Default to Kora (Smart Assistant) if no command provided
206
+ // This provides the unified experience
212
207
  if (!command || !command.startsWith('-')) {
213
- console.log('Starting Kora Smart Assistant...');
214
- runScript(join(rootDir, 'src', 'agent-chat.js'), args.slice(1));
208
+ console.log('Starting Kora (Smart DevOps Assistant)...');
209
+ // Pass all args to agent-chat
210
+ runScript(join(rootDir, 'src', 'agent-chat.js'), args);
215
211
  } else {
216
212
  // Show help for flags like -h, --help, or unknown flags
217
213
  console.log(`\nCS_DevOpsAgent - Intelligent Git Automation System`);
@@ -1,3 +1,18 @@
1
+ # Release Notes - s9n-devops-agent v2.0.18-dev.3
2
+
3
+ ## 🚀 Enhancements
4
+ - **Base Branch Selection**: You can now select a base branch (e.g., main, develop) when starting a session, allowing for cleaner feature branching from stable points.
5
+ - **Enhanced Setup Wizard**:
6
+ - Finds and merges contract files from subdirectories.
7
+ - Ensures versioning strategy is configured.
8
+ - Persists credentials in user home directory to survive package updates.
9
+
10
+ ## 🐛 Fixes
11
+ - **Update Logic**: Fixed update checker to respect dev versions.
12
+ - **Credentials**: Fixed issue where API keys were lost during updates.
13
+
14
+ ---
15
+
1
16
  # Release Notes - s9n-devops-agent v2.0.11-dev.0
2
17
 
3
18
  ## 🐛 Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "s9n-devops-agent",
3
- "version": "2.0.18-dev.1",
3
+ "version": "2.0.18-dev.12",
4
4
  "description": "CS_DevOpsAgent - Intelligent Git Automation System with multi-agent support and session management",
5
5
  "type": "module",
6
6
  "main": "src/cs-devops-agent-worker.js",
@@ -0,0 +1,100 @@
1
+ #!/bin/bash
2
+
3
+ # Deploy DevOpsAgent locally to a target directory
4
+ # Usage: ./scripts/deploy-local.sh <target_directory>
5
+
6
+ TARGET_DIR="$1"
7
+
8
+ if [ -z "$TARGET_DIR" ]; then
9
+ echo "Usage: $0 <target_directory>"
10
+ exit 1
11
+ fi
12
+
13
+ if [ ! -d "$TARGET_DIR" ]; then
14
+ echo "Error: Target directory $TARGET_DIR does not exist"
15
+ exit 1
16
+ fi
17
+
18
+ SOURCE_DIR="$(pwd)"
19
+ DEST_DIR="$TARGET_DIR/DevOpsAgent"
20
+
21
+ echo "Deploying DevOpsAgent to $DEST_DIR..."
22
+
23
+ # Create destination directory
24
+ mkdir -p "$DEST_DIR"
25
+
26
+ # Copy files
27
+ echo "Copying files..."
28
+ rsync -av --exclude 'node_modules' \
29
+ --exclude '.git' \
30
+ --exclude '.DS_Store' \
31
+ --exclude 'local_deploy' \
32
+ --exclude 'coverage' \
33
+ --exclude 'test_cases' \
34
+ "$SOURCE_DIR/" "$DEST_DIR/"
35
+
36
+ # Install dependencies in the target
37
+ echo "Installing dependencies in $DEST_DIR..."
38
+ cd "$DEST_DIR"
39
+ npm install --production --silent
40
+ cd "$SOURCE_DIR"
41
+
42
+ # Create universal runner script
43
+ RUNNER="$TARGET_DIR/devops"
44
+ LOG_FILE="$TARGET_DIR/devops-agent.log"
45
+
46
+ echo "Creating 'devops' CLI wrapper..."
47
+
48
+ cat > "$RUNNER" << EOF
49
+ #!/bin/bash
50
+
51
+ # DevOps Agent CLI Wrapper
52
+ # Supports all standard commands: start, chat, setup, worker, etc.
53
+
54
+ DIR="\$(cd "\$(dirname "\$0")" && pwd)"
55
+ AGENT_DIR="\$DIR/DevOpsAgent"
56
+ LOG_FILE="\$DIR/devops-agent.log"
57
+
58
+ # Ensure executable permissions
59
+ chmod +x "\$AGENT_DIR/bin/cs-devops-agent"
60
+
61
+ # If first arg is 'worker' or 'background', run in background
62
+ if [ "\$1" == "background" ]; then
63
+ echo "Starting worker in background..."
64
+ echo "--- Worker Start: \$(date) ---" >> "\$LOG_FILE"
65
+ export AC_DEBUG="true"
66
+ node "\$AGENT_DIR/src/cs-devops-agent-worker.js" >> "\$LOG_FILE" 2>&1 &
67
+ echo "Worker PID: \$!"
68
+ echo "Logs: \$LOG_FILE"
69
+ exit 0
70
+ fi
71
+
72
+ # Otherwise run interactive CLI
73
+ node "\$AGENT_DIR/bin/cs-devops-agent" "\$@"
74
+ EOF
75
+
76
+ chmod +x "$RUNNER"
77
+
78
+ # Add to gitignore in target
79
+ GITIGNORE="$TARGET_DIR/.gitignore"
80
+ if [ -f "$GITIGNORE" ]; then
81
+ if ! grep -q "DevOpsAgent/" "$GITIGNORE"; then
82
+ echo "" >> "$GITIGNORE"
83
+ echo "# DevOps Agent" >> "$GITIGNORE"
84
+ echo "DevOpsAgent/" >> "$GITIGNORE"
85
+ echo "devops-agent.log" >> "$GITIGNORE"
86
+ echo "devops" >> "$GITIGNORE"
87
+ echo "Added DevOpsAgent files to .gitignore"
88
+ fi
89
+ fi
90
+
91
+ echo "Deployment complete!"
92
+ echo "Use the './devops' script to interact with the agent:"
93
+ echo ""
94
+ echo " ./devops setup # Run first-time setup"
95
+ echo " ./devops chat # Chat with Kora"
96
+ echo " ./devops start # Start session manager (Interactive)"
97
+ echo " ./devops background # Run worker silently"
98
+ echo " ./devops --help # See all commands"
99
+ echo ""
100
+ echo "Logs location: '$LOG_FILE'"
package/src/agent-chat.js CHANGED
@@ -23,7 +23,7 @@ import readline from 'readline';
23
23
  import Groq from 'groq-sdk';
24
24
  import { fileURLToPath } from 'url';
25
25
  import { dirname } from 'path';
26
- import { spawn } from 'child_process';
26
+ import { spawn, execSync } from 'child_process';
27
27
  import { credentialsManager } from './credentials-manager.js';
28
28
  import HouseRulesManager from './house-rules-manager.js';
29
29
  // We'll import SessionCoordinator dynamically to avoid circular deps if any
@@ -51,9 +51,14 @@ const CONFIG = {
51
51
 
52
52
  class SmartAssistant {
53
53
  constructor() {
54
- this.groq = new Groq({
55
- apiKey: process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY
56
- });
54
+ // Initialize Groq client lazily or with null if key is missing
55
+ const apiKey = process.env.GROQ_API_KEY || process.env.OPENAI_API_KEY;
56
+
57
+ if (apiKey) {
58
+ this.groq = new Groq({ apiKey });
59
+ } else {
60
+ this.groq = null; // Will be initialized in start()
61
+ }
57
62
 
58
63
  this.history = [];
59
64
  this.repoRoot = process.cwd();
@@ -99,10 +104,50 @@ class SmartAssistant {
99
104
  description: "Check the status of active sessions and locks",
100
105
  parameters: { type: "object", properties: {} }
101
106
  }
107
+ },
108
+ {
109
+ type: "function",
110
+ function: {
111
+ name: "resume_session",
112
+ description: "Resume an existing or orphaned session",
113
+ parameters: {
114
+ type: "object",
115
+ properties: {
116
+ sessionId: { type: "string", description: "The ID of the session to resume" },
117
+ taskName: { type: "string", description: "The task name to search for (optional)" }
118
+ },
119
+ required: ["sessionId"]
120
+ }
121
+ }
122
+ },
123
+ {
124
+ type: "function",
125
+ function: {
126
+ name: "recover_sessions",
127
+ description: "Scan for and recover orphaned sessions from existing worktrees",
128
+ parameters: { type: "object", properties: {} }
129
+ }
102
130
  }
103
131
  ];
104
132
 
105
- this.systemPrompt = `You are Kora, the Smart DevOps Assistant.
133
+ // Load skills definition
134
+ let skillsDef = null;
135
+ try {
136
+ const skillsPath = path.join(__dirname, 'kora-skills.json');
137
+ if (fs.existsSync(skillsPath)) {
138
+ skillsDef = JSON.parse(fs.readFileSync(skillsPath, 'utf8'));
139
+ }
140
+ } catch (e) {
141
+ // Fallback if file missing or invalid
142
+ console.error('Warning: Could not load kora-skills.json, using defaults.');
143
+ }
144
+
145
+ if (skillsDef) {
146
+ // Build dynamic system prompt from skills definition
147
+ const allowedTopics = skillsDef.guardrails.allowed_topics.map(t => `- ${t}`).join('\n');
148
+ const disallowedTopics = skillsDef.guardrails.disallowed_topics.map(t => `- ${t}`).join('\n');
149
+
150
+ this.systemPrompt = `You are ${skillsDef.assistant_name}, the ${skillsDef.role}.
106
151
  Your goal is to help developers follow the House Rules and Contract System while being helpful and efficient.
107
152
 
108
153
  CONTEXT:
@@ -111,22 +156,81 @@ CONTEXT:
111
156
  - Users need to create "Sessions" to do work.
112
157
  - You can execute tools to help the user.
113
158
 
114
- STYLE:
159
+ CAPABILITIES (ALLOWED):
160
+ ${allowedTopics}
161
+
162
+ GUARDRAILS (STRICTLY PROHIBITED):
163
+ ${disallowedTopics}
164
+
165
+ AVAILABLE TOOLS:
166
+ 1. get_house_rules_summary - Read the project's rules.
167
+ 2. list_contracts - Check what contract files exist.
168
+ 3. check_session_status - See active work sessions.
169
+ 4. start_session - Begin a new task.
170
+ 5. resume_session - Resume an existing or orphaned session.
171
+ 6. recover_sessions - Scan and restore lost session locks.
172
+
173
+ IMPORTANT INSTRUCTIONS:
174
+ - ONLY use the tools listed above. Do NOT invent new tools like "check_compliance" or "run_tests".
175
+ - If the user asks for something OUTSIDE your capabilities, you MUST reply with exactly this message:
176
+ "${skillsDef.guardrails.fallback_response}"
177
+ - Be concise but helpful.
178
+ - Identify yourself as "${skillsDef.assistant_name}".
179
+ - If the user asks about starting a task, ask for a clear task name if not provided.
180
+ - If the user asks about rules, summarize them from the actual files.
181
+ - If the user wants to resume work, use check_session_status first.
182
+ - If a session seems missing but worktree exists, use recover_sessions.
183
+ - Always prefer "Structured" organization for new code.
184
+
185
+ When you want to perform an action, use the available tools.`;
186
+ } else {
187
+ // Fallback static prompt
188
+ this.systemPrompt = `You are Kora, the Smart DevOps Assistant.
189
+ Your goal is to help developers follow the House Rules and Contract System while being helpful and efficient.
190
+
191
+ CONTEXT:
192
+ - You are running inside a "DevOps Agent" environment.
193
+ - The project follows a strict "Contract System" (API, DB, Features, etc.).
194
+ - Users need to create "Sessions" to do work.
195
+ - You can execute tools to help the user.
196
+
197
+ AVAILABLE TOOLS:
198
+ 1. get_house_rules_summary - Read the project's rules.
199
+ 2. list_contracts - Check what contract files exist.
200
+ 3. check_session_status - See active work sessions.
201
+ 4. start_session - Begin a new task.
202
+ 5. resume_session - Resume an existing or orphaned session.
203
+ 6. recover_sessions - Scan and restore lost session locks.
204
+
205
+ IMPORTANT INSTRUCTIONS:
206
+ - ONLY use the tools listed above. Do NOT invent new tools like "check_compliance" or "run_tests".
207
+ - If a user asks for something you can't do with a tool (like running tests), tell them you can't do it yet but they can run "npm test" themselves.
115
208
  - Be concise but helpful.
116
209
  - Identify yourself as "Kora".
117
210
  - If the user asks about starting a task, ask for a clear task name if not provided.
118
211
  - If the user asks about rules, summarize them from the actual files.
212
+ - If the user wants to resume work, use check_session_status first.
213
+ - If a session seems missing but worktree exists, use recover_sessions.
119
214
  - Always prefer "Structured" organization for new code.
120
215
 
121
216
  When you want to perform an action, use the available tools.`;
217
+ }
122
218
  }
123
219
 
124
220
  /**
125
221
  * Initialize the chat session
126
222
  */
127
223
  async start() {
224
+ // Ensure Groq client is initialized
225
+ if (!this.groq) {
226
+ const apiKey = credentialsManager.getGroqApiKey();
227
+ if (apiKey) {
228
+ this.groq = new Groq({ apiKey });
229
+ }
230
+ }
231
+
128
232
  // Check for Groq API Key
129
- if (!credentialsManager.hasGroqApiKey()) {
233
+ if (!this.groq && !credentialsManager.hasGroqApiKey()) {
130
234
  console.log('\n' + '='.repeat(60));
131
235
  console.log(`${CONFIG.colors.yellow}⚠️ GROQ API KEY MISSING${CONFIG.colors.reset}`);
132
236
  console.log('='.repeat(60));
@@ -171,6 +275,41 @@ When you want to perform an action, use the available tools.`;
171
275
  console.log(`\n${CONFIG.colors.cyan}Hi! I'm Kora. How can I help you today?${CONFIG.colors.reset}`);
172
276
  console.log(`${CONFIG.colors.dim}(Try: "Start a new task for login", "Explain house rules", "Check contracts")${CONFIG.colors.reset}\n`);
173
277
 
278
+ // Check for command line arguments
279
+ const args = process.argv.slice(2);
280
+ const taskIndex = args.indexOf('--task') !== -1 ? args.indexOf('--task') : args.indexOf('-t');
281
+
282
+ if (taskIndex !== -1 && args[taskIndex + 1]) {
283
+ const taskName = args[taskIndex + 1];
284
+ console.log(`\n${CONFIG.colors.cyan}Auto-starting session for task: ${taskName}${CONFIG.colors.reset}\n`);
285
+ await this.startSession({ taskName });
286
+ return; // Exit after session? Or continue chat? Usually session start spawns child and returns.
287
+ // startSession re-initializes readline after child exit, so we can continue chat.
288
+ }
289
+
290
+ // Check for auto-resume
291
+ const resumeIndex = args.indexOf('resume');
292
+ const sessionIdIndex = args.indexOf('--session-id');
293
+
294
+ if (resumeIndex !== -1 || sessionIdIndex !== -1) {
295
+ let sessionId = null;
296
+ let taskName = null;
297
+
298
+ if (sessionIdIndex !== -1 && args[sessionIdIndex + 1]) {
299
+ sessionId = args[sessionIdIndex + 1];
300
+ }
301
+
302
+ if (taskIndex !== -1 && args[taskIndex + 1]) {
303
+ taskName = args[taskIndex + 1];
304
+ }
305
+
306
+ if (sessionId || taskName) {
307
+ console.log(`\n${CONFIG.colors.cyan}Auto-resuming session...${CONFIG.colors.reset}\n`);
308
+ await this.resumeSession({ sessionId, taskName });
309
+ return;
310
+ }
311
+ }
312
+
174
313
  this.startReadline();
175
314
  }
176
315
 
@@ -292,6 +431,12 @@ When you want to perform an action, use the available tools.`;
292
431
  case 'start_session':
293
432
  result = await this.startSession(args);
294
433
  break;
434
+ case 'resume_session':
435
+ result = await this.resumeSession(args);
436
+ break;
437
+ case 'recover_sessions':
438
+ result = await this.recoverSessions();
439
+ break;
295
440
  default:
296
441
  result = JSON.stringify({ error: "Unknown tool" });
297
442
  }
@@ -351,15 +496,50 @@ When you want to perform an action, use the available tools.`;
351
496
 
352
497
  async listContracts() {
353
498
  const contractsDir = path.join(this.repoRoot, 'House_Rules_Contracts');
354
- if (!fs.existsSync(contractsDir)) {
355
- return JSON.stringify({ exists: false, message: "Contracts folder not found." });
499
+ const centralExists = fs.existsSync(contractsDir);
500
+
501
+ // Recursive search for contracts (similar to setup script)
502
+ const findCommand = `find "${this.repoRoot}" -type f \\( -iname "*CONTRACT*.md" -o -iname "*CONTRACT*.json" \\) -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/local_deploy/*"`;
503
+
504
+ let allFiles = [];
505
+ try {
506
+ const output = execSync(findCommand, { encoding: 'utf8' }).trim();
507
+ allFiles = output.split('\n').filter(Boolean);
508
+ } catch (e) {
509
+ // Find failed or no files
356
510
  }
357
511
 
358
- const files = fs.readdirSync(contractsDir).filter(f => f.endsWith('.md') || f.endsWith('.json'));
359
- return JSON.stringify({
360
- exists: true,
361
- files: files,
362
- location: contractsDir
512
+ const requiredTypes = [
513
+ 'FEATURES_CONTRACT.md', 'API_CONTRACT.md', 'DATABASE_SCHEMA_CONTRACT.md',
514
+ 'SQL_CONTRACT.json', 'THIRD_PARTY_INTEGRATIONS.md', 'INFRA_CONTRACT.md'
515
+ ];
516
+
517
+ const status = {};
518
+ let scatteredCount = 0;
519
+
520
+ requiredTypes.forEach(type => {
521
+ // Check if in central folder
522
+ const isCentral = fs.existsSync(path.join(contractsDir, type));
523
+
524
+ // Check if anywhere in repo
525
+ const found = allFiles.filter(f => path.basename(f).toUpperCase() === type || path.basename(f).toUpperCase().includes(type.split('.')[0]));
526
+
527
+ status[type] = {
528
+ central: isCentral,
529
+ foundCount: found.length,
530
+ locations: found.map(f => path.relative(this.repoRoot, f))
531
+ };
532
+
533
+ if (!isCentral && found.length > 0) scatteredCount++;
534
+ });
535
+
536
+ return JSON.stringify({
537
+ centralFolderExists: centralExists,
538
+ scatteredContractsCount: scatteredCount,
539
+ details: status,
540
+ message: scatteredCount > 0
541
+ ? "Contracts found scattered in repository. Recommend running 'npm run setup' to consolidate."
542
+ : (centralExists ? "Contracts found in central folder." : "No contracts found.")
363
543
  });
364
544
  }
365
545
 
@@ -391,55 +571,138 @@ When you want to perform an action, use the available tools.`;
391
571
  }
392
572
 
393
573
  async startSession(args) {
394
- const taskName = args.taskName;
574
+ const { taskName, description } = args;
395
575
 
396
- console.log(`${CONFIG.colors.magenta}Kora > ${CONFIG.colors.reset}Starting new session for: ${taskName}...`);
576
+ console.log(`${CONFIG.colors.magenta}Kora > ${CONFIG.colors.reset}Starting session for task: ${taskName}...`);
397
577
 
398
- // Close readline interface to release stdin for the child process
578
+ // Close readline interface
399
579
  if (this.rl) {
400
580
  this.rl.close();
401
581
  this.rl = null;
402
582
  }
403
583
 
404
- // We need to run the session coordinator interactively
405
- // We'll use the 'create-and-start' command to jump straight to the task
406
584
  const scriptPath = path.join(__dirname, 'session-coordinator.js');
407
585
 
408
586
  return new Promise((resolve, reject) => {
409
- // Use 'inherit' for stdio to allow interactive input/output
410
587
  const child = spawn('node', [scriptPath, 'create-and-start', '--task', taskName], {
411
588
  stdio: 'inherit',
412
589
  cwd: this.repoRoot
413
590
  });
414
591
 
415
592
  child.on('close', (code) => {
416
- // Re-initialize readline interface after child process exits
417
593
  this.startReadline();
594
+ if (code === 0) {
595
+ resolve(JSON.stringify({ success: true, message: "Session started successfully." }));
596
+ } else {
597
+ resolve(JSON.stringify({ success: false, message: `Session process exited with code ${code}.` }));
598
+ }
599
+ console.log(`\n${CONFIG.colors.cyan}Welcome back to Kora!${CONFIG.colors.reset}`);
600
+ });
601
+
602
+ child.on('error', (err) => {
603
+ this.startReadline();
604
+ resolve(JSON.stringify({ success: false, error: err.message }));
605
+ });
606
+ });
607
+ }
418
608
 
609
+ async recoverSessions() {
610
+ console.log(`${CONFIG.colors.magenta}Kora > ${CONFIG.colors.reset}Scanning for orphaned sessions to recover...`);
611
+
612
+ // Close readline interface
613
+ if (this.rl) {
614
+ this.rl.close();
615
+ this.rl = null;
616
+ }
617
+
618
+ const scriptPath = path.join(__dirname, 'session-coordinator.js');
619
+
620
+ return new Promise((resolve, reject) => {
621
+ const child = spawn('node', [scriptPath, 'recover'], {
622
+ stdio: 'inherit',
623
+ cwd: this.repoRoot
624
+ });
625
+
626
+ child.on('close', (code) => {
627
+ this.startReadline();
419
628
  if (code === 0) {
420
- resolve(JSON.stringify({
421
- success: true,
422
- message: `Session for '${taskName}' completed successfully.`
629
+ // Instead of generic message, suggest checking status
630
+ resolve(JSON.stringify({
631
+ success: true,
632
+ message: "Recovery scan complete. Please run 'check_session_status' to see recovered sessions."
423
633
  }));
424
634
  } else {
425
- resolve(JSON.stringify({
426
- success: false,
427
- message: `Session process exited with code ${code}.`
428
- }));
635
+ resolve(JSON.stringify({ success: false, message: `Recovery process exited with code ${code}.` }));
636
+ }
637
+ // Don't print "Welcome back" here to keep flow cleaner
638
+ });
639
+
640
+ child.on('error', (err) => {
641
+ this.startReadline();
642
+ resolve(JSON.stringify({ success: false, error: err.message }));
643
+ });
644
+ });
645
+ }
646
+ async resumeSession(args) {
647
+ const { sessionId, taskName } = args;
648
+
649
+ console.log(`${CONFIG.colors.magenta}Kora > ${CONFIG.colors.reset}Resuming session: ${sessionId || taskName}...`);
650
+
651
+ // Close readline interface
652
+ if (this.rl) {
653
+ this.rl.close();
654
+ this.rl = null;
655
+ }
656
+
657
+ const scriptPath = path.join(__dirname, 'session-coordinator.js');
658
+
659
+ // Construct arguments for session coordinator
660
+ // We use the 'resume' command if we have an ID, or create-and-start with task if we're fuzzy matching
661
+ // But actually session-coordinator doesn't have a direct 'resume' command exposed easily via CLI args
662
+ // that jumps straight to monitoring without prompts, EXCEPT via the way createSession handles existing sessions.
663
+ // However, createSession with --task will prompt.
664
+ // Let's use a new approach: pass --resume-session-id if we have it.
665
+
666
+ // Wait, session-coordinator.js CLI handling (which I can't fully see but I saw 'create' and 'list')
667
+ // I need to check how to invoke resume.
668
+ // Looking at session-coordinator.js (which I read), it has 'requestSession' and 'createSession'.
669
+ // It doesn't seem to have a direct CLI command for 'resume' that takes an ID.
670
+ // However, 'create-and-start' (implied by startSession usage) might support it?
671
+ // In startSession: [scriptPath, 'create-and-start', '--task', taskName, '--skip-setup', '--skip-update']
672
+
673
+ // If I use 'create-and-start' with the SAME task name, it triggers the "Found existing session" logic
674
+ // but that logic is interactive (prompts Y/n).
675
+
676
+ // I should probably add a CLI command to session-coordinator.js to resume by ID non-interactively,
677
+ // OR just use the 'worker' command directly if I know the worktree?
678
+ // But the coordinator handles setting up the environment.
679
+
680
+ // Let's assume for now we can use a new 'resume' command in session-coordinator.js
681
+ // I will need to implement that in session-coordinator.js as well.
682
+ // But first let's implement the caller here.
683
+
684
+ const cmdArgs = ['resume', '--session-id', sessionId];
685
+ if (taskName) cmdArgs.push('--task', taskName);
686
+
687
+ return new Promise((resolve, reject) => {
688
+ const child = spawn('node', [scriptPath, ...cmdArgs], {
689
+ stdio: 'inherit',
690
+ cwd: this.repoRoot
691
+ });
692
+
693
+ child.on('close', (code) => {
694
+ this.startReadline();
695
+ if (code === 0) {
696
+ resolve(JSON.stringify({ success: true, message: "Session resumed successfully." }));
697
+ } else {
698
+ resolve(JSON.stringify({ success: false, message: `Session process exited with code ${code}.` }));
429
699
  }
430
-
431
- // Resume the chat interface after the child process exits
432
700
  console.log(`\n${CONFIG.colors.cyan}Welcome back to Kora!${CONFIG.colors.reset}`);
433
701
  });
434
702
 
435
703
  child.on('error', (err) => {
436
- // Re-initialize readline interface on error
437
704
  this.startReadline();
438
-
439
- resolve(JSON.stringify({
440
- success: false,
441
- error: err.message
442
- }));
705
+ resolve(JSON.stringify({ success: false, error: err.message }));
443
706
  });
444
707
  });
445
708
  }