opencode-swarm-plugin 0.14.0 → 0.16.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/dist/index.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
- return JSON.stringify({
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
- }, null, 2);
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
  }
@@ -29577,6 +29668,96 @@ var SwarmStatusSchema = exports_external.object({
29577
29668
  agents: exports_external.array(SpawnedAgentSchema),
29578
29669
  last_update: exports_external.string().datetime({ offset: true })
29579
29670
  });
29671
+ // src/schemas/mandate.ts
29672
+ init_zod();
29673
+ var MandateContentTypeSchema = exports_external.enum([
29674
+ "idea",
29675
+ "tip",
29676
+ "lore",
29677
+ "snippet",
29678
+ "feature_request"
29679
+ ]);
29680
+ var MandateStatusSchema = exports_external.enum([
29681
+ "candidate",
29682
+ "established",
29683
+ "mandate",
29684
+ "rejected"
29685
+ ]);
29686
+ var VoteTypeSchema = exports_external.enum(["upvote", "downvote"]);
29687
+ var MandateEntrySchema = exports_external.object({
29688
+ id: exports_external.string(),
29689
+ content: exports_external.string().min(1, "Content required"),
29690
+ content_type: MandateContentTypeSchema,
29691
+ author_agent: exports_external.string(),
29692
+ created_at: exports_external.string().datetime({ offset: true }),
29693
+ status: MandateStatusSchema.default("candidate"),
29694
+ tags: exports_external.array(exports_external.string()).default([]),
29695
+ metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
29696
+ });
29697
+ var VoteSchema = exports_external.object({
29698
+ id: exports_external.string(),
29699
+ mandate_id: exports_external.string(),
29700
+ agent_name: exports_external.string(),
29701
+ vote_type: VoteTypeSchema,
29702
+ timestamp: exports_external.string().datetime({ offset: true }),
29703
+ weight: exports_external.number().min(0).max(1).default(1)
29704
+ });
29705
+ var MandateScoreSchema = exports_external.object({
29706
+ mandate_id: exports_external.string(),
29707
+ net_votes: exports_external.number(),
29708
+ vote_ratio: exports_external.number().min(0).max(1),
29709
+ decayed_score: exports_external.number(),
29710
+ last_calculated: exports_external.string().datetime({ offset: true }),
29711
+ raw_upvotes: exports_external.number().int().min(0),
29712
+ raw_downvotes: exports_external.number().int().min(0),
29713
+ decayed_upvotes: exports_external.number().min(0),
29714
+ decayed_downvotes: exports_external.number().min(0)
29715
+ });
29716
+ var DEFAULT_MANDATE_DECAY_CONFIG = {
29717
+ halfLifeDays: 90,
29718
+ mandateNetVotesThreshold: 5,
29719
+ mandateVoteRatioThreshold: 0.7,
29720
+ establishedNetVotesThreshold: 2,
29721
+ rejectedNetVotesThreshold: -3
29722
+ };
29723
+ var CreateMandateArgsSchema = exports_external.object({
29724
+ content: exports_external.string().min(1, "Content required"),
29725
+ content_type: MandateContentTypeSchema,
29726
+ tags: exports_external.array(exports_external.string()).default([]),
29727
+ metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
29728
+ });
29729
+ var CastVoteArgsSchema = exports_external.object({
29730
+ mandate_id: exports_external.string(),
29731
+ vote_type: VoteTypeSchema,
29732
+ weight: exports_external.number().min(0).max(1).default(1)
29733
+ });
29734
+ var QueryMandatesArgsSchema = exports_external.object({
29735
+ status: MandateStatusSchema.optional(),
29736
+ content_type: MandateContentTypeSchema.optional(),
29737
+ tags: exports_external.array(exports_external.string()).optional(),
29738
+ author_agent: exports_external.string().optional(),
29739
+ limit: exports_external.number().int().positive().default(20),
29740
+ min_score: exports_external.number().optional()
29741
+ });
29742
+ var ScoreCalculationResultSchema = exports_external.object({
29743
+ mandate_id: exports_external.string(),
29744
+ previous_status: MandateStatusSchema,
29745
+ new_status: MandateStatusSchema,
29746
+ score: MandateScoreSchema,
29747
+ status_changed: exports_external.boolean()
29748
+ });
29749
+ var mandateSchemas = {
29750
+ MandateContentTypeSchema,
29751
+ MandateStatusSchema,
29752
+ VoteTypeSchema,
29753
+ MandateEntrySchema,
29754
+ VoteSchema,
29755
+ MandateScoreSchema,
29756
+ CreateMandateArgsSchema,
29757
+ CastVoteArgsSchema,
29758
+ QueryMandatesArgsSchema,
29759
+ ScoreCalculationResultSchema
29760
+ };
29580
29761
  // src/beads.ts
29581
29762
  var beadsWorkingDirectory = null;
29582
29763
  function setBeadsWorkingDirectory(directory) {
@@ -32378,6 +32559,89 @@ class InMemoryFeedbackStorage {
32378
32559
  return [...this.events];
32379
32560
  }
32380
32561
  }
32562
+ var StrikeRecordSchema = exports_external.object({
32563
+ bead_id: exports_external.string(),
32564
+ strike_count: exports_external.number().int().min(0).max(3),
32565
+ failures: exports_external.array(exports_external.object({
32566
+ attempt: exports_external.string(),
32567
+ reason: exports_external.string(),
32568
+ timestamp: exports_external.string()
32569
+ })),
32570
+ first_strike_at: exports_external.string().optional(),
32571
+ last_strike_at: exports_external.string().optional()
32572
+ });
32573
+
32574
+ class InMemoryStrikeStorage {
32575
+ strikes = new Map;
32576
+ async store(record2) {
32577
+ this.strikes.set(record2.bead_id, record2);
32578
+ }
32579
+ async get(beadId) {
32580
+ return this.strikes.get(beadId) ?? null;
32581
+ }
32582
+ async getAll() {
32583
+ return Array.from(this.strikes.values());
32584
+ }
32585
+ async clear(beadId) {
32586
+ this.strikes.delete(beadId);
32587
+ }
32588
+ }
32589
+ async function addStrike(beadId, attempt, reason, storage = new InMemoryStrikeStorage) {
32590
+ const existing = await storage.get(beadId);
32591
+ const now = new Date().toISOString();
32592
+ const record2 = existing ?? {
32593
+ bead_id: beadId,
32594
+ strike_count: 0,
32595
+ failures: []
32596
+ };
32597
+ record2.strike_count = Math.min(3, record2.strike_count + 1);
32598
+ record2.failures.push({ attempt, reason, timestamp: now });
32599
+ record2.last_strike_at = now;
32600
+ if (!record2.first_strike_at) {
32601
+ record2.first_strike_at = now;
32602
+ }
32603
+ await storage.store(record2);
32604
+ return record2;
32605
+ }
32606
+ async function getStrikes(beadId, storage = new InMemoryStrikeStorage) {
32607
+ const record2 = await storage.get(beadId);
32608
+ return record2?.strike_count ?? 0;
32609
+ }
32610
+ async function isStrikedOut(beadId, storage = new InMemoryStrikeStorage) {
32611
+ const count = await getStrikes(beadId, storage);
32612
+ return count >= 3;
32613
+ }
32614
+ async function getArchitecturePrompt(beadId, storage = new InMemoryStrikeStorage) {
32615
+ const record2 = await storage.get(beadId);
32616
+ if (!record2 || record2.strike_count < 3) {
32617
+ return "";
32618
+ }
32619
+ const failuresList = record2.failures.map((f, i) => `${i + 1}. **${f.attempt}** - Failed: ${f.reason}`).join(`
32620
+ `);
32621
+ return `## Architecture Review Required
32622
+
32623
+ This bead (\`${beadId}\`) has failed 3 consecutive fix attempts:
32624
+
32625
+ ${failuresList}
32626
+
32627
+ This pattern suggests an **architectural problem**, not a bug.
32628
+
32629
+ **Questions to consider:**
32630
+ - Is the current approach fundamentally sound?
32631
+ - Should we refactor the architecture instead?
32632
+ - Are we fixing symptoms instead of root cause?
32633
+
32634
+ **Options:**
32635
+ 1. **Refactor architecture** (describe new approach)
32636
+ 2. **Continue with Fix #4** (explain why this time is different)
32637
+ 3. **Abandon this approach entirely**
32638
+
32639
+ **DO NOT attempt Fix #4 without answering these questions.**
32640
+ `;
32641
+ }
32642
+ async function clearStrikes(beadId, storage = new InMemoryStrikeStorage) {
32643
+ await storage.clear(beadId);
32644
+ }
32381
32645
 
