opencode-swarm-plugin 0.13.2 → 0.15.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.
- package/.beads/analysis/skill-architecture-meta-skills.md +1562 -0
- package/.beads/issues.jsonl +73 -0
- package/README.md +20 -18
- package/VERIFICATION_QUALITY_PATTERNS.md +565 -0
- package/bin/swarm.ts +5 -5
- package/dist/index.js +425 -14
- package/dist/plugin.js +426 -27
- package/docs/analysis/subagent-coordination-patterns.md +900 -0
- package/docs/analysis-socratic-planner-pattern.md +504 -0
- package/examples/commands/swarm.md +69 -7
- package/examples/plugin-wrapper-template.ts +2 -145
- package/global-skills/swarm-coordination/SKILL.md +70 -20
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +1 -1
- package/package.json +1 -1
- package/src/index.ts +0 -2
- package/src/learning.integration.test.ts +310 -0
- package/src/learning.ts +198 -0
- package/src/skills.test.ts +194 -0
- package/src/skills.ts +184 -15
- package/src/swarm.integration.test.ts +4 -4
- package/src/swarm.ts +496 -19
- package/workflow-integration-analysis.md +876 -0
package/dist/plugin.js
CHANGED
|
@@ -28536,6 +28536,7 @@ var require_gray_matter = __commonJS((exports, module) => {
|
|
|
28536
28536
|
// src/skills.ts
|
|
28537
28537
|
var exports_skills = {};
|
|
28538
28538
|
__export(exports_skills, {
|
|
28539
|
+
validateCSOCompliance: () => validateCSOCompliance,
|
|
28539
28540
|
skills_use: () => skills_use,
|
|
28540
28541
|
skills_update: () => skills_update,
|
|
28541
28542
|
skills_read: () => skills_read,
|
|
@@ -28708,6 +28709,90 @@ async function listSkills() {
|
|
|
28708
28709
|
function invalidateSkillsCache() {
|
|
28709
28710
|
skillsCache = null;
|
|
28710
28711
|
}
|
|
28712
|
+
function validateCSOCompliance(name, description) {
|
|
28713
|
+
const warnings = {
|
|
28714
|
+
critical: [],
|
|
28715
|
+
suggestions: []
|
|
28716
|
+
};
|
|
28717
|
+
const hasUseWhen = /\buse when\b/i.test(description);
|
|
28718
|
+
if (!hasUseWhen) {
|
|
28719
|
+
warnings.critical.push("Description should include 'Use when...' to focus on triggering conditions");
|
|
28720
|
+
}
|
|
28721
|
+
if (description.length > 1024) {
|
|
28722
|
+
warnings.critical.push(`Description is ${description.length} chars (max 1024) - will be rejected`);
|
|
28723
|
+
} else if (description.length > 500) {
|
|
28724
|
+
warnings.suggestions.push(`Description is ${description.length} chars (aim for <500 for optimal discoverability)`);
|
|
28725
|
+
}
|
|
28726
|
+
const firstPersonPattern = /\b(I|I'm|I'll|my|mine|myself)\b/i;
|
|
28727
|
+
const secondPersonPattern = /\b(you|you're|you'll|your|yours|yourself)\b/i;
|
|
28728
|
+
if (firstPersonPattern.test(description)) {
|
|
28729
|
+
warnings.critical.push("Description uses first-person ('I', 'my') - skills are injected into system prompt, use third-person only");
|
|
28730
|
+
}
|
|
28731
|
+
if (secondPersonPattern.test(description)) {
|
|
28732
|
+
warnings.critical.push("Description uses second-person ('you', 'your') - use third-person voice (e.g., 'Handles X' not 'You can handle X')");
|
|
28733
|
+
}
|
|
28734
|
+
const nameWords = name.split("-");
|
|
28735
|
+
const firstWord = nameWords[0];
|
|
28736
|
+
const isGerund = /ing$/.test(firstWord);
|
|
28737
|
+
const isVerbForm = /(ing|ize|ify|ate)$/.test(firstWord);
|
|
28738
|
+
if (!isGerund && !isVerbForm) {
|
|
28739
|
+
const actionVerbs = [
|
|
28740
|
+
"test",
|
|
28741
|
+
"debug",
|
|
28742
|
+
"fix",
|
|
28743
|
+
"scan",
|
|
28744
|
+
"check",
|
|
28745
|
+
"validate",
|
|
28746
|
+
"create",
|
|
28747
|
+
"build",
|
|
28748
|
+
"deploy",
|
|
28749
|
+
"run",
|
|
28750
|
+
"load",
|
|
28751
|
+
"fetch",
|
|
28752
|
+
"parse"
|
|
28753
|
+
];
|
|
28754
|
+
const startsWithAction = actionVerbs.includes(firstWord);
|
|
28755
|
+
if (!startsWithAction) {
|
|
28756
|
+
warnings.suggestions.push(`Name '${name}' doesn't follow verb-first pattern. Consider gerunds (e.g., 'testing-skills' not 'test-skill') or action verbs for better clarity`);
|
|
28757
|
+
}
|
|
28758
|
+
}
|
|
28759
|
+
if (name.length > 64) {
|
|
28760
|
+
warnings.critical.push(`Name exceeds 64 character limit (${name.length} chars)`);
|
|
28761
|
+
}
|
|
28762
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
28763
|
+
warnings.critical.push("Name must be lowercase letters, numbers, and hyphens only");
|
|
28764
|
+
}
|
|
28765
|
+
return warnings;
|
|
28766
|
+
}
|
|
28767
|
+
function formatCSOWarnings(warnings) {
|
|
28768
|
+
if (warnings.critical.length === 0 && warnings.suggestions.length === 0) {
|
|
28769
|
+
return null;
|
|
28770
|
+
}
|
|
28771
|
+
const parts = [];
|
|
28772
|
+
if (warnings.critical.length > 0) {
|
|
28773
|
+
parts.push("**CSO Critical Issues:**");
|
|
28774
|
+
for (const warning of warnings.critical) {
|
|
28775
|
+
parts.push(` ⚠️ ${warning}`);
|
|
28776
|
+
}
|
|
28777
|
+
}
|
|
28778
|
+
if (warnings.suggestions.length > 0) {
|
|
28779
|
+
parts.push(`
|
|
28780
|
+
**CSO Suggestions:**`);
|
|
28781
|
+
for (const suggestion of warnings.suggestions) {
|
|
28782
|
+
parts.push(` \uD83D\uDCA1 ${suggestion}`);
|
|
28783
|
+
}
|
|
28784
|
+
}
|
|
28785
|
+
parts.push(`
|
|
28786
|
+
**CSO Guide:**`);
|
|
28787
|
+
parts.push(" • Start description with 'Use when...' (focus on triggering conditions)");
|
|
28788
|
+
parts.push(" • Keep description <500 chars (max 1024)");
|
|
28789
|
+
parts.push(" • Use third-person voice only (injected into system prompt)");
|
|
28790
|
+
parts.push(" • Name: verb-first or gerunds (e.g., 'testing-async' not 'async-test')");
|
|
28791
|
+
parts.push(`
|
|
28792
|
+
Example: 'Use when tests have race conditions - replaces arbitrary timeouts with condition polling'`);
|
|
28793
|
+
return parts.join(`
|
|
28794
|
+
`);
|
|
28795
|
+
}
|
|
28711
28796
|
function quoteYamlScalar(value) {
|
|
28712
28797
|
const needsQuoting = /[:\n\r#"'`\[\]{}|>&*!?@]/.test(value) || value.startsWith(" ") || value.endsWith(" ") || value === "" || /^[0-9]/.test(value) || ["true", "false", "null", "yes", "no", "on", "off"].includes(value.toLowerCase());
|
|
28713
28798
|
if (!needsQuoting) {
|
|
@@ -29059,6 +29144,7 @@ Good skills have:
|
|
|
29059
29144
|
if (existing) {
|
|
29060
29145
|
return `Skill '${args.name}' already exists at ${existing.path}. Use skills_update to modify it.`;
|
|
29061
29146
|
}
|
|
29147
|
+
const csoWarnings = validateCSOCompliance(args.name, args.description);
|
|
29062
29148
|
let skillDir;
|
|
29063
29149
|
if (args.directory === "global") {
|
|
29064
29150
|
skillDir = join5(getGlobalSkillsDir(), args.name);
|
|
@@ -29074,7 +29160,7 @@ Good skills have:
|
|
|
29074
29160
|
const content = generateSkillContent(args.name, args.description, args.body, { tags: args.tags, tools: args.tools });
|
|
29075
29161
|
await writeFile(skillPath, content, "utf-8");
|
|
29076
29162
|
invalidateSkillsCache();
|
|
29077
|
-
|
|
29163
|
+
const response = {
|
|
29078
29164
|
success: true,
|
|
29079
29165
|
skill: args.name,
|
|
29080
29166
|
path: skillPath,
|
|
@@ -29084,7 +29170,12 @@ Good skills have:
|
|
|
29084
29170
|
"Add examples.md or reference.md for supplementary content",
|
|
29085
29171
|
"Add scripts/ directory for executable helpers"
|
|
29086
29172
|
]
|
|
29087
|
-
}
|
|
29173
|
+
};
|
|
29174
|
+
const warningsMessage = formatCSOWarnings(csoWarnings);
|
|
29175
|
+
if (warningsMessage) {
|
|
29176
|
+
response.cso_warnings = warningsMessage;
|
|
29177
|
+
}
|
|
29178
|
+
return JSON.stringify(response, null, 2);
|
|
29088
29179
|
} catch (error45) {
|
|
29089
29180
|
return `Failed to create skill: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
29090
29181
|
}
|
|
@@ -31340,19 +31431,6 @@ var agentmail_restart = tool({
|
|
|
31340
31431
|
}, null, 2);
|
|
31341
31432
|
}
|
|
31342
31433
|
});
|
|
31343
|
-
var agentMailTools = {
|
|
31344
|
-
agentmail_init,
|
|
31345
|
-
agentmail_send,
|
|
31346
|
-
agentmail_inbox,
|
|
31347
|
-
agentmail_read_message,
|
|
31348
|
-
agentmail_summarize_thread,
|
|
31349
|
-
agentmail_reserve,
|
|
31350
|
-
agentmail_release,
|
|
31351
|
-
agentmail_ack,
|
|
31352
|
-
agentmail_search,
|
|
31353
|
-
agentmail_health,
|
|
31354
|
-
agentmail_restart
|
|
31355
|
-
};
|
|
31356
31434
|
|
|
31357
31435
|
// src/swarm-mail.ts
|
|
31358
31436
|
init_dist();
|
|
@@ -32242,6 +32320,90 @@ function outcomeToFeedback(outcome, criterion) {
|
|
|
32242
32320
|
raw_value: outcome.decayed_value
|
|
32243
32321
|
};
|
|
32244
32322
|
}
|
|
32323
|
+
var StrikeRecordSchema = exports_external.object({
|
|
32324
|
+
bead_id: exports_external.string(),
|
|
32325
|
+
strike_count: exports_external.number().int().min(0).max(3),
|
|
32326
|
+
failures: exports_external.array(exports_external.object({
|
|
32327
|
+
attempt: exports_external.string(),
|
|
32328
|
+
reason: exports_external.string(),
|
|
32329
|
+
timestamp: exports_external.string()
|
|
32330
|
+
})),
|
|
32331
|
+
first_strike_at: exports_external.string().optional(),
|
|
32332
|
+
last_strike_at: exports_external.string().optional()
|
|
32333
|
+
});
|
|
32334
|
+
|
|
32335
|
+
class InMemoryStrikeStorage {
|
|
32336
|
+
strikes = new Map;
|
|
32337
|
+
async store(record2) {
|
|
32338
|
+
this.strikes.set(record2.bead_id, record2);
|
|
32339
|
+
}
|
|
32340
|
+
async get(beadId) {
|
|
32341
|
+
return this.strikes.get(beadId) ?? null;
|
|
32342
|
+
}
|
|
32343
|
+
async getAll() {
|
|
32344
|
+
return Array.from(this.strikes.values());
|
|
32345
|
+
}
|
|
32346
|
+
async clear(beadId) {
|
|
32347
|
+
this.strikes.delete(beadId);
|
|
32348
|
+
}
|
|
32349
|
+
}
|
|
32350
|
+
async function addStrike(beadId, attempt, reason, storage = new InMemoryStrikeStorage) {
|
|
32351
|
+
const existing = await storage.get(beadId);
|
|
32352
|
+
const now = new Date().toISOString();
|
|
32353
|
+
const record2 = existing ?? {
|
|
32354
|
+
bead_id: beadId,
|
|
32355
|
+
strike_count: 0,
|
|
32356
|
+
failures: []
|
|
32357
|
+
};
|
|
32358
|
+
record2.strike_count = Math.min(3, record2.strike_count + 1);
|
|
32359
|
+
record2.failures.push({ attempt, reason, timestamp: now });
|
|
32360
|
+
record2.last_strike_at = now;
|
|
32361
|
+
if (!record2.first_strike_at) {
|
|
32362
|
+
record2.first_strike_at = now;
|
|
32363
|
+
}
|
|
32364
|
+
await storage.store(record2);
|
|
32365
|
+
return record2;
|
|
32366
|
+
}
|
|
32367
|
+
async function getStrikes(beadId, storage = new InMemoryStrikeStorage) {
|
|
32368
|
+
const record2 = await storage.get(beadId);
|
|
32369
|
+
return record2?.strike_count ?? 0;
|
|
32370
|
+
}
|
|
32371
|
+
async function isStrikedOut(beadId, storage = new InMemoryStrikeStorage) {
|
|
32372
|
+
const count = await getStrikes(beadId, storage);
|
|
32373
|
+
return count >= 3;
|
|
32374
|
+
}
|
|
32375
|
+
async function getArchitecturePrompt(beadId, storage = new InMemoryStrikeStorage) {
|
|
32376
|
+
const record2 = await storage.get(beadId);
|
|
32377
|
+
if (!record2 || record2.strike_count < 3) {
|
|
32378
|
+
return "";
|
|
32379
|
+
}
|
|
32380
|
+
const failuresList = record2.failures.map((f, i) => `${i + 1}. **${f.attempt}** - Failed: ${f.reason}`).join(`
|
|
32381
|
+
`);
|
|
32382
|
+
return `## Architecture Review Required
|
|
32383
|
+
|
|
32384
|
+
This bead (\`${beadId}\`) has failed 3 consecutive fix attempts:
|
|
32385
|
+
|
|
32386
|
+
${failuresList}
|
|
32387
|
+
|
|
32388
|
+
This pattern suggests an **architectural problem**, not a bug.
|
|
32389
|
+
|
|
32390
|
+
**Questions to consider:**
|
|
32391
|
+
- Is the current approach fundamentally sound?
|
|
32392
|
+
- Should we refactor the architecture instead?
|
|
32393
|
+
- Are we fixing symptoms instead of root cause?
|
|
32394
|
+
|
|
32395
|
+
**Options:**
|
|
32396
|
+
1. **Refactor architecture** (describe new approach)
|
|
32397
|
+
2. **Continue with Fix #4** (explain why this time is different)
|
|
32398
|
+
3. **Abandon this approach entirely**
|
|
32399
|
+
|
|
32400
|
+
**DO NOT attempt Fix #4 without answering these questions.**
|
|
32401
|
+
`;
|
|
32402
|
+
}
|
|
32403
|
+
async function clearStrikes(beadId, storage = new InMemoryStrikeStorage) {
|
|
32404
|
+
await storage.clear(beadId);
|
|
32405
|
+
}
|
|
32406
|
+
|
|
32245
32407
|
class InMemoryErrorStorage {
|
|
32246
32408
|
errors = [];
|
|
32247
32409
|
async store(entry) {
|
|
@@ -32760,7 +32922,7 @@ You MUST keep your bead updated as you work:
|
|
|
32760
32922
|
|
|
32761
32923
|
**Never work silently.** Your bead status is how the swarm tracks progress.
|
|
32762
32924
|
|
|
32763
|
-
## MANDATORY:
|
|
32925
|
+
## MANDATORY: Swarm Mail Communication
|
|
32764
32926
|
|
|
32765
32927
|
You MUST communicate with other agents:
|
|
32766
32928
|
|
|
@@ -32769,9 +32931,9 @@ You MUST communicate with other agents:
|
|
|
32769
32931
|
3. **Announce blockers** immediately - don't spin trying to fix alone
|
|
32770
32932
|
4. **Coordinate on shared concerns** - if you see something affecting other agents, say so
|
|
32771
32933
|
|
|
32772
|
-
Use
|
|
32934
|
+
Use Swarm Mail for all communication:
|
|
32773
32935
|
\`\`\`
|
|
32774
|
-
|
|
32936
|
+
swarmmail_send(
|
|
32775
32937
|
to: ["coordinator" or specific agent],
|
|
32776
32938
|
subject: "Brief subject",
|
|
32777
32939
|
body: "Message content",
|
|
@@ -32783,7 +32945,7 @@ agentmail_send(
|
|
|
32783
32945
|
|
|
32784
32946
|
1. **Start**: Your bead is already marked in_progress
|
|
32785
32947
|
2. **Progress**: Use swarm_progress to report status updates
|
|
32786
|
-
3. **Blocked**: Report immediately via
|
|
32948
|
+
3. **Blocked**: Report immediately via Swarm Mail - don't spin
|
|
32787
32949
|
4. **Complete**: Use swarm_complete when done - it handles:
|
|
32788
32950
|
- Closing your bead with a summary
|
|
32789
32951
|
- Releasing file reservations
|
|
@@ -32805,7 +32967,7 @@ Before writing code:
|
|
|
32805
32967
|
1. **Read the files** you're assigned to understand current state
|
|
32806
32968
|
2. **Plan your approach** - what changes, in what order?
|
|
32807
32969
|
3. **Identify risks** - what could go wrong? What dependencies?
|
|
32808
|
-
4. **Communicate your plan** via
|
|
32970
|
+
4. **Communicate your plan** via Swarm Mail if non-trivial
|
|
32809
32971
|
|
|
32810
32972
|
Begin work on your subtask now.`;
|
|
32811
32973
|
var SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
|
|
@@ -33508,6 +33670,139 @@ var swarm_progress = tool({
|
|
|
33508
33670
|
return `Progress reported: ${args.status}${args.progress_percent !== undefined ? ` (${args.progress_percent}%)` : ""}`;
|
|
33509
33671
|
}
|
|
33510
33672
|
});
|
|
33673
|
+
async function runTypecheckVerification() {
|
|
33674
|
+
const step = {
|
|
33675
|
+
name: "typecheck",
|
|
33676
|
+
command: "tsc --noEmit",
|
|
33677
|
+
passed: false,
|
|
33678
|
+
exitCode: -1
|
|
33679
|
+
};
|
|
33680
|
+
try {
|
|
33681
|
+
const tsconfigExists = await Bun.file("tsconfig.json").exists();
|
|
33682
|
+
if (!tsconfigExists) {
|
|
33683
|
+
step.skipped = true;
|
|
33684
|
+
step.skipReason = "No tsconfig.json found";
|
|
33685
|
+
step.passed = true;
|
|
33686
|
+
return step;
|
|
33687
|
+
}
|
|
33688
|
+
const result = await Bun.$`tsc --noEmit`.quiet().nothrow();
|
|
33689
|
+
step.exitCode = result.exitCode;
|
|
33690
|
+
step.passed = result.exitCode === 0;
|
|
33691
|
+
if (!step.passed) {
|
|
33692
|
+
step.error = result.stderr.toString().slice(0, 1000);
|
|
33693
|
+
step.output = result.stdout.toString().slice(0, 1000);
|
|
33694
|
+
}
|
|
33695
|
+
} catch (error45) {
|
|
33696
|
+
step.skipped = true;
|
|
33697
|
+
step.skipReason = `tsc not available: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
33698
|
+
step.passed = true;
|
|
33699
|
+
}
|
|
33700
|
+
return step;
|
|
33701
|
+
}
|
|
33702
|
+
async function runTestVerification(filesTouched) {
|
|
33703
|
+
const step = {
|
|
33704
|
+
name: "tests",
|
|
33705
|
+
command: "bun test <related-files>",
|
|
33706
|
+
passed: false,
|
|
33707
|
+
exitCode: -1
|
|
33708
|
+
};
|
|
33709
|
+
if (filesTouched.length === 0) {
|
|
33710
|
+
step.skipped = true;
|
|
33711
|
+
step.skipReason = "No files touched";
|
|
33712
|
+
step.passed = true;
|
|
33713
|
+
return step;
|
|
33714
|
+
}
|
|
33715
|
+
const testPatterns = [];
|
|
33716
|
+
for (const file2 of filesTouched) {
|
|
33717
|
+
if (file2.includes(".test.") || file2.includes(".spec.")) {
|
|
33718
|
+
testPatterns.push(file2);
|
|
33719
|
+
continue;
|
|
33720
|
+
}
|
|
33721
|
+
const baseName = file2.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
33722
|
+
testPatterns.push(`${baseName}.test.ts`);
|
|
33723
|
+
testPatterns.push(`${baseName}.test.tsx`);
|
|
33724
|
+
testPatterns.push(`${baseName}.spec.ts`);
|
|
33725
|
+
}
|
|
33726
|
+
const existingTests = [];
|
|
33727
|
+
for (const pattern of testPatterns) {
|
|
33728
|
+
try {
|
|
33729
|
+
const exists = await Bun.file(pattern).exists();
|
|
33730
|
+
if (exists) {
|
|
33731
|
+
existingTests.push(pattern);
|
|
33732
|
+
}
|
|
33733
|
+
} catch {}
|
|
33734
|
+
}
|
|
33735
|
+
if (existingTests.length === 0) {
|
|
33736
|
+
step.skipped = true;
|
|
33737
|
+
step.skipReason = "No related test files found";
|
|
33738
|
+
step.passed = true;
|
|
33739
|
+
return step;
|
|
33740
|
+
}
|
|
33741
|
+
try {
|
|
33742
|
+
step.command = `bun test ${existingTests.join(" ")}`;
|
|
33743
|
+
const result = await Bun.$`bun test ${existingTests}`.quiet().nothrow();
|
|
33744
|
+
step.exitCode = result.exitCode;
|
|
33745
|
+
step.passed = result.exitCode === 0;
|
|
33746
|
+
if (!step.passed) {
|
|
33747
|
+
step.error = result.stderr.toString().slice(0, 1000);
|
|
33748
|
+
step.output = result.stdout.toString().slice(0, 1000);
|
|
33749
|
+
}
|
|
33750
|
+
} catch (error45) {
|
|
33751
|
+
step.skipped = true;
|
|
33752
|
+
step.skipReason = `Test runner failed: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
33753
|
+
step.passed = true;
|
|
33754
|
+
}
|
|
33755
|
+
return step;
|
|
33756
|
+
}
|
|
33757
|
+
async function runVerificationGate(filesTouched, skipUbs = false) {
|
|
33758
|
+
const steps = [];
|
|
33759
|
+
const blockers = [];
|
|
33760
|
+
if (!skipUbs && filesTouched.length > 0) {
|
|
33761
|
+
const ubsResult = await runUbsScan(filesTouched);
|
|
33762
|
+
if (ubsResult) {
|
|
33763
|
+
const ubsStep = {
|
|
33764
|
+
name: "ubs_scan",
|
|
33765
|
+
command: `ubs scan ${filesTouched.join(" ")}`,
|
|
33766
|
+
passed: ubsResult.summary.critical === 0,
|
|
33767
|
+
exitCode: ubsResult.exitCode
|
|
33768
|
+
};
|
|
33769
|
+
if (!ubsStep.passed) {
|
|
33770
|
+
ubsStep.error = `Found ${ubsResult.summary.critical} critical bugs`;
|
|
33771
|
+
blockers.push(`UBS: ${ubsResult.summary.critical} critical bugs found`);
|
|
33772
|
+
}
|
|
33773
|
+
steps.push(ubsStep);
|
|
33774
|
+
} else {
|
|
33775
|
+
steps.push({
|
|
33776
|
+
name: "ubs_scan",
|
|
33777
|
+
command: "ubs scan",
|
|
33778
|
+
passed: true,
|
|
33779
|
+
exitCode: 0,
|
|
33780
|
+
skipped: true,
|
|
33781
|
+
skipReason: "UBS not available"
|
|
33782
|
+
});
|
|
33783
|
+
}
|
|
33784
|
+
}
|
|
33785
|
+
const typecheckStep = await runTypecheckVerification();
|
|
33786
|
+
steps.push(typecheckStep);
|
|
33787
|
+
if (!typecheckStep.passed && !typecheckStep.skipped) {
|
|
33788
|
+
blockers.push(`Typecheck: ${typecheckStep.error?.slice(0, 100) || "failed"}`);
|
|
33789
|
+
}
|
|
33790
|
+
const testStep = await runTestVerification(filesTouched);
|
|
33791
|
+
steps.push(testStep);
|
|
33792
|
+
if (!testStep.passed && !testStep.skipped) {
|
|
33793
|
+
blockers.push(`Tests: ${testStep.error?.slice(0, 100) || "failed"}`);
|
|
33794
|
+
}
|
|
33795
|
+
const passedCount = steps.filter((s) => s.passed).length;
|
|
33796
|
+
const skippedCount = steps.filter((s) => s.skipped).length;
|
|
33797
|
+
const failedCount = steps.filter((s) => !s.passed && !s.skipped).length;
|
|
33798
|
+
const summary = failedCount === 0 ? `Verification passed: ${passedCount} checks passed, ${skippedCount} skipped` : `Verification FAILED: ${failedCount} checks failed, ${passedCount} passed, ${skippedCount} skipped`;
|
|
33799
|
+
return {
|
|
33800
|
+
passed: failedCount === 0,
|
|
33801
|
+
steps,
|
|
33802
|
+
summary,
|
|
33803
|
+
blockers
|
|
33804
|
+
};
|
|
33805
|
+
}
|
|
33511
33806
|
async function runUbsScan(files) {
|
|
33512
33807
|
if (files.length === 0) {
|
|
33513
33808
|
return null;
|
|
@@ -33610,19 +33905,44 @@ ${args.files_affected.map((f) => `- \`${f}\``).join(`
|
|
|
33610
33905
|
}
|
|
33611
33906
|
});
|
|
33612
33907
|
var swarm_complete = tool({
|
|
33613
|
-
description: "Mark subtask complete
|
|
33908
|
+
description: "Mark subtask complete with Verification Gate. Runs UBS scan, typecheck, and tests before allowing completion.",
|
|
33614
33909
|
args: {
|
|
33615
33910
|
project_key: tool.schema.string().describe("Project path"),
|
|
33616
33911
|
agent_name: tool.schema.string().describe("Your Agent Mail name"),
|
|
33617
33912
|
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
33618
33913
|
summary: tool.schema.string().describe("Brief summary of work done"),
|
|
33619
33914
|
evaluation: tool.schema.string().optional().describe("Self-evaluation JSON (Evaluation schema)"),
|
|
33620
|
-
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be
|
|
33621
|
-
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)")
|
|
33915
|
+
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (UBS, typecheck, tests)"),
|
|
33916
|
+
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)"),
|
|
33917
|
+
skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (UBS, typecheck, tests). Use sparingly! (default: false)")
|
|
33622
33918
|
},
|
|
33623
33919
|
async execute(args) {
|
|
33920
|
+
let verificationResult = null;
|
|
33921
|
+
if (!args.skip_verification && args.files_touched?.length) {
|
|
33922
|
+
verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
|
|
33923
|
+
if (!verificationResult.passed) {
|
|
33924
|
+
return JSON.stringify({
|
|
33925
|
+
success: false,
|
|
33926
|
+
error: "Verification Gate FAILED - fix issues before completing",
|
|
33927
|
+
verification: {
|
|
33928
|
+
passed: false,
|
|
33929
|
+
summary: verificationResult.summary,
|
|
33930
|
+
blockers: verificationResult.blockers,
|
|
33931
|
+
steps: verificationResult.steps.map((s) => ({
|
|
33932
|
+
name: s.name,
|
|
33933
|
+
passed: s.passed,
|
|
33934
|
+
skipped: s.skipped,
|
|
33935
|
+
skipReason: s.skipReason,
|
|
33936
|
+
error: s.error?.slice(0, 200)
|
|
33937
|
+
}))
|
|
33938
|
+
},
|
|
33939
|
+
hint: "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
|
|
33940
|
+
gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
|
|
33941
|
+
}, null, 2);
|
|
33942
|
+
}
|
|
33943
|
+
}
|
|
33624
33944
|
let ubsResult = null;
|
|
33625
|
-
if (args.
|
|
33945
|
+
if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
|
|
33626
33946
|
ubsResult = await runUbsScan(args.files_touched);
|
|
33627
33947
|
if (ubsResult && ubsResult.summary.critical > 0) {
|
|
33628
33948
|
return JSON.stringify({
|
|
@@ -33693,12 +34013,22 @@ var swarm_complete = tool({
|
|
|
33693
34013
|
closed: true,
|
|
33694
34014
|
reservations_released: true,
|
|
33695
34015
|
message_sent: true,
|
|
34016
|
+
verification_gate: verificationResult ? {
|
|
34017
|
+
passed: true,
|
|
34018
|
+
summary: verificationResult.summary,
|
|
34019
|
+
steps: verificationResult.steps.map((s) => ({
|
|
34020
|
+
name: s.name,
|
|
34021
|
+
passed: s.passed,
|
|
34022
|
+
skipped: s.skipped,
|
|
34023
|
+
skipReason: s.skipReason
|
|
34024
|
+
}))
|
|
34025
|
+
} : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
|
|
33696
34026
|
ubs_scan: ubsResult ? {
|
|
33697
34027
|
ran: true,
|
|
33698
34028
|
bugs_found: ubsResult.summary.total,
|
|
33699
34029
|
summary: ubsResult.summary,
|
|
33700
34030
|
warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
|
|
33701
|
-
} : {
|
|
34031
|
+
} : verificationResult ? { ran: true, included_in_verification_gate: true } : {
|
|
33702
34032
|
ran: false,
|
|
33703
34033
|
reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
|
|
33704
34034
|
},
|
|
@@ -34266,6 +34596,77 @@ var swarmTools = {
|
|
|
34266
34596
|
swarm_get_error_context,
|
|
34267
34597
|
swarm_resolve_error
|
|
34268
34598
|
};
|
|
34599
|
+
var globalStrikeStorage = new InMemoryStrikeStorage;
|
|
34600
|
+
var swarm_check_strikes = tool({
|
|
34601
|
+
description: "Check 3-strike status for a bead. Records failures, detects architectural problems, generates architecture review prompts.",
|
|
34602
|
+
args: {
|
|
34603
|
+
bead_id: tool.schema.string().describe("Bead ID to check"),
|
|
34604
|
+
action: tool.schema.enum(["check", "add_strike", "clear", "get_prompt"]).describe("Action: check count, add strike, clear strikes, or get prompt"),
|
|
34605
|
+
attempt: tool.schema.string().optional().describe("Description of fix attempt (required for add_strike)"),
|
|
34606
|
+
reason: tool.schema.string().optional().describe("Why the fix failed (required for add_strike)")
|
|
34607
|
+
},
|
|
34608
|
+
async execute(args) {
|
|
34609
|
+
switch (args.action) {
|
|
34610
|
+
case "check": {
|
|
34611
|
+
const count = await getStrikes(args.bead_id, globalStrikeStorage);
|
|
34612
|
+
const strikedOut = await isStrikedOut(args.bead_id, globalStrikeStorage);
|
|
34613
|
+
return JSON.stringify({
|
|
34614
|
+
bead_id: args.bead_id,
|
|
34615
|
+
strike_count: count,
|
|
34616
|
+
is_striked_out: strikedOut,
|
|
34617
|
+
message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. Use get_prompt action for architecture review." : count === 0 ? "No strikes. Clear to proceed." : `${count} strike${count > 1 ? "s" : ""}. ${3 - count} remaining before architecture review required.`,
|
|
34618
|
+
next_action: strikedOut ? "Call with action=get_prompt to get architecture review questions" : "Continue with fix attempt"
|
|
34619
|
+
}, null, 2);
|
|
34620
|
+
}
|
|
34621
|
+
case "add_strike": {
|
|
34622
|
+
if (!args.attempt || !args.reason) {
|
|
34623
|
+
return JSON.stringify({
|
|
34624
|
+
error: "add_strike requires 'attempt' and 'reason' parameters"
|
|
34625
|
+
}, null, 2);
|
|
34626
|
+
}
|
|
34627
|
+
const record2 = await addStrike(args.bead_id, args.attempt, args.reason, globalStrikeStorage);
|
|
34628
|
+
const strikedOut = record2.strike_count >= 3;
|
|
34629
|
+
return JSON.stringify({
|
|
34630
|
+
bead_id: args.bead_id,
|
|
34631
|
+
strike_count: record2.strike_count,
|
|
34632
|
+
is_striked_out: strikedOut,
|
|
34633
|
+
failures: record2.failures,
|
|
34634
|
+
message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. STOP and question the architecture." : `Strike ${record2.strike_count} recorded. ${3 - record2.strike_count} remaining.`,
|
|
34635
|
+
warning: strikedOut ? "DO NOT attempt Fix #4. Call with action=get_prompt for architecture review." : undefined
|
|
34636
|
+
}, null, 2);
|
|
34637
|
+
}
|
|
34638
|
+
case "clear": {
|
|
34639
|
+
await clearStrikes(args.bead_id, globalStrikeStorage);
|
|
34640
|
+
return JSON.stringify({
|
|
34641
|
+
bead_id: args.bead_id,
|
|
34642
|
+
strike_count: 0,
|
|
34643
|
+
is_striked_out: false,
|
|
34644
|
+
message: "Strikes cleared. Fresh start."
|
|
34645
|
+
}, null, 2);
|
|
34646
|
+
}
|
|
34647
|
+
case "get_prompt": {
|
|
34648
|
+
const prompt = await getArchitecturePrompt(args.bead_id, globalStrikeStorage);
|
|
34649
|
+
if (!prompt) {
|
|
34650
|
+
return JSON.stringify({
|
|
34651
|
+
bead_id: args.bead_id,
|
|
34652
|
+
has_prompt: false,
|
|
34653
|
+
message: "No architecture prompt (not struck out yet)"
|
|
34654
|
+
}, null, 2);
|
|
34655
|
+
}
|
|
34656
|
+
return JSON.stringify({
|
|
34657
|
+
bead_id: args.bead_id,
|
|
34658
|
+
has_prompt: true,
|
|
34659
|
+
architecture_review_prompt: prompt,
|
|
34660
|
+
message: "Architecture review required. Present this prompt to the human partner."
|
|
34661
|
+
}, null, 2);
|
|
34662
|
+
}
|
|
34663
|
+
default:
|
|
34664
|
+
return JSON.stringify({
|
|
34665
|
+
error: `Unknown action: ${args.action}`
|
|
34666
|
+
}, null, 2);
|
|
34667
|
+
}
|
|
34668
|
+
}
|
|
34669
|
+
});
|
|
34269
34670
|
|
|
34270
34671
|
// src/repo-crawl.ts
|
|
34271
34672
|
init_dist();
|
|
@@ -34723,7 +35124,6 @@ var SwarmPlugin = async (input) => {
|
|
|
34723
35124
|
return {
|
|
34724
35125
|
tool: {
|
|
34725
35126
|
...beadsTools,
|
|
34726
|
-
...agentMailTools,
|
|
34727
35127
|
...swarmMailTools,
|
|
34728
35128
|
...structuredTools,
|
|
34729
35129
|
...swarmTools,
|
|
@@ -34770,7 +35170,6 @@ var SwarmPlugin = async (input) => {
|
|
|
34770
35170
|
};
|
|
34771
35171
|
var allTools = {
|
|
34772
35172
|
...beadsTools,
|
|
34773
|
-
...agentMailTools,
|
|
34774
35173
|
...swarmMailTools,
|
|
34775
35174
|
...structuredTools,
|
|
34776
35175
|
...swarmTools,
|