opencode-swarm-plugin 0.12.10 → 0.12.11
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/issues.jsonl +7 -7
- package/dist/index.js +459 -33
- package/dist/plugin.js +457 -33
- package/package.json +1 -1
- package/src/index.ts +21 -0
- package/src/learning.ts +19 -0
- package/src/repo-crawl.ts +610 -0
- package/src/schemas/evaluation.ts +21 -0
- package/src/swarm.ts +173 -29
package/dist/index.js
CHANGED
|
@@ -21683,6 +21683,16 @@ var ValidationResultSchema = exports_external.object({
|
|
|
21683
21683
|
errors: exports_external.array(exports_external.string()).optional(),
|
|
21684
21684
|
extractionMethod: exports_external.string().optional()
|
|
21685
21685
|
});
|
|
21686
|
+
var FailureModeSchema = exports_external.enum([
|
|
21687
|
+
"timeout",
|
|
21688
|
+
"conflict",
|
|
21689
|
+
"validation",
|
|
21690
|
+
"tool_failure",
|
|
21691
|
+
"context_overflow",
|
|
21692
|
+
"dependency_blocked",
|
|
21693
|
+
"user_cancelled",
|
|
21694
|
+
"unknown"
|
|
21695
|
+
]);
|
|
21686
21696
|
// src/schemas/task.ts
|
|
21687
21697
|
var EffortLevelSchema = exports_external.enum([
|
|
21688
21698
|
"trivial",
|
|
@@ -23777,6 +23787,16 @@ var DecompositionStrategySchema = exports_external.enum([
|
|
|
23777
23787
|
"risk-based",
|
|
23778
23788
|
"research-based"
|
|
23779
23789
|
]);
|
|
23790
|
+
var FailureModeSchema2 = exports_external.enum([
|
|
23791
|
+
"timeout",
|
|
23792
|
+
"conflict",
|
|
23793
|
+
"validation",
|
|
23794
|
+
"tool_failure",
|
|
23795
|
+
"context_overflow",
|
|
23796
|
+
"dependency_blocked",
|
|
23797
|
+
"user_cancelled",
|
|
23798
|
+
"unknown"
|
|
23799
|
+
]);
|
|
23780
23800
|
var OutcomeSignalsSchema = exports_external.object({
|
|
23781
23801
|
bead_id: exports_external.string(),
|
|
23782
23802
|
duration_ms: exports_external.number().int().min(0),
|
|
@@ -23785,7 +23805,9 @@ var OutcomeSignalsSchema = exports_external.object({
|
|
|
23785
23805
|
success: exports_external.boolean(),
|
|
23786
23806
|
files_touched: exports_external.array(exports_external.string()).default([]),
|
|
23787
23807
|
timestamp: exports_external.string(),
|
|
23788
|
-
strategy: DecompositionStrategySchema.optional()
|
|
23808
|
+
strategy: DecompositionStrategySchema.optional(),
|
|
23809
|
+
failure_mode: FailureModeSchema2.optional(),
|
|
23810
|
+
failure_details: exports_external.string().optional()
|
|
23789
23811
|
});
|
|
23790
23812
|
var ScoredOutcomeSchema = exports_external.object({
|
|
23791
23813
|
signals: OutcomeSignalsSchema,
|
|
@@ -24428,51 +24450,46 @@ Before writing code:
|
|
|
24428
24450
|
Begin work on your subtask now.`;
|
|
24429
24451
|
var SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
|
|
24430
24452
|
|
|
24431
|
-
##
|
|
24432
|
-
|
|
24433
|
-
|
|
24453
|
+
## [IDENTITY]
|
|
24454
|
+
Agent: (assigned at spawn)
|
|
24455
|
+
Bead: {bead_id}
|
|
24456
|
+
Epic: {epic_id}
|
|
24434
24457
|
|
|
24435
|
-
##
|
|
24458
|
+
## [TASK]
|
|
24436
24459
|
{subtask_description}
|
|
24437
24460
|
|
|
24438
|
-
##
|
|
24461
|
+
## [FILES]
|
|
24462
|
+
Reserved (exclusive):
|
|
24439
24463
|
{file_list}
|
|
24440
24464
|
|
|
24441
24465
|
Only modify these files. Need others? Message the coordinator.
|
|
24442
24466
|
|
|
24443
|
-
##
|
|
24467
|
+
## [CONTEXT]
|
|
24444
24468
|
{shared_context}
|
|
24445
24469
|
|
|
24446
24470
|
{compressed_context}
|
|
24447
24471
|
|
|
24448
24472
|
{error_context}
|
|
24449
24473
|
|
|
24450
|
-
##
|
|
24474
|
+
## [TOOLS]
|
|
24475
|
+
### Beads
|
|
24476
|
+
- beads_update (status: blocked)
|
|
24477
|
+
- beads_create (new bugs)
|
|
24478
|
+
- beads_close (via swarm_complete)
|
|
24451
24479
|
|
|
24452
|
-
### Agent Mail
|
|
24453
|
-
|
|
24454
|
-
// Report progress, ask questions, announce blockers
|
|
24455
|
-
agentmail_send({
|
|
24456
|
-
to: ["coordinator"],
|
|
24457
|
-
subject: "Progress update",
|
|
24458
|
-
body: "What you did or need",
|
|
24459
|
-
thread_id: "{epic_id}"
|
|
24460
|
-
})
|
|
24461
|
-
\`\`\`
|
|
24480
|
+
### Agent Mail
|
|
24481
|
+
- agentmail_send (thread_id: {epic_id})
|
|
24462
24482
|
|
|
24463
|
-
###
|
|
24464
|
-
-
|
|
24465
|
-
- **Found bug?** \`beads_create({ title: "Bug description", type: "bug" })\`
|
|
24466
|
-
- **Done?** \`swarm_complete({ bead_id: "{bead_id}", summary: "What you did", files_touched: [...] })\`
|
|
24483
|
+
### Completion
|
|
24484
|
+
- swarm_complete (REQUIRED when done)
|
|
24467
24485
|
|
|
24468
|
-
##
|
|
24486
|
+
## [OUTPUT]
|
|
24487
|
+
1. Read files first
|
|
24488
|
+
2. Implement changes
|
|
24489
|
+
3. Verify (typecheck)
|
|
24490
|
+
4. Complete with swarm_complete
|
|
24469
24491
|
|
|
24470
|
-
|
|
24471
|
-
2. **Plan** your approach (message coordinator if complex)
|
|
24472
|
-
3. **Implement** the changes
|
|
24473
|
-
4. **Verify** (typecheck, tests)
|
|
24474
|
-
5. **Report** progress via Agent Mail
|
|
24475
|
-
6. **Complete** with swarm_complete when done
|
|
24492
|
+
Return: Summary of changes made
|
|
24476
24493
|
|
|
24477
24494
|
**Never work silently.** Communicate progress and blockers immediately.
|
|
24478
24495
|
|
|
@@ -25071,6 +25088,52 @@ async function runUbsScan(files) {
|
|
|
25071
25088
|
return null;
|
|
25072
25089
|
}
|
|
25073
25090
|
}
|
|
25091
|
+
var swarm_broadcast = tool({
|
|
25092
|
+
description: "Broadcast context update to all agents working on the same epic",
|
|
25093
|
+
args: {
|
|
25094
|
+
epic_id: tool.schema.string().describe("Epic ID (e.g., bd-abc123)"),
|
|
25095
|
+
message: tool.schema.string().describe("Context update to share (what changed, what was learned)"),
|
|
25096
|
+
importance: tool.schema.enum(["info", "warning", "blocker"]).default("info").describe("Priority level (default: info)"),
|
|
25097
|
+
files_affected: tool.schema.array(tool.schema.string()).optional().describe("Files this context relates to")
|
|
25098
|
+
},
|
|
25099
|
+
async execute(args, ctx) {
|
|
25100
|
+
const state = requireState(ctx.sessionID);
|
|
25101
|
+
const beadId = ctx.beadId || "unknown";
|
|
25102
|
+
const body = [
|
|
25103
|
+
`## Context Update`,
|
|
25104
|
+
"",
|
|
25105
|
+
`**From**: ${state.agentName} (${beadId})`,
|
|
25106
|
+
`**Priority**: ${args.importance.toUpperCase()}`,
|
|
25107
|
+
"",
|
|
25108
|
+
args.message,
|
|
25109
|
+
"",
|
|
25110
|
+
args.files_affected && args.files_affected.length > 0 ? `**Files affected**:
|
|
25111
|
+
${args.files_affected.map((f) => `- \`${f}\``).join(`
|
|
25112
|
+
`)}` : ""
|
|
25113
|
+
].filter(Boolean).join(`
|
|
25114
|
+
`);
|
|
25115
|
+
const mailImportance = args.importance === "blocker" ? "urgent" : args.importance === "warning" ? "high" : "normal";
|
|
25116
|
+
await mcpCall("send_message", {
|
|
25117
|
+
project_key: state.projectKey,
|
|
25118
|
+
sender_name: state.agentName,
|
|
25119
|
+
to: [],
|
|
25120
|
+
subject: `[${args.importance.toUpperCase()}] Context update from ${state.agentName}`,
|
|
25121
|
+
body_md: body,
|
|
25122
|
+
thread_id: args.epic_id,
|
|
25123
|
+
importance: mailImportance,
|
|
25124
|
+
ack_required: args.importance === "blocker"
|
|
25125
|
+
});
|
|
25126
|
+
return JSON.stringify({
|
|
25127
|
+
broadcast: true,
|
|
25128
|
+
epic_id: args.epic_id,
|
|
25129
|
+
from: state.agentName,
|
|
25130
|
+
bead_id: beadId,
|
|
25131
|
+
importance: args.importance,
|
|
25132
|
+
recipients: "all agents in epic",
|
|
25133
|
+
ack_required: args.importance === "blocker"
|
|
25134
|
+
}, null, 2);
|
|
25135
|
+
}
|
|
25136
|
+
});
|
|
25074
25137
|
var swarm_complete = tool({
|
|
25075
25138
|
description: "Mark subtask complete, release reservations, notify coordinator. Runs UBS bug scan if files_touched provided.",
|
|
25076
25139
|
args: {
|
|
@@ -25163,6 +25226,25 @@ var swarm_complete = tool({
|
|
|
25163
25226
|
}, null, 2);
|
|
25164
25227
|
}
|
|
25165
25228
|
});
|
|
25229
|
+
function classifyFailure(error45) {
|
|
25230
|
+
const msg = (typeof error45 === "string" ? error45 : error45.message).toLowerCase();
|
|
25231
|
+
if (msg.includes("timeout"))
|
|
25232
|
+
return "timeout";
|
|
25233
|
+
if (msg.includes("conflict") || msg.includes("reservation"))
|
|
25234
|
+
return "conflict";
|
|
25235
|
+
if (msg.includes("validation") || msg.includes("schema"))
|
|
25236
|
+
return "validation";
|
|
25237
|
+
if (msg.includes("context") || msg.includes("token"))
|
|
25238
|
+
return "context_overflow";
|
|
25239
|
+
if (msg.includes("blocked") || msg.includes("dependency"))
|
|
25240
|
+
return "dependency_blocked";
|
|
25241
|
+
if (msg.includes("cancel"))
|
|
25242
|
+
return "user_cancelled";
|
|
25243
|
+
if (msg.includes("tool") || msg.includes("command") || msg.includes("failed to execute")) {
|
|
25244
|
+
return "tool_failure";
|
|
25245
|
+
}
|
|
25246
|
+
return "unknown";
|
|
25247
|
+
}
|
|
25166
25248
|
var swarm_record_outcome = tool({
|
|
25167
25249
|
description: "Record subtask outcome for implicit feedback scoring. Tracks duration, errors, retries to learn decomposition quality.",
|
|
25168
25250
|
args: {
|
|
@@ -25173,7 +25255,18 @@ var swarm_record_outcome = tool({
|
|
|
25173
25255
|
success: tool.schema.boolean().describe("Whether the subtask succeeded"),
|
|
25174
25256
|
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files that were modified"),
|
|
25175
25257
|
criteria: tool.schema.array(tool.schema.string()).optional().describe("Criteria to generate feedback for (default: all default criteria)"),
|
|
25176
|
-
strategy: tool.schema.enum(["file-based", "feature-based", "risk-based", "research-based"]).optional().describe("Decomposition strategy used for this task")
|
|
25258
|
+
strategy: tool.schema.enum(["file-based", "feature-based", "risk-based", "research-based"]).optional().describe("Decomposition strategy used for this task"),
|
|
25259
|
+
failure_mode: tool.schema.enum([
|
|
25260
|
+
"timeout",
|
|
25261
|
+
"conflict",
|
|
25262
|
+
"validation",
|
|
25263
|
+
"tool_failure",
|
|
25264
|
+
"context_overflow",
|
|
25265
|
+
"dependency_blocked",
|
|
25266
|
+
"user_cancelled",
|
|
25267
|
+
"unknown"
|
|
25268
|
+
]).optional().describe("Failure classification (only when success=false). Auto-classified if not provided."),
|
|
25269
|
+
failure_details: tool.schema.string().optional().describe("Detailed failure context (error message, stack trace, etc.)")
|
|
25177
25270
|
},
|
|
25178
25271
|
async execute(args) {
|
|
25179
25272
|
const signals = {
|
|
@@ -25184,8 +25277,13 @@ var swarm_record_outcome = tool({
|
|
|
25184
25277
|
success: args.success,
|
|
25185
25278
|
files_touched: args.files_touched ?? [],
|
|
25186
25279
|
timestamp: new Date().toISOString(),
|
|
25187
|
-
strategy: args.strategy
|
|
25280
|
+
strategy: args.strategy,
|
|
25281
|
+
failure_mode: args.failure_mode,
|
|
25282
|
+
failure_details: args.failure_details
|
|
25188
25283
|
};
|
|
25284
|
+
if (!args.success && !args.failure_mode && args.failure_details) {
|
|
25285
|
+
signals.failure_mode = classifyFailure(args.failure_details);
|
|
25286
|
+
}
|
|
25189
25287
|
const validated = OutcomeSignalsSchema.parse(signals);
|
|
25190
25288
|
const scored = scoreImplicitFeedback(validated, DEFAULT_LEARNING_CONFIG);
|
|
25191
25289
|
const errorStats = await globalErrorAccumulator.getErrorStats(args.bead_id);
|
|
@@ -25225,6 +25323,8 @@ var swarm_record_outcome = tool({
|
|
|
25225
25323
|
retry_count: args.retry_count ?? 0,
|
|
25226
25324
|
success: args.success,
|
|
25227
25325
|
strategy: args.strategy,
|
|
25326
|
+
failure_mode: validated.failure_mode,
|
|
25327
|
+
failure_details: validated.failure_details,
|
|
25228
25328
|
accumulated_errors: errorStats.total,
|
|
25229
25329
|
unresolved_errors: errorStats.unresolved
|
|
25230
25330
|
},
|
|
@@ -25516,6 +25616,7 @@ var swarmTools = {
|
|
|
25516
25616
|
swarm_validate_decomposition,
|
|
25517
25617
|
swarm_status,
|
|
25518
25618
|
swarm_progress,
|
|
25619
|
+
swarm_broadcast,
|
|
25519
25620
|
swarm_complete,
|
|
25520
25621
|
swarm_record_outcome,
|
|
25521
25622
|
swarm_subtask_prompt,
|
|
@@ -25526,6 +25627,327 @@ var swarmTools = {
|
|
|
25526
25627
|
swarm_get_error_context,
|
|
25527
25628
|
swarm_resolve_error
|
|
25528
25629
|
};
|
|
25630
|
+
|
|
25631
|
+
// src/repo-crawl.ts
|
|
25632
|
+
var GITHUB_API_URL = "https://api.github.com";
|
|
25633
|
+
var GITHUB_TOKEN = process.env.GITHUB_TOKEN;
|
|
25634
|
+
var DEFAULT_MAX_RESULTS = 10;
|
|
25635
|
+
var DEFAULT_MAX_LENGTH = 1e4;
|
|
25636
|
+
var DEFAULT_DEPTH = 2;
|
|
25637
|
+
|
|
25638
|
+
class RepoCrawlError extends Error {
|
|
25639
|
+
statusCode;
|
|
25640
|
+
endpoint;
|
|
25641
|
+
constructor(message, statusCode, endpoint) {
|
|
25642
|
+
super(message);
|
|
25643
|
+
this.statusCode = statusCode;
|
|
25644
|
+
this.endpoint = endpoint;
|
|
25645
|
+
this.name = "RepoCrawlError";
|
|
25646
|
+
}
|
|
25647
|
+
}
|
|
25648
|
+
function parseRepo(input) {
|
|
25649
|
+
let normalized = input.replace(/^https?:\/\//, "").replace(/\.git$/, "").replace(/^github\.com\//, "");
|
|
25650
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
25651
|
+
if (parts.length < 2) {
|
|
25652
|
+
throw new RepoCrawlError(`Invalid repo format: "${input}". Expected "owner/repo" or GitHub URL.`);
|
|
25653
|
+
}
|
|
25654
|
+
const [owner, repo] = parts;
|
|
25655
|
+
return { owner, repo };
|
|
25656
|
+
}
|
|
25657
|
+
async function githubFetch(endpoint, options = {}) {
|
|
25658
|
+
const headers = {
|
|
25659
|
+
Accept: "application/vnd.github.v3+json",
|
|
25660
|
+
"User-Agent": "opencode-swarm-plugin",
|
|
25661
|
+
...options.headers || {}
|
|
25662
|
+
};
|
|
25663
|
+
if (GITHUB_TOKEN) {
|
|
25664
|
+
headers.Authorization = `Bearer ${GITHUB_TOKEN}`;
|
|
25665
|
+
}
|
|
25666
|
+
const url2 = `${GITHUB_API_URL}${endpoint}`;
|
|
25667
|
+
const response = await fetch(url2, { ...options, headers });
|
|
25668
|
+
if (response.status === 403) {
|
|
25669
|
+
const rateLimitRemaining = response.headers.get("x-ratelimit-remaining");
|
|
25670
|
+
const rateLimitReset = response.headers.get("x-ratelimit-reset");
|
|
25671
|
+
if (rateLimitRemaining === "0" && rateLimitReset) {
|
|
25672
|
+
const resetDate = new Date(parseInt(rateLimitReset) * 1000);
|
|
25673
|
+
throw new RepoCrawlError(`GitHub API rate limit exceeded. Resets at ${resetDate.toISOString()}. ` + `${GITHUB_TOKEN ? "Using authenticated token." : "Set GITHUB_TOKEN env var for higher limits."}`, 403, endpoint);
|
|
25674
|
+
}
|
|
25675
|
+
}
|
|
25676
|
+
if (response.status === 404) {
|
|
25677
|
+
throw new RepoCrawlError(`Resource not found: ${endpoint}`, 404, endpoint);
|
|
25678
|
+
}
|
|
25679
|
+
if (!response.ok) {
|
|
25680
|
+
const body = await response.text();
|
|
25681
|
+
throw new RepoCrawlError(`GitHub API error (${response.status}): ${body}`, response.status, endpoint);
|
|
25682
|
+
}
|
|
25683
|
+
return response.json();
|
|
25684
|
+
}
|
|
25685
|
+
function decodeContent(content, encoding) {
|
|
25686
|
+
if (encoding === "base64") {
|
|
25687
|
+
return Buffer.from(content, "base64").toString("utf-8");
|
|
25688
|
+
}
|
|
25689
|
+
return content;
|
|
25690
|
+
}
|
|
25691
|
+
function detectTechStack(tree) {
|
|
25692
|
+
const stack = new Set;
|
|
25693
|
+
const filePatterns = {
|
|
25694
|
+
"package.json": "Node.js/npm",
|
|
25695
|
+
"yarn.lock": "Yarn",
|
|
25696
|
+
"pnpm-lock.yaml": "pnpm",
|
|
25697
|
+
"bun.lockb": "Bun",
|
|
25698
|
+
"Cargo.toml": "Rust",
|
|
25699
|
+
"go.mod": "Go",
|
|
25700
|
+
"requirements.txt": "Python/pip",
|
|
25701
|
+
Pipfile: "Python/pipenv",
|
|
25702
|
+
"pyproject.toml": "Python/poetry",
|
|
25703
|
+
Gemfile: "Ruby/Bundler",
|
|
25704
|
+
"composer.json": "PHP/Composer",
|
|
25705
|
+
"pom.xml": "Java/Maven",
|
|
25706
|
+
"build.gradle": "Java/Gradle",
|
|
25707
|
+
"tsconfig.json": "TypeScript",
|
|
25708
|
+
"next.config.js": "Next.js",
|
|
25709
|
+
"nuxt.config.js": "Nuxt.js",
|
|
25710
|
+
"vue.config.js": "Vue.js",
|
|
25711
|
+
"angular.json": "Angular",
|
|
25712
|
+
"svelte.config.js": "Svelte",
|
|
25713
|
+
Dockerfile: "Docker",
|
|
25714
|
+
"docker-compose.yml": "Docker Compose",
|
|
25715
|
+
".terraform": "Terraform",
|
|
25716
|
+
Makefile: "Make"
|
|
25717
|
+
};
|
|
25718
|
+
for (const item of tree) {
|
|
25719
|
+
const basename = item.path.split("/").pop() || "";
|
|
25720
|
+
if (filePatterns[basename]) {
|
|
25721
|
+
stack.add(filePatterns[basename]);
|
|
25722
|
+
}
|
|
25723
|
+
if (basename.endsWith(".rs"))
|
|
25724
|
+
stack.add("Rust");
|
|
25725
|
+
if (basename.endsWith(".go"))
|
|
25726
|
+
stack.add("Go");
|
|
25727
|
+
if (basename.endsWith(".py"))
|
|
25728
|
+
stack.add("Python");
|
|
25729
|
+
if (basename.endsWith(".rb"))
|
|
25730
|
+
stack.add("Ruby");
|
|
25731
|
+
if (basename.endsWith(".php"))
|
|
25732
|
+
stack.add("PHP");
|
|
25733
|
+
if (basename.endsWith(".java"))
|
|
25734
|
+
stack.add("Java");
|
|
25735
|
+
if (basename.endsWith(".kt"))
|
|
25736
|
+
stack.add("Kotlin");
|
|
25737
|
+
if (basename.endsWith(".swift"))
|
|
25738
|
+
stack.add("Swift");
|
|
25739
|
+
if (basename.endsWith(".ts") || basename.endsWith(".tsx"))
|
|
25740
|
+
stack.add("TypeScript");
|
|
25741
|
+
if (basename.endsWith(".jsx"))
|
|
25742
|
+
stack.add("React");
|
|
25743
|
+
}
|
|
25744
|
+
return Array.from(stack).sort();
|
|
25745
|
+
}
|
|
25746
|
+
function truncate(text, maxLength) {
|
|
25747
|
+
if (text.length <= maxLength) {
|
|
25748
|
+
return text;
|
|
25749
|
+
}
|
|
25750
|
+
return text.slice(0, maxLength) + `
|
|
25751
|
+
|
|
25752
|
+
[... truncated ...]`;
|
|
25753
|
+
}
|
|
25754
|
+
var repo_readme = tool({
|
|
25755
|
+
description: "Get README.md content from a GitHub repository",
|
|
25756
|
+
args: {
|
|
25757
|
+
repo: tool.schema.string().describe('Repository (e.g., "owner/repo" or GitHub URL)'),
|
|
25758
|
+
maxLength: tool.schema.number().optional().describe(`Max content length (default: ${DEFAULT_MAX_LENGTH})`)
|
|
25759
|
+
},
|
|
25760
|
+
async execute(args, ctx) {
|
|
25761
|
+
try {
|
|
25762
|
+
const { owner, repo } = parseRepo(args.repo);
|
|
25763
|
+
const maxLength = args.maxLength || DEFAULT_MAX_LENGTH;
|
|
25764
|
+
const readme = await githubFetch(`/repos/${owner}/${repo}/readme`);
|
|
25765
|
+
if (!readme.content || !readme.encoding) {
|
|
25766
|
+
return "README exists but content is not available";
|
|
25767
|
+
}
|
|
25768
|
+
const content = decodeContent(readme.content, readme.encoding);
|
|
25769
|
+
const truncated = truncate(content, maxLength);
|
|
25770
|
+
return JSON.stringify({
|
|
25771
|
+
repo: `${owner}/${repo}`,
|
|
25772
|
+
path: readme.path,
|
|
25773
|
+
size: readme.size,
|
|
25774
|
+
content: truncated,
|
|
25775
|
+
truncated: content.length > maxLength
|
|
25776
|
+
}, null, 2);
|
|
25777
|
+
} catch (error45) {
|
|
25778
|
+
if (error45 instanceof RepoCrawlError) {
|
|
25779
|
+
return JSON.stringify({ error: error45.message }, null, 2);
|
|
25780
|
+
}
|
|
25781
|
+
throw error45;
|
|
25782
|
+
}
|
|
25783
|
+
}
|
|
25784
|
+
});
|
|
25785
|
+
var repo_structure = tool({
|
|
25786
|
+
description: "Get repository structure with tech stack detection (root level only by default)",
|
|
25787
|
+
args: {
|
|
25788
|
+
repo: tool.schema.string().describe('Repository (e.g., "owner/repo" or GitHub URL)'),
|
|
25789
|
+
depth: tool.schema.number().optional().describe(`Tree depth (1=root only, 2=one level deep, etc. Default: ${DEFAULT_DEPTH})`)
|
|
25790
|
+
},
|
|
25791
|
+
async execute(args, ctx) {
|
|
25792
|
+
try {
|
|
25793
|
+
const { owner, repo } = parseRepo(args.repo);
|
|
25794
|
+
const depth = args.depth || DEFAULT_DEPTH;
|
|
25795
|
+
const repoInfo = await githubFetch(`/repos/${owner}/${repo}`);
|
|
25796
|
+
const tree = await githubFetch(`/repos/${owner}/${repo}/git/trees/${repoInfo.default_branch}`, {
|
|
25797
|
+
method: "GET"
|
|
25798
|
+
});
|
|
25799
|
+
const filtered = tree.tree.filter((item) => {
|
|
25800
|
+
const pathDepth = item.path.split("/").length;
|
|
25801
|
+
return pathDepth <= depth;
|
|
25802
|
+
});
|
|
25803
|
+
const techStack = detectTechStack(filtered);
|
|
25804
|
+
const dirs = filtered.filter((item) => item.type === "tree").map((item) => item.path);
|
|
25805
|
+
const files = filtered.filter((item) => item.type === "blob").map((item) => item.path);
|
|
25806
|
+
return JSON.stringify({
|
|
25807
|
+
repo: repoInfo.full_name,
|
|
25808
|
+
description: repoInfo.description,
|
|
25809
|
+
language: repoInfo.language,
|
|
25810
|
+
stars: repoInfo.stargazers_count,
|
|
25811
|
+
topics: repoInfo.topics,
|
|
25812
|
+
techStack,
|
|
25813
|
+
directories: dirs.slice(0, 50),
|
|
25814
|
+
files: files.slice(0, 50),
|
|
25815
|
+
truncated: tree.truncated || dirs.length > 50 || files.length > 50
|
|
25816
|
+
}, null, 2);
|
|
25817
|
+
} catch (error45) {
|
|
25818
|
+
if (error45 instanceof RepoCrawlError) {
|
|
25819
|
+
return JSON.stringify({ error: error45.message }, null, 2);
|
|
25820
|
+
}
|
|
25821
|
+
throw error45;
|
|
25822
|
+
}
|
|
25823
|
+
}
|
|
25824
|
+
});
|
|
25825
|
+
var repo_tree = tool({
|
|
25826
|
+
description: "Get directory tree for a path in a repository",
|
|
25827
|
+
args: {
|
|
25828
|
+
repo: tool.schema.string().describe('Repository (e.g., "owner/repo" or GitHub URL)'),
|
|
25829
|
+
path: tool.schema.string().optional().describe("Path in repo (default: root)"),
|
|
25830
|
+
maxDepth: tool.schema.number().optional().describe(`Max depth to traverse (default: ${DEFAULT_DEPTH})`)
|
|
25831
|
+
},
|
|
25832
|
+
async execute(args, ctx) {
|
|
25833
|
+
try {
|
|
25834
|
+
const { owner, repo } = parseRepo(args.repo);
|
|
25835
|
+
const targetPath = args.path || "";
|
|
25836
|
+
const maxDepth = args.maxDepth || DEFAULT_DEPTH;
|
|
25837
|
+
const repoInfo = await githubFetch(`/repos/${owner}/${repo}`);
|
|
25838
|
+
const contents = await githubFetch(`/repos/${owner}/${repo}/contents/${targetPath}`);
|
|
25839
|
+
if (!Array.isArray(contents)) {
|
|
25840
|
+
return JSON.stringify({ error: "Path is a file, not a directory" });
|
|
25841
|
+
}
|
|
25842
|
+
const tree = [];
|
|
25843
|
+
for (const item of contents) {
|
|
25844
|
+
tree.push({
|
|
25845
|
+
path: item.path,
|
|
25846
|
+
type: item.type,
|
|
25847
|
+
size: item.size
|
|
25848
|
+
});
|
|
25849
|
+
if (item.type === "dir" && maxDepth > 1) {
|
|
25850
|
+
try {
|
|
25851
|
+
const subContents = await githubFetch(`/repos/${owner}/${repo}/contents/${item.path}`);
|
|
25852
|
+
if (Array.isArray(subContents)) {
|
|
25853
|
+
for (const subItem of subContents.slice(0, 20)) {
|
|
25854
|
+
tree.push({
|
|
25855
|
+
path: subItem.path,
|
|
25856
|
+
type: subItem.type,
|
|
25857
|
+
size: subItem.size
|
|
25858
|
+
});
|
|
25859
|
+
}
|
|
25860
|
+
}
|
|
25861
|
+
} catch {}
|
|
25862
|
+
}
|
|
25863
|
+
}
|
|
25864
|
+
return JSON.stringify({
|
|
25865
|
+
repo: `${owner}/${repo}`,
|
|
25866
|
+
path: targetPath || "(root)",
|
|
25867
|
+
items: tree
|
|
25868
|
+
}, null, 2);
|
|
25869
|
+
} catch (error45) {
|
|
25870
|
+
if (error45 instanceof RepoCrawlError) {
|
|
25871
|
+
return JSON.stringify({ error: error45.message }, null, 2);
|
|
25872
|
+
}
|
|
25873
|
+
throw error45;
|
|
25874
|
+
}
|
|
25875
|
+
}
|
|
25876
|
+
});
|
|
25877
|
+
var repo_file = tool({
|
|
25878
|
+
description: "Get file content from a GitHub repository",
|
|
25879
|
+
args: {
|
|
25880
|
+
repo: tool.schema.string().describe('Repository (e.g., "owner/repo" or GitHub URL)'),
|
|
25881
|
+
path: tool.schema.string().describe("File path in repository"),
|
|
25882
|
+
maxLength: tool.schema.number().optional().describe(`Max content length (default: ${DEFAULT_MAX_LENGTH})`)
|
|
25883
|
+
},
|
|
25884
|
+
async execute(args, ctx) {
|
|
25885
|
+
try {
|
|
25886
|
+
const { owner, repo } = parseRepo(args.repo);
|
|
25887
|
+
const maxLength = args.maxLength || DEFAULT_MAX_LENGTH;
|
|
25888
|
+
const file2 = await githubFetch(`/repos/${owner}/${repo}/contents/${args.path}`);
|
|
25889
|
+
if (file2.type !== "file") {
|
|
25890
|
+
return JSON.stringify({ error: "Path is not a file" });
|
|
25891
|
+
}
|
|
25892
|
+
if (!file2.content || !file2.encoding) {
|
|
25893
|
+
return JSON.stringify({ error: "File content not available" });
|
|
25894
|
+
}
|
|
25895
|
+
const content = decodeContent(file2.content, file2.encoding);
|
|
25896
|
+
const truncated = truncate(content, maxLength);
|
|
25897
|
+
return JSON.stringify({
|
|
25898
|
+
repo: `${owner}/${repo}`,
|
|
25899
|
+
path: file2.path,
|
|
25900
|
+
size: file2.size,
|
|
25901
|
+
content: truncated,
|
|
25902
|
+
truncated: content.length > maxLength
|
|
25903
|
+
}, null, 2);
|
|
25904
|
+
} catch (error45) {
|
|
25905
|
+
if (error45 instanceof RepoCrawlError) {
|
|
25906
|
+
return JSON.stringify({ error: error45.message }, null, 2);
|
|
25907
|
+
}
|
|
25908
|
+
throw error45;
|
|
25909
|
+
}
|
|
25910
|
+
}
|
|
25911
|
+
});
|
|
25912
|
+
var repo_search = tool({
|
|
25913
|
+
description: "Search code in a GitHub repository",
|
|
25914
|
+
args: {
|
|
25915
|
+
repo: tool.schema.string().describe('Repository (e.g., "owner/repo" or GitHub URL)'),
|
|
25916
|
+
query: tool.schema.string().describe("Search query (GitHub code search)"),
|
|
25917
|
+
maxResults: tool.schema.number().optional().describe(`Max results (default: ${DEFAULT_MAX_RESULTS})`)
|
|
25918
|
+
},
|
|
25919
|
+
async execute(args, ctx) {
|
|
25920
|
+
try {
|
|
25921
|
+
const { owner, repo } = parseRepo(args.repo);
|
|
25922
|
+
const maxResults = args.maxResults || DEFAULT_MAX_RESULTS;
|
|
25923
|
+
const searchQuery = `${args.query} repo:${owner}/${repo}`;
|
|
25924
|
+
const results = await githubFetch(`/search/code?q=${encodeURIComponent(searchQuery)}&per_page=${maxResults}`);
|
|
25925
|
+
const items = results.items.map((item) => ({
|
|
25926
|
+
path: item.path,
|
|
25927
|
+
url: item.html_url,
|
|
25928
|
+
matches: item.text_matches?.map((m) => m.fragment) || []
|
|
25929
|
+
}));
|
|
25930
|
+
return JSON.stringify({
|
|
25931
|
+
repo: `${owner}/${repo}`,
|
|
25932
|
+
query: args.query,
|
|
25933
|
+
totalCount: results.total_count,
|
|
25934
|
+
results: items
|
|
25935
|
+
}, null, 2);
|
|
25936
|
+
} catch (error45) {
|
|
25937
|
+
if (error45 instanceof RepoCrawlError) {
|
|
25938
|
+
return JSON.stringify({ error: error45.message }, null, 2);
|
|
25939
|
+
}
|
|
25940
|
+
throw error45;
|
|
25941
|
+
}
|
|
25942
|
+
}
|
|
25943
|
+
});
|
|
25944
|
+
var repoCrawlTools = {
|
|
25945
|
+
repo_readme,
|
|
25946
|
+
repo_structure,
|
|
25947
|
+
repo_tree,
|
|
25948
|
+
repo_file,
|
|
25949
|
+
repo_search
|
|
25950
|
+
};
|
|
25529
25951
|
// src/anti-patterns.ts
|
|
25530
25952
|
var PatternKindSchema = exports_external.enum(["pattern", "anti_pattern"]);
|
|
25531
25953
|
var DecompositionPatternSchema = exports_external.object({
|
|
@@ -25972,7 +26394,8 @@ var SwarmPlugin = async (input) => {
|
|
|
25972
26394
|
...beadsTools,
|
|
25973
26395
|
...agentMailTools,
|
|
25974
26396
|
...structuredTools,
|
|
25975
|
-
...swarmTools
|
|
26397
|
+
...swarmTools,
|
|
26398
|
+
...repoCrawlTools
|
|
25976
26399
|
},
|
|
25977
26400
|
event: async ({ event }) => {
|
|
25978
26401
|
if (event.type === "session.idle") {
|
|
@@ -26017,7 +26440,8 @@ var allTools = {
|
|
|
26017
26440
|
...beadsTools,
|
|
26018
26441
|
...agentMailTools,
|
|
26019
26442
|
...structuredTools,
|
|
26020
|
-
...swarmTools
|
|
26443
|
+
...swarmTools,
|
|
26444
|
+
...repoCrawlTools
|
|
26021
26445
|
};
|
|
26022
26446
|
export {
|
|
26023
26447
|
withToolFallback,
|
|
@@ -26029,6 +26453,7 @@ export {
|
|
|
26029
26453
|
resetToolCache,
|
|
26030
26454
|
resetStorage,
|
|
26031
26455
|
requireTool,
|
|
26456
|
+
repoCrawlTools,
|
|
26032
26457
|
isToolAvailable,
|
|
26033
26458
|
isSemanticMemoryAvailable,
|
|
26034
26459
|
ifToolAvailable,
|
|
@@ -26074,6 +26499,7 @@ export {
|
|
|
26074
26499
|
SemanticMemoryStorage,
|
|
26075
26500
|
SUBTASK_PROMPT_V2,
|
|
26076
26501
|
STRATEGIES,
|
|
26502
|
+
RepoCrawlError,
|
|
26077
26503
|
InMemoryStorage,
|
|
26078
26504
|
FileReservationConflictError,
|
|
26079
26505
|
EvaluationSchema,
|