opencode-swarm 6.13.2 → 6.14.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/README.md +86 -33
- package/dist/cli/index.js +8 -5
- package/dist/config/evidence-schema.d.ts +75 -3
- package/dist/hooks/system-enhancer.d.ts +7 -0
- package/dist/index.js +591 -289
- package/dist/tools/phase-complete.d.ts +16 -0
- package/package.json +3 -4
package/dist/index.js
CHANGED
|
@@ -14001,7 +14001,23 @@ var init_evidence_schema = __esm(() => {
|
|
|
14001
14001
|
task_count: exports_external.number().int().min(1),
|
|
14002
14002
|
task_complexity: exports_external.enum(["trivial", "simple", "moderate", "complex"]),
|
|
14003
14003
|
top_rejection_reasons: exports_external.array(exports_external.string()).default([]),
|
|
14004
|
-
lessons_learned: exports_external.array(exports_external.string()).max(5).default([])
|
|
14004
|
+
lessons_learned: exports_external.array(exports_external.string()).max(5).default([]),
|
|
14005
|
+
user_directives: exports_external.array(exports_external.object({
|
|
14006
|
+
directive: exports_external.string().min(1),
|
|
14007
|
+
category: exports_external.enum([
|
|
14008
|
+
"tooling",
|
|
14009
|
+
"code_style",
|
|
14010
|
+
"architecture",
|
|
14011
|
+
"process",
|
|
14012
|
+
"other"
|
|
14013
|
+
]),
|
|
14014
|
+
scope: exports_external.enum(["session", "project", "global"])
|
|
14015
|
+
})).default([]),
|
|
14016
|
+
approaches_tried: exports_external.array(exports_external.object({
|
|
14017
|
+
approach: exports_external.string().min(1),
|
|
14018
|
+
result: exports_external.enum(["success", "failure", "partial"]),
|
|
14019
|
+
abandoned_reason: exports_external.string().optional()
|
|
14020
|
+
})).max(10).default([])
|
|
14005
14021
|
});
|
|
14006
14022
|
SyntaxEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14007
14023
|
type: exports_external.literal("syntax"),
|
|
@@ -30184,9 +30200,10 @@ function hasCompoundTestExtension(filename) {
|
|
|
30184
30200
|
function getTestFilesFromConvention(sourceFiles) {
|
|
30185
30201
|
const testFiles = [];
|
|
30186
30202
|
for (const file3 of sourceFiles) {
|
|
30203
|
+
const normalizedPath = file3.replace(/\\/g, "/");
|
|
30187
30204
|
const basename2 = path12.basename(file3);
|
|
30188
30205
|
const dirname4 = path12.dirname(file3);
|
|
30189
|
-
if (hasCompoundTestExtension(basename2) || basename2.includes(".spec.") || basename2.includes(".test.")) {
|
|
30206
|
+
if (hasCompoundTestExtension(basename2) || basename2.includes(".spec.") || basename2.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
|
|
30190
30207
|
if (!testFiles.includes(file3)) {
|
|
30191
30208
|
testFiles.push(file3);
|
|
30192
30209
|
}
|
|
@@ -31423,7 +31440,7 @@ var init_preflight_integration = __esm(() => {
|
|
|
31423
31440
|
});
|
|
31424
31441
|
|
|
31425
31442
|
// src/index.ts
|
|
31426
|
-
import * as
|
|
31443
|
+
import * as path31 from "path";
|
|
31427
31444
|
|
|
31428
31445
|
// src/tools/tool-names.ts
|
|
31429
31446
|
var TOOL_NAMES = [
|
|
@@ -31566,16 +31583,15 @@ for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
|
|
|
31566
31583
|
}
|
|
31567
31584
|
}
|
|
31568
31585
|
var DEFAULT_MODELS = {
|
|
31569
|
-
|
|
31570
|
-
|
|
31571
|
-
|
|
31572
|
-
test_engineer: "
|
|
31573
|
-
sme: "
|
|
31574
|
-
|
|
31575
|
-
|
|
31576
|
-
|
|
31577
|
-
|
|
31578
|
-
default: "google/gemini-2.5-flash"
|
|
31586
|
+
explorer: "opencode/trinity-large-preview-free",
|
|
31587
|
+
coder: "opencode/minimax-m2.5-free",
|
|
31588
|
+
reviewer: "opencode/big-pickle",
|
|
31589
|
+
test_engineer: "opencode/gpt-5-nano",
|
|
31590
|
+
sme: "opencode/trinity-large-preview-free",
|
|
31591
|
+
critic: "opencode/trinity-large-preview-free",
|
|
31592
|
+
docs: "opencode/trinity-large-preview-free",
|
|
31593
|
+
designer: "opencode/trinity-large-preview-free",
|
|
31594
|
+
default: "opencode/trinity-large-preview-free"
|
|
31579
31595
|
};
|
|
31580
31596
|
var DEFAULT_SCORING_CONFIG = {
|
|
31581
31597
|
enabled: false,
|
|
@@ -32481,6 +32497,37 @@ Identify 1-3 relevant domains from the task requirements.
|
|
|
32481
32497
|
Call {{AGENT_PREFIX}}sme once per domain, serially. Max 3 SME calls per project phase.
|
|
32482
32498
|
Re-consult if a new domain emerges or if significant changes require fresh evaluation.
|
|
32483
32499
|
Cache guidance in context.md.
|
|
32500
|
+
### MODE: PRE-PHASE BRIEFING (Required Before Starting Any Phase)
|
|
32501
|
+
|
|
32502
|
+
Before creating or resuming any plan, you MUST read the previous phase's retrospective.
|
|
32503
|
+
|
|
32504
|
+
**Phase 2+ (continuing a multi-phase project):**
|
|
32505
|
+
1. Check \`.swarm/evidence/retro-{N-1}/evidence.json\` for the previous phase's retrospective
|
|
32506
|
+
2. If it exists: read and internalize \`lessons_learned\` and \`top_rejection_reasons\`
|
|
32507
|
+
3. If it does NOT exist: note this as a process gap, but proceed
|
|
32508
|
+
4. Print a briefing acknowledgment:
|
|
32509
|
+
\`\`\`
|
|
32510
|
+
\u2192 BRIEFING: Read Phase {N-1} retrospective.
|
|
32511
|
+
Key lessons: {list 1-3 most relevant lessons}
|
|
32512
|
+
Applying to Phase {N}: {one sentence on how you'll apply them}
|
|
32513
|
+
\`\`\`
|
|
32514
|
+
|
|
32515
|
+
**Phase 1 (starting any new project):**
|
|
32516
|
+
1. Scan \`.swarm/evidence/\` for any \`retro-*\` bundles from prior projects
|
|
32517
|
+
2. If found: review the 1-3 most recent retrospectives for relevant lessons
|
|
32518
|
+
3. Pay special attention to \`user_directives\` \u2014 these carry across projects
|
|
32519
|
+
4. Print a briefing acknowledgment:
|
|
32520
|
+
\`\`\`
|
|
32521
|
+
\u2192 BRIEFING: Reviewed {N} historical retrospectives from this workspace.
|
|
32522
|
+
Relevant lessons: {list applicable lessons}
|
|
32523
|
+
User directives carried forward: {list any persistent directives}
|
|
32524
|
+
\`\`\`
|
|
32525
|
+
OR if no historical retros exist:
|
|
32526
|
+
\`\`\`
|
|
32527
|
+
\u2192 BRIEFING: No historical retrospectives found. Starting fresh.
|
|
32528
|
+
\`\`\`
|
|
32529
|
+
|
|
32530
|
+
This briefing is a HARD REQUIREMENT for ALL phases. Skipping it is a process violation.
|
|
32484
32531
|
|
|
32485
32532
|
### MODE: PLAN
|
|
32486
32533
|
|
|
@@ -32497,6 +32544,15 @@ TASK GRANULARITY RULES:
|
|
|
32497
32544
|
- NEVER write a task with compound verbs: "implement X and add Y and update Z" = 3 tasks, not 1. Split before writing to plan.
|
|
32498
32545
|
- Coder receives ONE task. You make ALL scope decisions in the plan. Coder makes zero scope decisions.
|
|
32499
32546
|
|
|
32547
|
+
PHASE COUNT GUIDANCE:
|
|
32548
|
+
- Plans with 5+ tasks SHOULD be split into at least 2 phases.
|
|
32549
|
+
- Plans with 10+ tasks MUST be split into at least 3 phases.
|
|
32550
|
+
- Each phase should be a coherent unit of work that can be reviewed and learned from
|
|
32551
|
+
before proceeding to the next.
|
|
32552
|
+
- Single-phase plans are acceptable ONLY for small projects (1-4 tasks).
|
|
32553
|
+
- Rationale: Retrospectives at phase boundaries capture lessons that improve subsequent
|
|
32554
|
+
phases. A single-phase plan gets zero iterative learning benefit.
|
|
32555
|
+
|
|
32500
32556
|
Create .swarm/context.md
|
|
32501
32557
|
- Decisions, patterns, SME cache, file map
|
|
32502
32558
|
|
|
@@ -32634,6 +32690,54 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
|
|
|
32634
32690
|
|
|
32635
32691
|
5o. Update plan.md [x], proceed to next task.
|
|
32636
32692
|
|
|
32693
|
+
## \u26D4 RETROSPECTIVE GATE
|
|
32694
|
+
|
|
32695
|
+
**MANDATORY before calling phase_complete.** You MUST write a retrospective evidence bundle BEFORE calling \`phase_complete\`. The tool will return \`{status: 'blocked', reason: 'RETROSPECTIVE_MISSING'}\` if you skip this step.
|
|
32696
|
+
|
|
32697
|
+
**How to write the retrospective:**
|
|
32698
|
+
|
|
32699
|
+
Use the evidence manager tool to write a bundle at \`retro-{N}\` (where N is the phase number being completed):
|
|
32700
|
+
|
|
32701
|
+
\`\`\`json
|
|
32702
|
+
{
|
|
32703
|
+
"type": "retrospective",
|
|
32704
|
+
"phase_number": <N>,
|
|
32705
|
+
"verdict": "pass",
|
|
32706
|
+
"reviewer_rejections": <count>,
|
|
32707
|
+
"coder_revisions": <count>,
|
|
32708
|
+
"test_failures": <count>,
|
|
32709
|
+
"security_findings": <count>,
|
|
32710
|
+
"lessons_learned": ["lesson 1 (max 5)", "lesson 2"],
|
|
32711
|
+
"top_rejection_reasons": ["reason 1"],
|
|
32712
|
+
"user_directives": [],
|
|
32713
|
+
"approaches_tried": [],
|
|
32714
|
+
"task_complexity": "low|medium|high",
|
|
32715
|
+
"timestamp": "<ISO 8601>",
|
|
32716
|
+
"agent": "architect",
|
|
32717
|
+
"metadata": { "plan_id": "<current plan title from .swarm/plan.json>" }
|
|
32718
|
+
}
|
|
32719
|
+
\`\`\`
|
|
32720
|
+
|
|
32721
|
+
**Required field rules:**
|
|
32722
|
+
- \`verdict\` MUST be \`"pass"\` \u2014 a verdict of \`"fail"\` or missing verdict blocks phase_complete
|
|
32723
|
+
- \`phase_number\` MUST match the phase number you are completing
|
|
32724
|
+
- \`lessons_learned\` should be 3-5 concrete, actionable items from this phase
|
|
32725
|
+
- Write the bundle as task_id \`retro-{N}\` (e.g., \`retro-1\` for Phase 1, \`retro-2\` for Phase 2)
|
|
32726
|
+
- \`metadata.plan_id\` should be set to the current project's plan title (from \`.swarm/plan.json\` header). This enables cross-project filtering in the retrospective injection system.
|
|
32727
|
+
|
|
32728
|
+
### Additional retrospective fields (capture when applicable):
|
|
32729
|
+
- \`user_directives\`: Any corrections or preferences the user expressed during this phase
|
|
32730
|
+
- \`directive\`: what the user said (non-empty string)
|
|
32731
|
+
- \`category\`: \`tooling\` | \`code_style\` | \`architecture\` | \`process\` | \`other\`
|
|
32732
|
+
- \`scope\`: \`session\` (one-time, do not carry forward) | \`project\` (persist to context.md) | \`global\` (user preference)
|
|
32733
|
+
- \`approaches_tried\`: Approaches attempted during this phase (max 10)
|
|
32734
|
+
- \`approach\`: what was tried (non-empty string)
|
|
32735
|
+
- \`result\`: \`success\` | \`failure\` | \`partial\`
|
|
32736
|
+
- \`abandoned_reason\`: why it was abandoned (required when result is \`failure\` or \`partial\`)
|
|
32737
|
+
|
|
32738
|
+
**\u26A0\uFE0F WARNING:** Calling \`phase_complete(N)\` without a valid \`retro-N\` bundle will be BLOCKED. The error response will be:
|
|
32739
|
+
\`{ "status": "blocked", "reason": "RETROSPECTIVE_MISSING" }\`
|
|
32740
|
+
|
|
32637
32741
|
### MODE: PHASE-WRAP
|
|
32638
32742
|
1. {{AGENT_PREFIX}}explorer - Rescan
|
|
32639
32743
|
2. {{AGENT_PREFIX}}docs - Update documentation for all changes in this phase. Provide:
|
|
@@ -36786,6 +36890,26 @@ function isOutsideSwarmDir(filePath) {
|
|
|
36786
36890
|
const relative2 = path15.relative(swarmDir, resolved);
|
|
36787
36891
|
return relative2.startsWith("..") || path15.isAbsolute(relative2);
|
|
36788
36892
|
}
|
|
36893
|
+
function isSourceCodePath(filePath) {
|
|
36894
|
+
if (!filePath)
|
|
36895
|
+
return false;
|
|
36896
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
36897
|
+
const nonSourcePatterns = [
|
|
36898
|
+
/^README(\..+)?$/i,
|
|
36899
|
+
/\/README(\..+)?$/i,
|
|
36900
|
+
/^CHANGELOG(\..+)?$/i,
|
|
36901
|
+
/\/CHANGELOG(\..+)?$/i,
|
|
36902
|
+
/^package\.json$/,
|
|
36903
|
+
/\/package\.json$/,
|
|
36904
|
+
/^\.github\//,
|
|
36905
|
+
/\/\.github\//,
|
|
36906
|
+
/^docs\//,
|
|
36907
|
+
/\/docs\//,
|
|
36908
|
+
/^\.swarm\//,
|
|
36909
|
+
/\/\.swarm\//
|
|
36910
|
+
];
|
|
36911
|
+
return !nonSourcePatterns.some((pattern) => pattern.test(normalized));
|
|
36912
|
+
}
|
|
36789
36913
|
function isGateTool(toolName) {
|
|
36790
36914
|
const normalized = toolName.replace(/^[^:]+[:.]/, "");
|
|
36791
36915
|
const gateTools = [
|
|
@@ -36831,7 +36955,7 @@ function createGuardrailsHooks(config3) {
|
|
|
36831
36955
|
if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
|
|
36832
36956
|
const args2 = output.args;
|
|
36833
36957
|
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
36834
|
-
if (typeof targetPath === "string" && isOutsideSwarmDir(targetPath)) {
|
|
36958
|
+
if (typeof targetPath === "string" && isOutsideSwarmDir(targetPath) && isSourceCodePath(targetPath)) {
|
|
36835
36959
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
36836
36960
|
if (session2) {
|
|
36837
36961
|
session2.architectWriteCount++;
|
|
@@ -37344,8 +37468,7 @@ ${originalText}`;
|
|
|
37344
37468
|
};
|
|
37345
37469
|
}
|
|
37346
37470
|
// src/hooks/system-enhancer.ts
|
|
37347
|
-
|
|
37348
|
-
import * as path17 from "path";
|
|
37471
|
+
init_manager();
|
|
37349
37472
|
init_manager2();
|
|
37350
37473
|
|
|
37351
37474
|
// src/services/decision-drift-analyzer.ts
|
|
@@ -37722,6 +37845,170 @@ function estimateContentType(text) {
|
|
|
37722
37845
|
}
|
|
37723
37846
|
return "prose";
|
|
37724
37847
|
}
|
|
37848
|
+
async function buildRetroInjection(directory, currentPhaseNumber, currentPlanTitle) {
|
|
37849
|
+
try {
|
|
37850
|
+
const prevPhase = currentPhaseNumber - 1;
|
|
37851
|
+
if (prevPhase >= 1) {
|
|
37852
|
+
const bundle = await loadEvidence(directory, `retro-${prevPhase}`);
|
|
37853
|
+
if (bundle && bundle.entries.length > 0) {
|
|
37854
|
+
const retroEntry = bundle.entries.find((entry) => entry.type === "retrospective");
|
|
37855
|
+
if (retroEntry && retroEntry.verdict !== "fail") {
|
|
37856
|
+
const lessons = retroEntry.lessons_learned ?? [];
|
|
37857
|
+
const rejections = retroEntry.top_rejection_reasons ?? [];
|
|
37858
|
+
const nonSessionDirectives = (retroEntry.user_directives ?? []).filter((d) => d.scope !== "session");
|
|
37859
|
+
let block = `## Previous Phase Retrospective (Phase ${prevPhase})
|
|
37860
|
+
**Outcome:** ${retroEntry.summary ?? "Phase completed."}
|
|
37861
|
+
**Rejection reasons:** ${rejections.join(", ") || "None"}
|
|
37862
|
+
**Lessons learned:**
|
|
37863
|
+
${lessons.map((l) => `- ${l}`).join(`
|
|
37864
|
+
`)}
|
|
37865
|
+
|
|
37866
|
+
\u26A0\uFE0F Apply these lessons to the current phase. Do not repeat the same mistakes.`;
|
|
37867
|
+
if (nonSessionDirectives.length > 0) {
|
|
37868
|
+
const top5 = nonSessionDirectives.slice(0, 5);
|
|
37869
|
+
block += `
|
|
37870
|
+
|
|
37871
|
+
## User Directives (from Phase ${prevPhase})
|
|
37872
|
+
${top5.map((d) => `- [${d.category}] ${d.directive}`).join(`
|
|
37873
|
+
`)}`;
|
|
37874
|
+
}
|
|
37875
|
+
return block;
|
|
37876
|
+
}
|
|
37877
|
+
}
|
|
37878
|
+
const taskIds = await listEvidenceTaskIds(directory);
|
|
37879
|
+
const retroIds = taskIds.filter((id) => id.startsWith("retro-"));
|
|
37880
|
+
let latestRetro = null;
|
|
37881
|
+
for (const taskId of retroIds) {
|
|
37882
|
+
const b = await loadEvidence(directory, taskId);
|
|
37883
|
+
if (b && b.entries.length > 0) {
|
|
37884
|
+
for (const entry of b.entries) {
|
|
37885
|
+
if (entry.type === "retrospective") {
|
|
37886
|
+
const retro = entry;
|
|
37887
|
+
if (retro.verdict !== "fail") {
|
|
37888
|
+
if (latestRetro === null || retro.phase_number > latestRetro.phase) {
|
|
37889
|
+
latestRetro = { entry: retro, phase: retro.phase_number };
|
|
37890
|
+
}
|
|
37891
|
+
}
|
|
37892
|
+
}
|
|
37893
|
+
}
|
|
37894
|
+
}
|
|
37895
|
+
}
|
|
37896
|
+
if (latestRetro) {
|
|
37897
|
+
const { entry, phase } = latestRetro;
|
|
37898
|
+
const lessons = entry.lessons_learned ?? [];
|
|
37899
|
+
const rejections = entry.top_rejection_reasons ?? [];
|
|
37900
|
+
const nonSessionDirectives = (entry.user_directives ?? []).filter((d) => d.scope !== "session");
|
|
37901
|
+
let block = `## Previous Phase Retrospective (Phase ${phase})
|
|
37902
|
+
**Outcome:** ${entry.summary ?? "Phase completed."}
|
|
37903
|
+
**Rejection reasons:** ${rejections.join(", ") || "None"}
|
|
37904
|
+
**Lessons learned:**
|
|
37905
|
+
${lessons.map((l) => `- ${l}`).join(`
|
|
37906
|
+
`)}
|
|
37907
|
+
|
|
37908
|
+
\u26A0\uFE0F Apply these lessons to the current phase. Do not repeat the same mistakes.`;
|
|
37909
|
+
if (nonSessionDirectives.length > 0) {
|
|
37910
|
+
const top5 = nonSessionDirectives.slice(0, 5);
|
|
37911
|
+
block += `
|
|
37912
|
+
|
|
37913
|
+
## User Directives (from Phase ${phase})
|
|
37914
|
+
${top5.map((d) => `- [${d.category}] ${d.directive}`).join(`
|
|
37915
|
+
`)}`;
|
|
37916
|
+
}
|
|
37917
|
+
return block;
|
|
37918
|
+
}
|
|
37919
|
+
return null;
|
|
37920
|
+
}
|
|
37921
|
+
const allTaskIds = await listEvidenceTaskIds(directory);
|
|
37922
|
+
const allRetroIds = allTaskIds.filter((id) => id.startsWith("retro-"));
|
|
37923
|
+
if (allRetroIds.length === 0) {
|
|
37924
|
+
return null;
|
|
37925
|
+
}
|
|
37926
|
+
const allRetros = [];
|
|
37927
|
+
const cutoffMs = 30 * 24 * 60 * 60 * 1000;
|
|
37928
|
+
const now = Date.now();
|
|
37929
|
+
for (const taskId of allRetroIds) {
|
|
37930
|
+
const b = await loadEvidence(directory, taskId);
|
|
37931
|
+
if (!b)
|
|
37932
|
+
continue;
|
|
37933
|
+
for (const e of b.entries) {
|
|
37934
|
+
if (e.type === "retrospective") {
|
|
37935
|
+
const retro = e;
|
|
37936
|
+
if (retro.verdict === "fail")
|
|
37937
|
+
continue;
|
|
37938
|
+
if (currentPlanTitle && typeof retro.metadata === "object" && retro.metadata !== null && "plan_id" in retro.metadata && retro.metadata.plan_id === currentPlanTitle)
|
|
37939
|
+
continue;
|
|
37940
|
+
const ts = retro.timestamp ?? b.created_at;
|
|
37941
|
+
const ageMs = now - new Date(ts).getTime();
|
|
37942
|
+
if (isNaN(ageMs) || ageMs > cutoffMs)
|
|
37943
|
+
continue;
|
|
37944
|
+
allRetros.push({ entry: retro, timestamp: ts });
|
|
37945
|
+
}
|
|
37946
|
+
}
|
|
37947
|
+
}
|
|
37948
|
+
if (allRetros.length === 0) {
|
|
37949
|
+
return null;
|
|
37950
|
+
}
|
|
37951
|
+
allRetros.sort((a, b) => {
|
|
37952
|
+
const ta = new Date(a.timestamp).getTime();
|
|
37953
|
+
const tb = new Date(b.timestamp).getTime();
|
|
37954
|
+
if (isNaN(ta) && isNaN(tb))
|
|
37955
|
+
return 0;
|
|
37956
|
+
if (isNaN(ta))
|
|
37957
|
+
return 1;
|
|
37958
|
+
if (isNaN(tb))
|
|
37959
|
+
return -1;
|
|
37960
|
+
return tb - ta;
|
|
37961
|
+
});
|
|
37962
|
+
const top3 = allRetros.slice(0, 3);
|
|
37963
|
+
const lines = [
|
|
37964
|
+
"## Historical Lessons (from recent prior projects)"
|
|
37965
|
+
];
|
|
37966
|
+
lines.push("Most recent retrospectives in this workspace:");
|
|
37967
|
+
const allCarriedDirectives = [];
|
|
37968
|
+
for (const { entry, timestamp } of top3) {
|
|
37969
|
+
const date9 = timestamp.split("T")[0] ?? "unknown";
|
|
37970
|
+
const summary = entry.summary ?? `Phase ${entry.phase_number} completed`;
|
|
37971
|
+
const topLesson = entry.lessons_learned?.[0] ?? "No lessons recorded";
|
|
37972
|
+
lines.push(`- Phase ${entry.phase_number} (${date9}): ${summary}`);
|
|
37973
|
+
lines.push(` Key lesson: ${topLesson}`);
|
|
37974
|
+
const nonSession = (entry.user_directives ?? []).filter((d) => d.scope !== "session");
|
|
37975
|
+
allCarriedDirectives.push(...nonSession);
|
|
37976
|
+
}
|
|
37977
|
+
if (allCarriedDirectives.length > 0) {
|
|
37978
|
+
const top5 = allCarriedDirectives.slice(0, 5);
|
|
37979
|
+
lines.push("User directives carried forward:");
|
|
37980
|
+
for (const d of top5) {
|
|
37981
|
+
lines.push(`- [${d.category}] ${d.directive}`);
|
|
37982
|
+
}
|
|
37983
|
+
}
|
|
37984
|
+
const tier2Block = lines.join(`
|
|
37985
|
+
`);
|
|
37986
|
+
return tier2Block.length <= 800 ? tier2Block : `${tier2Block.substring(0, 797)}...`;
|
|
37987
|
+
} catch {
|
|
37988
|
+
return null;
|
|
37989
|
+
}
|
|
37990
|
+
}
|
|
37991
|
+
async function buildCoderRetroInjection(directory, currentPhaseNumber) {
|
|
37992
|
+
try {
|
|
37993
|
+
const prevPhase = currentPhaseNumber - 1;
|
|
37994
|
+
if (prevPhase < 1)
|
|
37995
|
+
return null;
|
|
37996
|
+
const bundle = await loadEvidence(directory, `retro-${prevPhase}`);
|
|
37997
|
+
if (!bundle || bundle.entries.length === 0)
|
|
37998
|
+
return null;
|
|
37999
|
+
const retroEntry = bundle.entries.find((entry) => entry.type === "retrospective");
|
|
38000
|
+
if (!retroEntry || retroEntry.verdict === "fail")
|
|
38001
|
+
return null;
|
|
38002
|
+
const lessons = retroEntry.lessons_learned ?? [];
|
|
38003
|
+
const summaryLine = `[SWARM RETROSPECTIVE] From Phase ${prevPhase}:${retroEntry.summary ? " " + retroEntry.summary : ""}`;
|
|
38004
|
+
const allLines = [summaryLine, ...lessons];
|
|
38005
|
+
const text = allLines.join(`
|
|
38006
|
+
`);
|
|
38007
|
+
return text.length <= 400 ? text : `${text.substring(0, 397)}...`;
|
|
38008
|
+
} catch {
|
|
38009
|
+
return null;
|
|
38010
|
+
}
|
|
38011
|
+
}
|
|
37725
38012
|
function createSystemEnhancerHook(config3, directory) {
|
|
37726
38013
|
const enabled = config3.hooks?.system_enhancer !== false;
|
|
37727
38014
|
if (!enabled) {
|
|
@@ -37841,43 +38128,27 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
37841
38128
|
}
|
|
37842
38129
|
}
|
|
37843
38130
|
}
|
|
38131
|
+
if (baseRole === "coder") {
|
|
38132
|
+
try {
|
|
38133
|
+
const currentPhaseNum_coder = plan2?.current_phase ?? 1;
|
|
38134
|
+
const coderRetro = await buildCoderRetroInjection(directory, currentPhaseNum_coder);
|
|
38135
|
+
if (coderRetro) {
|
|
38136
|
+
tryInject(coderRetro);
|
|
38137
|
+
}
|
|
38138
|
+
} catch {}
|
|
38139
|
+
}
|
|
37844
38140
|
const sessionId_retro = _input.sessionID;
|
|
37845
38141
|
const activeAgent_retro = swarmState.activeAgent.get(sessionId_retro ?? "");
|
|
37846
38142
|
const isArchitect2 = !activeAgent_retro || stripKnownSwarmPrefix(activeAgent_retro) === "architect";
|
|
37847
38143
|
if (isArchitect2) {
|
|
37848
38144
|
try {
|
|
37849
|
-
const
|
|
37850
|
-
|
|
37851
|
-
|
|
37852
|
-
|
|
37853
|
-
|
|
37854
|
-
|
|
37855
|
-
|
|
37856
|
-
} catch {
|
|
37857
|
-
continue;
|
|
37858
|
-
}
|
|
37859
|
-
if (content !== null && typeof content === "object" && content.type === "retrospective") {
|
|
37860
|
-
const retro = content;
|
|
37861
|
-
const hints = [];
|
|
37862
|
-
if (retro.reviewer_rejections > 2) {
|
|
37863
|
-
hints.push(`Phase ${retro.phase_number} had ${retro.reviewer_rejections} reviewer rejections.`);
|
|
37864
|
-
}
|
|
37865
|
-
if (retro.top_rejection_reasons.length > 0) {
|
|
37866
|
-
hints.push(`Common rejection reasons: ${retro.top_rejection_reasons.join(", ")}.`);
|
|
37867
|
-
}
|
|
37868
|
-
if (retro.lessons_learned.length > 0) {
|
|
37869
|
-
hints.push(`Lessons: ${retro.lessons_learned.join("; ")}.`);
|
|
37870
|
-
}
|
|
37871
|
-
if (hints.length > 0) {
|
|
37872
|
-
const retroHint = `[SWARM RETROSPECTIVE] From Phase ${retro.phase_number}: ${hints.join(" ")}`;
|
|
37873
|
-
if (retroHint.length <= 800) {
|
|
37874
|
-
tryInject(retroHint);
|
|
37875
|
-
} else {
|
|
37876
|
-
tryInject(`${retroHint.substring(0, 800)}...`);
|
|
37877
|
-
}
|
|
37878
|
-
}
|
|
37879
|
-
break;
|
|
37880
|
-
}
|
|
38145
|
+
const currentPhaseNum = plan2?.current_phase ?? 1;
|
|
38146
|
+
const retroText = await buildRetroInjection(directory, currentPhaseNum, plan2?.title ?? undefined);
|
|
38147
|
+
if (retroText) {
|
|
38148
|
+
if (retroText.length <= 1600) {
|
|
38149
|
+
tryInject(retroText);
|
|
38150
|
+
} else {
|
|
38151
|
+
tryInject(`${retroText.substring(0, 1600)}...`);
|
|
37881
38152
|
}
|
|
37882
38153
|
}
|
|
37883
38154
|
} catch {}
|
|
@@ -38137,43 +38408,18 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
38137
38408
|
const isArchitect_b = !activeAgent_retro_b || stripKnownSwarmPrefix(activeAgent_retro_b) === "architect";
|
|
38138
38409
|
if (isArchitect_b) {
|
|
38139
38410
|
try {
|
|
38140
|
-
const
|
|
38141
|
-
|
|
38142
|
-
|
|
38143
|
-
|
|
38144
|
-
|
|
38145
|
-
|
|
38146
|
-
|
|
38147
|
-
|
|
38148
|
-
|
|
38149
|
-
|
|
38150
|
-
|
|
38151
|
-
|
|
38152
|
-
const hints_b = [];
|
|
38153
|
-
if (retro_b.reviewer_rejections > 2) {
|
|
38154
|
-
hints_b.push(`Phase ${retro_b.phase_number} had ${retro_b.reviewer_rejections} reviewer rejections.`);
|
|
38155
|
-
}
|
|
38156
|
-
if (retro_b.top_rejection_reasons.length > 0) {
|
|
38157
|
-
hints_b.push(`Common rejection reasons: ${retro_b.top_rejection_reasons.join(", ")}.`);
|
|
38158
|
-
}
|
|
38159
|
-
if (retro_b.lessons_learned.length > 0) {
|
|
38160
|
-
hints_b.push(`Lessons: ${retro_b.lessons_learned.join("; ")}.`);
|
|
38161
|
-
}
|
|
38162
|
-
if (hints_b.length > 0) {
|
|
38163
|
-
const retroHint_b = `[SWARM RETROSPECTIVE] From Phase ${retro_b.phase_number}: ${hints_b.join(" ")}`;
|
|
38164
|
-
const retroText = retroHint_b.length <= 800 ? retroHint_b : `${retroHint_b.substring(0, 800)}...`;
|
|
38165
|
-
candidates.push({
|
|
38166
|
-
id: `candidate-${idCounter++}`,
|
|
38167
|
-
kind: "phase",
|
|
38168
|
-
text: retroText,
|
|
38169
|
-
tokens: estimateTokens(retroText),
|
|
38170
|
-
priority: 2,
|
|
38171
|
-
metadata: { contentType: "prose" }
|
|
38172
|
-
});
|
|
38173
|
-
}
|
|
38174
|
-
break;
|
|
38175
|
-
}
|
|
38176
|
-
}
|
|
38411
|
+
const currentPhaseNum_b = plan?.current_phase ?? 1;
|
|
38412
|
+
const retroText_b = await buildRetroInjection(directory, currentPhaseNum_b, plan?.title ?? undefined);
|
|
38413
|
+
if (retroText_b) {
|
|
38414
|
+
const text = retroText_b.length <= 1600 ? retroText_b : `${retroText_b.substring(0, 1597)}...`;
|
|
38415
|
+
candidates.push({
|
|
38416
|
+
id: `candidate-${idCounter++}`,
|
|
38417
|
+
kind: "phase",
|
|
38418
|
+
text,
|
|
38419
|
+
tokens: estimateTokens(text),
|
|
38420
|
+
priority: 2,
|
|
38421
|
+
metadata: { contentType: "prose" }
|
|
38422
|
+
});
|
|
38177
38423
|
}
|
|
38178
38424
|
} catch {}
|
|
38179
38425
|
if (mode_b !== "DISCOVER") {
|
|
@@ -38211,6 +38457,24 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
38211
38457
|
}
|
|
38212
38458
|
}
|
|
38213
38459
|
}
|
|
38460
|
+
const activeAgent_coder_b = swarmState.activeAgent.get(_input.sessionID ?? "");
|
|
38461
|
+
const isCoder_b = activeAgent_coder_b && stripKnownSwarmPrefix(activeAgent_coder_b) === "coder";
|
|
38462
|
+
if (isCoder_b) {
|
|
38463
|
+
try {
|
|
38464
|
+
const currentPhaseNum_coder_b = plan?.current_phase ?? 1;
|
|
38465
|
+
const coderRetro_b = await buildCoderRetroInjection(directory, currentPhaseNum_coder_b);
|
|
38466
|
+
if (coderRetro_b) {
|
|
38467
|
+
candidates.push({
|
|
38468
|
+
id: `candidate-${idCounter++}`,
|
|
38469
|
+
kind: "agent_context",
|
|
38470
|
+
text: coderRetro_b,
|
|
38471
|
+
tokens: estimateTokens(coderRetro_b),
|
|
38472
|
+
priority: 2,
|
|
38473
|
+
metadata: { contentType: "prose" }
|
|
38474
|
+
});
|
|
38475
|
+
}
|
|
38476
|
+
} catch {}
|
|
38477
|
+
}
|
|
38214
38478
|
const automationCapabilities_b = config3.automation?.capabilities;
|
|
38215
38479
|
if (automationCapabilities_b?.decision_drift_detection === true && sessionId_retro_b) {
|
|
38216
38480
|
const activeAgentForDrift_b = swarmState.activeAgent.get(sessionId_retro_b ?? "");
|
|
@@ -38465,8 +38729,8 @@ init_dist();
|
|
|
38465
38729
|
|
|
38466
38730
|
// src/build/discovery.ts
|
|
38467
38731
|
init_dist();
|
|
38468
|
-
import * as
|
|
38469
|
-
import * as
|
|
38732
|
+
import * as fs11 from "fs";
|
|
38733
|
+
import * as path17 from "path";
|
|
38470
38734
|
var ECOSYSTEMS = [
|
|
38471
38735
|
{
|
|
38472
38736
|
ecosystem: "node",
|
|
@@ -38514,7 +38778,10 @@ var ECOSYSTEMS = [
|
|
|
38514
38778
|
buildFiles: ["build.gradle", "build.gradle.kts", "gradle.properties"],
|
|
38515
38779
|
toolchainCommands: ["gradle", "gradlew"],
|
|
38516
38780
|
commands: [
|
|
38517
|
-
{
|
|
38781
|
+
{
|
|
38782
|
+
command: process.platform === "win32" ? "gradlew.bat build" : "./gradlew build",
|
|
38783
|
+
priority: 1
|
|
38784
|
+
},
|
|
38518
38785
|
{ command: "gradle build", priority: 2 }
|
|
38519
38786
|
]
|
|
38520
38787
|
},
|
|
@@ -38578,18 +38845,18 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
38578
38845
|
if (pattern.includes("*")) {
|
|
38579
38846
|
const dir = workingDir;
|
|
38580
38847
|
try {
|
|
38581
|
-
const files =
|
|
38848
|
+
const files = fs11.readdirSync(dir);
|
|
38582
38849
|
const matches = files.filter((f) => {
|
|
38583
38850
|
const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
|
|
38584
38851
|
return regex.test(f);
|
|
38585
38852
|
});
|
|
38586
38853
|
if (matches.length > 0) {
|
|
38587
|
-
return
|
|
38854
|
+
return path17.join(dir, matches[0]);
|
|
38588
38855
|
}
|
|
38589
38856
|
} catch {}
|
|
38590
38857
|
} else {
|
|
38591
|
-
const filePath =
|
|
38592
|
-
if (
|
|
38858
|
+
const filePath = path17.join(workingDir, pattern);
|
|
38859
|
+
if (fs11.existsSync(filePath)) {
|
|
38593
38860
|
return filePath;
|
|
38594
38861
|
}
|
|
38595
38862
|
}
|
|
@@ -38597,12 +38864,12 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
38597
38864
|
return null;
|
|
38598
38865
|
}
|
|
38599
38866
|
function getRepoDefinedScripts(workingDir, scripts) {
|
|
38600
|
-
const packageJsonPath =
|
|
38601
|
-
if (!
|
|
38867
|
+
const packageJsonPath = path17.join(workingDir, "package.json");
|
|
38868
|
+
if (!fs11.existsSync(packageJsonPath)) {
|
|
38602
38869
|
return [];
|
|
38603
38870
|
}
|
|
38604
38871
|
try {
|
|
38605
|
-
const content =
|
|
38872
|
+
const content = fs11.readFileSync(packageJsonPath, "utf-8");
|
|
38606
38873
|
const pkg = JSON.parse(content);
|
|
38607
38874
|
if (!pkg.scripts || typeof pkg.scripts !== "object") {
|
|
38608
38875
|
return [];
|
|
@@ -38638,8 +38905,8 @@ function findAllBuildFiles(workingDir) {
|
|
|
38638
38905
|
const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
|
|
38639
38906
|
findFilesRecursive(workingDir, regex, allBuildFiles);
|
|
38640
38907
|
} else {
|
|
38641
|
-
const filePath =
|
|
38642
|
-
if (
|
|
38908
|
+
const filePath = path17.join(workingDir, pattern);
|
|
38909
|
+
if (fs11.existsSync(filePath)) {
|
|
38643
38910
|
allBuildFiles.add(filePath);
|
|
38644
38911
|
}
|
|
38645
38912
|
}
|
|
@@ -38649,9 +38916,9 @@ function findAllBuildFiles(workingDir) {
|
|
|
38649
38916
|
}
|
|
38650
38917
|
function findFilesRecursive(dir, regex, results) {
|
|
38651
38918
|
try {
|
|
38652
|
-
const entries =
|
|
38919
|
+
const entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
38653
38920
|
for (const entry of entries) {
|
|
38654
|
-
const fullPath =
|
|
38921
|
+
const fullPath = path17.join(dir, entry.name);
|
|
38655
38922
|
if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
|
|
38656
38923
|
findFilesRecursive(fullPath, regex, results);
|
|
38657
38924
|
} else if (entry.isFile() && regex.test(entry.name)) {
|
|
@@ -38892,8 +39159,8 @@ var build_check = tool({
|
|
|
38892
39159
|
// src/tools/checkpoint.ts
|
|
38893
39160
|
init_tool();
|
|
38894
39161
|
import { spawnSync } from "child_process";
|
|
38895
|
-
import * as
|
|
38896
|
-
import * as
|
|
39162
|
+
import * as fs12 from "fs";
|
|
39163
|
+
import * as path18 from "path";
|
|
38897
39164
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
38898
39165
|
var MAX_LABEL_LENGTH = 100;
|
|
38899
39166
|
var GIT_TIMEOUT_MS = 30000;
|
|
@@ -38944,13 +39211,13 @@ function validateLabel(label) {
|
|
|
38944
39211
|
return null;
|
|
38945
39212
|
}
|
|
38946
39213
|
function getCheckpointLogPath() {
|
|
38947
|
-
return
|
|
39214
|
+
return path18.join(process.cwd(), CHECKPOINT_LOG_PATH);
|
|
38948
39215
|
}
|
|
38949
39216
|
function readCheckpointLog() {
|
|
38950
39217
|
const logPath = getCheckpointLogPath();
|
|
38951
39218
|
try {
|
|
38952
|
-
if (
|
|
38953
|
-
const content =
|
|
39219
|
+
if (fs12.existsSync(logPath)) {
|
|
39220
|
+
const content = fs12.readFileSync(logPath, "utf-8");
|
|
38954
39221
|
const parsed = JSON.parse(content);
|
|
38955
39222
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
38956
39223
|
return { version: 1, checkpoints: [] };
|
|
@@ -38962,13 +39229,13 @@ function readCheckpointLog() {
|
|
|
38962
39229
|
}
|
|
38963
39230
|
function writeCheckpointLog(log2) {
|
|
38964
39231
|
const logPath = getCheckpointLogPath();
|
|
38965
|
-
const dir =
|
|
38966
|
-
if (!
|
|
38967
|
-
|
|
39232
|
+
const dir = path18.dirname(logPath);
|
|
39233
|
+
if (!fs12.existsSync(dir)) {
|
|
39234
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
38968
39235
|
}
|
|
38969
39236
|
const tempPath = `${logPath}.tmp`;
|
|
38970
|
-
|
|
38971
|
-
|
|
39237
|
+
fs12.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
39238
|
+
fs12.renameSync(tempPath, logPath);
|
|
38972
39239
|
}
|
|
38973
39240
|
function gitExec(args2) {
|
|
38974
39241
|
const result = spawnSync("git", args2, {
|
|
@@ -39168,8 +39435,8 @@ var checkpoint = tool({
|
|
|
39168
39435
|
});
|
|
39169
39436
|
// src/tools/complexity-hotspots.ts
|
|
39170
39437
|
init_dist();
|
|
39171
|
-
import * as
|
|
39172
|
-
import * as
|
|
39438
|
+
import * as fs13 from "fs";
|
|
39439
|
+
import * as path19 from "path";
|
|
39173
39440
|
var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
|
|
39174
39441
|
var DEFAULT_DAYS = 90;
|
|
39175
39442
|
var DEFAULT_TOP_N = 20;
|
|
@@ -39297,11 +39564,11 @@ function estimateComplexity(content) {
|
|
|
39297
39564
|
}
|
|
39298
39565
|
function getComplexityForFile(filePath) {
|
|
39299
39566
|
try {
|
|
39300
|
-
const stat =
|
|
39567
|
+
const stat = fs13.statSync(filePath);
|
|
39301
39568
|
if (stat.size > MAX_FILE_SIZE_BYTES2) {
|
|
39302
39569
|
return null;
|
|
39303
39570
|
}
|
|
39304
|
-
const content =
|
|
39571
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
39305
39572
|
return estimateComplexity(content);
|
|
39306
39573
|
} catch {
|
|
39307
39574
|
return null;
|
|
@@ -39312,7 +39579,7 @@ async function analyzeHotspots(days, topN, extensions) {
|
|
|
39312
39579
|
const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
|
|
39313
39580
|
const filteredChurn = new Map;
|
|
39314
39581
|
for (const [file3, count] of churnMap) {
|
|
39315
|
-
const ext =
|
|
39582
|
+
const ext = path19.extname(file3).toLowerCase();
|
|
39316
39583
|
if (extSet.has(ext)) {
|
|
39317
39584
|
filteredChurn.set(file3, count);
|
|
39318
39585
|
}
|
|
@@ -39322,8 +39589,8 @@ async function analyzeHotspots(days, topN, extensions) {
|
|
|
39322
39589
|
let analyzedFiles = 0;
|
|
39323
39590
|
for (const [file3, churnCount] of filteredChurn) {
|
|
39324
39591
|
let fullPath = file3;
|
|
39325
|
-
if (!
|
|
39326
|
-
fullPath =
|
|
39592
|
+
if (!fs13.existsSync(fullPath)) {
|
|
39593
|
+
fullPath = path19.join(cwd, file3);
|
|
39327
39594
|
}
|
|
39328
39595
|
const complexity = getComplexityForFile(fullPath);
|
|
39329
39596
|
if (complexity !== null) {
|
|
@@ -39481,14 +39748,14 @@ function validateBase(base) {
|
|
|
39481
39748
|
function validatePaths(paths) {
|
|
39482
39749
|
if (!paths)
|
|
39483
39750
|
return null;
|
|
39484
|
-
for (const
|
|
39485
|
-
if (!
|
|
39751
|
+
for (const path20 of paths) {
|
|
39752
|
+
if (!path20 || path20.length === 0) {
|
|
39486
39753
|
return "empty path not allowed";
|
|
39487
39754
|
}
|
|
39488
|
-
if (
|
|
39755
|
+
if (path20.length > MAX_PATH_LENGTH) {
|
|
39489
39756
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
39490
39757
|
}
|
|
39491
|
-
if (SHELL_METACHARACTERS2.test(
|
|
39758
|
+
if (SHELL_METACHARACTERS2.test(path20)) {
|
|
39492
39759
|
return "path contains shell metacharacters";
|
|
39493
39760
|
}
|
|
39494
39761
|
}
|
|
@@ -39551,8 +39818,8 @@ var diff = tool({
|
|
|
39551
39818
|
if (parts2.length >= 3) {
|
|
39552
39819
|
const additions = parseInt(parts2[0], 10) || 0;
|
|
39553
39820
|
const deletions = parseInt(parts2[1], 10) || 0;
|
|
39554
|
-
const
|
|
39555
|
-
files.push({ path:
|
|
39821
|
+
const path20 = parts2[2];
|
|
39822
|
+
files.push({ path: path20, additions, deletions });
|
|
39556
39823
|
}
|
|
39557
39824
|
}
|
|
39558
39825
|
const contractChanges = [];
|
|
@@ -39780,8 +40047,8 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
39780
40047
|
});
|
|
39781
40048
|
// src/tools/evidence-check.ts
|
|
39782
40049
|
init_dist();
|
|
39783
|
-
import * as
|
|
39784
|
-
import * as
|
|
40050
|
+
import * as fs14 from "fs";
|
|
40051
|
+
import * as path20 from "path";
|
|
39785
40052
|
var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
|
|
39786
40053
|
var MAX_EVIDENCE_FILES = 1000;
|
|
39787
40054
|
var EVIDENCE_DIR = ".swarm/evidence";
|
|
@@ -39804,9 +40071,9 @@ function validateRequiredTypes(input) {
|
|
|
39804
40071
|
return null;
|
|
39805
40072
|
}
|
|
39806
40073
|
function isPathWithinSwarm(filePath, cwd) {
|
|
39807
|
-
const normalizedCwd =
|
|
39808
|
-
const swarmPath =
|
|
39809
|
-
const normalizedPath =
|
|
40074
|
+
const normalizedCwd = path20.resolve(cwd);
|
|
40075
|
+
const swarmPath = path20.join(normalizedCwd, ".swarm");
|
|
40076
|
+
const normalizedPath = path20.resolve(filePath);
|
|
39810
40077
|
return normalizedPath.startsWith(swarmPath);
|
|
39811
40078
|
}
|
|
39812
40079
|
function parseCompletedTasks(planContent) {
|
|
@@ -39822,12 +40089,12 @@ function parseCompletedTasks(planContent) {
|
|
|
39822
40089
|
}
|
|
39823
40090
|
function readEvidenceFiles(evidenceDir, _cwd) {
|
|
39824
40091
|
const evidence = [];
|
|
39825
|
-
if (!
|
|
40092
|
+
if (!fs14.existsSync(evidenceDir) || !fs14.statSync(evidenceDir).isDirectory()) {
|
|
39826
40093
|
return evidence;
|
|
39827
40094
|
}
|
|
39828
40095
|
let files;
|
|
39829
40096
|
try {
|
|
39830
|
-
files =
|
|
40097
|
+
files = fs14.readdirSync(evidenceDir);
|
|
39831
40098
|
} catch {
|
|
39832
40099
|
return evidence;
|
|
39833
40100
|
}
|
|
@@ -39836,14 +40103,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
39836
40103
|
if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
|
|
39837
40104
|
continue;
|
|
39838
40105
|
}
|
|
39839
|
-
const filePath =
|
|
40106
|
+
const filePath = path20.join(evidenceDir, filename);
|
|
39840
40107
|
try {
|
|
39841
|
-
const resolvedPath =
|
|
39842
|
-
const evidenceDirResolved =
|
|
40108
|
+
const resolvedPath = path20.resolve(filePath);
|
|
40109
|
+
const evidenceDirResolved = path20.resolve(evidenceDir);
|
|
39843
40110
|
if (!resolvedPath.startsWith(evidenceDirResolved)) {
|
|
39844
40111
|
continue;
|
|
39845
40112
|
}
|
|
39846
|
-
const stat =
|
|
40113
|
+
const stat = fs14.lstatSync(filePath);
|
|
39847
40114
|
if (!stat.isFile()) {
|
|
39848
40115
|
continue;
|
|
39849
40116
|
}
|
|
@@ -39852,7 +40119,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
39852
40119
|
}
|
|
39853
40120
|
let fileStat;
|
|
39854
40121
|
try {
|
|
39855
|
-
fileStat =
|
|
40122
|
+
fileStat = fs14.statSync(filePath);
|
|
39856
40123
|
if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
|
|
39857
40124
|
continue;
|
|
39858
40125
|
}
|
|
@@ -39861,7 +40128,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
39861
40128
|
}
|
|
39862
40129
|
let content;
|
|
39863
40130
|
try {
|
|
39864
|
-
content =
|
|
40131
|
+
content = fs14.readFileSync(filePath, "utf-8");
|
|
39865
40132
|
} catch {
|
|
39866
40133
|
continue;
|
|
39867
40134
|
}
|
|
@@ -39946,7 +40213,7 @@ var evidence_check = tool({
|
|
|
39946
40213
|
return JSON.stringify(errorResult, null, 2);
|
|
39947
40214
|
}
|
|
39948
40215
|
const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
39949
|
-
const planPath =
|
|
40216
|
+
const planPath = path20.join(cwd, PLAN_FILE);
|
|
39950
40217
|
if (!isPathWithinSwarm(planPath, cwd)) {
|
|
39951
40218
|
const errorResult = {
|
|
39952
40219
|
error: "plan file path validation failed",
|
|
@@ -39960,7 +40227,7 @@ var evidence_check = tool({
|
|
|
39960
40227
|
}
|
|
39961
40228
|
let planContent;
|
|
39962
40229
|
try {
|
|
39963
|
-
planContent =
|
|
40230
|
+
planContent = fs14.readFileSync(planPath, "utf-8");
|
|
39964
40231
|
} catch {
|
|
39965
40232
|
const result2 = {
|
|
39966
40233
|
message: "No completed tasks found in plan.",
|
|
@@ -39978,7 +40245,7 @@ var evidence_check = tool({
|
|
|
39978
40245
|
};
|
|
39979
40246
|
return JSON.stringify(result2, null, 2);
|
|
39980
40247
|
}
|
|
39981
|
-
const evidenceDir =
|
|
40248
|
+
const evidenceDir = path20.join(cwd, EVIDENCE_DIR);
|
|
39982
40249
|
const evidence = readEvidenceFiles(evidenceDir, cwd);
|
|
39983
40250
|
const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
|
|
39984
40251
|
const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
|
|
@@ -39994,8 +40261,8 @@ var evidence_check = tool({
|
|
|
39994
40261
|
});
|
|
39995
40262
|
// src/tools/file-extractor.ts
|
|
39996
40263
|
init_tool();
|
|
39997
|
-
import * as
|
|
39998
|
-
import * as
|
|
40264
|
+
import * as fs15 from "fs";
|
|
40265
|
+
import * as path21 from "path";
|
|
39999
40266
|
var EXT_MAP = {
|
|
40000
40267
|
python: ".py",
|
|
40001
40268
|
py: ".py",
|
|
@@ -40057,8 +40324,8 @@ var extract_code_blocks = tool({
|
|
|
40057
40324
|
execute: async (args2) => {
|
|
40058
40325
|
const { content, output_dir, prefix } = args2;
|
|
40059
40326
|
const targetDir = output_dir || process.cwd();
|
|
40060
|
-
if (!
|
|
40061
|
-
|
|
40327
|
+
if (!fs15.existsSync(targetDir)) {
|
|
40328
|
+
fs15.mkdirSync(targetDir, { recursive: true });
|
|
40062
40329
|
}
|
|
40063
40330
|
const pattern = /```(\w*)\n([\s\S]*?)```/g;
|
|
40064
40331
|
const matches = [...content.matchAll(pattern)];
|
|
@@ -40073,16 +40340,16 @@ var extract_code_blocks = tool({
|
|
|
40073
40340
|
if (prefix) {
|
|
40074
40341
|
filename = `${prefix}_${filename}`;
|
|
40075
40342
|
}
|
|
40076
|
-
let filepath =
|
|
40077
|
-
const base =
|
|
40078
|
-
const ext =
|
|
40343
|
+
let filepath = path21.join(targetDir, filename);
|
|
40344
|
+
const base = path21.basename(filepath, path21.extname(filepath));
|
|
40345
|
+
const ext = path21.extname(filepath);
|
|
40079
40346
|
let counter = 1;
|
|
40080
|
-
while (
|
|
40081
|
-
filepath =
|
|
40347
|
+
while (fs15.existsSync(filepath)) {
|
|
40348
|
+
filepath = path21.join(targetDir, `${base}_${counter}${ext}`);
|
|
40082
40349
|
counter++;
|
|
40083
40350
|
}
|
|
40084
40351
|
try {
|
|
40085
|
-
|
|
40352
|
+
fs15.writeFileSync(filepath, code.trim(), "utf-8");
|
|
40086
40353
|
savedFiles.push(filepath);
|
|
40087
40354
|
} catch (error93) {
|
|
40088
40355
|
errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
@@ -40190,8 +40457,8 @@ var gitingest = tool({
|
|
|
40190
40457
|
});
|
|
40191
40458
|
// src/tools/imports.ts
|
|
40192
40459
|
init_dist();
|
|
40193
|
-
import * as
|
|
40194
|
-
import * as
|
|
40460
|
+
import * as fs16 from "fs";
|
|
40461
|
+
import * as path22 from "path";
|
|
40195
40462
|
var MAX_FILE_PATH_LENGTH2 = 500;
|
|
40196
40463
|
var MAX_SYMBOL_LENGTH = 256;
|
|
40197
40464
|
var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
|
|
@@ -40245,7 +40512,7 @@ function validateSymbolInput(symbol3) {
|
|
|
40245
40512
|
return null;
|
|
40246
40513
|
}
|
|
40247
40514
|
function isBinaryFile2(filePath, buffer) {
|
|
40248
|
-
const ext =
|
|
40515
|
+
const ext = path22.extname(filePath).toLowerCase();
|
|
40249
40516
|
if (ext === ".json" || ext === ".md" || ext === ".txt") {
|
|
40250
40517
|
return false;
|
|
40251
40518
|
}
|
|
@@ -40269,15 +40536,15 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
40269
40536
|
const imports = [];
|
|
40270
40537
|
let _resolvedTarget;
|
|
40271
40538
|
try {
|
|
40272
|
-
_resolvedTarget =
|
|
40539
|
+
_resolvedTarget = path22.resolve(targetFile);
|
|
40273
40540
|
} catch {
|
|
40274
40541
|
_resolvedTarget = targetFile;
|
|
40275
40542
|
}
|
|
40276
|
-
const targetBasename =
|
|
40543
|
+
const targetBasename = path22.basename(targetFile, path22.extname(targetFile));
|
|
40277
40544
|
const targetWithExt = targetFile;
|
|
40278
40545
|
const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
40279
|
-
const normalizedTargetWithExt =
|
|
40280
|
-
const normalizedTargetWithoutExt =
|
|
40546
|
+
const normalizedTargetWithExt = path22.normalize(targetWithExt).replace(/\\/g, "/");
|
|
40547
|
+
const normalizedTargetWithoutExt = path22.normalize(targetWithoutExt).replace(/\\/g, "/");
|
|
40281
40548
|
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
40282
40549
|
for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
|
|
40283
40550
|
const modulePath = match[1] || match[2] || match[3];
|
|
@@ -40300,9 +40567,9 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
40300
40567
|
}
|
|
40301
40568
|
const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
|
|
40302
40569
|
let isMatch = false;
|
|
40303
|
-
const _targetDir =
|
|
40304
|
-
const targetExt =
|
|
40305
|
-
const targetBasenameNoExt =
|
|
40570
|
+
const _targetDir = path22.dirname(targetFile);
|
|
40571
|
+
const targetExt = path22.extname(targetFile);
|
|
40572
|
+
const targetBasenameNoExt = path22.basename(targetFile, targetExt);
|
|
40306
40573
|
const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
40307
40574
|
const moduleName = modulePath.split(/[/\\]/).pop() || "";
|
|
40308
40575
|
const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
@@ -40359,7 +40626,7 @@ var SKIP_DIRECTORIES2 = new Set([
|
|
|
40359
40626
|
function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
|
|
40360
40627
|
let entries;
|
|
40361
40628
|
try {
|
|
40362
|
-
entries =
|
|
40629
|
+
entries = fs16.readdirSync(dir);
|
|
40363
40630
|
} catch (e) {
|
|
40364
40631
|
stats.fileErrors.push({
|
|
40365
40632
|
path: dir,
|
|
@@ -40370,13 +40637,13 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
|
|
|
40370
40637
|
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
40371
40638
|
for (const entry of entries) {
|
|
40372
40639
|
if (SKIP_DIRECTORIES2.has(entry)) {
|
|
40373
|
-
stats.skippedDirs.push(
|
|
40640
|
+
stats.skippedDirs.push(path22.join(dir, entry));
|
|
40374
40641
|
continue;
|
|
40375
40642
|
}
|
|
40376
|
-
const fullPath =
|
|
40643
|
+
const fullPath = path22.join(dir, entry);
|
|
40377
40644
|
let stat;
|
|
40378
40645
|
try {
|
|
40379
|
-
stat =
|
|
40646
|
+
stat = fs16.statSync(fullPath);
|
|
40380
40647
|
} catch (e) {
|
|
40381
40648
|
stats.fileErrors.push({
|
|
40382
40649
|
path: fullPath,
|
|
@@ -40387,7 +40654,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
|
|
|
40387
40654
|
if (stat.isDirectory()) {
|
|
40388
40655
|
findSourceFiles2(fullPath, files, stats);
|
|
40389
40656
|
} else if (stat.isFile()) {
|
|
40390
|
-
const ext =
|
|
40657
|
+
const ext = path22.extname(fullPath).toLowerCase();
|
|
40391
40658
|
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
40392
40659
|
files.push(fullPath);
|
|
40393
40660
|
}
|
|
@@ -40443,8 +40710,8 @@ var imports = tool({
|
|
|
40443
40710
|
return JSON.stringify(errorResult, null, 2);
|
|
40444
40711
|
}
|
|
40445
40712
|
try {
|
|
40446
|
-
const targetFile =
|
|
40447
|
-
if (!
|
|
40713
|
+
const targetFile = path22.resolve(file3);
|
|
40714
|
+
if (!fs16.existsSync(targetFile)) {
|
|
40448
40715
|
const errorResult = {
|
|
40449
40716
|
error: `target file not found: ${file3}`,
|
|
40450
40717
|
target: file3,
|
|
@@ -40454,7 +40721,7 @@ var imports = tool({
|
|
|
40454
40721
|
};
|
|
40455
40722
|
return JSON.stringify(errorResult, null, 2);
|
|
40456
40723
|
}
|
|
40457
|
-
const targetStat =
|
|
40724
|
+
const targetStat = fs16.statSync(targetFile);
|
|
40458
40725
|
if (!targetStat.isFile()) {
|
|
40459
40726
|
const errorResult = {
|
|
40460
40727
|
error: "target must be a file, not a directory",
|
|
@@ -40465,7 +40732,7 @@ var imports = tool({
|
|
|
40465
40732
|
};
|
|
40466
40733
|
return JSON.stringify(errorResult, null, 2);
|
|
40467
40734
|
}
|
|
40468
|
-
const baseDir =
|
|
40735
|
+
const baseDir = path22.dirname(targetFile);
|
|
40469
40736
|
const scanStats = {
|
|
40470
40737
|
skippedDirs: [],
|
|
40471
40738
|
skippedFiles: 0,
|
|
@@ -40480,12 +40747,12 @@ var imports = tool({
|
|
|
40480
40747
|
if (consumers.length >= MAX_CONSUMERS)
|
|
40481
40748
|
break;
|
|
40482
40749
|
try {
|
|
40483
|
-
const stat =
|
|
40750
|
+
const stat = fs16.statSync(filePath);
|
|
40484
40751
|
if (stat.size > MAX_FILE_SIZE_BYTES4) {
|
|
40485
40752
|
skippedFileCount++;
|
|
40486
40753
|
continue;
|
|
40487
40754
|
}
|
|
40488
|
-
const buffer =
|
|
40755
|
+
const buffer = fs16.readFileSync(filePath);
|
|
40489
40756
|
if (isBinaryFile2(filePath, buffer)) {
|
|
40490
40757
|
skippedFileCount++;
|
|
40491
40758
|
continue;
|
|
@@ -40554,7 +40821,8 @@ init_lint();
|
|
|
40554
40821
|
|
|
40555
40822
|
// src/tools/phase-complete.ts
|
|
40556
40823
|
init_tool();
|
|
40557
|
-
import * as
|
|
40824
|
+
import * as fs17 from "fs";
|
|
40825
|
+
init_manager();
|
|
40558
40826
|
init_utils2();
|
|
40559
40827
|
function getDelegationsSince(sessionID, sinceTimestamp) {
|
|
40560
40828
|
const chain = swarmState.delegationChains.get(sessionID);
|
|
@@ -40576,7 +40844,10 @@ function normalizeAgentsFromDelegations(delegations) {
|
|
|
40576
40844
|
}
|
|
40577
40845
|
return agents;
|
|
40578
40846
|
}
|
|
40579
|
-
|
|
40847
|
+
function isValidRetroEntry(entry, phase) {
|
|
40848
|
+
return entry.type === "retrospective" && "phase_number" in entry && entry.phase_number === phase && "verdict" in entry && entry.verdict === "pass";
|
|
40849
|
+
}
|
|
40850
|
+
async function executePhaseComplete(args2, workingDirectory) {
|
|
40580
40851
|
const phase = Number(args2.phase);
|
|
40581
40852
|
const summary = args2.summary;
|
|
40582
40853
|
const sessionID = args2.sessionID;
|
|
@@ -40607,8 +40878,8 @@ async function executePhaseComplete(args2) {
|
|
|
40607
40878
|
const trackedAgents = session.phaseAgentsDispatched ?? new Set;
|
|
40608
40879
|
const allAgents = new Set([...delegationAgents, ...trackedAgents]);
|
|
40609
40880
|
const agentsDispatched = Array.from(allAgents).sort();
|
|
40610
|
-
const
|
|
40611
|
-
const { config: config3 } = loadPluginConfigWithMeta(
|
|
40881
|
+
const dir = workingDirectory ?? process.cwd();
|
|
40882
|
+
const { config: config3 } = loadPluginConfigWithMeta(dir);
|
|
40612
40883
|
let phaseCompleteConfig;
|
|
40613
40884
|
try {
|
|
40614
40885
|
phaseCompleteConfig = PhaseCompleteConfigSchema.parse(config3.phase_complete ?? {});
|
|
@@ -40634,6 +40905,37 @@ async function executePhaseComplete(args2) {
|
|
|
40634
40905
|
warnings: []
|
|
40635
40906
|
}, null, 2);
|
|
40636
40907
|
}
|
|
40908
|
+
const retroBundle = await loadEvidence(dir, `retro-${phase}`);
|
|
40909
|
+
let retroFound = false;
|
|
40910
|
+
if (retroBundle !== null) {
|
|
40911
|
+
retroFound = retroBundle.entries?.some((entry) => isValidRetroEntry(entry, phase)) ?? false;
|
|
40912
|
+
}
|
|
40913
|
+
if (!retroFound) {
|
|
40914
|
+
const allTaskIds = await listEvidenceTaskIds(dir);
|
|
40915
|
+
const retroTaskIds = allTaskIds.filter((id) => id.startsWith("retro-"));
|
|
40916
|
+
for (const taskId of retroTaskIds) {
|
|
40917
|
+
const bundle = await loadEvidence(dir, taskId);
|
|
40918
|
+
if (bundle === null)
|
|
40919
|
+
continue;
|
|
40920
|
+
retroFound = bundle.entries?.some((entry) => isValidRetroEntry(entry, phase)) ?? false;
|
|
40921
|
+
if (retroFound)
|
|
40922
|
+
break;
|
|
40923
|
+
}
|
|
40924
|
+
}
|
|
40925
|
+
if (!retroFound) {
|
|
40926
|
+
return JSON.stringify({
|
|
40927
|
+
success: false,
|
|
40928
|
+
phase,
|
|
40929
|
+
status: "blocked",
|
|
40930
|
+
reason: "RETROSPECTIVE_MISSING",
|
|
40931
|
+
message: `Phase ${phase} cannot be completed: no valid retrospective evidence found. Write a retrospective bundle at .swarm/evidence/retro-${phase}/evidence.json with type='retrospective', phase_number=${phase}, verdict='pass' before calling phase_complete.`,
|
|
40932
|
+
agentsDispatched: [],
|
|
40933
|
+
agentsMissing: [],
|
|
40934
|
+
warnings: [
|
|
40935
|
+
`Retrospective missing for phase ${phase}. Write a retro bundle with verdict='pass' at .swarm/evidence/retro-${phase}/evidence.json`
|
|
40936
|
+
]
|
|
40937
|
+
}, null, 2);
|
|
40938
|
+
}
|
|
40637
40939
|
const effectiveRequired = [...phaseCompleteConfig.required_agents];
|
|
40638
40940
|
if (phaseCompleteConfig.require_docs && !effectiveRequired.includes("docs")) {
|
|
40639
40941
|
effectiveRequired.push("docs");
|
|
@@ -40666,8 +40968,8 @@ async function executePhaseComplete(args2) {
|
|
|
40666
40968
|
summary: safeSummary ?? null
|
|
40667
40969
|
};
|
|
40668
40970
|
try {
|
|
40669
|
-
const eventsPath = validateSwarmPath(
|
|
40670
|
-
|
|
40971
|
+
const eventsPath = validateSwarmPath(dir, "events.jsonl");
|
|
40972
|
+
fs17.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
40671
40973
|
`, "utf-8");
|
|
40672
40974
|
} catch (writeError) {
|
|
40673
40975
|
warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -40717,8 +41019,8 @@ var phase_complete = tool({
|
|
|
40717
41019
|
});
|
|
40718
41020
|
// src/tools/pkg-audit.ts
|
|
40719
41021
|
init_dist();
|
|
40720
|
-
import * as
|
|
40721
|
-
import * as
|
|
41022
|
+
import * as fs18 from "fs";
|
|
41023
|
+
import * as path23 from "path";
|
|
40722
41024
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
40723
41025
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
40724
41026
|
function isValidEcosystem(value) {
|
|
@@ -40736,13 +41038,13 @@ function validateArgs3(args2) {
|
|
|
40736
41038
|
function detectEcosystems() {
|
|
40737
41039
|
const ecosystems = [];
|
|
40738
41040
|
const cwd = process.cwd();
|
|
40739
|
-
if (
|
|
41041
|
+
if (fs18.existsSync(path23.join(cwd, "package.json"))) {
|
|
40740
41042
|
ecosystems.push("npm");
|
|
40741
41043
|
}
|
|
40742
|
-
if (
|
|
41044
|
+
if (fs18.existsSync(path23.join(cwd, "pyproject.toml")) || fs18.existsSync(path23.join(cwd, "requirements.txt"))) {
|
|
40743
41045
|
ecosystems.push("pip");
|
|
40744
41046
|
}
|
|
40745
|
-
if (
|
|
41047
|
+
if (fs18.existsSync(path23.join(cwd, "Cargo.toml"))) {
|
|
40746
41048
|
ecosystems.push("cargo");
|
|
40747
41049
|
}
|
|
40748
41050
|
return ecosystems;
|
|
@@ -42650,11 +42952,11 @@ var Module2 = (() => {
|
|
|
42650
42952
|
throw toThrow;
|
|
42651
42953
|
}, "quit_");
|
|
42652
42954
|
var scriptDirectory = "";
|
|
42653
|
-
function locateFile(
|
|
42955
|
+
function locateFile(path24) {
|
|
42654
42956
|
if (Module["locateFile"]) {
|
|
42655
|
-
return Module["locateFile"](
|
|
42957
|
+
return Module["locateFile"](path24, scriptDirectory);
|
|
42656
42958
|
}
|
|
42657
|
-
return scriptDirectory +
|
|
42959
|
+
return scriptDirectory + path24;
|
|
42658
42960
|
}
|
|
42659
42961
|
__name(locateFile, "locateFile");
|
|
42660
42962
|
var readAsync, readBinary;
|
|
@@ -44468,7 +44770,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
44468
44770
|
]);
|
|
44469
44771
|
// src/tools/pre-check-batch.ts
|
|
44470
44772
|
init_dist();
|
|
44471
|
-
import * as
|
|
44773
|
+
import * as path26 from "path";
|
|
44472
44774
|
|
|
44473
44775
|
// node_modules/yocto-queue/index.js
|
|
44474
44776
|
class Node2 {
|
|
@@ -44634,8 +44936,8 @@ init_lint();
|
|
|
44634
44936
|
init_manager();
|
|
44635
44937
|
|
|
44636
44938
|
// src/quality/metrics.ts
|
|
44637
|
-
import * as
|
|
44638
|
-
import * as
|
|
44939
|
+
import * as fs19 from "fs";
|
|
44940
|
+
import * as path24 from "path";
|
|
44639
44941
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
44640
44942
|
var MIN_DUPLICATION_LINES = 10;
|
|
44641
44943
|
function estimateCyclomaticComplexity(content) {
|
|
@@ -44673,11 +44975,11 @@ function estimateCyclomaticComplexity(content) {
|
|
|
44673
44975
|
}
|
|
44674
44976
|
function getComplexityForFile2(filePath) {
|
|
44675
44977
|
try {
|
|
44676
|
-
const stat =
|
|
44978
|
+
const stat = fs19.statSync(filePath);
|
|
44677
44979
|
if (stat.size > MAX_FILE_SIZE_BYTES5) {
|
|
44678
44980
|
return null;
|
|
44679
44981
|
}
|
|
44680
|
-
const content =
|
|
44982
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
44681
44983
|
return estimateCyclomaticComplexity(content);
|
|
44682
44984
|
} catch {
|
|
44683
44985
|
return null;
|
|
@@ -44687,8 +44989,8 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
44687
44989
|
let totalComplexity = 0;
|
|
44688
44990
|
const analyzedFiles = [];
|
|
44689
44991
|
for (const file3 of files) {
|
|
44690
|
-
const fullPath =
|
|
44691
|
-
if (!
|
|
44992
|
+
const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
|
|
44993
|
+
if (!fs19.existsSync(fullPath)) {
|
|
44692
44994
|
continue;
|
|
44693
44995
|
}
|
|
44694
44996
|
const complexity = getComplexityForFile2(fullPath);
|
|
@@ -44809,8 +45111,8 @@ function countGoExports(content) {
|
|
|
44809
45111
|
}
|
|
44810
45112
|
function getExportCountForFile(filePath) {
|
|
44811
45113
|
try {
|
|
44812
|
-
const content =
|
|
44813
|
-
const ext =
|
|
45114
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
45115
|
+
const ext = path24.extname(filePath).toLowerCase();
|
|
44814
45116
|
switch (ext) {
|
|
44815
45117
|
case ".ts":
|
|
44816
45118
|
case ".tsx":
|
|
@@ -44836,8 +45138,8 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
44836
45138
|
let totalExports = 0;
|
|
44837
45139
|
const analyzedFiles = [];
|
|
44838
45140
|
for (const file3 of files) {
|
|
44839
|
-
const fullPath =
|
|
44840
|
-
if (!
|
|
45141
|
+
const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
|
|
45142
|
+
if (!fs19.existsSync(fullPath)) {
|
|
44841
45143
|
continue;
|
|
44842
45144
|
}
|
|
44843
45145
|
const exports = getExportCountForFile(fullPath);
|
|
@@ -44870,16 +45172,16 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
44870
45172
|
let duplicateLines = 0;
|
|
44871
45173
|
const analyzedFiles = [];
|
|
44872
45174
|
for (const file3 of files) {
|
|
44873
|
-
const fullPath =
|
|
44874
|
-
if (!
|
|
45175
|
+
const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
|
|
45176
|
+
if (!fs19.existsSync(fullPath)) {
|
|
44875
45177
|
continue;
|
|
44876
45178
|
}
|
|
44877
45179
|
try {
|
|
44878
|
-
const stat =
|
|
45180
|
+
const stat = fs19.statSync(fullPath);
|
|
44879
45181
|
if (stat.size > MAX_FILE_SIZE_BYTES5) {
|
|
44880
45182
|
continue;
|
|
44881
45183
|
}
|
|
44882
|
-
const content =
|
|
45184
|
+
const content = fs19.readFileSync(fullPath, "utf-8");
|
|
44883
45185
|
const lines = content.split(`
|
|
44884
45186
|
`).filter((line) => line.trim().length > 0);
|
|
44885
45187
|
if (lines.length < MIN_DUPLICATION_LINES) {
|
|
@@ -44903,8 +45205,8 @@ function countCodeLines(content) {
|
|
|
44903
45205
|
return lines.length;
|
|
44904
45206
|
}
|
|
44905
45207
|
function isTestFile(filePath) {
|
|
44906
|
-
const basename5 =
|
|
44907
|
-
const _ext =
|
|
45208
|
+
const basename5 = path24.basename(filePath);
|
|
45209
|
+
const _ext = path24.extname(filePath).toLowerCase();
|
|
44908
45210
|
const testPatterns = [
|
|
44909
45211
|
".test.",
|
|
44910
45212
|
".spec.",
|
|
@@ -44946,31 +45248,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
|
|
|
44946
45248
|
async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
44947
45249
|
let testLines = 0;
|
|
44948
45250
|
let codeLines = 0;
|
|
44949
|
-
const srcDir =
|
|
44950
|
-
if (
|
|
45251
|
+
const srcDir = path24.join(workingDir, "src");
|
|
45252
|
+
if (fs19.existsSync(srcDir)) {
|
|
44951
45253
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
44952
45254
|
codeLines += lines;
|
|
44953
45255
|
});
|
|
44954
45256
|
}
|
|
44955
45257
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
44956
45258
|
for (const dir of possibleSrcDirs) {
|
|
44957
|
-
const dirPath =
|
|
44958
|
-
if (
|
|
45259
|
+
const dirPath = path24.join(workingDir, dir);
|
|
45260
|
+
if (fs19.existsSync(dirPath)) {
|
|
44959
45261
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
44960
45262
|
codeLines += lines;
|
|
44961
45263
|
});
|
|
44962
45264
|
}
|
|
44963
45265
|
}
|
|
44964
|
-
const testsDir =
|
|
44965
|
-
if (
|
|
45266
|
+
const testsDir = path24.join(workingDir, "tests");
|
|
45267
|
+
if (fs19.existsSync(testsDir)) {
|
|
44966
45268
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
44967
45269
|
testLines += lines;
|
|
44968
45270
|
});
|
|
44969
45271
|
}
|
|
44970
45272
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
44971
45273
|
for (const dir of possibleTestDirs) {
|
|
44972
|
-
const dirPath =
|
|
44973
|
-
if (
|
|
45274
|
+
const dirPath = path24.join(workingDir, dir);
|
|
45275
|
+
if (fs19.existsSync(dirPath) && dirPath !== testsDir) {
|
|
44974
45276
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
44975
45277
|
testLines += lines;
|
|
44976
45278
|
});
|
|
@@ -44982,9 +45284,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
44982
45284
|
}
|
|
44983
45285
|
async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
|
|
44984
45286
|
try {
|
|
44985
|
-
const entries =
|
|
45287
|
+
const entries = fs19.readdirSync(dirPath, { withFileTypes: true });
|
|
44986
45288
|
for (const entry of entries) {
|
|
44987
|
-
const fullPath =
|
|
45289
|
+
const fullPath = path24.join(dirPath, entry.name);
|
|
44988
45290
|
if (entry.isDirectory()) {
|
|
44989
45291
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
|
|
44990
45292
|
continue;
|
|
@@ -44992,7 +45294,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
44992
45294
|
await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
|
|
44993
45295
|
} else if (entry.isFile()) {
|
|
44994
45296
|
const relativePath = fullPath.replace(`${process.cwd()}/`, "");
|
|
44995
|
-
const ext =
|
|
45297
|
+
const ext = path24.extname(entry.name).toLowerCase();
|
|
44996
45298
|
const validExts = [
|
|
44997
45299
|
".ts",
|
|
44998
45300
|
".tsx",
|
|
@@ -45030,7 +45332,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
45030
45332
|
continue;
|
|
45031
45333
|
}
|
|
45032
45334
|
try {
|
|
45033
|
-
const content =
|
|
45335
|
+
const content = fs19.readFileSync(fullPath, "utf-8");
|
|
45034
45336
|
const lines = countCodeLines(content);
|
|
45035
45337
|
callback(lines);
|
|
45036
45338
|
} catch {}
|
|
@@ -45246,8 +45548,8 @@ async function qualityBudget(input, directory) {
|
|
|
45246
45548
|
|
|
45247
45549
|
// src/tools/sast-scan.ts
|
|
45248
45550
|
init_manager();
|
|
45249
|
-
import * as
|
|
45250
|
-
import * as
|
|
45551
|
+
import * as fs20 from "fs";
|
|
45552
|
+
import * as path25 from "path";
|
|
45251
45553
|
import { extname as extname7 } from "path";
|
|
45252
45554
|
|
|
45253
45555
|
// src/sast/rules/c.ts
|
|
@@ -46110,17 +46412,17 @@ var SEVERITY_ORDER = {
|
|
|
46110
46412
|
};
|
|
46111
46413
|
function shouldSkipFile(filePath) {
|
|
46112
46414
|
try {
|
|
46113
|
-
const stats =
|
|
46415
|
+
const stats = fs20.statSync(filePath);
|
|
46114
46416
|
if (stats.size > MAX_FILE_SIZE_BYTES6) {
|
|
46115
46417
|
return { skip: true, reason: "file too large" };
|
|
46116
46418
|
}
|
|
46117
46419
|
if (stats.size === 0) {
|
|
46118
46420
|
return { skip: true, reason: "empty file" };
|
|
46119
46421
|
}
|
|
46120
|
-
const fd =
|
|
46422
|
+
const fd = fs20.openSync(filePath, "r");
|
|
46121
46423
|
const buffer = Buffer.alloc(8192);
|
|
46122
|
-
const bytesRead =
|
|
46123
|
-
|
|
46424
|
+
const bytesRead = fs20.readSync(fd, buffer, 0, 8192, 0);
|
|
46425
|
+
fs20.closeSync(fd);
|
|
46124
46426
|
if (bytesRead > 0) {
|
|
46125
46427
|
let nullCount = 0;
|
|
46126
46428
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -46159,7 +46461,7 @@ function countBySeverity(findings) {
|
|
|
46159
46461
|
}
|
|
46160
46462
|
function scanFileWithTierA(filePath, language) {
|
|
46161
46463
|
try {
|
|
46162
|
-
const content =
|
|
46464
|
+
const content = fs20.readFileSync(filePath, "utf-8");
|
|
46163
46465
|
const findings = executeRulesSync(filePath, content, language);
|
|
46164
46466
|
return findings.map((f) => ({
|
|
46165
46467
|
rule_id: f.rule_id,
|
|
@@ -46202,8 +46504,8 @@ async function sastScan(input, directory, config3) {
|
|
|
46202
46504
|
const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
|
|
46203
46505
|
const filesByLanguage = new Map;
|
|
46204
46506
|
for (const filePath of changed_files) {
|
|
46205
|
-
const resolvedPath =
|
|
46206
|
-
if (!
|
|
46507
|
+
const resolvedPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(directory, filePath);
|
|
46508
|
+
if (!fs20.existsSync(resolvedPath)) {
|
|
46207
46509
|
_filesSkipped++;
|
|
46208
46510
|
continue;
|
|
46209
46511
|
}
|
|
@@ -46311,10 +46613,10 @@ function validatePath(inputPath, baseDir) {
|
|
|
46311
46613
|
if (!inputPath || inputPath.length === 0) {
|
|
46312
46614
|
return "path is required";
|
|
46313
46615
|
}
|
|
46314
|
-
const resolved =
|
|
46315
|
-
const baseResolved =
|
|
46316
|
-
const relative3 =
|
|
46317
|
-
if (relative3.startsWith("..") ||
|
|
46616
|
+
const resolved = path26.resolve(baseDir, inputPath);
|
|
46617
|
+
const baseResolved = path26.resolve(baseDir);
|
|
46618
|
+
const relative3 = path26.relative(baseResolved, resolved);
|
|
46619
|
+
if (relative3.startsWith("..") || path26.isAbsolute(relative3)) {
|
|
46318
46620
|
return "path traversal detected";
|
|
46319
46621
|
}
|
|
46320
46622
|
return null;
|
|
@@ -46436,7 +46738,7 @@ async function runPreCheckBatch(input) {
|
|
|
46436
46738
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
46437
46739
|
continue;
|
|
46438
46740
|
}
|
|
46439
|
-
changedFiles.push(
|
|
46741
|
+
changedFiles.push(path26.resolve(directory, file3));
|
|
46440
46742
|
}
|
|
46441
46743
|
} else {
|
|
46442
46744
|
changedFiles = [];
|
|
@@ -46626,8 +46928,8 @@ var retrieve_summary = tool({
|
|
|
46626
46928
|
// src/tools/sbom-generate.ts
|
|
46627
46929
|
init_dist();
|
|
46628
46930
|
init_manager();
|
|
46629
|
-
import * as
|
|
46630
|
-
import * as
|
|
46931
|
+
import * as fs21 from "fs";
|
|
46932
|
+
import * as path27 from "path";
|
|
46631
46933
|
|
|
46632
46934
|
// src/sbom/detectors/dart.ts
|
|
46633
46935
|
function parsePubspecLock(content) {
|
|
@@ -47472,9 +47774,9 @@ function findManifestFiles(rootDir) {
|
|
|
47472
47774
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
47473
47775
|
function searchDir(dir) {
|
|
47474
47776
|
try {
|
|
47475
|
-
const entries =
|
|
47777
|
+
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
47476
47778
|
for (const entry of entries) {
|
|
47477
|
-
const fullPath =
|
|
47779
|
+
const fullPath = path27.join(dir, entry.name);
|
|
47478
47780
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
47479
47781
|
continue;
|
|
47480
47782
|
}
|
|
@@ -47484,7 +47786,7 @@ function findManifestFiles(rootDir) {
|
|
|
47484
47786
|
for (const pattern of patterns) {
|
|
47485
47787
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
47486
47788
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
47487
|
-
manifestFiles.push(
|
|
47789
|
+
manifestFiles.push(path27.relative(cwd, fullPath));
|
|
47488
47790
|
break;
|
|
47489
47791
|
}
|
|
47490
47792
|
}
|
|
@@ -47501,14 +47803,14 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
47501
47803
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
47502
47804
|
for (const dir of directories) {
|
|
47503
47805
|
try {
|
|
47504
|
-
const entries =
|
|
47806
|
+
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
47505
47807
|
for (const entry of entries) {
|
|
47506
|
-
const fullPath =
|
|
47808
|
+
const fullPath = path27.join(dir, entry.name);
|
|
47507
47809
|
if (entry.isFile()) {
|
|
47508
47810
|
for (const pattern of patterns) {
|
|
47509
47811
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
47510
47812
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
47511
|
-
found.push(
|
|
47813
|
+
found.push(path27.relative(workingDir, fullPath));
|
|
47512
47814
|
break;
|
|
47513
47815
|
}
|
|
47514
47816
|
}
|
|
@@ -47521,11 +47823,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
47521
47823
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
47522
47824
|
const dirs = new Set;
|
|
47523
47825
|
for (const file3 of changedFiles) {
|
|
47524
|
-
let currentDir =
|
|
47826
|
+
let currentDir = path27.dirname(file3);
|
|
47525
47827
|
while (true) {
|
|
47526
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
47527
|
-
dirs.add(
|
|
47528
|
-
const parent =
|
|
47828
|
+
if (currentDir && currentDir !== "." && currentDir !== path27.sep) {
|
|
47829
|
+
dirs.add(path27.join(workingDir, currentDir));
|
|
47830
|
+
const parent = path27.dirname(currentDir);
|
|
47529
47831
|
if (parent === currentDir)
|
|
47530
47832
|
break;
|
|
47531
47833
|
currentDir = parent;
|
|
@@ -47539,7 +47841,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
47539
47841
|
}
|
|
47540
47842
|
function ensureOutputDir(outputDir) {
|
|
47541
47843
|
try {
|
|
47542
|
-
|
|
47844
|
+
fs21.mkdirSync(outputDir, { recursive: true });
|
|
47543
47845
|
} catch (error93) {
|
|
47544
47846
|
if (!error93 || error93.code !== "EEXIST") {
|
|
47545
47847
|
throw error93;
|
|
@@ -47632,11 +47934,11 @@ var sbom_generate = tool({
|
|
|
47632
47934
|
const processedFiles = [];
|
|
47633
47935
|
for (const manifestFile of manifestFiles) {
|
|
47634
47936
|
try {
|
|
47635
|
-
const fullPath =
|
|
47636
|
-
if (!
|
|
47937
|
+
const fullPath = path27.isAbsolute(manifestFile) ? manifestFile : path27.join(workingDir, manifestFile);
|
|
47938
|
+
if (!fs21.existsSync(fullPath)) {
|
|
47637
47939
|
continue;
|
|
47638
47940
|
}
|
|
47639
|
-
const content =
|
|
47941
|
+
const content = fs21.readFileSync(fullPath, "utf-8");
|
|
47640
47942
|
const components = detectComponents(manifestFile, content);
|
|
47641
47943
|
processedFiles.push(manifestFile);
|
|
47642
47944
|
if (components.length > 0) {
|
|
@@ -47649,8 +47951,8 @@ var sbom_generate = tool({
|
|
|
47649
47951
|
const bom = generateCycloneDX(allComponents);
|
|
47650
47952
|
const bomJson = serializeCycloneDX(bom);
|
|
47651
47953
|
const filename = generateSbomFilename();
|
|
47652
|
-
const outputPath =
|
|
47653
|
-
|
|
47954
|
+
const outputPath = path27.join(outputDir, filename);
|
|
47955
|
+
fs21.writeFileSync(outputPath, bomJson, "utf-8");
|
|
47654
47956
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
47655
47957
|
try {
|
|
47656
47958
|
const timestamp = new Date().toISOString();
|
|
@@ -47691,8 +47993,8 @@ var sbom_generate = tool({
|
|
|
47691
47993
|
});
|
|
47692
47994
|
// src/tools/schema-drift.ts
|
|
47693
47995
|
init_dist();
|
|
47694
|
-
import * as
|
|
47695
|
-
import * as
|
|
47996
|
+
import * as fs22 from "fs";
|
|
47997
|
+
import * as path28 from "path";
|
|
47696
47998
|
var SPEC_CANDIDATES = [
|
|
47697
47999
|
"openapi.json",
|
|
47698
48000
|
"openapi.yaml",
|
|
@@ -47724,28 +48026,28 @@ function normalizePath(p) {
|
|
|
47724
48026
|
}
|
|
47725
48027
|
function discoverSpecFile(cwd, specFileArg) {
|
|
47726
48028
|
if (specFileArg) {
|
|
47727
|
-
const resolvedPath =
|
|
47728
|
-
const normalizedCwd = cwd.endsWith(
|
|
48029
|
+
const resolvedPath = path28.resolve(cwd, specFileArg);
|
|
48030
|
+
const normalizedCwd = cwd.endsWith(path28.sep) ? cwd : cwd + path28.sep;
|
|
47729
48031
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
47730
48032
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
47731
48033
|
}
|
|
47732
|
-
const ext =
|
|
48034
|
+
const ext = path28.extname(resolvedPath).toLowerCase();
|
|
47733
48035
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
47734
48036
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
47735
48037
|
}
|
|
47736
|
-
const stats =
|
|
48038
|
+
const stats = fs22.statSync(resolvedPath);
|
|
47737
48039
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
47738
48040
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
47739
48041
|
}
|
|
47740
|
-
if (!
|
|
48042
|
+
if (!fs22.existsSync(resolvedPath)) {
|
|
47741
48043
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
47742
48044
|
}
|
|
47743
48045
|
return resolvedPath;
|
|
47744
48046
|
}
|
|
47745
48047
|
for (const candidate of SPEC_CANDIDATES) {
|
|
47746
|
-
const candidatePath =
|
|
47747
|
-
if (
|
|
47748
|
-
const stats =
|
|
48048
|
+
const candidatePath = path28.resolve(cwd, candidate);
|
|
48049
|
+
if (fs22.existsSync(candidatePath)) {
|
|
48050
|
+
const stats = fs22.statSync(candidatePath);
|
|
47749
48051
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
47750
48052
|
return candidatePath;
|
|
47751
48053
|
}
|
|
@@ -47754,8 +48056,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
47754
48056
|
return null;
|
|
47755
48057
|
}
|
|
47756
48058
|
function parseSpec(specFile) {
|
|
47757
|
-
const content =
|
|
47758
|
-
const ext =
|
|
48059
|
+
const content = fs22.readFileSync(specFile, "utf-8");
|
|
48060
|
+
const ext = path28.extname(specFile).toLowerCase();
|
|
47759
48061
|
if (ext === ".json") {
|
|
47760
48062
|
return parseJsonSpec(content);
|
|
47761
48063
|
}
|
|
@@ -47821,12 +48123,12 @@ function extractRoutes(cwd) {
|
|
|
47821
48123
|
function walkDir(dir) {
|
|
47822
48124
|
let entries;
|
|
47823
48125
|
try {
|
|
47824
|
-
entries =
|
|
48126
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
47825
48127
|
} catch {
|
|
47826
48128
|
return;
|
|
47827
48129
|
}
|
|
47828
48130
|
for (const entry of entries) {
|
|
47829
|
-
const fullPath =
|
|
48131
|
+
const fullPath = path28.join(dir, entry.name);
|
|
47830
48132
|
if (entry.isSymbolicLink()) {
|
|
47831
48133
|
continue;
|
|
47832
48134
|
}
|
|
@@ -47836,7 +48138,7 @@ function extractRoutes(cwd) {
|
|
|
47836
48138
|
}
|
|
47837
48139
|
walkDir(fullPath);
|
|
47838
48140
|
} else if (entry.isFile()) {
|
|
47839
|
-
const ext =
|
|
48141
|
+
const ext = path28.extname(entry.name).toLowerCase();
|
|
47840
48142
|
const baseName = entry.name.toLowerCase();
|
|
47841
48143
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
47842
48144
|
continue;
|
|
@@ -47854,7 +48156,7 @@ function extractRoutes(cwd) {
|
|
|
47854
48156
|
}
|
|
47855
48157
|
function extractRoutesFromFile(filePath) {
|
|
47856
48158
|
const routes = [];
|
|
47857
|
-
const content =
|
|
48159
|
+
const content = fs22.readFileSync(filePath, "utf-8");
|
|
47858
48160
|
const lines = content.split(/\r?\n/);
|
|
47859
48161
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
47860
48162
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -48004,8 +48306,8 @@ init_secretscan();
|
|
|
48004
48306
|
|
|
48005
48307
|
// src/tools/symbols.ts
|
|
48006
48308
|
init_tool();
|
|
48007
|
-
import * as
|
|
48008
|
-
import * as
|
|
48309
|
+
import * as fs23 from "fs";
|
|
48310
|
+
import * as path29 from "path";
|
|
48009
48311
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
48010
48312
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
48011
48313
|
function containsControlCharacters(str) {
|
|
@@ -48034,11 +48336,11 @@ function containsWindowsAttacks(str) {
|
|
|
48034
48336
|
}
|
|
48035
48337
|
function isPathInWorkspace(filePath, workspace) {
|
|
48036
48338
|
try {
|
|
48037
|
-
const resolvedPath =
|
|
48038
|
-
const realWorkspace =
|
|
48039
|
-
const realResolvedPath =
|
|
48040
|
-
const relativePath =
|
|
48041
|
-
if (relativePath.startsWith("..") ||
|
|
48339
|
+
const resolvedPath = path29.resolve(workspace, filePath);
|
|
48340
|
+
const realWorkspace = fs23.realpathSync(workspace);
|
|
48341
|
+
const realResolvedPath = fs23.realpathSync(resolvedPath);
|
|
48342
|
+
const relativePath = path29.relative(realWorkspace, realResolvedPath);
|
|
48343
|
+
if (relativePath.startsWith("..") || path29.isAbsolute(relativePath)) {
|
|
48042
48344
|
return false;
|
|
48043
48345
|
}
|
|
48044
48346
|
return true;
|
|
@@ -48050,17 +48352,17 @@ function validatePathForRead(filePath, workspace) {
|
|
|
48050
48352
|
return isPathInWorkspace(filePath, workspace);
|
|
48051
48353
|
}
|
|
48052
48354
|
function extractTSSymbols(filePath, cwd) {
|
|
48053
|
-
const fullPath =
|
|
48355
|
+
const fullPath = path29.join(cwd, filePath);
|
|
48054
48356
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
48055
48357
|
return [];
|
|
48056
48358
|
}
|
|
48057
48359
|
let content;
|
|
48058
48360
|
try {
|
|
48059
|
-
const stats =
|
|
48361
|
+
const stats = fs23.statSync(fullPath);
|
|
48060
48362
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
48061
48363
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
48062
48364
|
}
|
|
48063
|
-
content =
|
|
48365
|
+
content = fs23.readFileSync(fullPath, "utf-8");
|
|
48064
48366
|
} catch {
|
|
48065
48367
|
return [];
|
|
48066
48368
|
}
|
|
@@ -48202,17 +48504,17 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
48202
48504
|
});
|
|
48203
48505
|
}
|
|
48204
48506
|
function extractPythonSymbols(filePath, cwd) {
|
|
48205
|
-
const fullPath =
|
|
48507
|
+
const fullPath = path29.join(cwd, filePath);
|
|
48206
48508
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
48207
48509
|
return [];
|
|
48208
48510
|
}
|
|
48209
48511
|
let content;
|
|
48210
48512
|
try {
|
|
48211
|
-
const stats =
|
|
48513
|
+
const stats = fs23.statSync(fullPath);
|
|
48212
48514
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
48213
48515
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
48214
48516
|
}
|
|
48215
|
-
content =
|
|
48517
|
+
content = fs23.readFileSync(fullPath, "utf-8");
|
|
48216
48518
|
} catch {
|
|
48217
48519
|
return [];
|
|
48218
48520
|
}
|
|
@@ -48284,7 +48586,7 @@ var symbols = tool({
|
|
|
48284
48586
|
}, null, 2);
|
|
48285
48587
|
}
|
|
48286
48588
|
const cwd = process.cwd();
|
|
48287
|
-
const ext =
|
|
48589
|
+
const ext = path29.extname(file3);
|
|
48288
48590
|
if (containsControlCharacters(file3)) {
|
|
48289
48591
|
return JSON.stringify({
|
|
48290
48592
|
file: file3,
|
|
@@ -48352,8 +48654,8 @@ init_test_runner();
|
|
|
48352
48654
|
|
|
48353
48655
|
// src/tools/todo-extract.ts
|
|
48354
48656
|
init_dist();
|
|
48355
|
-
import * as
|
|
48356
|
-
import * as
|
|
48657
|
+
import * as fs24 from "fs";
|
|
48658
|
+
import * as path30 from "path";
|
|
48357
48659
|
var MAX_TEXT_LENGTH = 200;
|
|
48358
48660
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
48359
48661
|
var SUPPORTED_EXTENSIONS2 = new Set([
|
|
@@ -48424,9 +48726,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
48424
48726
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
48425
48727
|
}
|
|
48426
48728
|
try {
|
|
48427
|
-
const resolvedPath =
|
|
48428
|
-
const normalizedCwd =
|
|
48429
|
-
const normalizedResolved =
|
|
48729
|
+
const resolvedPath = path30.resolve(paths);
|
|
48730
|
+
const normalizedCwd = path30.resolve(cwd);
|
|
48731
|
+
const normalizedResolved = path30.resolve(resolvedPath);
|
|
48430
48732
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
48431
48733
|
return {
|
|
48432
48734
|
error: "paths must be within the current working directory",
|
|
@@ -48442,13 +48744,13 @@ function validatePathsInput(paths, cwd) {
|
|
|
48442
48744
|
}
|
|
48443
48745
|
}
|
|
48444
48746
|
function isSupportedExtension(filePath) {
|
|
48445
|
-
const ext =
|
|
48747
|
+
const ext = path30.extname(filePath).toLowerCase();
|
|
48446
48748
|
return SUPPORTED_EXTENSIONS2.has(ext);
|
|
48447
48749
|
}
|
|
48448
48750
|
function findSourceFiles3(dir, files = []) {
|
|
48449
48751
|
let entries;
|
|
48450
48752
|
try {
|
|
48451
|
-
entries =
|
|
48753
|
+
entries = fs24.readdirSync(dir);
|
|
48452
48754
|
} catch {
|
|
48453
48755
|
return files;
|
|
48454
48756
|
}
|
|
@@ -48457,10 +48759,10 @@ function findSourceFiles3(dir, files = []) {
|
|
|
48457
48759
|
if (SKIP_DIRECTORIES3.has(entry)) {
|
|
48458
48760
|
continue;
|
|
48459
48761
|
}
|
|
48460
|
-
const fullPath =
|
|
48762
|
+
const fullPath = path30.join(dir, entry);
|
|
48461
48763
|
let stat;
|
|
48462
48764
|
try {
|
|
48463
|
-
stat =
|
|
48765
|
+
stat = fs24.statSync(fullPath);
|
|
48464
48766
|
} catch {
|
|
48465
48767
|
continue;
|
|
48466
48768
|
}
|
|
@@ -48553,7 +48855,7 @@ var todo_extract = tool({
|
|
|
48553
48855
|
return JSON.stringify(errorResult, null, 2);
|
|
48554
48856
|
}
|
|
48555
48857
|
const scanPath = resolvedPath;
|
|
48556
|
-
if (!
|
|
48858
|
+
if (!fs24.existsSync(scanPath)) {
|
|
48557
48859
|
const errorResult = {
|
|
48558
48860
|
error: `path not found: ${pathsInput}`,
|
|
48559
48861
|
total: 0,
|
|
@@ -48563,13 +48865,13 @@ var todo_extract = tool({
|
|
|
48563
48865
|
return JSON.stringify(errorResult, null, 2);
|
|
48564
48866
|
}
|
|
48565
48867
|
const filesToScan = [];
|
|
48566
|
-
const stat =
|
|
48868
|
+
const stat = fs24.statSync(scanPath);
|
|
48567
48869
|
if (stat.isFile()) {
|
|
48568
48870
|
if (isSupportedExtension(scanPath)) {
|
|
48569
48871
|
filesToScan.push(scanPath);
|
|
48570
48872
|
} else {
|
|
48571
48873
|
const errorResult = {
|
|
48572
|
-
error: `unsupported file extension: ${
|
|
48874
|
+
error: `unsupported file extension: ${path30.extname(scanPath)}`,
|
|
48573
48875
|
total: 0,
|
|
48574
48876
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
48575
48877
|
entries: []
|
|
@@ -48582,11 +48884,11 @@ var todo_extract = tool({
|
|
|
48582
48884
|
const allEntries = [];
|
|
48583
48885
|
for (const filePath of filesToScan) {
|
|
48584
48886
|
try {
|
|
48585
|
-
const fileStat =
|
|
48887
|
+
const fileStat = fs24.statSync(filePath);
|
|
48586
48888
|
if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
|
|
48587
48889
|
continue;
|
|
48588
48890
|
}
|
|
48589
|
-
const content =
|
|
48891
|
+
const content = fs24.readFileSync(filePath, "utf-8");
|
|
48590
48892
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
48591
48893
|
allEntries.push(...entries);
|
|
48592
48894
|
} catch {}
|
|
@@ -48684,7 +48986,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
48684
48986
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
48685
48987
|
preflightTriggerManager = new PTM(automationConfig);
|
|
48686
48988
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
48687
|
-
const swarmDir =
|
|
48989
|
+
const swarmDir = path31.resolve(ctx.directory, ".swarm");
|
|
48688
48990
|
statusArtifact = new ASA(swarmDir);
|
|
48689
48991
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
48690
48992
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|