elsabro 7.2.0 → 7.3.1
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 +34 -0
- package/agents/elsabro-orchestrator.md +2 -0
- package/commands/elsabro/execute.md +346 -1471
- package/commands/elsabro/quick.md +11 -11
- package/commands/elsabro/start.md +156 -14
- package/flow-engine/src/callbacks.js +79 -0
- package/flow-engine/src/checkpoint.js +41 -0
- package/flow-engine/src/cli.js +214 -6
- package/flow-engine/src/executors.js +135 -4
- package/flow-engine/src/graph.js +30 -2
- package/flow-engine/src/template.js +152 -72
- package/flow-engine/tests/checkpoint.test.js +476 -0
- package/flow-engine/tests/execute-dispatcher.test.js +738 -0
- package/flow-engine/tests/executors-complex.test.js +259 -0
- package/flow-engine/tests/graph.test.js +193 -0
- package/flow-engine/tests/skill-install.test.js +254 -0
- package/flow-engine/tests/validation.test.js +137 -0
- package/flows/development-flow.json +23 -23
- package/hooks/skill-discovery.sh +0 -0
- package/package.json +1 -1
- package/references/SYSTEM_INDEX.md +1 -1
- package/references/agent-teams-integration.md +7 -15
|
@@ -371,4 +371,258 @@ describe('skill-install.sh', () => {
|
|
|
371
371
|
assert.ok(['ok', 'error'].includes(errResult.status));
|
|
372
372
|
});
|
|
373
373
|
});
|
|
374
|
+
|
|
375
|
+
// ---- Auto-install integration ----
|
|
376
|
+
describe('Auto-install integration', () => {
|
|
377
|
+
it('completes full workflow: check → install → validate', () => {
|
|
378
|
+
const mockDir = createMockNpx('exit 0');
|
|
379
|
+
const mockHome = createMockSkillsDir({
|
|
380
|
+
'integration-test.md': '---\nname: integration-test\n---\n# Test skill'
|
|
381
|
+
});
|
|
382
|
+
try {
|
|
383
|
+
// Step 1: Check registry availability
|
|
384
|
+
const checkResult = runInstall('check', {
|
|
385
|
+
env: { PATH: `${mockDir}:${process.env.PATH}` }
|
|
386
|
+
});
|
|
387
|
+
assert.equal(checkResult.status, 'ok', 'Registry check should succeed');
|
|
388
|
+
|
|
389
|
+
// Step 2: Install skill
|
|
390
|
+
const installResult = runInstall(
|
|
391
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill integration-test -g -a claude-code"',
|
|
392
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
393
|
+
);
|
|
394
|
+
assert.equal(installResult.status, 'ok', 'Install should succeed');
|
|
395
|
+
assert.equal(installResult.skill, 'integration-test', 'Should extract correct skill name');
|
|
396
|
+
|
|
397
|
+
// Step 3: Validate installed skill
|
|
398
|
+
const validateResult = runInstall('validate "integration-test"', {
|
|
399
|
+
env: { HOME: mockHome }
|
|
400
|
+
});
|
|
401
|
+
assert.equal(validateResult.status, 'ok', 'Validation should succeed');
|
|
402
|
+
assert.equal(validateResult.has_frontmatter, true, 'Should detect frontmatter');
|
|
403
|
+
} finally {
|
|
404
|
+
cleanup(mockDir);
|
|
405
|
+
cleanup(mockHome);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('handles install failure gracefully in workflow', () => {
|
|
410
|
+
const mockDir = createMockNpx('echo "Network error" >&2; exit 1');
|
|
411
|
+
const mockHome = createMockSkillsDir();
|
|
412
|
+
try {
|
|
413
|
+
// Step 1: Check succeeds (mock npx returns 0 for some calls)
|
|
414
|
+
const mockDirCheck = createMockNpx('exit 0');
|
|
415
|
+
const checkResult = runInstall('check', {
|
|
416
|
+
env: { PATH: `${mockDirCheck}:${process.env.PATH}` }
|
|
417
|
+
});
|
|
418
|
+
cleanup(mockDirCheck);
|
|
419
|
+
assert.equal(checkResult.status, 'ok');
|
|
420
|
+
|
|
421
|
+
// Step 2: Install fails
|
|
422
|
+
const installResult = runInstall(
|
|
423
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill bad-network -g -a claude-code"',
|
|
424
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
425
|
+
);
|
|
426
|
+
assert.equal(installResult.status, 'error', 'Install should fail on network error');
|
|
427
|
+
assert.ok(installResult.message.includes('install failed'), 'Error message should indicate install failure');
|
|
428
|
+
|
|
429
|
+
// Step 3: Validation fails (skill not installed)
|
|
430
|
+
const validateResult = runInstall('validate "bad-network"', {
|
|
431
|
+
env: { HOME: mockHome }
|
|
432
|
+
});
|
|
433
|
+
assert.equal(validateResult.status, 'error', 'Validation should fail for non-existent skill');
|
|
434
|
+
} finally {
|
|
435
|
+
cleanup(mockDir);
|
|
436
|
+
cleanup(mockHome);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('detects missing skill and triggers install suggestion', () => {
|
|
441
|
+
const mockHome = createMockSkillsDir({
|
|
442
|
+
'existing-skill.md': '---\nname: existing-skill\n---\n# Exists'
|
|
443
|
+
});
|
|
444
|
+
try {
|
|
445
|
+
// Try to validate a skill that doesn't exist
|
|
446
|
+
const validateResult = runInstall('validate "missing-skill"', {
|
|
447
|
+
env: { HOME: mockHome }
|
|
448
|
+
});
|
|
449
|
+
assert.equal(validateResult.status, 'error', 'Should error on missing skill');
|
|
450
|
+
assert.ok(validateResult.message.includes('not found'), 'Error should indicate skill not found');
|
|
451
|
+
} finally {
|
|
452
|
+
cleanup(mockHome);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('verifies skill availability after successful install', () => {
|
|
457
|
+
const mockDir = createMockNpx('exit 0');
|
|
458
|
+
const mockHome = createMockSkillsDir();
|
|
459
|
+
try {
|
|
460
|
+
// Install skill
|
|
461
|
+
const installResult = runInstall(
|
|
462
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill verify-test -g -a claude-code"',
|
|
463
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
464
|
+
);
|
|
465
|
+
assert.equal(installResult.status, 'ok');
|
|
466
|
+
|
|
467
|
+
// Create the skill file to simulate successful installation
|
|
468
|
+
const skillPath = path.join(mockHome, '.claude', 'skills', 'verify-test.md');
|
|
469
|
+
fs.writeFileSync(skillPath, '---\nname: verify-test\n---\n# Installed');
|
|
470
|
+
|
|
471
|
+
// Verify skill is now available
|
|
472
|
+
const validateResult = runInstall('validate "verify-test"', {
|
|
473
|
+
env: { HOME: mockHome }
|
|
474
|
+
});
|
|
475
|
+
assert.equal(validateResult.status, 'ok', 'Skill should be available after install');
|
|
476
|
+
assert.equal(validateResult.has_frontmatter, true);
|
|
477
|
+
} finally {
|
|
478
|
+
cleanup(mockDir);
|
|
479
|
+
cleanup(mockHome);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('handles concurrent install attempts gracefully', () => {
|
|
484
|
+
const mockDir = createMockNpx('sleep 0.1; exit 0');
|
|
485
|
+
const mockHome = createMockSkillsDir();
|
|
486
|
+
try {
|
|
487
|
+
// Simulate checking multiple skills in sequence (not parallel, per spec)
|
|
488
|
+
const skills = ['skill-a', 'skill-b', 'skill-c'];
|
|
489
|
+
const results = skills.map(skill =>
|
|
490
|
+
runInstall(
|
|
491
|
+
`install "npx -y skills add vercel-labs/agent-skills --skill ${skill} -g -a claude-code"`,
|
|
492
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
493
|
+
)
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
// All should succeed
|
|
497
|
+
results.forEach((result, idx) => {
|
|
498
|
+
assert.equal(result.status, 'ok', `Install ${idx + 1} should succeed`);
|
|
499
|
+
assert.equal(result.skill, skills[idx], `Should extract correct skill name ${skills[idx]}`);
|
|
500
|
+
});
|
|
501
|
+
} finally {
|
|
502
|
+
cleanup(mockDir);
|
|
503
|
+
cleanup(mockHome);
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it('recovers from partial install failure', () => {
|
|
508
|
+
const mockDir = createMockNpx('exit 0');
|
|
509
|
+
const mockHome = createMockSkillsDir();
|
|
510
|
+
try {
|
|
511
|
+
// First install succeeds
|
|
512
|
+
const install1 = runInstall(
|
|
513
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill partial-1 -g -a claude-code"',
|
|
514
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
515
|
+
);
|
|
516
|
+
assert.equal(install1.status, 'ok');
|
|
517
|
+
|
|
518
|
+
// Second install with failing npx
|
|
519
|
+
const mockDirFail = createMockNpx('exit 1');
|
|
520
|
+
const install2 = runInstall(
|
|
521
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill partial-2 -g -a claude-code"',
|
|
522
|
+
{ env: { PATH: `${mockDirFail}:${process.env.PATH}`, HOME: mockHome } }
|
|
523
|
+
);
|
|
524
|
+
cleanup(mockDirFail);
|
|
525
|
+
assert.equal(install2.status, 'error', 'Second install should fail');
|
|
526
|
+
|
|
527
|
+
// Third install succeeds (recovery)
|
|
528
|
+
const install3 = runInstall(
|
|
529
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill partial-3 -g -a claude-code"',
|
|
530
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
531
|
+
);
|
|
532
|
+
assert.equal(install3.status, 'ok', 'Should recover after failure');
|
|
533
|
+
} finally {
|
|
534
|
+
cleanup(mockDir);
|
|
535
|
+
cleanup(mockHome);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('validates skill format after auto-install', () => {
|
|
540
|
+
const mockDir = createMockNpx('exit 0');
|
|
541
|
+
const mockHome = createMockSkillsDir();
|
|
542
|
+
try {
|
|
543
|
+
// Install skill
|
|
544
|
+
runInstall(
|
|
545
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill format-test -g -a claude-code"',
|
|
546
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
// Create skill with invalid format (no frontmatter)
|
|
550
|
+
const skillPath = path.join(mockHome, '.claude', 'skills', 'format-test.md');
|
|
551
|
+
fs.writeFileSync(skillPath, '# No frontmatter\nJust content');
|
|
552
|
+
|
|
553
|
+
// Validate should still succeed but report no frontmatter
|
|
554
|
+
const validateResult = runInstall('validate "format-test"', {
|
|
555
|
+
env: { HOME: mockHome }
|
|
556
|
+
});
|
|
557
|
+
assert.equal(validateResult.status, 'ok', 'Should succeed even without frontmatter');
|
|
558
|
+
assert.equal(validateResult.has_frontmatter, false, 'Should detect missing frontmatter');
|
|
559
|
+
} finally {
|
|
560
|
+
cleanup(mockDir);
|
|
561
|
+
cleanup(mockHome);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it('handles registry unavailable during auto-install', () => {
|
|
566
|
+
const mockDir = createMockNpx('echo "Registry unavailable" >&2; exit 1');
|
|
567
|
+
try {
|
|
568
|
+
const checkResult = runInstall('check', {
|
|
569
|
+
env: { PATH: `${mockDir}:${process.env.PATH}` }
|
|
570
|
+
});
|
|
571
|
+
assert.equal(checkResult.status, 'error', 'Should fail when registry unavailable');
|
|
572
|
+
assert.ok(checkResult.message.includes('registry check failed'), 'Should indicate registry failure');
|
|
573
|
+
} finally {
|
|
574
|
+
cleanup(mockDir);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it('provides clear error for malformed install commands', () => {
|
|
579
|
+
const invalidCommands = [
|
|
580
|
+
'', // Empty
|
|
581
|
+
'not-npx-command', // Wrong prefix
|
|
582
|
+
'npx skills add', // Missing --skill flag
|
|
583
|
+
];
|
|
584
|
+
|
|
585
|
+
for (const cmd of invalidCommands) {
|
|
586
|
+
const result = runInstall(`install "${cmd}"`);
|
|
587
|
+
assert.equal(result.status, 'error', `Should error for invalid command: ${cmd}`);
|
|
588
|
+
assert.ok(result.message, 'Should provide error message');
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('maintains cache consistency across install workflow', () => {
|
|
593
|
+
const mockDir = createMockNpx('exit 0');
|
|
594
|
+
const mockHome = createMockSkillsDir();
|
|
595
|
+
const cacheDir = path.join(PROJECT_ROOT, '.cache');
|
|
596
|
+
const cacheFile = path.join(cacheDir, 'skill-discovery-cache.json');
|
|
597
|
+
|
|
598
|
+
// Save original cache state
|
|
599
|
+
const originalContent = fs.existsSync(cacheFile)
|
|
600
|
+
? fs.readFileSync(cacheFile, 'utf8')
|
|
601
|
+
: null;
|
|
602
|
+
|
|
603
|
+
try {
|
|
604
|
+
// Create cache
|
|
605
|
+
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
606
|
+
fs.writeFileSync(cacheFile, '{"cached":"before-install"}');
|
|
607
|
+
assert.equal(fs.existsSync(cacheFile), true, 'Cache should exist before install');
|
|
608
|
+
|
|
609
|
+
// Install should invalidate cache
|
|
610
|
+
runInstall(
|
|
611
|
+
'install "npx -y skills add vercel-labs/agent-skills --skill cache-workflow -g -a claude-code"',
|
|
612
|
+
{ env: { PATH: `${mockDir}:${process.env.PATH}`, HOME: mockHome } }
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
assert.equal(fs.existsSync(cacheFile), false, 'Cache should be invalidated after install');
|
|
616
|
+
} finally {
|
|
617
|
+
cleanup(mockDir);
|
|
618
|
+
cleanup(mockHome);
|
|
619
|
+
// Restore original cache state
|
|
620
|
+
if (originalContent !== null) {
|
|
621
|
+
fs.writeFileSync(cacheFile, originalContent);
|
|
622
|
+
} else if (fs.existsSync(cacheFile)) {
|
|
623
|
+
fs.unlinkSync(cacheFile);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
});
|
|
374
628
|
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { describe, it } = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const { resolveExpression } = require('../src/template');
|
|
6
|
+
|
|
7
|
+
describe('hasCriticalIssues validation', () => {
|
|
8
|
+
it('detects "critical" pattern', () => {
|
|
9
|
+
const ctx = {
|
|
10
|
+
review: {
|
|
11
|
+
issues: [{ severity: 'critical', msg: 'bug found' }]
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
15
|
+
assert.equal(result, true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('detects "blocking" pattern', () => {
|
|
19
|
+
const ctx = {
|
|
20
|
+
review: {
|
|
21
|
+
issues: [{ priority: 'blocking', msg: 'deployment blocked' }]
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
25
|
+
assert.equal(result, true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('detects "P0" pattern', () => {
|
|
29
|
+
const ctx = {
|
|
30
|
+
review: {
|
|
31
|
+
issues: [{ priority: 'P0', msg: 'critical bug' }]
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
35
|
+
assert.equal(result, true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('detects "MUST_FIX" pattern', () => {
|
|
39
|
+
const ctx = {
|
|
40
|
+
review: {
|
|
41
|
+
issues: [{ status: 'MUST_FIX', msg: 'security vulnerability' }]
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
45
|
+
assert.equal(result, true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('detects "URGENT" pattern', () => {
|
|
49
|
+
const ctx = {
|
|
50
|
+
review: {
|
|
51
|
+
issues: [{ severity: 'URGENT', msg: 'production down' }]
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
55
|
+
assert.equal(result, true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('returns false when no critical patterns found', () => {
|
|
59
|
+
const ctx = {
|
|
60
|
+
review: {
|
|
61
|
+
issues: [{ severity: 'low', msg: 'minor issue' }]
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
65
|
+
assert.equal(result, false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns false for empty object', () => {
|
|
69
|
+
const ctx = {
|
|
70
|
+
review: {}
|
|
71
|
+
};
|
|
72
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
73
|
+
assert.equal(result, false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('returns false for null/undefined', () => {
|
|
77
|
+
const ctx = {
|
|
78
|
+
review: null
|
|
79
|
+
};
|
|
80
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
81
|
+
assert.equal(result, false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('detects multiple critical patterns in same object', () => {
|
|
85
|
+
const ctx = {
|
|
86
|
+
review: {
|
|
87
|
+
issues: [
|
|
88
|
+
{ severity: 'critical', msg: 'bug 1' },
|
|
89
|
+
{ priority: 'P0', msg: 'bug 2' },
|
|
90
|
+
{ status: 'MUST_FIX', msg: 'bug 3' }
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
95
|
+
assert.equal(result, true);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('works with deeply nested critical patterns', () => {
|
|
99
|
+
const ctx = {
|
|
100
|
+
review: {
|
|
101
|
+
results: {
|
|
102
|
+
branches: {
|
|
103
|
+
security: {
|
|
104
|
+
findings: [{ level: 'critical', detail: 'SQL injection' }]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const result = resolveExpression('hasCriticalIssues(review)', ctx);
|
|
111
|
+
assert.equal(result, true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('works with string input instead of object', () => {
|
|
115
|
+
const ctx = {
|
|
116
|
+
report: '{"status":"P0","message":"critical error"}'
|
|
117
|
+
};
|
|
118
|
+
const result = resolveExpression('hasCriticalIssues(report)', ctx);
|
|
119
|
+
assert.equal(result, true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('detects patterns in array of strings', () => {
|
|
123
|
+
const ctx = {
|
|
124
|
+
tags: ['urgent', 'needs-review', 'URGENT']
|
|
125
|
+
};
|
|
126
|
+
const result = resolveExpression('hasCriticalIssues(tags)', ctx);
|
|
127
|
+
assert.equal(result, true);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('returns false for safe array of strings', () => {
|
|
131
|
+
const ctx = {
|
|
132
|
+
tags: ['low-priority', 'needs-review', 'documentation']
|
|
133
|
+
};
|
|
134
|
+
const result = resolveExpression('hasCriticalIssues(tags)', ctx);
|
|
135
|
+
assert.equal(result, false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
|
|
14
14
|
"sync_metadata": {
|
|
15
|
-
"last_audit": "2026-02-
|
|
15
|
+
"last_audit": "2026-02-09",
|
|
16
16
|
"audit_result": {
|
|
17
17
|
"total_nodes": 44,
|
|
18
18
|
"implemented": 42,
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"deprecated": 2,
|
|
22
22
|
"implementation_rate": "95%"
|
|
23
23
|
},
|
|
24
|
-
"note": "M5-
|
|
24
|
+
"note": "M5-P3: Fixed teams profile to bypass deprecated nodes. interview_teams now routes to standard_analyze instead of teams_spawn. Agent Teams enforced inline via callbacks.js at parallel nodes. 2 deprecated nodes (teams_spawn, interrupt_teams_failed) kept for test coverage but unreachable in normal flow."
|
|
25
25
|
},
|
|
26
26
|
|
|
27
27
|
"inputs": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"id": "start",
|
|
45
45
|
"type": "entry",
|
|
46
46
|
"runtime_status": "implemented",
|
|
47
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
47
|
+
"implemented_in": "commands/elsabro/execute.md#1-inicializar",
|
|
48
48
|
"next": "skill_discovery"
|
|
49
49
|
},
|
|
50
50
|
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"type": "sequence",
|
|
54
54
|
"description": "Descubrir skills necesarios antes de cargar contexto",
|
|
55
55
|
"runtime_status": "implemented",
|
|
56
|
-
"implemented_in": "
|
|
56
|
+
"implemented_in": "flow-engine/src/cli.js (sequence executor)",
|
|
57
57
|
"errorPolicy": "continue",
|
|
58
58
|
"steps": [
|
|
59
59
|
{
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"type": "sequence",
|
|
78
78
|
"description": "Cargar contexto del proyecto",
|
|
79
79
|
"runtime_status": "implemented",
|
|
80
|
-
"implemented_in": "
|
|
80
|
+
"implemented_in": "flow-engine/src/cli.js (sequence executor)",
|
|
81
81
|
"steps": [
|
|
82
82
|
{
|
|
83
83
|
"action": "read_files",
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
"task": "{{inputs.task}}",
|
|
244
244
|
"topics": "prioridades del equipo y qué entregar primero, deadline y nivel de calidad esperado, áreas que necesitan más atención (frontend/backend/testing), integraciones críticas que no pueden fallar"
|
|
245
245
|
},
|
|
246
|
-
"next": "
|
|
246
|
+
"next": "standard_analyze"
|
|
247
247
|
},
|
|
248
248
|
|
|
249
249
|
{
|
|
@@ -577,7 +577,7 @@
|
|
|
577
577
|
"type": "parallel",
|
|
578
578
|
"description": "Análisis paralelo estándar (4 agentes)",
|
|
579
579
|
"runtime_status": "implemented",
|
|
580
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
580
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: parallel, haiku subagents)",
|
|
581
581
|
"branches": [
|
|
582
582
|
{
|
|
583
583
|
"id": "explore",
|
|
@@ -614,7 +614,7 @@
|
|
|
614
614
|
"type": "agent",
|
|
615
615
|
"description": "Consolidar análisis de los 4 agentes",
|
|
616
616
|
"runtime_status": "implemented",
|
|
617
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
617
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: agent)",
|
|
618
618
|
"agent": "elsabro-analyst",
|
|
619
619
|
"config": { "model": "opus" },
|
|
620
620
|
"inputs": {
|
|
@@ -633,7 +633,7 @@
|
|
|
633
633
|
"type": "parallel",
|
|
634
634
|
"description": "Implementación paralela (2 agentes opus)",
|
|
635
635
|
"runtime_status": "implemented",
|
|
636
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
636
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: parallel, Agent Teams via callbacks.js)",
|
|
637
637
|
"branches": [
|
|
638
638
|
{
|
|
639
639
|
"id": "implementation",
|
|
@@ -666,7 +666,7 @@
|
|
|
666
666
|
"type": "sequence",
|
|
667
667
|
"description": "Quality gate: tests, types, lint",
|
|
668
668
|
"runtime_status": "implemented",
|
|
669
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
669
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: sequence)",
|
|
670
670
|
"captureExitCode": true,
|
|
671
671
|
"steps": [
|
|
672
672
|
{
|
|
@@ -699,7 +699,7 @@
|
|
|
699
699
|
"type": "condition",
|
|
700
700
|
"description": "Verificar si pasó quality gate (tests + typescript + lint)",
|
|
701
701
|
"runtime_status": "implemented",
|
|
702
|
-
"implemented_in": "
|
|
702
|
+
"implemented_in": "flow-engine/src/cli.js (condition auto-resolve)",
|
|
703
703
|
"condition": "{{nodes.quality_gate.outputs.tests.exitCode === 0 && nodes.quality_gate.outputs.typescript.exitCode === 0 && nodes.quality_gate.outputs.lint.exitCode === 0}}",
|
|
704
704
|
"true": "parallel_review",
|
|
705
705
|
"false": "fix_issues"
|
|
@@ -710,7 +710,7 @@
|
|
|
710
710
|
"type": "parallel",
|
|
711
711
|
"description": "Corregir problemas detectados (3 agentes)",
|
|
712
712
|
"runtime_status": "implemented",
|
|
713
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
713
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: parallel, Agent Teams via callbacks.js)",
|
|
714
714
|
"branches": [
|
|
715
715
|
{
|
|
716
716
|
"id": "debugger",
|
|
@@ -742,7 +742,7 @@
|
|
|
742
742
|
"type": "interrupt",
|
|
743
743
|
"description": "Auto-fix failed after 3 attempts",
|
|
744
744
|
"runtime_status": "implemented",
|
|
745
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
745
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: interrupt)",
|
|
746
746
|
"gaps": [],
|
|
747
747
|
"reason": "Auto-fix failed after 3 attempts",
|
|
748
748
|
"display": {
|
|
@@ -765,7 +765,7 @@
|
|
|
765
765
|
"type": "interrupt",
|
|
766
766
|
"description": "Waiting for manual fix",
|
|
767
767
|
"runtime_status": "implemented",
|
|
768
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
768
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: interrupt)",
|
|
769
769
|
"gaps": [],
|
|
770
770
|
"reason": "Waiting for manual fix",
|
|
771
771
|
"display": {
|
|
@@ -785,7 +785,7 @@
|
|
|
785
785
|
"type": "parallel",
|
|
786
786
|
"description": "Code review paralelo (3 agentes opus)",
|
|
787
787
|
"runtime_status": "implemented",
|
|
788
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
788
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: parallel, Agent Teams via callbacks.js)",
|
|
789
789
|
"branches": [
|
|
790
790
|
{
|
|
791
791
|
"id": "code_reviewer",
|
|
@@ -825,7 +825,7 @@
|
|
|
825
825
|
"type": "condition",
|
|
826
826
|
"description": "Verificar si hay issues críticos en review",
|
|
827
827
|
"runtime_status": "implemented",
|
|
828
|
-
"implemented_in": "
|
|
828
|
+
"implemented_in": "flow-engine/src/cli.js (condition auto-resolve)",
|
|
829
829
|
"condition": "{{!hasCriticalIssues(nodes.parallel_review.outputs)}}",
|
|
830
830
|
"true": "verify_final",
|
|
831
831
|
"false": "fix_review_issues"
|
|
@@ -836,7 +836,7 @@
|
|
|
836
836
|
"type": "agent",
|
|
837
837
|
"description": "Corregir issues del code review - BLOCKING: no avanza hasta 0 errores",
|
|
838
838
|
"runtime_status": "implemented",
|
|
839
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
839
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: agent)",
|
|
840
840
|
"agent": "elsabro-executor",
|
|
841
841
|
"config": { "model": "opus" },
|
|
842
842
|
"inputs": {
|
|
@@ -855,7 +855,7 @@
|
|
|
855
855
|
"type": "interrupt",
|
|
856
856
|
"description": "Code review still has issues after 5 fix iterations - BLOCKED",
|
|
857
857
|
"runtime_status": "implemented",
|
|
858
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
858
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: interrupt)",
|
|
859
859
|
"reason": "Code review still has issues after 5 fix iterations - BLOCKED",
|
|
860
860
|
"display": {
|
|
861
861
|
"title": "🚫 BLOQUEADO: Code Review con Errores Pendientes",
|
|
@@ -880,7 +880,7 @@
|
|
|
880
880
|
"type": "interrupt",
|
|
881
881
|
"description": "Waiting for manual fix of review issues",
|
|
882
882
|
"runtime_status": "implemented",
|
|
883
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
883
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: interrupt)",
|
|
884
884
|
"reason": "Waiting for manual fix of review issues",
|
|
885
885
|
"display": {
|
|
886
886
|
"title": "👤 Esperando Corrección Manual de Review Issues",
|
|
@@ -911,7 +911,7 @@
|
|
|
911
911
|
"type": "condition",
|
|
912
912
|
"description": "Gate check on verification result",
|
|
913
913
|
"runtime_status": "implemented",
|
|
914
|
-
"implemented_in": "
|
|
914
|
+
"implemented_in": "flow-engine/src/cli.js (condition auto-resolve)",
|
|
915
915
|
"condition": "{{nodes.verify_final.outputs.output.passed || nodes.quick_verify.outputs.output.passed}}",
|
|
916
916
|
"true": "post_mortem",
|
|
917
917
|
"false": "interrupt_verification_failed"
|
|
@@ -922,7 +922,7 @@
|
|
|
922
922
|
"type": "interrupt",
|
|
923
923
|
"description": "Final verification failed",
|
|
924
924
|
"runtime_status": "implemented",
|
|
925
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
925
|
+
"implemented_in": "commands/elsabro/execute.md#3-dispatch (type: interrupt)",
|
|
926
926
|
"reason": "Final verification failed",
|
|
927
927
|
"display": {
|
|
928
928
|
"title": "❌ Verificación Final Fallida",
|
|
@@ -966,7 +966,7 @@
|
|
|
966
966
|
"type": "exit",
|
|
967
967
|
"description": "Success exit",
|
|
968
968
|
"runtime_status": "implemented",
|
|
969
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
969
|
+
"implemented_in": "commands/elsabro/execute.md#5-finalizar + #6-siguiente-paso",
|
|
970
970
|
"status": "success",
|
|
971
971
|
"outputs": {
|
|
972
972
|
"filesCreated": "{{collectOutputs('filesCreated')}}",
|
|
@@ -1022,7 +1022,7 @@
|
|
|
1022
1022
|
"type": "exit",
|
|
1023
1023
|
"description": "Cancellation exit",
|
|
1024
1024
|
"runtime_status": "implemented",
|
|
1025
|
-
"implemented_in": "commands/elsabro/execute.md#
|
|
1025
|
+
"implemented_in": "commands/elsabro/execute.md#2-loop-principal (error handling)",
|
|
1026
1026
|
"status": "cancelled",
|
|
1027
1027
|
"outputs": {
|
|
1028
1028
|
"success": false,
|
package/hooks/skill-discovery.sh
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elsabro",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.1",
|
|
4
4
|
"description": "Sistema de desarrollo AI-powered para Claude Code - BMAD Method Integration, Spec-Driven Development, Party Mode, Next Step Suggestions, Stitch UI Design, Agent Teams, blocking code review, orquestación avanzada con flows declarativos",
|
|
5
5
|
"bin": {
|
|
6
6
|
"elsabro": "bin/install.js",
|
|
@@ -501,7 +501,7 @@ Para verificar que el sistema está correctamente configurado:
|
|
|
501
501
|
### Comandos Core
|
|
502
502
|
- [ ] `start.md` tiene sección `<enforcement>` y `<state_sync>`
|
|
503
503
|
- [ ] `plan.md` tiene sección `<state_sync>` y `dispatcher` en frontmatter
|
|
504
|
-
- [ ] `execute.md` tiene
|
|
504
|
+
- [ ] `execute.md` tiene sync config en frontmatter y referencia a @references/state-sync.md
|
|
505
505
|
- [ ] `verify-work.md` tiene sección `<state_sync>` y `dispatcher` en frontmatter
|
|
506
506
|
|
|
507
507
|
### Referencias
|
|
@@ -336,21 +336,13 @@ New "teams" profile added:
|
|
|
336
336
|
}
|
|
337
337
|
```
|
|
338
338
|
|
|
339
|
-
### execute.md
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
method: "agent-team" # v4.2.0: always agent-team for 2+ agents (Rule 8)
|
|
347
|
-
agent_team_config:
|
|
348
|
-
team_members: [elsabro-executor, elsabro-qa, elsabro-planner]
|
|
349
|
-
when: "always" # v4.2.0: mandatory, no longer conditional
|
|
350
|
-
verification:
|
|
351
|
-
blocking: true
|
|
352
|
-
max_review_iterations: 5
|
|
353
|
-
```
|
|
339
|
+
### execute.md Dispatch (v7.3.0+)
|
|
340
|
+
|
|
341
|
+
execute.md is a thin CLI wrapper. Agent Teams dispatch is in section `#3-dispatch (type: parallel)`.
|
|
342
|
+
The CLI flow engine + `callbacks.js` (`requiresTeam()` / `composeTeam()`) decide automatically
|
|
343
|
+
whether to use Agent Teams (2+ non-haiku branches) or loose subagents (all-haiku).
|
|
344
|
+
|
|
345
|
+
See: `commands/elsabro/execute.md#3-dispatch`
|
|
354
346
|
|
|
355
347
|
## Changelog (v4.0.0)
|
|
356
348
|
|