orchestrix-yuri 4.7.0 โ†’ 4.7.2

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.
@@ -129,7 +129,7 @@ class Dispatcher {
129
129
  try {
130
130
  const parsed = JSON.parse(line);
131
131
  if (parsed.action) {
132
- const valid = ['change', 'plan', 'develop', 'test', 'deploy', 'status', 'iterate', 'conversation'];
132
+ const valid = ['bugfix', 'change', 'plan', 'develop', 'test', 'deploy', 'status', 'iterate', 'conversation'];
133
133
  if (valid.includes(parsed.action)) {
134
134
  return {
135
135
  action: parsed.action,
@@ -1404,6 +1404,57 @@ class PhaseOrchestrator {
1404
1404
  this.onComplete('iterate', `๐Ÿ”„ New iteration launched!\n\nSM is drafting stories in dev session: ${devSession}\nAgents will chain automatically via handoff-detector.`);
1405
1405
  }
1406
1406
 
1407
+ // โ”€โ”€ Quick Fix โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1408
+
1409
+ /**
1410
+ * Execute a quick bug fix via Dev agent.
1411
+ * Sends *quick-fix to the Dev window โ€” no scope assessment, immediate action.
1412
+ *
1413
+ * @param {string} projectRoot
1414
+ * @param {string} bugDesc โ€” description of the bug/issue
1415
+ * @returns {string} immediate status message
1416
+ */
1417
+ startQuickFix(projectRoot, bugDesc) {
1418
+ if (this._phase) {
1419
+ return `โš ๏ธ Phase "${this._phase}" is already running. Finish it first or *cancel.`;
1420
+ }
1421
+
1422
+ this._projectRoot = projectRoot;
1423
+ this._phase = 'change';
1424
+ this._lastHash = '';
1425
+ this._stableCount = 0;
1426
+
1427
+ try {
1428
+ const scriptPath = path.join(SKILL_DIR, 'scripts', 'ensure-session.sh');
1429
+ const result = execSync(`bash "${scriptPath}" dev "${projectRoot}"`, {
1430
+ encoding: 'utf8', timeout: 60000,
1431
+ }).trim();
1432
+ const lines = result.split('\n');
1433
+ this._session = lines[lines.length - 1].trim();
1434
+ } catch (err) {
1435
+ this._phase = null;
1436
+ return `โŒ Failed to ensure dev session: ${err.message}`;
1437
+ }
1438
+
1439
+ // Reload Dev agent and send *quick-fix
1440
+ tmx.sendKeysWithEnter(this._session, 2, '/clear');
1441
+ execSync('sleep 2');
1442
+ tmx.sendKeysWithEnter(this._session, 2, '/o dev');
1443
+ execSync('sleep 12');
1444
+
1445
+ const safeBug = (bugDesc || 'bug fix').replace(/"/g, '\\"');
1446
+ tmx.sendKeysWithEnter(this._session, 2, `*quick-fix "${safeBug}"`);
1447
+
1448
+ // Poll for completion (reuses _pollChange which watches dev window 2)
1449
+ const pollInterval = this.config.phase_poll_interval || 30000;
1450
+ this._step = 0;
1451
+ this._changeContext = { scope: 'small', description: bugDesc };
1452
+ this._timer = setInterval(() => this._pollChange(), pollInterval);
1453
+
1454
+ log.engine(`Quick fix started: "${bugDesc.slice(0, 60)}..."`);
1455
+ return `๐Ÿ› Quick fix started โ†’ Dev *quick-fix\n\n"${bugDesc.slice(0, 100)}"\n\nI'll notify you when it's done.`;
1456
+ }
1457
+
1407
1458
  // โ”€โ”€ Change Management โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1408
1459
 
1409
1460
  /**
@@ -70,6 +70,19 @@ class ChatHistory {
70
70
  this._trimIfNeeded(filePath);
71
71
  }
72
72
 
73
+ /**
74
+ * Get the last assistant message for a chat.
75
+ * @param {string} chatId
76
+ * @returns {string|null}
77
+ */
78
+ getLastAssistantMessage(chatId) {
79
+ const messages = this.getRecent(chatId);
80
+ for (let i = messages.length - 1; i >= 0; i--) {
81
+ if (messages[i].role === 'assistant') return messages[i].text;
82
+ }
83
+ return null;
84
+ }
85
+
73
86
  _trimIfNeeded(filePath) {
74
87
  const lines = fs.readFileSync(filePath, 'utf8').trim().split('\n').filter(Boolean);
75
88
  const limit = this.maxMessages * 2;
@@ -282,18 +282,37 @@ class Router {
282
282
  }
283
283
 
284
284
  // โ•โ•โ• DISPATCHER CLASSIFY โ€” NL intent detection via persistent Claude agent โ•โ•โ•
285
- if (this.dispatcher && this.dispatcher.isReady()) {
286
- try {
287
- const classified = await this.dispatcher.classify(msg.text);
288
- log.router(`Dispatcher: ${classified.action} โ† "${msg.text.slice(0, 50)}..." (${classified.reasoning})`);
285
+ {
286
+ // Inject last assistant response as context for the dispatcher
287
+ const lastReply = this.history.getLastAssistantMessage(msg.chatId);
288
+ const contextText = lastReply
289
+ ? `[CONTEXT] Previous assistant response: ${lastReply.slice(0, 300)}\n\nUser message: ${msg.text}`
290
+ : msg.text;
291
+
292
+ let classified = null;
293
+ if (this.dispatcher) {
294
+ try {
295
+ classified = await this.dispatcher.classify(contextText);
296
+ log.router(`Dispatcher: ${classified.action} โ† "${msg.text.slice(0, 50)}..." (${classified.reasoning})`);
297
+ } catch (err) {
298
+ log.warn(`Dispatcher classify failed: ${err.message}`);
299
+ }
300
+ }
301
+
302
+ // If dispatcher unavailable or failed, default to 'change' for work-like messages
303
+ if (!classified) {
304
+ classified = { action: 'change', description: msg.text, reasoning: 'dispatcher unavailable, defaulting to change' };
305
+ log.router(`Dispatcher unavailable, defaulting to change for: "${msg.text.slice(0, 50)}..."`);
306
+ }
289
307
 
290
- if (classified.action !== 'conversation') {
308
+ if (classified.action !== 'conversation') {
309
+ try {
291
310
  const intentResult = await this._executeClassifiedIntent(classified, msg);
292
311
  if (intentResult) return intentResult;
312
+ } catch (err) {
313
+ log.warn(`Intent execution failed: ${err.message}`);
314
+ // Fall through to conversation
293
315
  }
294
- } catch (err) {
295
- log.warn(`Dispatcher classify failed: ${err.message}`);
296
- // Fall through to normal processing
297
316
  }
298
317
  }
299
318
 
@@ -321,6 +340,13 @@ class Router {
321
340
  this.history.append(msg.chatId, 'user', msg.text);
322
341
 
323
342
  switch (classified.action) {
343
+ case 'bugfix': {
344
+ const desc = classified.description || msg.text;
345
+ const response = this.orchestrator.startQuickFix(projectRoot, desc);
346
+ this.history.append(msg.chatId, 'assistant', response.slice(0, 2000));
347
+ this._updateGlobalFocus(msg, projectRoot);
348
+ return { text: response };
349
+ }
324
350
  case 'change': {
325
351
  const desc = classified.description || msg.text;
326
352
  msg.text = `*change ${desc}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrix-yuri",
3
- "version": "4.7.0",
3
+ "version": "4.7.2",
4
4
  "description": "Yuri โ€” Meta-Orchestrator for Orchestrix. Drive your entire project lifecycle with natural language.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {
@@ -1,19 +1,21 @@
1
1
  You are a message classifier for Yuri, a project orchestrator.
2
2
 
3
3
  Your ONLY job is to classify incoming user messages into structured actions.
4
+ Messages may include "[CONTEXT]" with the previous assistant response for reference.
4
5
 
5
6
  ## Available Actions
6
7
 
7
8
  | Action | When to use | Examples |
8
9
  |--------|-------------|---------|
9
- | change | User wants something fixed, modified, added, redesigned, or wants a specific agent to do work | "่ฟ™ไธช bug ไฟฎไธ€ไธ‹", "่ฎฉ UX ้‡ๆ–ฐ่ฎพ่ฎก็•Œ้ข", "Add dark mode", "่ฎฉ architect ่ฏ„ไผฐๅฏ่กŒๆ€ง" |
10
- | plan | User wants to start or restart the planning phase | "ๅผ€ๅง‹่ง„ๅˆ’", "้‡ๆ–ฐๅš PRD", "้‡ๆ–ฐ่ง„ๅˆ’ๆžถๆž„" |
11
- | develop | User wants to start or continue development | "ๅผ€ๅง‹ๅผ€ๅ‘", "็ปง็ปญๅ†™ไปฃ็ " |
10
+ | bugfix | User reports a bug, error, or specific problem that needs fixing | "่ฟ™ไธช bug ไฟฎไธ€ไธ‹", "Fix the login error", "้กต้ขๆŠฅ้”™ไบ†", "ๆŒ‰้’ฎ็‚นไบ†ๆฒกๅๅบ”", "่ฟ™ไธช้—ฎ้ข˜ๅค„็†ไธ€ไธ‹" |
11
+ | change | User wants work done: modify, add, redesign, create a plan, write something, have an agent do work (NOT a bug) | "่ฎฉ UX ้‡ๆ–ฐ่ฎพ่ฎก็•Œ้ข", "Add dark mode", "ๅ…ˆๅšไธชๅฎŒๆ•ด็š„่ฎพ่ฎก plan", "ๅ†™ไธ€ไปฝๆ–นๆกˆ", "ไปŽ PO ๅผ€ๅง‹ๆ”น" |
12
+ | plan | User explicitly wants to START the planning phase from scratch | "ๅผ€ๅง‹่ง„ๅˆ’", "*plan" |
13
+ | develop | User explicitly wants to START development phase | "ๅผ€ๅง‹ๅผ€ๅ‘", "*develop" |
12
14
  | test | User wants to run tests | "่ท‘ไธ€ไธ‹ๆต‹่ฏ•", "ๅ†’็ƒŸๆต‹่ฏ•" |
13
15
  | deploy | User wants to deploy or release | "้ƒจ็ฝฒๅˆฐ็บฟไธŠ", "ๅ‘ๅธƒ", "ไธŠ็บฟ" |
14
16
  | status | User asks about progress, previous results, or what happened | "ไน‹ๅ‰็š„ๆ–นๆกˆๅ‘ข๏ผŸ", "่ฟ›ๅฑ•ๅฆ‚ไฝ•๏ผŸ", "ๆ€Žไนˆๆ ทไบ†", "็ป“ๆžœๅ‡บๆฅไบ†ๅ—" |
15
17
  | iterate | User wants a new iteration cycle | "ๆ–ฐ่ฟญไปฃ", "ไธ‹ไธ€่ฝฎ" |
16
- | conversation | Pure question, opinion, or chat โ€” no actionable request | "ไฝ ่ง‰ๅพ—ๆ€Žไนˆๆ ท๏ผŸ", "่ฟ™ไธชๆžถๆž„ๅฅฝไธๅฅฝ๏ผŸ", "่งฃ้‡Šไธ€ไธ‹่ฟ™ๆฎตไปฃ็ " |
18
+ | conversation | ONLY for pure questions or opinions with NO actionable request | "ไฝ ่ง‰ๅพ—ๆ€Žไนˆๆ ท๏ผŸ", "่ฟ™ไธชๆžถๆž„ๅฅฝไธๅฅฝ๏ผŸ", "่งฃ้‡Šไธ€ไธ‹่ฟ™ๆฎตไปฃ็ " |
17
19
 
18
20
  ## Response Format
19
21
 
@@ -21,12 +23,14 @@ Reply with EXACTLY this JSON format on a single line, nothing else:
21
23
 
22
24
  {"action":"<action>","description":"<brief description for the agent>","reasoning":"<one sentence why>"}
23
25
 
24
- ## Rules
26
+ ## Critical Rules
25
27
 
26
- - If the user references a specific agent role (UX, UI, architect, PM, QA, dev, SM), and wants them to DO something, classify as "change"
27
- - If the user asks about previous results, output, or progress, classify as "status"
28
- - If the user expresses dissatisfaction and wants improvement, classify as "change" (they want something fixed/redesigned)
29
- - If the user expresses an opinion or asks a question WITHOUT requesting any action, classify as "conversation"
30
- - When in doubt, prefer "conversation" โ€” it is safer to chat than to trigger an unintended action
31
- - The "description" field should be a concise summary of what the user wants done, suitable for passing to an agent
28
+ - **Bug vs Change**: If the user reports a bug, error, problem, or something broken โ†’ "bugfix". If the user wants new work, redesign, or feature โ†’ "change". When in doubt between bugfix and change, prefer "bugfix" for problems and "change" for new work.
29
+ - **Bias toward action**: If the user wants ANYTHING done (create, write, fix, modify, redesign, evaluate, plan something), classify as "change" or "bugfix". Never let work requests fall into "conversation".
30
+ - If the user is replying to a question with a choice/decision (e.g., "ๅ…ˆๅš plan" answering "่ฆ A ่ฟ˜ๆ˜ฏ B๏ผŸ"), that reply IS an instruction โ€” classify as "change", NOT "conversation".
31
+ - If the user references a specific agent role (UX, UI, architect, PM, QA, dev, SM) and wants them to do something, classify as "change".
32
+ - If the user asks about previous results, output, or progress, classify as "status".
33
+ - "conversation" is ONLY for messages that require NO action at all โ€” pure questions, opinions, greetings.
34
+ - When in doubt between "change" and "conversation", ALWAYS choose "change".
35
+ - The "description" field should be a concise summary of what the user wants done.
32
36
  - ALWAYS respond with the JSON format above. Never add explanation, markdown, or anything else.