32382
32646
  class InMemoryErrorStorage {
32383
32647
  errors = [];
@@ -32897,7 +33161,7 @@ You MUST keep your bead updated as you work:
32897
33161
 
32898
33162
  **Never work silently.** Your bead status is how the swarm tracks progress.
32899
33163
 
32900
- ## MANDATORY: Agent Mail Communication
33164
+ ## MANDATORY: Swarm Mail Communication
32901
33165
 
32902
33166
  You MUST communicate with other agents:
32903
33167
 
@@ -32906,9 +33170,9 @@ You MUST communicate with other agents:
32906
33170
  3. **Announce blockers** immediately - don't spin trying to fix alone
32907
33171
  4. **Coordinate on shared concerns** - if you see something affecting other agents, say so
32908
33172
 
32909
- Use Agent Mail for all communication:
33173
+ Use Swarm Mail for all communication:
32910
33174
  \`\`\`
32911
- agentmail_send(
33175
+ swarmmail_send(
32912
33176
  to: ["coordinator" or specific agent],
32913
33177
  subject: "Brief subject",
32914
33178
  body: "Message content",
@@ -32920,7 +33184,7 @@ agentmail_send(
32920
33184
 
32921
33185
  1. **Start**: Your bead is already marked in_progress
32922
33186
  2. **Progress**: Use swarm_progress to report status updates
32923
- 3. **Blocked**: Report immediately via Agent Mail - don't spin
33187
+ 3. **Blocked**: Report immediately via Swarm Mail - don't spin
32924
33188
  4. **Complete**: Use swarm_complete when done - it handles:
32925
33189
  - Closing your bead with a summary
32926
33190
  - Releasing file reservations
@@ -32942,7 +33206,7 @@ Before writing code:
32942
33206
  1. **Read the files** you're assigned to understand current state
32943
33207
  2. **Plan your approach** - what changes, in what order?
32944
33208
  3. **Identify risks** - what could go wrong? What dependencies?
32945
- 4. **Communicate your plan** via Agent Mail if non-trivial
33209
+ 4. **Communicate your plan** via Swarm Mail if non-trivial
32946
33210
 
32947
33211
  Begin work on your subtask now.`;
32948
33212
  var SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
@@ -33653,6 +33917,139 @@ var swarm_progress = tool({
33653
33917
  return `Progress reported: ${args.status}${args.progress_percent !== undefined ? ` (${args.progress_percent}%)` : ""}`;
33654
33918
  }
33655
33919
  });
