orchestrix-yuri 4.7.12 → 4.8.0
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.
|
@@ -309,7 +309,7 @@ function runClaude(args, cwd, timeout) {
|
|
|
309
309
|
}, (err, stdout, stderr) => {
|
|
310
310
|
if (err && err.killed) {
|
|
311
311
|
log.warn('Claude CLI timed out');
|
|
312
|
-
return resolve({ reply: '⏱
|
|
312
|
+
return resolve({ reply: '⏱ This is taking longer than usual. The operation may still be running.\n\nTry `*status` to check progress, or send your message again.', raw: '' });
|
|
313
313
|
}
|
|
314
314
|
// Claude CLI may return non-zero exit code even with valid JSON output
|
|
315
315
|
// (e.g., stderr "Warning: no stdin data received" causes exit code 1).
|
|
@@ -317,7 +317,7 @@ function runClaude(args, cwd, timeout) {
|
|
|
317
317
|
if (err && !stdout.trim()) {
|
|
318
318
|
log.error(`Claude CLI error: ${err.message.slice(0, 200)}`);
|
|
319
319
|
if (stderr) log.info(`stderr: ${stderr.slice(0, 200)}`);
|
|
320
|
-
return resolve({ reply:
|
|
320
|
+
return resolve({ reply: '❌ Something went wrong. Try again, or use `*status` to check state.', raw: stderr });
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
try {
|
|
@@ -478,7 +478,7 @@ function composePrompt(userMessage) {
|
|
|
478
478
|
if (l2) sections.push(l2);
|
|
479
479
|
|
|
480
480
|
log.engine('Memory context changed, injecting refresh into prompt');
|
|
481
|
-
return
|
|
481
|
+
return `<system-context-update silent="true">\nDo not mention or reference this context update in your response. This is an automatic memory refresh.\n${sections.join('\n\n')}\n</system-context-update>\n\n${userMessage}`;
|
|
482
482
|
}
|
|
483
483
|
|
|
484
484
|
/**
|
|
@@ -67,7 +67,8 @@ class Dispatcher {
|
|
|
67
67
|
async classify(text) {
|
|
68
68
|
// Re-entry guard
|
|
69
69
|
if (this._busy) {
|
|
70
|
-
|
|
70
|
+
log.warn('Dispatcher: busy, defaulting to change');
|
|
71
|
+
return { action: 'change', description: text, reasoning: 'dispatcher busy' };
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
// Auto-recover if session died
|
|
@@ -75,7 +76,8 @@ class Dispatcher {
|
|
|
75
76
|
this._ready = false;
|
|
76
77
|
await this.start();
|
|
77
78
|
if (!this._ready) {
|
|
78
|
-
|
|
79
|
+
log.warn('Dispatcher: unavailable after recovery attempt, defaulting to change');
|
|
80
|
+
return { action: 'change', description: text, reasoning: 'dispatcher unavailable' };
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
@@ -165,17 +167,32 @@ class Dispatcher {
|
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
// Strategy 3:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
// Strategy 3: Claude re-classify (replaces keyword matching)
|
|
171
|
+
// Only reached when tmux output was garbled or unparseable — rare but important.
|
|
172
|
+
log.warn('Dispatcher: JSON parse failed, attempting Claude re-classify');
|
|
173
|
+
try {
|
|
174
|
+
const engine = require('./claude-sdk');
|
|
175
|
+
const binary = engine.findClaudeBinary();
|
|
176
|
+
const safeText = originalText.slice(0, 500).replace(/'/g, "'\\''");
|
|
177
|
+
const prompt = `Classify this user message into ONE action. Valid actions: ${valid.join(', ')}. Reply with ONLY a JSON object like {"action":"change","description":"summary","reasoning":"why"}. User message: "${safeText}"`;
|
|
178
|
+
const result = execSync(
|
|
179
|
+
`${binary} -p '${prompt.replace(/'/g, "'\\''")}' --output-format json 2>/dev/null`,
|
|
180
|
+
{ encoding: 'utf8', timeout: 15000 }
|
|
181
|
+
).trim();
|
|
182
|
+
const json = JSON.parse(result);
|
|
183
|
+
const reply = json.result || '';
|
|
184
|
+
const reParsed = JSON.parse(reply);
|
|
185
|
+
if (reParsed.action && valid.includes(reParsed.action)) {
|
|
186
|
+
log.engine(`Dispatcher: re-classified as "${reParsed.action}"`);
|
|
187
|
+
return { action: reParsed.action, description: reParsed.description || originalText, reasoning: 'reclassified' };
|
|
173
188
|
}
|
|
189
|
+
} catch (err) {
|
|
190
|
+
log.warn(`Dispatcher: re-classify failed: ${err.message}`);
|
|
174
191
|
}
|
|
175
192
|
|
|
176
|
-
//
|
|
177
|
-
log.warn('Dispatcher:
|
|
178
|
-
return { action: 'change', description: originalText, reasoning: 'parse failed' };
|
|
193
|
+
// Absolute last resort — default to "change" (bias toward action)
|
|
194
|
+
log.warn('Dispatcher: all strategies failed, defaulting to change');
|
|
195
|
+
return { action: 'change', description: originalText, reasoning: 'all parse strategies failed' };
|
|
179
196
|
}
|
|
180
197
|
|
|
181
198
|
/**
|
|
@@ -194,7 +194,6 @@ class PhaseOrchestrator {
|
|
|
194
194
|
|
|
195
195
|
const waiting = this._waitingForInput ? ' (WAITING FOR YOUR INPUT)' : '';
|
|
196
196
|
return `[LIVE AGENT CONTEXT] Phase: plan, Agent: ${agent.name} (${this._step + 1}/${PLAN_AGENTS.length})${waiting}\n` +
|
|
197
|
-
`tmux session: ${this._session}, window: ${agent.window}\n` +
|
|
198
197
|
`--- Agent output (last 40 lines) ---\n${pane}\n--- End agent output ---`;
|
|
199
198
|
}
|
|
200
199
|
|
|
@@ -205,9 +204,9 @@ class PhaseOrchestrator {
|
|
|
205
204
|
for (let w = 0; w < 4; w++) {
|
|
206
205
|
const tail = tmx.capturePane(this._session, w, 5);
|
|
207
206
|
const lastLine = tail.split('\n').filter((l) => l.trim()).pop() || '(empty)';
|
|
208
|
-
summaries.push(`
|
|
207
|
+
summaries.push(` ${windows[w]}: ${lastLine.trim().slice(0, 80)}`);
|
|
209
208
|
}
|
|
210
|
-
return `[LIVE AGENT CONTEXT] Phase: develop
|
|
209
|
+
return `[LIVE AGENT CONTEXT] Phase: develop\n${summaries.join('\n')}`;
|
|
211
210
|
}
|
|
212
211
|
|
|
213
212
|
return null;
|
|
@@ -539,8 +538,19 @@ class PhaseOrchestrator {
|
|
|
539
538
|
this._phase = null;
|
|
540
539
|
this._step = 0;
|
|
541
540
|
|
|
541
|
+
// Build output summary
|
|
542
|
+
const outputs = [];
|
|
543
|
+
for (const agent of PLAN_AGENTS) {
|
|
544
|
+
if (agent.output) {
|
|
545
|
+
const outputPath = path.join(this._projectRoot, agent.output);
|
|
546
|
+
const exists = fs.existsSync(outputPath);
|
|
547
|
+
outputs.push(` ${exists ? '✅' : '⚠️'} ${agent.name} → ${agent.output}`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const outputSummary = outputs.length > 0 ? `\n\n${outputs.join('\n')}` : '';
|
|
551
|
+
|
|
542
552
|
log.engine('Plan phase complete');
|
|
543
|
-
this.onComplete('plan',
|
|
553
|
+
this.onComplete('plan', `🎉 Planning complete!${outputSummary}\n\nNext: review the docs above, then run \`*develop\` to start building.`);
|
|
544
554
|
}
|
|
545
555
|
|
|
546
556
|
/**
|
|
@@ -819,7 +829,7 @@ class PhaseOrchestrator {
|
|
|
819
829
|
// Current activity
|
|
820
830
|
if (p.currentAgent) {
|
|
821
831
|
const storyInfo = p.currentStory ? ` → ${p.currentStory}` : '';
|
|
822
|
-
lines.push(`Current: ${p.currentAgent}
|
|
832
|
+
lines.push(`Current: ${p.currentAgent}${storyInfo}`);
|
|
823
833
|
} else {
|
|
824
834
|
lines.push(`Current: waiting for next handoff`);
|
|
825
835
|
}
|
|
@@ -875,7 +885,7 @@ class PhaseOrchestrator {
|
|
|
875
885
|
}
|
|
876
886
|
|
|
877
887
|
log.engine('Dev phase complete');
|
|
878
|
-
this.onComplete('develop', '🎉 Development complete! All stories
|
|
888
|
+
this.onComplete('develop', '🎉 Development complete! All stories implemented.\n\nNext: run `*test` to validate each epic with smoke tests.');
|
|
879
889
|
}
|
|
880
890
|
|
|
881
891
|
// ── Test Phase ──────────────────────────────────────────────────────────────
|
|
@@ -1432,7 +1442,7 @@ class PhaseOrchestrator {
|
|
|
1432
1442
|
|
|
1433
1443
|
this._changeContext = null;
|
|
1434
1444
|
log.engine('Iterate complete — dev automation started');
|
|
1435
|
-
this.onComplete('iterate', `🔄 New iteration launched!\n\nSM is drafting stories
|
|
1445
|
+
this.onComplete('iterate', `🔄 New iteration launched!\n\nSM is drafting new stories. Agents will chain automatically (SM → Architect → Dev → QA).`);
|
|
1436
1446
|
|
|
1437
1447
|
// Transition to dev monitoring so SM → Architect → Dev → QA cycle is tracked.
|
|
1438
1448
|
// Without this, the entire dev cycle runs unmonitored after iterate completes.
|
|
@@ -1711,7 +1721,25 @@ class PhaseOrchestrator {
|
|
|
1711
1721
|
}
|
|
1712
1722
|
this._phase = null;
|
|
1713
1723
|
log.error(`Phase ${phase} error: ${message}`);
|
|
1714
|
-
|
|
1724
|
+
|
|
1725
|
+
// Sanitize internal details from user-facing message
|
|
1726
|
+
const cleanMsg = message
|
|
1727
|
+
.replace(/orchestrix-[\w-]+/g, 'dev session')
|
|
1728
|
+
.replace(/op-[\w-]+/g, 'planning session')
|
|
1729
|
+
.replace(/yuri-[\w-]+/g, 'dispatcher')
|
|
1730
|
+
.replace(/\/Users\/\S+/g, '')
|
|
1731
|
+
.replace(/tmux session/gi, 'agent session');
|
|
1732
|
+
|
|
1733
|
+
const hints = {
|
|
1734
|
+
plan: 'Run `*plan` to restart, or `*status` to check saved progress.',
|
|
1735
|
+
develop: 'Run `*develop` to restart, or `*status` to see completed stories.',
|
|
1736
|
+
test: 'Run `*test` to restart testing.',
|
|
1737
|
+
change: 'Send your change request again.',
|
|
1738
|
+
iterate: 'Run `*iterate` again.',
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
const recovery = hints[phase] || 'Use `*status` to check current state.';
|
|
1742
|
+
this.onError(phase, `❌ ${cleanMsg}\n\n${recovery}`);
|
|
1715
1743
|
}
|
|
1716
1744
|
|
|
1717
1745
|
_updatePlanMemory(status) {
|
package/lib/gateway/router.js
CHANGED
|
@@ -65,7 +65,7 @@ class Router {
|
|
|
65
65
|
config: config.engine,
|
|
66
66
|
onProgress: (msg) => this._sendProactive(msg),
|
|
67
67
|
onComplete: (phase, summary) => this._sendProactive(summary),
|
|
68
|
-
onError: (phase, err) => this._sendProactive(
|
|
68
|
+
onError: (phase, err) => this._sendProactive(err),
|
|
69
69
|
onQuestionAsked: (text) => this._sendProactiveWithId(text),
|
|
70
70
|
});
|
|
71
71
|
|
|
@@ -1123,7 +1123,7 @@ Reply with ONLY one word: small, medium, or large. Nothing else.`;
|
|
|
1123
1123
|
const last = lines.slice(-3).map((l) => l.trim().slice(0, 100)).join('\n ');
|
|
1124
1124
|
summaries.push(` Window ${w} (${windows[w]}):\n ${last || '(idle)'}`);
|
|
1125
1125
|
}
|
|
1126
|
-
return `[LIVE DEV SESSION]
|
|
1126
|
+
return `[LIVE DEV SESSION] (agents running independently)\n${summaries.join('\n')}`;
|
|
1127
1127
|
} catch { return null; }
|
|
1128
1128
|
}
|
|
1129
1129
|
|