33920
+ async function runTypecheckVerification() {
33921
+ const step = {
33922
+ name: "typecheck",
33923
+ command: "tsc --noEmit",
33924
+ passed: false,
33925
+ exitCode: -1
33926
+ };
33927
+ try {
33928
+ const tsconfigExists = await Bun.file("tsconfig.json").exists();
33929
+ if (!tsconfigExists) {
33930
+ step.skipped = true;
33931
+ step.skipReason = "No tsconfig.json found";
33932
+ step.passed = true;
33933
+ return step;
33934
+ }
33935
+ const result = await Bun.$`tsc --noEmit`.quiet().nothrow();
33936
+ step.exitCode = result.exitCode;
33937
+ step.passed = result.exitCode === 0;
33938
+ if (!step.passed) {
33939
+ step.error = result.stderr.toString().slice(0, 1000);
33940
+ step.output = result.stdout.toString().slice(0, 1000);
33941
+ }
33942
+ } catch (error45) {
33943
+ step.skipped = true;
33944
+ step.skipReason = `tsc not available: ${error45 instanceof Error ? error45.message : String(error45)}`;
33945
+ step.passed = true;
33946
+ }
33947
+ return step;
33948
+ }
33949
+ async function runTestVerification(filesTouched) {
33950
+ const step = {
33951
+ name: "tests",
33952
+ command: "bun test <related-files>",
33953
+ passed: false,
33954
+ exitCode: -1
33955
+ };
33956
+ if (filesTouched.length === 0) {
33957
+ step.skipped = true;
33958
+ step.skipReason = "No files touched";
33959
+ step.passed = true;
33960
+ return step;
33961
+ }
33962
+ const testPatterns = [];
33963
+ for (const file2 of filesTouched) {
33964
+ if (file2.includes(".test.") || file2.includes(".spec.")) {
33965
+ testPatterns.push(file2);
33966
+ continue;
33967
+ }
33968
+ const baseName = file2.replace(/\.(ts|tsx|js|jsx)$/, "");
33969
+ testPatterns.push(`${baseName}.test.ts`);
33970
+ testPatterns.push(`${baseName}.test.tsx`);
33971
+ testPatterns.push(`${baseName}.spec.ts`);
33972
+ }
33973
+ const existingTests = [];
33974
+ for (const pattern of testPatterns) {
33975
+ try {
33976
+ const exists = await Bun.file(pattern).exists();
33977
+ if (exists) {
33978
+ existingTests.push(pattern);
33979
+ }
33980
+ } catch {}
33981
+ }
33982
+ if (existingTests.length === 0) {
33983
+ step.skipped = true;
33984
+ step.skipReason = "No related test files found";
33985
+ step.passed = true;
33986
+ return step;
33987
+ }
33988
+ try {
33989
+ step.command = `bun test ${existingTests.join(" ")}`;
33990
+ const result = await Bun.$`bun test ${existingTests}`.quiet().nothrow();
33991
+ step.exitCode = result.exitCode;
33992
+ step.passed = result.exitCode === 0;
33993
+ if (!step.passed) {
33994
+ step.error = result.stderr.toString().slice(0, 1000);
33995
+ step.output = result.stdout.toString().slice(0, 1000);
33996
+ }
33997
+ } catch (error45) {
33998
+ step.skipped = true;
33999
+ step.skipReason = `Test runner failed: ${error45 instanceof Error ? error45.message : String(error45)}`;
34000
+ step.passed = true;
34001
+ }
34002
+ return step;
34003
+ }
34004
+ async function runVerificationGate(filesTouched, skipUbs = false) {
34005
+ const steps = [];
34006
+ const blockers = [];
34007
+ if (!skipUbs && filesTouched.length > 0) {
34008
+ const ubsResult = await runUbsScan(filesTouched);
34009
+ if (ubsResult) {
34010
+ const ubsStep = {
34011
+ name: "ubs_scan",
34012
+ command: `ubs scan ${filesTouched.join(" ")}`,
34013
+ passed: ubsResult.summary.critical === 0,
34014
+ exitCode: ubsResult.exitCode
34015
+ };
34016
+ if (!ubsStep.passed) {
34017
+ ubsStep.error = `Found ${ubsResult.summary.critical} critical bugs`;
34018
+ blockers.push(`UBS: ${ubsResult.summary.critical} critical bugs found`);
34019
+ }
34020
+ steps.push(ubsStep);
34021
+ } else {
34022
+ steps.push({
34023
+ name: "ubs_scan",
34024
+ command: "ubs scan",
34025
+ passed: true,
34026
+ exitCode: 0,
34027
+ skipped: true,
34028
+ skipReason: "UBS not available"
34029
+ });
34030
+ }
34031
+ }
34032
+ const typecheckStep = await runTypecheckVerification();
34033
+ steps.push(typecheckStep);
34034
+ if (!typecheckStep.passed && !typecheckStep.skipped) {
34035
+ blockers.push(`Typecheck: ${typecheckStep.error?.slice(0, 100) || "failed"}`);
34036
+ }
34037
+ const testStep = await runTestVerification(filesTouched);
34038
+ steps.push(testStep);
34039
+ if (!testStep.passed && !testStep.skipped) {
34040
+ blockers.push(`Tests: ${testStep.error?.slice(0, 100) || "failed"}`);
34041
+ }
34042
+ const passedCount = steps.filter((s) => s.passed).length;
34043
+ const skippedCount = steps.filter((s) => s.skipped).length;
34044
+ const failedCount = steps.filter((s) => !s.passed && !s.skipped).length;
34045
+ const summary = failedCount === 0 ? `Verification passed: ${passedCount} checks passed, ${skippedCount} skipped` : `Verification FAILED: ${failedCount} checks failed, ${passedCount} passed, ${skippedCount} skipped`;
34046
+ return {
34047
+ passed: failedCount === 0,
34048
+ steps,
34049
+ summary,
34050
+ blockers
34051
+ };
34052
+ }
33656
34053
  async function runUbsScan(files) {
33657
34054
  if (files.length === 0) {
33658
34055
  return null;
@@ -33755,19 +34152,44 @@ ${args.files_affected.map((f) => `- \`${f}\``).join(`
33755
34152
  }
33756
34153
  });
33757
34154
  var swarm_complete = tool({
33758
- description: "Mark subtask complete, release reservations, notify coordinator. Runs UBS bug scan if files_touched provided.",
34155
+ description: "Mark subtask complete with Verification Gate. Runs UBS scan, typecheck, and tests before allowing completion.",
33759
34156
  args: {
33760
34157
  project_key: tool.schema.string().describe("Project path"),
33761
34158
  agent_name: tool.schema.string().describe("Your Agent Mail name"),
33762
34159
  bead_id: tool.schema.string().describe("Subtask bead ID"),
33763
34160
  summary: tool.schema.string().describe("Brief summary of work done"),
33764
34161
  evaluation: tool.schema.string().optional().describe("Self-evaluation JSON (Evaluation schema)"),
33765
- files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be scanned by UBS for bugs"),
33766
- skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)")
34162
+ files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (UBS, typecheck, tests)"),
34163
+ skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)"),
34164
+ skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (UBS, typecheck, tests). Use sparingly! (default: false)")
33767
34165
  },
33768
34166
  async execute(args) {
34167
+ let verificationResult = null;
34168
+ if (!args.skip_verification && args.files_touched?.length) {
34169
+ verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
34170
+ if (!verificationResult.passed) {
34171
+ return JSON.stringify({
34172
+ success: false,
34173
+ error: "Verification Gate FAILED - fix issues before completing",
34174
+ verification: {
34175
+ passed: false,
34176
+ summary: verificationResult.summary,
34177
+ blockers: verificationResult.blockers,
34178
+ steps: verificationResult.steps.map((s) => ({
34179
+ name: s.name,
34180
+ passed: s.passed,
34181
+ skipped: s.skipped,
34182
+ skipReason: s.skipReason,
34183
+ error: s.error?.slice(0, 200)
34184
+ }))
34185
+ },
34186
+ hint: "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
34187
+ gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
34188
+ }, null, 2);
34189
+ }
34190
+ }
33769
34191
  let ubsResult = null;
33770
- if (args.files_touched && args.files_touched.length > 0 && !args.skip_ubs_scan) {
34192
+ if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
33771
34193
  ubsResult = await runUbsScan(args.files_touched);
33772
34194
  if (ubsResult && ubsResult.summary.critical > 0) {
33773
34195
  return JSON.stringify({
@@ -33838,12 +34260,22 @@ var swarm_complete = tool({
33838
34260
  closed: true,
33839
34261
  reservations_released: true,
33840
34262
  message_sent: true,
34263
+ verification_gate: verificationResult ? {
34264
+ passed: true,
34265
+ summary: verificationResult.summary,
34266
+ steps: verificationResult.steps.map((s) => ({
34267
+ name: s.name,
34268
+ passed: s.passed,
34269
+ skipped: s.skipped,
34270
+ skipReason: s.skipReason
34271
+ }))
34272
+ } : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
33841
34273
  ubs_scan: ubsResult ? {
33842
34274
  ran: true,
33843
34275
  bugs_found: ubsResult.summary.total,
33844
34276
  summary: ubsResult.summary,
33845
34277
  warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
33846
- } : {
34278
+ } : verificationResult ? { ran: true, included_in_verification_gate: true } : {
33847
34279
  ran: false,
33848
34280
  reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
33849
34281
  },
@@ -34411,6 +34843,77 @@ var swarmTools = {
34411
34843
  swarm_get_error_context,
34412
34844
  swarm_resolve_error
34413
34845
  };
34846
+ var globalStrikeStorage = new InMemoryStrikeStorage;
34847
+ var swarm_check_strikes = tool({
34848
+ description: "Check 3-strike status for a bead. Records failures, detects architectural problems, generates architecture review prompts.",
34849
+ args: {
34850
+ bead_id: tool.schema.string().describe("Bead ID to check"),
34851
+ action: tool.schema.enum(["check", "add_strike", "clear", "get_prompt"]).describe("Action: check count, add strike, clear strikes, or get prompt"),
34852
+ attempt: tool.schema.string().optional().describe("Description of fix attempt (required for add_strike)"),
34853
+ reason: tool.schema.string().optional().describe("Why the fix failed (required for add_strike)")
34854
+ },
34855
+ async execute(args) {
34856
+ switch (args.action) {
34857
+ case "check": {
34858
+ const count = await getStrikes(args.bead_id, globalStrikeStorage);
34859
+ const strikedOut = await isStrikedOut(args.bead_id, globalStrikeStorage);
34860
+ return JSON.stringify({
34861
+ bead_id: args.bead_id,
34862
+ strike_count: count,
34863
+ is_striked_out: strikedOut,
34864
+ 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.`,
34865
+ next_action: strikedOut ? "Call with action=get_prompt to get architecture review questions" : "Continue with fix attempt"
34866
+ }, null, 2);
34867
+ }
34868
+ case "add_strike": {
34869
+ if (!args.attempt || !args.reason) {
34870
+ return JSON.stringify({
34871
+ error: "add_strike requires 'attempt' and 'reason' parameters"
34872
+ }, null, 2);
34873
+ }
34874
+ const record2 = await addStrike(args.bead_id, args.attempt, args.reason, globalStrikeStorage);
34875
+ const strikedOut = record2.strike_count >= 3;
34876
+ return JSON.stringify({
34877
+ bead_id: args.bead_id,
34878
+ strike_count: record2.strike_count,
34879
+ is_striked_out: strikedOut,
34880
+ failures: record2.failures,
34881
+ message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. STOP and question the architecture." : `Strike ${record2.strike_count} recorded. ${3 - record2.strike_count} remaining.`,
34882
+ warning: strikedOut ? "DO NOT attempt Fix #4. Call with action=get_prompt for architecture review." : undefined
34883
+ }, null, 2);
34884
+ }
34885
+ case "clear": {
34886
+ await clearStrikes(args.bead_id, globalStrikeStorage);
34887
+ return JSON.stringify({
34888
+ bead_id: args.bead_id,
34889
+ strike_count: 0,
34890
+ is_striked_out: false,
34891
+ message: "Strikes cleared. Fresh start."
34892
+ }, null, 2);
34893
+ }
34894
+ case "get_prompt": {
34895
+ const prompt = await getArchitecturePrompt(args.bead_id, globalStrikeStorage);
34896
+ if (!prompt) {
34897
+ return JSON.stringify({
34898
+ bead_id: args.bead_id,
34899
+ has_prompt: false,
34900
+ message: "No architecture prompt (not struck out yet)"
34901
+ }, null, 2);
34902
+ }
34903
+ return JSON.stringify({
34904
+ bead_id: args.bead_id,
34905
+ has_prompt: true,
34906
+ architecture_review_prompt: prompt,
34907
+ message: "Architecture review required. Present this prompt to the human partner."
34908
+ }, null, 2);
34909
+ }
34910
+ default:
34911
+ return JSON.stringify({
34912
+ error: `Unknown action: ${args.action}`
34913
+ }, null, 2);
34914
+ }
34915
+ }
34916
+ });
34414
34917
 
34415
34918
  // src/repo-crawl.ts
34416
34919
  init_dist();
@@ -34737,6 +35240,762 @@ var repoCrawlTools = {
34737
35240
  // src/index.ts
34738
35241
  init_skills();
34739
35242
 
35243
+ // src/mandates.ts
35244
+ init_dist();
35245
+
35246
+ // src/mandate-storage.ts
35247
+ var cachedCommand = null;
35248
+ async function resolveSemanticMemoryCommand() {
35249
+ if (cachedCommand)
35250
+ return cachedCommand;
35251
+ const nativeResult = await Bun.$`which semantic-memory`.quiet().nothrow();
35252
+ if (nativeResult.exitCode === 0) {
35253
+ cachedCommand = ["semantic-memory"];
35254
+ return cachedCommand;
35255
+ }
35256
+ cachedCommand = ["bunx", "semantic-memory"];
35257
+ return cachedCommand;
35258
+ }
35259
+ async function execSemanticMemory(args) {
35260
+ try {
35261
+ const cmd = await resolveSemanticMemoryCommand();
35262
+ const fullCmd = [...cmd, ...args];
35263
+ const proc = Bun.spawn(fullCmd, {
35264
+ stdout: "pipe",
35265
+ stderr: "pipe"
35266
+ });
35267
+ try {
35268
+ const stdout = Buffer.from(await new Response(proc.stdout).arrayBuffer());
35269
+ const stderr = Buffer.from(await new Response(proc.stderr).arrayBuffer());
35270
+ const exitCode = await proc.exited;
35271
+ return { exitCode, stdout, stderr };
35272
+ } finally {
35273
+ proc.kill();
35274
+ }
35275
+ } catch (error45) {
35276
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
35277
+ return {
35278
+ exitCode: 1,
35279
+ stdout: Buffer.from(""),
35280
+ stderr: Buffer.from(`Error executing semantic-memory: ${errorMessage}`)
35281
+ };
35282
+ }
35283
+ }
35284
+ var DEFAULT_MANDATE_STORAGE_CONFIG = {
35285
+ backend: "semantic-memory",
35286
+ collections: {
35287
+ mandates: "swarm-mandates",
35288
+ votes: "swarm-votes"
35289
+ },
35290
+ decay: DEFAULT_MANDATE_DECAY_CONFIG,
35291
+ useSemanticSearch: true
35292
+ };
35293
+
35294
+ class SemanticMemoryMandateStorage {
35295
+ config;
35296
+ constructor(config2 = {}) {
35297
+ this.config = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
35298
+ }
35299
+ async storeInternal(collection, data, metadata) {
35300
+ const content = typeof data === "string" ? data : JSON.stringify(data);
35301
+ const args = ["store", content, "--collection", collection];
35302
+ if (metadata) {
35303
+ args.push("--metadata", JSON.stringify(metadata));
35304
+ }
35305
+ await execSemanticMemory(args);
35306
+ }
35307
+ async findInternal(collection, query, limit = 10, useFts = false) {
35308
+ const args = [
35309
+ "find",
35310
+ query,
35311
+ "--collection",
35312
+ collection,
35313
+ "--limit",
35314
+ String(limit),
35315
+ "--json"
35316
+ ];
35317
+ if (useFts) {
35318
+ args.push("--fts");
35319
+ }
35320
+ const result = await execSemanticMemory(args);
35321
+ if (result.exitCode !== 0) {
35322
+ console.warn(`[mandate-storage] semantic-memory find() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
35323
+ return [];
35324
+ }
35325
+ try {
35326
+ const output = result.stdout.toString().trim();
35327
+ if (!output)
35328
+ return [];
35329
+ const parsed = JSON.parse(output);
35330
+ const results = Array.isArray(parsed) ? parsed : parsed.results || [];
35331
+ return results.map((r) => {
35332
+ const content = r.content || r.information || "";
35333
+ try {
35334
+ return JSON.parse(content);
35335
+ } catch {
35336
+ return content;
35337
+ }
35338
+ });
35339
+ } catch (error45) {
35340
+ console.warn(`[mandate-storage] Failed to parse semantic-memory find() output: ${error45 instanceof Error ? error45.message : String(error45)}`);
35341
+ return [];
35342
+ }
35343
+ }
35344
+ async listInternal(collection) {
35345
+ const result = await execSemanticMemory([
35346
+ "list",
35347
+ "--collection",
35348
+ collection,
35349
+ "--json"
35350
+ ]);
35351
+ if (result.exitCode !== 0) {
35352
+ console.warn(`[mandate-storage] semantic-memory list() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
35353
+ return [];
35354
+ }
35355
+ try {
35356
+ const output = result.stdout.toString().trim();
35357
+ if (!output)
35358
+ return [];
35359
+ const parsed = JSON.parse(output);
35360
+ const items = Array.isArray(parsed) ? parsed : parsed.items || [];
35361
+ return items.map((item) => {
35362
+ const content = item.content || item.information || "";
35363
+ try {
35364
+ return JSON.parse(content);
35365
+ } catch {
35366
+ return content;
35367
+ }
35368
+ });
35369
+ } catch (error45) {
35370
+ console.warn(`[mandate-storage] Failed to parse semantic-memory list() output: ${error45 instanceof Error ? error45.message : String(error45)}`);
35371
+ return [];
35372
+ }
35373
+ }
35374
+ async store(entry) {
35375
+ await this.storeInternal(this.config.collections.mandates, entry, {
35376
+ id: entry.id,
35377
+ content_type: entry.content_type,
35378
+ author_agent: entry.author_agent,
35379
+ status: entry.status,
35380
+ tags: entry.tags.join(","),
35381
+ created_at: entry.created_at
35382
+ });
35383
+ }
35384
+ async get(id) {
35385
+ const all = await this.listInternal(this.config.collections.mandates);
35386
+ return all.find((entry) => entry.id === id) || null;
35387
+ }
35388
+ async find(query, limit = 10) {
35389
+ return this.findInternal(this.config.collections.mandates, query, limit, !this.config.useSemanticSearch);
35390
+ }
35391
+ async list(filter2) {
35392
+ const all = await this.listInternal(this.config.collections.mandates);
35393
+ if (!filter2)
35394
+ return all;
35395
+ return all.filter((entry) => {
35396
+ if (filter2.status && entry.status !== filter2.status)
35397
+ return false;
35398
+ if (filter2.content_type && entry.content_type !== filter2.content_type)
35399
+ return false;
35400
+ return true;
35401
+ });
35402
+ }
35403
+ async update(id, updates) {
35404
+ const existing = await this.get(id);
35405
+ if (!existing) {
35406
+ throw new Error(`Mandate ${id} not found`);
35407
+ }
35408
+ const updated = { ...existing, ...updates };
35409
+ await this.store(updated);
35410
+ }
35411
+ async vote(vote) {
35412
+ const existing = await this.hasVoted(vote.mandate_id, vote.agent_name);
35413
+ if (existing) {
35414
+ throw new Error(`Agent ${vote.agent_name} has already voted on mandate ${vote.mandate_id}`);
35415
+ }
35416
+ await this.storeInternal(this.config.collections.votes, vote, {
35417
+ id: vote.id,
35418
+ mandate_id: vote.mandate_id,
35419
+ agent_name: vote.agent_name,
35420
+ vote_type: vote.vote_type,
35421
+ timestamp: vote.timestamp,
35422
+ weight: vote.weight
35423
+ });
35424
+ }
35425
+ async getVotes(mandateId) {
35426
+ const all = await this.listInternal(this.config.collections.votes);
35427
+ return all.filter((vote) => vote.mandate_id === mandateId);
35428
+ }
35429
+ async hasVoted(mandateId, agentName) {
35430
+ const votes = await this.getVotes(mandateId);
35431
+ return votes.some((vote) => vote.agent_name === agentName);
35432
+ }
35433
+ async calculateScore(mandateId) {
35434
+ const votes = await this.getVotes(mandateId);
35435
+ const now = new Date;
35436
+ let rawUpvotes = 0;
35437
+ let rawDownvotes = 0;
35438
+ let decayedUpvotes = 0;
35439
+ let decayedDownvotes = 0;
35440
+ for (const vote of votes) {
35441
+ const decayed = calculateDecayedValue(vote.timestamp, now, this.config.decay.halfLifeDays);
35442
+ const value = vote.weight * decayed;
35443
+ if (vote.vote_type === "upvote") {
35444
+ rawUpvotes++;
35445
+ decayedUpvotes += value;
35446
+ } else {
35447
+ rawDownvotes++;
35448
+ decayedDownvotes += value;
35449
+ }
35450
+ }
35451
+ const totalDecayed = decayedUpvotes + decayedDownvotes;
35452
+ const voteRatio = totalDecayed > 0 ? decayedUpvotes / totalDecayed : 0;
35453
+ const netVotes = decayedUpvotes - decayedDownvotes;
35454
+ const decayedScore = netVotes * voteRatio;
35455
+ return {
35456
+ mandate_id: mandateId,
35457
+ net_votes: netVotes,
35458
+ vote_ratio: voteRatio,
35459
+ decayed_score: decayedScore,
35460
+ last_calculated: now.toISOString(),
35461
+ raw_upvotes: rawUpvotes,
35462
+ raw_downvotes: rawDownvotes,
35463
+ decayed_upvotes: decayedUpvotes,
35464
+ decayed_downvotes: decayedDownvotes
35465
+ };
35466
+ }
35467
+ async close() {}
35468
+ }
35469
+
35470
+ class InMemoryMandateStorage {
35471
+ entries = new Map;
35472
+ votes = new Map;
35473
+ config;
35474
+ constructor(config2 = {}) {
35475
+ const fullConfig = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
35476
+ this.config = fullConfig.decay;
35477
+ }
35478
+ async store(entry) {
35479
+ this.entries.set(entry.id, entry);
35480
+ }
35481
+ async get(id) {
35482
+ return this.entries.get(id) || null;
35483
+ }
35484
+ async find(query, limit = 10) {
35485
+ const lowerQuery = query.toLowerCase();
35486
+ const results = Array.from(this.entries.values()).filter((entry) => entry.content.toLowerCase().includes(lowerQuery) || entry.tags.some((tag) => tag.toLowerCase().includes(lowerQuery)));
35487
+ return results.slice(0, limit);
35488
+ }
35489
+ async list(filter2) {
35490
+ let results = Array.from(this.entries.values());
35491
+ if (filter2) {
35492
+ results = results.filter((entry) => {
35493
+ if (filter2.status && entry.status !== filter2.status)
35494
+ return false;
35495
+ if (filter2.content_type && entry.content_type !== filter2.content_type)
35496
+ return false;
35497
+ return true;
35498
+ });
35499
+ }
35500
+ return results;
35501
+ }
35502
+ async update(id, updates) {
35503
+ const existing = await this.get(id);
35504
+ if (!existing) {
35505
+ throw new Error(`Mandate ${id} not found`);
35506
+ }
35507
+ const updated = { ...existing, ...updates };
35508
+ this.entries.set(id, updated);
35509
+ }
35510
+ async vote(vote) {
35511
+ const existing = await this.hasVoted(vote.mandate_id, vote.agent_name);
35512
+ if (existing) {
35513
+ throw new Error(`Agent ${vote.agent_name} has already voted on mandate ${vote.mandate_id}`);
35514
+ }
35515
+ this.votes.set(vote.id, vote);
35516
+ }
35517
+ async getVotes(mandateId) {
35518
+ return Array.from(this.votes.values()).filter((vote) => vote.mandate_id === mandateId);
35519
+ }
35520
+ async hasVoted(mandateId, agentName) {
35521
+ const votes = await this.getVotes(mandateId);
35522
+ return votes.some((vote) => vote.agent_name === agentName);
35523
+ }
35524
+ async calculateScore(mandateId) {
35525
+ const votes = await this.getVotes(mandateId);
35526
+ const now = new Date;
35527
+ let rawUpvotes = 0;
35528
+ let rawDownvotes = 0;
35529
+ let decayedUpvotes = 0;
35530
+ let decayedDownvotes = 0;
35531
+ for (const vote of votes) {
35532
+ const decayed = calculateDecayedValue(vote.timestamp, now, this.config.halfLifeDays);
35533
+ const value = vote.weight * decayed;
35534
+ if (vote.vote_type === "upvote") {
35535
+ rawUpvotes++;
35536
+ decayedUpvotes += value;
35537
+ } else {
35538
+ rawDownvotes++;
35539
+ decayedDownvotes += value;
35540
+ }
35541
+ }
35542
+ const totalDecayed = decayedUpvotes + decayedDownvotes;
35543
+ const voteRatio = totalDecayed > 0 ? decayedUpvotes / totalDecayed : 0;
35544
+ const netVotes = decayedUpvotes - decayedDownvotes;
35545
+ const decayedScore = netVotes * voteRatio;
35546
+ return {
35547
+ mandate_id: mandateId,
35548
+ net_votes: netVotes,
35549
+ vote_ratio: voteRatio,
35550
+ decayed_score: decayedScore,
35551
+ last_calculated: now.toISOString(),
35552
+ raw_upvotes: rawUpvotes,
35553
+ raw_downvotes: rawDownvotes,
35554
+ decayed_upvotes: decayedUpvotes,
35555
+ decayed_downvotes: decayedDownvotes
35556
+ };
35557
+ }
35558
+ async close() {}
35559
+ }
35560
+ function createMandateStorage(config2 = {}) {
35561
+ const fullConfig = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
35562
+ switch (fullConfig.backend) {
35563
+ case "semantic-memory":
35564
+ return new SemanticMemoryMandateStorage(fullConfig);
35565
+ case "memory":
35566
+ return new InMemoryMandateStorage(fullConfig);
35567
+ default:
35568
+ throw new Error(`Unknown storage backend: ${fullConfig.backend}`);
35569
+ }
35570
+ }
35571
+ async function updateMandateStatus(mandateId, storage) {
35572
+ const entry = await storage.get(mandateId);
35573
+ if (!entry) {
35574
+ throw new Error(`Mandate ${mandateId} not found`);
35575
+ }
35576
+ const score = await storage.calculateScore(mandateId);
35577
+ const previousStatus = entry.status;
35578
+ let newStatus;
35579
+ const config2 = DEFAULT_MANDATE_DECAY_CONFIG;
35580
+ if (score.net_votes >= config2.mandateNetVotesThreshold && score.vote_ratio >= config2.mandateVoteRatioThreshold) {
35581
+ newStatus = "mandate";
35582
+ } else if (score.net_votes <= config2.rejectedNetVotesThreshold) {
35583
+ newStatus = "rejected";
35584
+ } else if (score.net_votes >= config2.establishedNetVotesThreshold) {
35585
+ newStatus = "established";
35586
+ } else {
35587
+ newStatus = "candidate";
35588
+ }
35589
+ if (newStatus !== previousStatus) {
35590
+ await storage.update(mandateId, { status: newStatus });
35591
+ }
35592
+ return {
35593
+ mandate_id: mandateId,
35594
+ previous_status: previousStatus,
35595
+ new_status: newStatus,
35596
+ score,
35597
+ status_changed: newStatus !== previousStatus
35598
+ };
35599
+ }
35600
+ async function updateAllMandateStatuses(storage) {
35601
+ const allEntries = await storage.list();
35602
+ const results = [];
35603
+ for (const entry of allEntries) {
35604
+ const result = await updateMandateStatus(entry.id, storage);
35605
+ results.push(result);
35606
+ }
35607
+ return results;
35608
+ }
35609
+ var globalMandateStorage = null;
35610
+ function getMandateStorage() {
35611
+ if (!globalMandateStorage) {
35612
+ globalMandateStorage = createMandateStorage();
35613
+ }
35614
+ return globalMandateStorage;
35615
+ }
35616
+ function setMandateStorage(storage) {
35617
+ globalMandateStorage = storage;
35618
+ }
35619
+ async function resetMandateStorage() {
35620
+ if (globalMandateStorage) {
35621
+ await globalMandateStorage.close();
35622
+ globalMandateStorage = null;
35623
+ }
35624
+ }
35625
+
35626
+ // src/mandate-promotion.ts
35627
+ function shouldPromote(score, currentStatus, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
35628
+ if (currentStatus === "rejected") {
35629
+ return "rejected";
35630
+ }
35631
+ if (currentStatus === "mandate") {
35632
+ return "mandate";
35633
+ }
35634
+ if (score.net_votes <= config2.rejectedNetVotesThreshold) {
35635
+ return "rejected";
35636
+ }
35637
+ if (currentStatus === "established") {
35638
+ if (score.net_votes >= config2.mandateNetVotesThreshold && score.vote_ratio >= config2.mandateVoteRatioThreshold) {
35639
+ return "mandate";
35640
+ }
35641
+ return "established";
35642
+ }
35643
+ if (score.net_votes >= config2.establishedNetVotesThreshold) {
35644
+ return "established";
35645
+ }
35646
+ return "candidate";
35647
+ }
35648
+ function evaluatePromotion(entry, score, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
35649
+ const previousStatus = entry.status;
35650
+ const newStatus = shouldPromote(score, previousStatus, config2);
35651
+ const promoted = newStatus !== previousStatus;
35652
+ let reason;
35653
+ if (newStatus === "rejected" && previousStatus === "rejected") {
35654
+ reason = `Remains rejected (permanent)`;
35655
+ } else if (newStatus === "rejected") {
35656
+ reason = `Rejected due to negative consensus (net_votes: ${score.net_votes.toFixed(2)} ≤ ${config2.rejectedNetVotesThreshold})`;
35657
+ } else if (newStatus === "mandate" && previousStatus === "mandate") {
35658
+ reason = `Remains mandate (no demotion)`;
35659
+ } else if (newStatus === "mandate" && previousStatus === "established") {
35660
+ reason = `Promoted to mandate (net_votes: ${score.net_votes.toFixed(2)} ≥ ${config2.mandateNetVotesThreshold}, ratio: ${score.vote_ratio.toFixed(2)} ≥ ${config2.mandateVoteRatioThreshold})`;
35661
+ } else if (newStatus === "established" && previousStatus === "established") {
35662
+ reason = `Remains established (net_votes: ${score.net_votes.toFixed(2)}, ratio: ${score.vote_ratio.toFixed(2)} below mandate threshold)`;
35663
+ } else if (newStatus === "established" && previousStatus === "candidate") {
35664
+ reason = `Promoted to established (net_votes: ${score.net_votes.toFixed(2)} ≥ ${config2.establishedNetVotesThreshold})`;
35665
+ } else if (newStatus === "candidate") {
35666
+ reason = `Remains candidate (net_votes: ${score.net_votes.toFixed(2)} below threshold)`;
35667
+ } else {
35668
+ reason = `No status change (current: ${previousStatus})`;
35669
+ }
35670
+ return {
35671
+ mandate_id: entry.id,
35672
+ previous_status: previousStatus,
35673
+ new_status: newStatus,
35674
+ score,
35675
+ promoted,
35676
+ reason
35677
+ };
35678
+ }
35679
+ function formatPromotionResult(result) {
35680
+ const arrow = result.promoted ? `${result.previous_status} → ${result.new_status}` : result.new_status;
35681
+ return `[${result.mandate_id}] ${arrow}: ${result.reason}`;
35682
+ }
35683
+ function evaluateBatchPromotions(entries, scores, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
35684
+ const results = [];
35685
+ for (const [id, entry] of entries) {
35686
+ const score = scores.get(id);
35687
+ if (!score) {
35688
+ continue;
35689
+ }
35690
+ const result = evaluatePromotion(entry, score, config2);
35691
+ results.push(result);
35692
+ }
35693
+ return results;
35694
+ }
35695
+ function getStatusChanges(results) {
35696
+ return results.filter((r) => r.promoted);
35697
+ }
35698
+ function groupByTransition(results) {
35699
+ const groups = new Map;
35700
+ for (const result of results) {
35701
+ const key = result.promoted ? `${result.previous_status}→${result.new_status}` : result.new_status;
35702
+ const existing = groups.get(key);
35703
+ if (existing) {
35704
+ existing.push(result);
35705
+ } else {
35706
+ groups.set(key, [result]);
35707
+ }
35708
+ }
35709
+ return groups;
35710
+ }
35711
+
35712
+ // src/mandates.ts
35713
+ class MandateError extends Error {
35714
+ operation;
35715
+ details;
35716
+ constructor(message, operation, details) {
35717
+ super(message);
35718
+ this.operation = operation;
35719
+ this.details = details;
35720
+ this.name = "MandateError";
35721
+ }
35722
+ }
35723
+ function generateMandateId() {
35724
+ const timestamp = Date.now().toString(36);
35725
+ const random = Math.random().toString(36).substring(2, 8);
35726
+ return `mandate-${timestamp}-${random}`;
35727
+ }
35728
+ function generateVoteId() {
35729
+ const timestamp = Date.now().toString(36);
35730
+ const random = Math.random().toString(36).substring(2, 8);
35731
+ return `vote-${timestamp}-${random}`;
35732
+ }
35733
+ var mandate_file = tool({
35734
+ description: "Submit a new idea, tip, lore, snippet, or feature request to the mandate system",
35735
+ args: {
35736
+ content: tool.schema.string().min(1).describe("The content to submit"),
35737
+ content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).describe("Type of content"),
35738
+ tags: tool.schema.array(tool.schema.string()).optional().describe("Optional tags for categorization"),
35739
+ metadata: tool.schema.record(tool.schema.string(), tool.schema.unknown()).optional().describe("Optional metadata (e.g., code language for snippets)")
35740
+ },
35741
+ async execute(args) {
35742
+ const validated = CreateMandateArgsSchema.parse(args);
35743
+ const agentName = "system";
35744
+ const entry = {
35745
+ id: generateMandateId(),
35746
+ content: validated.content,
35747
+ content_type: validated.content_type,
35748
+ author_agent: agentName,
35749
+ created_at: new Date().toISOString(),
35750
+ status: "candidate",
35751
+ tags: validated.tags || [],
35752
+ metadata: validated.metadata
35753
+ };
35754
+ const validatedEntry = MandateEntrySchema.parse(entry);
35755
+ const storage = getMandateStorage();
35756
+ try {
35757
+ await storage.store(validatedEntry);
35758
+ } catch (error45) {
35759
+ throw new MandateError(`Failed to store mandate: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_file", error45);
35760
+ }
35761
+ return JSON.stringify({
35762
+ success: true,
35763
+ mandate: validatedEntry,
35764
+ message: `Mandate ${validatedEntry.id} filed successfully`
35765
+ }, null, 2);
35766
+ }
35767
+ });
35768
+ var mandate_vote = tool({
35769
+ description: "Cast a vote (upvote or downvote) on an existing mandate",
35770
+ args: {
35771
+ mandate_id: tool.schema.string().describe("Mandate ID to vote on"),
35772
+ vote_type: tool.schema.enum(["upvote", "downvote"]).describe("Type of vote"),
35773
+ agent_name: tool.schema.string().describe("Agent name casting the vote")
35774
+ },
35775
+ async execute(args) {
35776
+ const validated = CastVoteArgsSchema.parse({
35777
+ mandate_id: args.mandate_id,
35778
+ vote_type: args.vote_type,
35779
+ weight: 1
35780
+ });
35781
+ const storage = getMandateStorage();
35782
+ const mandate = await storage.get(validated.mandate_id);
35783
+ if (!mandate) {
35784
+ throw new MandateError(`Mandate ${validated.mandate_id} not found`, "mandate_vote");
35785
+ }
35786
+ const hasVoted = await storage.hasVoted(validated.mandate_id, args.agent_name);
35787
+ if (hasVoted) {
35788
+ throw new MandateError(`Agent ${args.agent_name} has already voted on mandate ${validated.mandate_id}`, "mandate_vote");
35789
+ }
35790
+ const vote = {
35791
+ id: generateVoteId(),
35792
+ mandate_id: validated.mandate_id,
35793
+ agent_name: args.agent_name,
35794
+ vote_type: validated.vote_type,
35795
+ timestamp: new Date().toISOString(),
35796
+ weight: validated.weight
35797
+ };
35798
+ const validatedVote = VoteSchema.parse(vote);
35799
+ try {
35800
+ await storage.vote(validatedVote);
35801
+ } catch (error45) {
35802
+ throw new MandateError(`Failed to cast vote: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_vote", error45);
35803
+ }
35804
+ const promotion = await updateMandateStatus(validated.mandate_id, storage);
35805
+ return JSON.stringify({
35806
+ success: true,
35807
+ vote: validatedVote,
35808
+ promotion: {
35809
+ previous_status: promotion.previous_status,
35810
+ new_status: promotion.new_status,
35811
+ status_changed: promotion.status_changed,
35812
+ score: promotion.score
35813
+ },
35814
+ message: formatPromotionResult({
35815
+ mandate_id: promotion.mandate_id,
35816
+ previous_status: promotion.previous_status,
35817
+ new_status: promotion.new_status,
35818
+ score: promotion.score,
35819
+ promoted: promotion.status_changed,
35820
+ reason: evaluatePromotion(mandate, promotion.score).reason || "Vote recorded"
35821
+ })
35822
+ }, null, 2);
35823
+ }
35824
+ });
35825
+ var mandate_query = tool({
35826
+ description: "Search for relevant mandates using semantic search (by meaning, not keywords)",
35827
+ args: {
35828
+ query: tool.schema.string().min(1).describe("Natural language query"),
35829
+ limit: tool.schema.number().int().positive().optional().describe("Max results to return (default: 5)"),
35830
+ status: tool.schema.enum(["candidate", "established", "mandate", "rejected"]).optional().describe("Filter by status"),
35831
+ content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).optional().describe("Filter by content type")
35832
+ },
35833
+ async execute(args) {
35834
+ const storage = getMandateStorage();
35835
+ const limit = args.limit ?? 5;
35836
+ try {
35837
+ let results = await storage.find(args.query, limit * 2);
35838
+ if (args.status) {
35839
+ results = results.filter((m) => m.status === args.status);
35840
+ }
35841
+ if (args.content_type) {
35842
+ results = results.filter((m) => m.content_type === args.content_type);
35843
+ }
35844
+ results = results.slice(0, limit);
35845
+ const resultsWithScores = await Promise.all(results.map(async (mandate) => {
35846
+ const score = await storage.calculateScore(mandate.id);
35847
+ return { mandate, score };
35848
+ }));
35849
+ resultsWithScores.sort((a, b) => b.score.decayed_score - a.score.decayed_score);
35850
+ return JSON.stringify({
35851
+ query: args.query,
35852
+ count: resultsWithScores.length,
35853
+ results: resultsWithScores.map(({ mandate, score }) => ({
35854
+ id: mandate.id,
35855
+ content: mandate.content,
35856
+ content_type: mandate.content_type,
35857
+ status: mandate.status,
35858
+ author: mandate.author_agent,
35859
+ created_at: mandate.created_at,
35860
+ tags: mandate.tags,
35861
+ score: {
35862
+ net_votes: score.net_votes,
35863
+ vote_ratio: score.vote_ratio,
35864
+ decayed_score: score.decayed_score
35865
+ }
35866
+ }))
35867
+ }, null, 2);
35868
+ } catch (error45) {
35869
+ throw new MandateError(`Failed to query mandates: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_query", error45);
35870
+ }
35871
+ }
35872
+ });
35873
+ var mandate_list = tool({
35874
+ description: "List mandates with optional filters (status, content type)",
35875
+ args: {
35876
+ status: tool.schema.enum(["candidate", "established", "mandate", "rejected"]).optional().describe("Filter by status"),
35877
+ content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).optional().describe("Filter by content type"),
35878
+ limit: tool.schema.number().int().positive().optional().describe("Max results to return (default: 20)")
35879
+ },
35880
+ async execute(args) {
35881
+ const storage = getMandateStorage();
35882
+ const limit = args.limit ?? 20;
35883
+ try {
35884
+ let results = await storage.list({
35885
+ status: args.status,
35886
+ content_type: args.content_type
35887
+ });
35888
+ results = results.slice(0, limit);
35889
+ const resultsWithScores = await Promise.all(results.map(async (mandate) => {
35890
+ const score = await storage.calculateScore(mandate.id);
35891
+ return { mandate, score };
35892
+ }));
35893
+ resultsWithScores.sort((a, b) => b.score.decayed_score - a.score.decayed_score);
35894
+ return JSON.stringify({
35895
+ filters: {
35896
+ status: args.status || "all",
35897
+ content_type: args.content_type || "all"
35898
+ },
35899
+ count: resultsWithScores.length,
35900
+ results: resultsWithScores.map(({ mandate, score }) => ({
35901
+ id: mandate.id,
35902
+ content: mandate.content.slice(0, 200),
35903
+ content_type: mandate.content_type,
35904
+ status: mandate.status,
35905
+ author: mandate.author_agent,
35906
+ created_at: mandate.created_at,
35907
+ tags: mandate.tags,
35908
+ score: {
35909
+ net_votes: score.net_votes,
35910
+ vote_ratio: score.vote_ratio,
35911
+ decayed_score: score.decayed_score
35912
+ }
35913
+ }))
35914
+ }, null, 2);
35915
+ } catch (error45) {
35916
+ throw new MandateError(`Failed to list mandates: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_list", error45);
35917
+ }
35918
+ }
35919
+ });
35920
+ var mandate_stats = tool({
35921
+ description: "Get voting statistics for a specific mandate or overall system",
35922
+ args: {
35923
+ mandate_id: tool.schema.string().optional().describe("Mandate ID (omit for overall stats)")
35924
+ },
35925
+ async execute(args) {
35926
+ const storage = getMandateStorage();
35927
+ try {
35928
+ if (args.mandate_id) {
35929
+ const mandate = await storage.get(args.mandate_id);
35930
+ if (!mandate) {
35931
+ throw new MandateError(`Mandate ${args.mandate_id} not found`, "mandate_stats");
35932
+ }
35933
+ const score = await storage.calculateScore(args.mandate_id);
35934
+ const votes = await storage.getVotes(args.mandate_id);
35935
+ return JSON.stringify({
35936
+ mandate_id: args.mandate_id,
35937
+ status: mandate.status,
35938
+ content_type: mandate.content_type,
35939
+ author: mandate.author_agent,
35940
+ created_at: mandate.created_at,
35941
+ votes: {
35942
+ total: votes.length,
35943
+ raw_upvotes: score.raw_upvotes,
35944
+ raw_downvotes: score.raw_downvotes,
35945
+ decayed_upvotes: score.decayed_upvotes,
35946
+ decayed_downvotes: score.decayed_downvotes,
35947
+ net_votes: score.net_votes,
35948
+ vote_ratio: score.vote_ratio,
35949
+ decayed_score: score.decayed_score
35950
+ },
35951
+ voters: votes.map((v) => ({
35952
+ agent: v.agent_name,
35953
+ vote_type: v.vote_type,
35954
+ timestamp: v.timestamp
35955
+ }))
35956
+ }, null, 2);
35957
+ } else {
35958
+ const allMandates = await storage.list();
35959
+ const stats = {
35960
+ total_mandates: allMandates.length,
35961
+ by_status: {
35962
+ candidate: 0,
35963
+ established: 0,
35964
+ mandate: 0,
35965
+ rejected: 0
35966
+ },
35967
+ by_content_type: {
35968
+ idea: 0,
35969
+ tip: 0,
35970
+ lore: 0,
35971
+ snippet: 0,
35972
+ feature_request: 0
35973
+ },
35974
+ total_votes: 0
35975
+ };
35976
+ for (const mandate of allMandates) {
35977
+ stats.by_status[mandate.status]++;
35978
+ stats.by_content_type[mandate.content_type]++;
35979
+ const votes = await storage.getVotes(mandate.id);
35980
+ stats.total_votes += votes.length;
35981
+ }
35982
+ return JSON.stringify(stats, null, 2);
35983
+ }
35984
+ } catch (error45) {
35985
+ if (error45 instanceof MandateError) {
35986
+ throw error45;
35987
+ }
35988
+ throw new MandateError(`Failed to get mandate stats: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_stats", error45);
35989
+ }
35990
+ }
35991
+ });
35992
+ var mandateTools = {
35993
+ mandate_file,
35994
+ mandate_vote,
35995
+ mandate_query,
35996
+ mandate_list,
35997
+ mandate_stats
35998
+ };
34740
35999
  // src/anti-patterns.ts
34741
36000
  init_zod();
34742
36001
  var PatternKindSchema = exports_external.enum(["pattern", "anti_pattern"]);
@@ -34828,21 +36087,21 @@ class InMemoryMaturityStorage {
34828
36087
  }
34829
36088
 
34830
36089
  // src/storage.ts
34831
- var cachedCommand = null;
34832
- async function resolveSemanticMemoryCommand() {
34833
- if (cachedCommand)
34834
- return cachedCommand;
36090
+ var cachedCommand2 = null;
36091
+ async function resolveSemanticMemoryCommand2() {
36092
+ if (cachedCommand2)
36093
+ return cachedCommand2;
34835
36094
  const nativeResult = await Bun.$`which semantic-memory`.quiet().nothrow();
34836
36095
  if (nativeResult.exitCode === 0) {
34837
- cachedCommand = ["semantic-memory"];
34838
- return cachedCommand;
36096
+ cachedCommand2 = ["semantic-memory"];
36097
+ return cachedCommand2;
34839
36098
  }
34840
- cachedCommand = ["bunx", "semantic-memory"];
34841
- return cachedCommand;
36099
+ cachedCommand2 = ["bunx", "semantic-memory"];
36100
+ return cachedCommand2;
34842
36101
  }
34843
- async function execSemanticMemory(args) {
36102
+ async function execSemanticMemory2(args) {
34844
36103
  try {
34845
- const cmd = await resolveSemanticMemoryCommand();
36104
+ const cmd = await resolveSemanticMemoryCommand2();
34846
36105
  const fullCmd = [...cmd, ...args];
34847
36106
  const proc = Bun.spawn(fullCmd, {
34848
36107
  stdout: "pipe",
@@ -34886,7 +36145,7 @@ class SemanticMemoryStorage {
34886
36145
  if (metadata) {
34887
36146
  args.push("--metadata", JSON.stringify(metadata));
34888
36147
  }
34889
- await execSemanticMemory(args);
36148
+ await execSemanticMemory2(args);
34890
36149
  }
34891
36150
  async find(collection, query, limit = 10, useFts = false) {
34892
36151
  const args = [
@@ -34901,7 +36160,7 @@ class SemanticMemoryStorage {
34901
36160
  if (useFts) {
34902
36161
  args.push("--fts");
34903
36162
  }
34904
- const result = await execSemanticMemory(args);
36163
+ const result = await execSemanticMemory2(args);
34905
36164
  if (result.exitCode !== 0) {
34906
36165
  console.warn(`[storage] semantic-memory find() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
34907
36166
  return [];
@@ -34926,7 +36185,7 @@ class SemanticMemoryStorage {
34926
36185
  }
34927
36186
  }
34928
36187
  async list(collection) {
34929
- const result = await execSemanticMemory([
36188
+ const result = await execSemanticMemory2([
34930
36189
  "list",
34931
36190
  "--collection",
34932
36191
  collection,
@@ -35111,7 +36370,7 @@ function createStorage(config2 = {}) {
35111
36370
  }
35112
36371
  async function isSemanticMemoryAvailable() {
35113
36372
  try {
35114
- const result = await execSemanticMemory(["stats"]);
36373
+ const result = await execSemanticMemory2(["stats"]);
35115
36374
  return result.exitCode === 0;
35116
36375
  } catch {
35117
36376
  return false;
@@ -35196,7 +36455,8 @@ var SwarmPlugin = async (input) => {
35196
36455
  ...structuredTools,
35197
36456
  ...swarmTools,
35198
36457
  ...repoCrawlTools,
35199
- ...skillsTools
36458
+ ...skillsTools,
36459
+ ...mandateTools
35200
36460
  },
35201
36461
  event: async ({ event }) => {
35202
36462
  if (event.type === "session.idle") {
@@ -35243,27 +36503,35 @@ var allTools = {
35243
36503
  ...structuredTools,
35244
36504
  ...swarmTools,
35245
36505
  ...repoCrawlTools,
35246
- ...skillsTools
36506
+ ...skillsTools,
36507
+ ...mandateTools
35247
36508
  };
35248
36509
  export {
35249
36510
  withToolFallback,
35250
36511
  warnMissingTool,
36512
+ updateMandateStatus,
36513
+ updateAllMandateStatuses,
35251
36514
  swarmTools,
35252
36515
  swarmMailTools,
35253
36516
  structuredTools,
35254
36517
  skillsTools,
36518
+ shouldPromote,
35255
36519
  setSwarmMailProjectDirectory,
35256
36520
  setStorage,
35257
36521
  setSkillsProjectDirectory,
36522
+ setMandateStorage,
35258
36523
  setBeadsWorkingDirectory,
35259
36524
  setAgentMailProjectDirectory,
35260
36525
  selectStrategy,
35261
36526
  resetToolCache,
35262
36527
  resetStorage,
36528
+ resetMandateStorage,
35263
36529
  requireTool,
35264
36530
  repoCrawlTools,
35265
36531
  parseFrontmatter,
35266
36532
  mcpCallWithAutoInit,
36533
+ mandateTools,
36534
+ mandateSchemas,
35267
36535
  listSkills,
35268
36536
  isToolAvailable,
35269
36537
  isSemanticMemoryAvailable,
@@ -35271,12 +36539,15 @@ export {
35271
36539
  isAgentNotFoundError,
35272
36540
  invalidateSkillsCache,
35273
36541
  ifToolAvailable,
36542
+ groupByTransition,
35274
36543
  getToolAvailability,
35275
36544
  getSwarmMailProjectDirectory,
35276
36545
  getStorage,
36546
+ getStatusChanges,
35277
36547
  getSkillsContextForSwarm,
35278
36548
  getSkill,
35279
36549
  getSchemaByName,
36550
+ getMandateStorage,
35280
36551
  getBeadsWorkingDirectory,
35281
36552
  getAgentMailProjectDirectory,
35282
36553
  formatZodErrors,
@@ -35284,13 +36555,17 @@ export {
35284
36555
  formatSubtaskPromptV2,
35285
36556
  formatSubtaskPrompt,
35286
36557
  formatStrategyGuidelines,
36558
+ formatPromotionResult,
35287
36559
  formatEvaluationPrompt,
35288
36560
  findRelevantSkills,
35289
36561
  extractJsonFromText,
36562
+ evaluatePromotion,
36563
+ evaluateBatchPromotions,
35290
36564
  discoverSkills,
35291
36565
  src_default as default,
35292
36566
  createStorageWithFallback,
35293
36567
  createStorage,
36568
+ createMandateStorage,
35294
36569
  createAgentMailError,
35295
36570
  clearSessionState,
35296
36571
  checkTool,
@@ -35309,6 +36584,8 @@ export {
35309
36584
  agentMailTools,
35310
36585
  WeightedEvaluationSchema,
35311
36586
  WeightedCriterionEvaluationSchema,
36587
+ VoteTypeSchema,
36588
+ VoteSchema,
35312
36589
  ValidationResultSchema,
35313
36590
  TaskDecompositionSchema,
35314
36591
  SwarmStatusSchema,
@@ -35320,10 +36597,19 @@ export {
35320
36597
  SubtaskDependencySchema,
35321
36598
  SpawnedAgentSchema,
35322
36599
  SemanticMemoryStorage,
36600
+ SemanticMemoryMandateStorage,
36601
+ ScoreCalculationResultSchema,
35323
36602
  SUBTASK_PROMPT_V2,
35324
36603
  STRATEGIES,
35325
36604
  RepoCrawlError,
36605
+ QueryMandatesArgsSchema,
36606
+ MandateStatusSchema,
36607
+ MandateScoreSchema,
36608
+ MandateError,
36609
+ MandateEntrySchema,
36610
+ MandateContentTypeSchema,
35326
36611
  InMemoryStorage,
36612
+ InMemoryMandateStorage,
35327
36613
  FileReservationConflictError,
35328
36614
  EvaluationSchema,
35329
36615
  EvaluationRequestSchema,
@@ -35335,8 +36621,12 @@ export {
35335
36621
  DecomposedSubtaskSchema,
35336
36622
  DecomposeArgsSchema,
35337
36623
  DEFAULT_STORAGE_CONFIG,
36624
+ DEFAULT_MANDATE_STORAGE_CONFIG,
36625
+ DEFAULT_MANDATE_DECAY_CONFIG,
35338
36626
  DEFAULT_CRITERIA,
35339
36627
  CriterionEvaluationSchema,
36628
+ CreateMandateArgsSchema,
36629
+ CastVoteArgsSchema,
35340
36630
  BeadValidationError,
35341
36631
  BeadUpdateArgsSchema,
35342
36632
  BeadTypeSchema,