opencode-swarm 7.7.0 → 7.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/agents/index.d.ts +11 -3
- package/dist/cli/index.js +788 -358
- package/dist/commands/full-auto.d.ts +13 -2
- package/dist/config/evidence-schema.d.ts +3 -3
- package/dist/config/schema.d.ts +82 -9
- package/dist/full-auto/cadence.d.ts +64 -0
- package/dist/full-auto/input-probe.d.ts +22 -0
- package/dist/full-auto/oversight.d.ts +93 -0
- package/dist/full-auto/phase-approval.d.ts +7 -0
- package/dist/full-auto/policy.d.ts +85 -0
- package/dist/full-auto/state.d.ts +121 -0
- package/dist/hooks/full-auto-delegation.d.ts +28 -0
- package/dist/hooks/full-auto-input-probe.d.ts +27 -0
- package/dist/hooks/full-auto-intercept.d.ts +1 -1
- package/dist/hooks/full-auto-permission.d.ts +39 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/utils.d.ts +40 -0
- package/dist/index.js +4014 -1267
- package/dist/lang/runtime.d.ts +10 -0
- package/dist/state.d.ts +8 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.
|
|
37
|
+
version: "7.8.1",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
|
@@ -142,6 +142,14 @@ function warn(message, data) {
|
|
|
142
142
|
console.warn(`[opencode-swarm ${timestamp}] WARN: ${message}`);
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
+
function error(message, data) {
|
|
146
|
+
const timestamp = new Date().toISOString();
|
|
147
|
+
if (data !== undefined) {
|
|
148
|
+
console.error(`[opencode-swarm ${timestamp}] ERROR: ${message}`, data);
|
|
149
|
+
} else {
|
|
150
|
+
console.error(`[opencode-swarm ${timestamp}] ERROR: ${message}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
145
153
|
var init_logger = () => {};
|
|
146
154
|
|
|
147
155
|
// src/utils/merge.ts
|
|
@@ -16410,12 +16418,26 @@ var init_constants = __esm(() => {
|
|
|
16410
16418
|
"repo_map"
|
|
16411
16419
|
],
|
|
16412
16420
|
critic_oversight: [
|
|
16413
|
-
"
|
|
16414
|
-
"
|
|
16415
|
-
"
|
|
16421
|
+
"diff",
|
|
16422
|
+
"diff_summary",
|
|
16423
|
+
"evidence_check",
|
|
16424
|
+
"check_gate_status",
|
|
16425
|
+
"completion_verify",
|
|
16426
|
+
"get_approved_plan",
|
|
16427
|
+
"req_coverage",
|
|
16428
|
+
"test_impact",
|
|
16429
|
+
"pkg_audit",
|
|
16430
|
+
"secretscan",
|
|
16431
|
+
"sast_scan",
|
|
16432
|
+
"repo_map",
|
|
16416
16433
|
"retrieve_summary",
|
|
16434
|
+
"knowledge_recall",
|
|
16417
16435
|
"symbols",
|
|
16418
|
-
"
|
|
16436
|
+
"batch_symbols",
|
|
16437
|
+
"search",
|
|
16438
|
+
"imports",
|
|
16439
|
+
"complexity_hotspots",
|
|
16440
|
+
"detect_domains"
|
|
16419
16441
|
],
|
|
16420
16442
|
docs: [
|
|
16421
16443
|
"detect_domains",
|
|
@@ -16509,60 +16531,43 @@ var init_constants = __esm(() => {
|
|
|
16509
16531
|
});
|
|
16510
16532
|
|
|
16511
16533
|
// src/config/schema.ts
|
|
16512
|
-
function
|
|
16534
|
+
function getCanonicalAgentRole(agentName, generatedAgentNames) {
|
|
16513
16535
|
if (!agentName)
|
|
16514
16536
|
return agentName;
|
|
16515
16537
|
const normalized = agentName.toLowerCase();
|
|
16516
|
-
|
|
16517
|
-
|
|
16518
|
-
while (stripped !== previous) {
|
|
16519
|
-
previous = stripped;
|
|
16520
|
-
for (const prefix of KNOWN_SWARM_PREFIXES) {
|
|
16521
|
-
for (const sep2 of SEPARATORS) {
|
|
16522
|
-
const prefixWithSep = prefix + sep2;
|
|
16523
|
-
if (stripped.startsWith(prefixWithSep)) {
|
|
16524
|
-
stripped = stripped.slice(prefixWithSep.length);
|
|
16525
|
-
break;
|
|
16526
|
-
}
|
|
16527
|
-
}
|
|
16528
|
-
if (stripped !== previous)
|
|
16529
|
-
break;
|
|
16530
|
-
}
|
|
16538
|
+
if (CANONICAL_ROLES_SET.has(normalized)) {
|
|
16539
|
+
return normalized;
|
|
16531
16540
|
}
|
|
16532
|
-
if (
|
|
16533
|
-
|
|
16541
|
+
if (generatedAgentNames) {
|
|
16542
|
+
const registry2 = new Set;
|
|
16543
|
+
for (const n of generatedAgentNames)
|
|
16544
|
+
registry2.add(n.toLowerCase());
|
|
16545
|
+
if (!registry2.has(normalized)) {
|
|
16546
|
+
return agentName;
|
|
16547
|
+
}
|
|
16534
16548
|
}
|
|
16535
|
-
for (const
|
|
16549
|
+
for (const role of CANONICAL_ROLES_LONGEST_FIRST) {
|
|
16536
16550
|
for (const sep2 of SEPARATORS) {
|
|
16537
|
-
const suffix = sep2 +
|
|
16551
|
+
const suffix = sep2 + role;
|
|
16538
16552
|
if (normalized.endsWith(suffix)) {
|
|
16539
|
-
return
|
|
16553
|
+
return role;
|
|
16540
16554
|
}
|
|
16541
16555
|
}
|
|
16542
|
-
if (normalized === agent) {
|
|
16543
|
-
return agent;
|
|
16544
|
-
}
|
|
16545
16556
|
}
|
|
16546
16557
|
return agentName;
|
|
16547
16558
|
}
|
|
16548
|
-
|
|
16559
|
+
function stripKnownSwarmPrefix(agentName) {
|
|
16560
|
+
return getCanonicalAgentRole(agentName);
|
|
16561
|
+
}
|
|
16562
|
+
var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, PluginConfigSchema;
|
|
16549
16563
|
var init_schema = __esm(() => {
|
|
16550
16564
|
init_zod();
|
|
16551
16565
|
init_constants();
|
|
16552
|
-
KNOWN_SWARM_PREFIXES = [
|
|
16553
|
-
"paid",
|
|
16554
|
-
"local",
|
|
16555
|
-
"cloud",
|
|
16556
|
-
"enterprise",
|
|
16557
|
-
"mega",
|
|
16558
|
-
"default",
|
|
16559
|
-
"custom",
|
|
16560
|
-
"team",
|
|
16561
|
-
"project",
|
|
16562
|
-
"swarm",
|
|
16563
|
-
"synthetic"
|
|
16564
|
-
];
|
|
16565
16566
|
SEPARATORS = ["_", "-", " "];
|
|
16567
|
+
CANONICAL_ROLES_LONGEST_FIRST = [
|
|
16568
|
+
...ALL_AGENT_NAMES
|
|
16569
|
+
].sort((a, b) => b.length - a.length);
|
|
16570
|
+
CANONICAL_ROLES_SET = new Set(ALL_AGENT_NAMES);
|
|
16566
16571
|
AgentOverrideConfigSchema = exports_external.object({
|
|
16567
16572
|
model: exports_external.string().optional(),
|
|
16568
16573
|
variant: exports_external.string().min(1).optional(),
|
|
@@ -17175,13 +17180,143 @@ var init_schema = __esm(() => {
|
|
|
17175
17180
|
critic_model: exports_external.string().optional(),
|
|
17176
17181
|
max_interactions_per_phase: exports_external.number().int().min(5).max(200).default(50),
|
|
17177
17182
|
deadlock_threshold: exports_external.number().int().min(2).max(10).default(3),
|
|
17178
|
-
escalation_mode: exports_external.enum(["pause", "terminate"]).default("pause")
|
|
17179
|
-
|
|
17183
|
+
escalation_mode: exports_external.enum(["pause", "terminate"]).default("pause"),
|
|
17184
|
+
mode: exports_external.enum(["assisted", "supervised", "strict"]).default("supervised"),
|
|
17185
|
+
fail_closed: exports_external.boolean().default(true),
|
|
17186
|
+
permission_policy: exports_external.object({
|
|
17187
|
+
enabled: exports_external.boolean().default(true),
|
|
17188
|
+
trusted_roots: exports_external.array(exports_external.string()).default(["."]),
|
|
17189
|
+
trusted_domains: exports_external.array(exports_external.string()).default([]),
|
|
17190
|
+
protected_paths: exports_external.array(exports_external.string()).default([
|
|
17191
|
+
".git",
|
|
17192
|
+
".github/workflows",
|
|
17193
|
+
".swarm",
|
|
17194
|
+
"package.json",
|
|
17195
|
+
"package-lock.json",
|
|
17196
|
+
"bun.lock",
|
|
17197
|
+
"CHANGELOG.md",
|
|
17198
|
+
".release-please-manifest.json",
|
|
17199
|
+
"release-please-config.json",
|
|
17200
|
+
"src/index.ts",
|
|
17201
|
+
"src/hooks/guardrails.ts",
|
|
17202
|
+
"src/hooks/delegation-gate.ts",
|
|
17203
|
+
"src/hooks/scope-guard.ts",
|
|
17204
|
+
"src/hooks/full-auto-permission.ts",
|
|
17205
|
+
"src/hooks/full-auto-intercept.ts",
|
|
17206
|
+
"src/full-auto",
|
|
17207
|
+
"src/config/schema.ts",
|
|
17208
|
+
"src/config/constants.ts",
|
|
17209
|
+
"src/tools/phase-complete.ts",
|
|
17210
|
+
"dist"
|
|
17211
|
+
]),
|
|
17212
|
+
allow_defaults: exports_external.boolean().default(true)
|
|
17213
|
+
}).default(() => ({
|
|
17214
|
+
enabled: true,
|
|
17215
|
+
trusted_roots: ["."],
|
|
17216
|
+
trusted_domains: [],
|
|
17217
|
+
protected_paths: [
|
|
17218
|
+
".git",
|
|
17219
|
+
".github/workflows",
|
|
17220
|
+
".swarm",
|
|
17221
|
+
"package.json",
|
|
17222
|
+
"package-lock.json",
|
|
17223
|
+
"bun.lock",
|
|
17224
|
+
"CHANGELOG.md",
|
|
17225
|
+
".release-please-manifest.json",
|
|
17226
|
+
"release-please-config.json",
|
|
17227
|
+
"src/index.ts",
|
|
17228
|
+
"src/hooks/guardrails.ts",
|
|
17229
|
+
"src/hooks/delegation-gate.ts",
|
|
17230
|
+
"src/hooks/scope-guard.ts",
|
|
17231
|
+
"src/hooks/full-auto-permission.ts",
|
|
17232
|
+
"src/hooks/full-auto-intercept.ts",
|
|
17233
|
+
"src/full-auto",
|
|
17234
|
+
"src/config/schema.ts",
|
|
17235
|
+
"src/config/constants.ts",
|
|
17236
|
+
"src/tools/phase-complete.ts",
|
|
17237
|
+
"dist"
|
|
17238
|
+
],
|
|
17239
|
+
allow_defaults: true
|
|
17240
|
+
})),
|
|
17241
|
+
denials: exports_external.object({
|
|
17242
|
+
max_consecutive: exports_external.number().int().min(1).max(50).default(3),
|
|
17243
|
+
max_total: exports_external.number().int().min(1).max(500).default(20),
|
|
17244
|
+
on_limit: exports_external.enum(["pause", "terminate"]).default("pause")
|
|
17245
|
+
}).default(() => ({
|
|
17246
|
+
max_consecutive: 3,
|
|
17247
|
+
max_total: 20,
|
|
17248
|
+
on_limit: "pause"
|
|
17249
|
+
})),
|
|
17250
|
+
oversight: exports_external.object({
|
|
17251
|
+
on_plan_change: exports_external.boolean().default(true),
|
|
17252
|
+
on_task_completion: exports_external.boolean().default(false),
|
|
17253
|
+
on_phase_boundary: exports_external.boolean().default(true),
|
|
17254
|
+
on_high_risk_action: exports_external.boolean().default(true),
|
|
17255
|
+
on_subagent_return_warning: exports_external.boolean().default(true),
|
|
17256
|
+
every_tool_calls: exports_external.number().int().min(0).max(1e4).default(25),
|
|
17257
|
+
every_architect_turns: exports_external.number().int().min(0).max(1000).default(5),
|
|
17258
|
+
every_minutes: exports_external.number().int().min(0).max(1440).default(20)
|
|
17259
|
+
}).default(() => ({
|
|
17260
|
+
on_plan_change: true,
|
|
17261
|
+
on_task_completion: false,
|
|
17262
|
+
on_phase_boundary: true,
|
|
17263
|
+
on_high_risk_action: true,
|
|
17264
|
+
on_subagent_return_warning: true,
|
|
17265
|
+
every_tool_calls: 25,
|
|
17266
|
+
every_architect_turns: 5,
|
|
17267
|
+
every_minutes: 20
|
|
17268
|
+
}))
|
|
17269
|
+
}).optional().default(() => ({
|
|
17180
17270
|
enabled: false,
|
|
17181
17271
|
max_interactions_per_phase: 50,
|
|
17182
17272
|
deadlock_threshold: 3,
|
|
17183
|
-
escalation_mode: "pause"
|
|
17184
|
-
|
|
17273
|
+
escalation_mode: "pause",
|
|
17274
|
+
mode: "supervised",
|
|
17275
|
+
fail_closed: true,
|
|
17276
|
+
permission_policy: {
|
|
17277
|
+
enabled: true,
|
|
17278
|
+
trusted_roots: ["."],
|
|
17279
|
+
trusted_domains: [],
|
|
17280
|
+
protected_paths: [
|
|
17281
|
+
".git",
|
|
17282
|
+
".github/workflows",
|
|
17283
|
+
".swarm",
|
|
17284
|
+
"package.json",
|
|
17285
|
+
"package-lock.json",
|
|
17286
|
+
"bun.lock",
|
|
17287
|
+
"CHANGELOG.md",
|
|
17288
|
+
".release-please-manifest.json",
|
|
17289
|
+
"release-please-config.json",
|
|
17290
|
+
"src/index.ts",
|
|
17291
|
+
"src/hooks/guardrails.ts",
|
|
17292
|
+
"src/hooks/delegation-gate.ts",
|
|
17293
|
+
"src/hooks/scope-guard.ts",
|
|
17294
|
+
"src/hooks/full-auto-permission.ts",
|
|
17295
|
+
"src/hooks/full-auto-intercept.ts",
|
|
17296
|
+
"src/full-auto",
|
|
17297
|
+
"src/config/schema.ts",
|
|
17298
|
+
"src/config/constants.ts",
|
|
17299
|
+
"src/tools/phase-complete.ts",
|
|
17300
|
+
"dist"
|
|
17301
|
+
],
|
|
17302
|
+
allow_defaults: true
|
|
17303
|
+
},
|
|
17304
|
+
denials: {
|
|
17305
|
+
max_consecutive: 3,
|
|
17306
|
+
max_total: 20,
|
|
17307
|
+
on_limit: "pause"
|
|
17308
|
+
},
|
|
17309
|
+
oversight: {
|
|
17310
|
+
on_plan_change: true,
|
|
17311
|
+
on_task_completion: false,
|
|
17312
|
+
on_phase_boundary: true,
|
|
17313
|
+
on_high_risk_action: true,
|
|
17314
|
+
on_subagent_return_warning: true,
|
|
17315
|
+
every_tool_calls: 25,
|
|
17316
|
+
every_architect_turns: 5,
|
|
17317
|
+
every_minutes: 20
|
|
17318
|
+
}
|
|
17319
|
+
}))
|
|
17185
17320
|
});
|
|
17186
17321
|
});
|
|
17187
17322
|
|
|
@@ -20563,6 +20698,7 @@ function resetSwarmState() {
|
|
|
20563
20698
|
swarmState.opencodeClient = null;
|
|
20564
20699
|
swarmState.curatorInitAgentNames = [];
|
|
20565
20700
|
swarmState.curatorPhaseAgentNames = [];
|
|
20701
|
+
swarmState.generatedAgentNames = [];
|
|
20566
20702
|
_rehydrationCache = null;
|
|
20567
20703
|
swarmState.fullAutoEnabledInConfig = false;
|
|
20568
20704
|
swarmState.environmentProfiles.clear();
|
|
@@ -20607,6 +20743,7 @@ var init_state = __esm(() => {
|
|
|
20607
20743
|
opencodeClient: null,
|
|
20608
20744
|
curatorInitAgentNames: [],
|
|
20609
20745
|
curatorPhaseAgentNames: [],
|
|
20746
|
+
generatedAgentNames: [],
|
|
20610
20747
|
lastBudgetPct: 0,
|
|
20611
20748
|
agentSessions: defaultRunContext.agentSessions,
|
|
20612
20749
|
pendingRehydrations: new Set,
|
|
@@ -40762,8 +40899,254 @@ var init_export = __esm(() => {
|
|
|
40762
40899
|
init_export_service();
|
|
40763
40900
|
});
|
|
40764
40901
|
|
|
40902
|
+
// src/full-auto/state.ts
|
|
40903
|
+
import * as fs11 from "fs";
|
|
40904
|
+
import * as path24 from "path";
|
|
40905
|
+
function nowISO() {
|
|
40906
|
+
return new Date().toISOString();
|
|
40907
|
+
}
|
|
40908
|
+
function ensureSwarmDir(directory) {
|
|
40909
|
+
const swarmDir = path24.resolve(directory, ".swarm");
|
|
40910
|
+
if (!fs11.existsSync(swarmDir)) {
|
|
40911
|
+
fs11.mkdirSync(swarmDir, { recursive: true });
|
|
40912
|
+
}
|
|
40913
|
+
return swarmDir;
|
|
40914
|
+
}
|
|
40915
|
+
function emptyCounters() {
|
|
40916
|
+
return {
|
|
40917
|
+
architectTurns: 0,
|
|
40918
|
+
toolCalls: 0,
|
|
40919
|
+
coderDelegations: 0,
|
|
40920
|
+
reviewerRejections: 0,
|
|
40921
|
+
testFailures: 0,
|
|
40922
|
+
oversightChecks: 0,
|
|
40923
|
+
consecutiveNoProgressTurns: 0
|
|
40924
|
+
};
|
|
40925
|
+
}
|
|
40926
|
+
function emptyState(sessionID, mode = "supervised") {
|
|
40927
|
+
const now = nowISO();
|
|
40928
|
+
return {
|
|
40929
|
+
status: "idle",
|
|
40930
|
+
sessionID,
|
|
40931
|
+
mode,
|
|
40932
|
+
startedAt: now,
|
|
40933
|
+
updatedAt: now,
|
|
40934
|
+
denialCounters: { consecutive: 0, total: 0 },
|
|
40935
|
+
denialHistory: [],
|
|
40936
|
+
counters: emptyCounters()
|
|
40937
|
+
};
|
|
40938
|
+
}
|
|
40939
|
+
function withStateLock(directory, fn) {
|
|
40940
|
+
let release;
|
|
40941
|
+
try {
|
|
40942
|
+
const lockTarget = validateSwarmPath(directory, STATE_FILE);
|
|
40943
|
+
if (!fs11.existsSync(lockTarget)) {
|
|
40944
|
+
ensureSwarmDir(directory);
|
|
40945
|
+
const seed = {
|
|
40946
|
+
version: 2,
|
|
40947
|
+
updatedAt: nowISO(),
|
|
40948
|
+
oversightSequence: 0,
|
|
40949
|
+
sessions: {}
|
|
40950
|
+
};
|
|
40951
|
+
fs11.writeFileSync(lockTarget, `${JSON.stringify(seed, null, 2)}
|
|
40952
|
+
`, "utf-8");
|
|
40953
|
+
}
|
|
40954
|
+
release = lockfile5.lockSync(lockTarget, {
|
|
40955
|
+
retries: { retries: 5, minTimeout: 5, maxTimeout: 50 },
|
|
40956
|
+
stale: 5000
|
|
40957
|
+
});
|
|
40958
|
+
} catch (error93) {
|
|
40959
|
+
warn(`[full-auto/state] cross-process lock unavailable; proceeding unlocked: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
40960
|
+
}
|
|
40961
|
+
try {
|
|
40962
|
+
return fn();
|
|
40963
|
+
} finally {
|
|
40964
|
+
if (release) {
|
|
40965
|
+
try {
|
|
40966
|
+
release();
|
|
40967
|
+
} catch (releaseError) {
|
|
40968
|
+
warn(`[full-auto/state] lock release failed: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`);
|
|
40969
|
+
}
|
|
40970
|
+
}
|
|
40971
|
+
}
|
|
40972
|
+
}
|
|
40973
|
+
function emptyPersisted() {
|
|
40974
|
+
return {
|
|
40975
|
+
version: 2,
|
|
40976
|
+
updatedAt: nowISO(),
|
|
40977
|
+
oversightSequence: 0,
|
|
40978
|
+
sessions: {}
|
|
40979
|
+
};
|
|
40980
|
+
}
|
|
40981
|
+
function markStateUnreadable(reason) {
|
|
40982
|
+
stateUnreadable = true;
|
|
40983
|
+
stateUnreadableReason = reason;
|
|
40984
|
+
error(`[full-auto/state] state file unreadable: ${reason} \u2014 failing closed`);
|
|
40985
|
+
}
|
|
40986
|
+
function clearStateUnreadable() {
|
|
40987
|
+
stateUnreadable = false;
|
|
40988
|
+
stateUnreadableReason = "";
|
|
40989
|
+
}
|
|
40990
|
+
function readPersisted(directory) {
|
|
40991
|
+
try {
|
|
40992
|
+
const filePath = validateSwarmPath(directory, STATE_FILE);
|
|
40993
|
+
if (!fs11.existsSync(filePath)) {
|
|
40994
|
+
clearStateUnreadable();
|
|
40995
|
+
return emptyPersisted();
|
|
40996
|
+
}
|
|
40997
|
+
const raw = fs11.readFileSync(filePath, "utf-8");
|
|
40998
|
+
const parsed = JSON.parse(raw);
|
|
40999
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 2 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
41000
|
+
markStateUnreadable(`malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
41001
|
+
return emptyPersisted();
|
|
41002
|
+
}
|
|
41003
|
+
clearStateUnreadable();
|
|
41004
|
+
return {
|
|
41005
|
+
version: 2,
|
|
41006
|
+
updatedAt: parsed.updatedAt ?? nowISO(),
|
|
41007
|
+
oversightSequence: typeof parsed.oversightSequence === "number" ? parsed.oversightSequence : 0,
|
|
41008
|
+
sessions: parsed.sessions
|
|
41009
|
+
};
|
|
41010
|
+
} catch (error93) {
|
|
41011
|
+
const reason = error93 instanceof Error ? error93.message : String(error93);
|
|
41012
|
+
error(`[full-auto/state] Failed to read ${STATE_FILE}: ${reason} \u2014 attempting .bak recovery`);
|
|
41013
|
+
try {
|
|
41014
|
+
const bakPath = validateSwarmPath(directory, `${STATE_FILE}.bak`);
|
|
41015
|
+
if (fs11.existsSync(bakPath)) {
|
|
41016
|
+
const raw = fs11.readFileSync(bakPath, "utf-8");
|
|
41017
|
+
const parsed = JSON.parse(raw);
|
|
41018
|
+
if (parsed?.version === 2 && parsed.sessions && !Array.isArray(parsed.sessions)) {
|
|
41019
|
+
warn(`[full-auto/state] Recovered from ${STATE_FILE}.bak`);
|
|
41020
|
+
clearStateUnreadable();
|
|
41021
|
+
return {
|
|
41022
|
+
version: 2,
|
|
41023
|
+
updatedAt: parsed.updatedAt ?? nowISO(),
|
|
41024
|
+
oversightSequence: typeof parsed.oversightSequence === "number" ? parsed.oversightSequence : 0,
|
|
41025
|
+
sessions: parsed.sessions
|
|
41026
|
+
};
|
|
41027
|
+
}
|
|
41028
|
+
}
|
|
41029
|
+
} catch (bakError) {
|
|
41030
|
+
error(`[full-auto/state] .bak recovery also failed: ${bakError instanceof Error ? bakError.message : String(bakError)}`);
|
|
41031
|
+
}
|
|
41032
|
+
markStateUnreadable(`canonical=${reason}; .bak=missing-or-corrupt`);
|
|
41033
|
+
return emptyPersisted();
|
|
41034
|
+
}
|
|
41035
|
+
}
|
|
41036
|
+
function writePersisted(directory, persisted) {
|
|
41037
|
+
let filePath;
|
|
41038
|
+
let tmpPath;
|
|
41039
|
+
let bakPath;
|
|
41040
|
+
let payload;
|
|
41041
|
+
try {
|
|
41042
|
+
ensureSwarmDir(directory);
|
|
41043
|
+
filePath = validateSwarmPath(directory, STATE_FILE);
|
|
41044
|
+
tmpPath = validateSwarmPath(directory, `${STATE_FILE}.tmp`);
|
|
41045
|
+
bakPath = validateSwarmPath(directory, `${STATE_FILE}.bak`);
|
|
41046
|
+
persisted.updatedAt = nowISO();
|
|
41047
|
+
payload = `${JSON.stringify(persisted, null, 2)}
|
|
41048
|
+
`;
|
|
41049
|
+
} catch (error93) {
|
|
41050
|
+
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
41051
|
+
error(`[full-auto/state] Failed to prepare ${STATE_FILE} write: ${msg}`);
|
|
41052
|
+
throw new Error(`Full-Auto state persistence prepare failed: ${msg}`);
|
|
41053
|
+
}
|
|
41054
|
+
try {
|
|
41055
|
+
if (fs11.existsSync(filePath)) {
|
|
41056
|
+
fs11.copyFileSync(filePath, bakPath);
|
|
41057
|
+
}
|
|
41058
|
+
} catch {}
|
|
41059
|
+
try {
|
|
41060
|
+
fs11.writeFileSync(tmpPath, payload, "utf-8");
|
|
41061
|
+
try {
|
|
41062
|
+
const fd = fs11.openSync(tmpPath, "r+");
|
|
41063
|
+
try {
|
|
41064
|
+
fs11.fsyncSync(fd);
|
|
41065
|
+
} finally {
|
|
41066
|
+
fs11.closeSync(fd);
|
|
41067
|
+
}
|
|
41068
|
+
} catch {}
|
|
41069
|
+
fs11.renameSync(tmpPath, filePath);
|
|
41070
|
+
const readback = fs11.readFileSync(filePath, "utf-8");
|
|
41071
|
+
const parsed = JSON.parse(readback);
|
|
41072
|
+
if (parsed?.version !== 2) {
|
|
41073
|
+
throw new Error("Round-trip readback returned wrong version");
|
|
41074
|
+
}
|
|
41075
|
+
} catch (error93) {
|
|
41076
|
+
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
41077
|
+
error(`[full-auto/state] Failed to persist ${STATE_FILE} atomically: ${msg}`);
|
|
41078
|
+
throw new Error(`Full-Auto state persistence failed: ${msg}`);
|
|
41079
|
+
}
|
|
41080
|
+
}
|
|
41081
|
+
function startFullAutoRun(directory, sessionID, config3, options = {}) {
|
|
41082
|
+
return withStateLock(directory, () => {
|
|
41083
|
+
const persisted = readPersisted(directory);
|
|
41084
|
+
const existing = persisted.sessions[sessionID];
|
|
41085
|
+
const mode = config3?.mode ?? existing?.mode ?? "supervised";
|
|
41086
|
+
const state = existing ? {
|
|
41087
|
+
...existing,
|
|
41088
|
+
status: "running",
|
|
41089
|
+
mode,
|
|
41090
|
+
planID: options.planID ?? existing.planID,
|
|
41091
|
+
currentPhase: options.phase ?? existing.currentPhase,
|
|
41092
|
+
currentTaskID: options.taskID ?? existing.currentTaskID,
|
|
41093
|
+
pauseReason: undefined,
|
|
41094
|
+
terminateReason: undefined,
|
|
41095
|
+
updatedAt: nowISO(),
|
|
41096
|
+
denialCounters: {
|
|
41097
|
+
consecutive: 0,
|
|
41098
|
+
total: existing.denialCounters.total
|
|
41099
|
+
}
|
|
41100
|
+
} : {
|
|
41101
|
+
...emptyState(sessionID, mode),
|
|
41102
|
+
status: "running",
|
|
41103
|
+
planID: options.planID,
|
|
41104
|
+
currentPhase: options.phase,
|
|
41105
|
+
currentTaskID: options.taskID
|
|
41106
|
+
};
|
|
41107
|
+
persisted.sessions[sessionID] = state;
|
|
41108
|
+
writePersisted(directory, persisted);
|
|
41109
|
+
return state;
|
|
41110
|
+
});
|
|
41111
|
+
}
|
|
41112
|
+
function pauseFullAutoRun(directory, sessionID, reason) {
|
|
41113
|
+
return withStateLock(directory, () => {
|
|
41114
|
+
const persisted = readPersisted(directory);
|
|
41115
|
+
const state = persisted.sessions[sessionID];
|
|
41116
|
+
if (!state)
|
|
41117
|
+
return;
|
|
41118
|
+
state.status = "paused";
|
|
41119
|
+
state.pauseReason = reason;
|
|
41120
|
+
state.updatedAt = nowISO();
|
|
41121
|
+
persisted.sessions[sessionID] = state;
|
|
41122
|
+
writePersisted(directory, persisted);
|
|
41123
|
+
return state;
|
|
41124
|
+
});
|
|
41125
|
+
}
|
|
41126
|
+
function terminateFullAutoRun(directory, sessionID, reason) {
|
|
41127
|
+
return withStateLock(directory, () => {
|
|
41128
|
+
const persisted = readPersisted(directory);
|
|
41129
|
+
const state = persisted.sessions[sessionID];
|
|
41130
|
+
if (!state)
|
|
41131
|
+
return;
|
|
41132
|
+
state.status = "terminated";
|
|
41133
|
+
state.terminateReason = reason;
|
|
41134
|
+
state.updatedAt = nowISO();
|
|
41135
|
+
persisted.sessions[sessionID] = state;
|
|
41136
|
+
writePersisted(directory, persisted);
|
|
41137
|
+
return state;
|
|
41138
|
+
});
|
|
41139
|
+
}
|
|
41140
|
+
var import_proper_lockfile5, lockfile5, STATE_FILE = "full-auto-state.json", stateUnreadable = false, stateUnreadableReason = "";
|
|
41141
|
+
var init_state2 = __esm(() => {
|
|
41142
|
+
init_utils2();
|
|
41143
|
+
init_logger();
|
|
41144
|
+
import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
|
|
41145
|
+
lockfile5 = import_proper_lockfile5.default;
|
|
41146
|
+
});
|
|
41147
|
+
|
|
40765
41148
|
// src/commands/full-auto.ts
|
|
40766
|
-
async function handleFullAutoCommand(
|
|
41149
|
+
async function handleFullAutoCommand(directory, args, sessionID) {
|
|
40767
41150
|
if (!sessionID || sessionID.trim() === "") {
|
|
40768
41151
|
return "Error: No active session context. Full-Auto Mode requires an active session. Use /swarm-full-auto from within an OpenCode session, or start a session first.";
|
|
40769
41152
|
}
|
|
@@ -40783,16 +41166,63 @@ async function handleFullAutoCommand(_directory, args, sessionID) {
|
|
|
40783
41166
|
if (newFullAutoMode && !swarmState.fullAutoEnabledInConfig) {
|
|
40784
41167
|
return "Error: Full-Auto Mode cannot be enabled because full_auto.enabled is not set to true in the swarm plugin config. The autonomous oversight hook is inactive without config-level enablement. Set full_auto.enabled = true in your opencode-swarm config and restart.";
|
|
40785
41168
|
}
|
|
41169
|
+
let v2Status = "unavailable";
|
|
41170
|
+
let modeLabel = "supervised";
|
|
41171
|
+
let denialMaxConsecutive = 3;
|
|
41172
|
+
let denialMaxTotal = 20;
|
|
41173
|
+
let failClosed = true;
|
|
41174
|
+
let durableError;
|
|
41175
|
+
try {
|
|
41176
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
41177
|
+
const fullAutoConfig = config3.full_auto;
|
|
41178
|
+
modeLabel = fullAutoConfig?.mode ?? "supervised";
|
|
41179
|
+
denialMaxConsecutive = fullAutoConfig?.denials?.max_consecutive ?? 3;
|
|
41180
|
+
denialMaxTotal = fullAutoConfig?.denials?.max_total ?? 20;
|
|
41181
|
+
failClosed = fullAutoConfig?.fail_closed !== false;
|
|
41182
|
+
if (newFullAutoMode) {
|
|
41183
|
+
startFullAutoRun(directory, sessionID, fullAutoConfig);
|
|
41184
|
+
v2Status = "running";
|
|
41185
|
+
} else {
|
|
41186
|
+
const paused = pauseFullAutoRun(directory, sessionID, "/swarm full-auto off");
|
|
41187
|
+
if (!paused) {
|
|
41188
|
+
terminateFullAutoRun(directory, sessionID, "never started");
|
|
41189
|
+
}
|
|
41190
|
+
v2Status = "paused";
|
|
41191
|
+
}
|
|
41192
|
+
} catch (error93) {
|
|
41193
|
+
durableError = error93 instanceof Error ? error93.message : String(error93);
|
|
41194
|
+
error(`[full-auto] durable run-state write failed: ${durableError}`);
|
|
41195
|
+
}
|
|
41196
|
+
if (newFullAutoMode && durableError) {
|
|
41197
|
+
return [
|
|
41198
|
+
"Error: Full-Auto Mode could NOT be enabled \u2014 durable run-state write failed.",
|
|
41199
|
+
`Reason: ${durableError}.`,
|
|
41200
|
+
"Inspect .swarm/ permissions and disk space, then retry."
|
|
41201
|
+
].join(" ");
|
|
41202
|
+
}
|
|
40786
41203
|
session.fullAutoMode = newFullAutoMode;
|
|
40787
41204
|
if (!newFullAutoMode) {
|
|
40788
41205
|
session.fullAutoInteractionCount = 0;
|
|
40789
41206
|
session.fullAutoDeadlockCount = 0;
|
|
40790
41207
|
session.fullAutoLastQuestionHash = null;
|
|
40791
41208
|
}
|
|
40792
|
-
|
|
41209
|
+
if (!newFullAutoMode) {
|
|
41210
|
+
return [
|
|
41211
|
+
"Full-Auto Mode disabled",
|
|
41212
|
+
`(v2 run-state: ${v2Status}; mode=${modeLabel})`
|
|
41213
|
+
].join(" ");
|
|
41214
|
+
}
|
|
41215
|
+
return [
|
|
41216
|
+
"Full-Auto Mode enabled",
|
|
41217
|
+
`(v2 mode=${modeLabel}, fail_closed=${failClosed},`,
|
|
41218
|
+
`denials max ${denialMaxConsecutive} consecutive / ${denialMaxTotal} total)`
|
|
41219
|
+
].join(" ");
|
|
40793
41220
|
}
|
|
40794
41221
|
var init_full_auto = __esm(() => {
|
|
41222
|
+
init_config();
|
|
41223
|
+
init_state2();
|
|
40795
41224
|
init_state();
|
|
41225
|
+
init_logger();
|
|
40796
41226
|
});
|
|
40797
41227
|
|
|
40798
41228
|
// src/services/handoff-service.ts
|
|
@@ -41177,19 +41607,19 @@ var init_handoff_service = __esm(() => {
|
|
|
41177
41607
|
|
|
41178
41608
|
// src/commands/handoff.ts
|
|
41179
41609
|
import crypto4 from "crypto";
|
|
41180
|
-
import { renameSync as
|
|
41610
|
+
import { renameSync as renameSync7 } from "fs";
|
|
41181
41611
|
async function handleHandoffCommand(directory, _args) {
|
|
41182
41612
|
const handoffData = await getHandoffData(directory);
|
|
41183
41613
|
const markdown = formatHandoffMarkdown(handoffData);
|
|
41184
41614
|
const resolvedPath = validateSwarmPath(directory, "handoff.md");
|
|
41185
41615
|
const tempPath = `${resolvedPath}.tmp.${crypto4.randomUUID()}`;
|
|
41186
41616
|
await bunWrite(tempPath, markdown);
|
|
41187
|
-
|
|
41617
|
+
renameSync7(tempPath, resolvedPath);
|
|
41188
41618
|
const continuationPrompt = formatContinuationPrompt(handoffData);
|
|
41189
41619
|
const promptPath = validateSwarmPath(directory, "handoff-prompt.md");
|
|
41190
41620
|
const promptTempPath = `${promptPath}.tmp.${crypto4.randomUUID()}`;
|
|
41191
41621
|
await bunWrite(promptTempPath, continuationPrompt);
|
|
41192
|
-
|
|
41622
|
+
renameSync7(promptTempPath, promptPath);
|
|
41193
41623
|
await writeSnapshot(directory, swarmState);
|
|
41194
41624
|
await flushPendingSnapshot(directory);
|
|
41195
41625
|
return `## Handoff Brief Written
|
|
@@ -41564,9 +41994,9 @@ var init_issue = __esm(() => {
|
|
|
41564
41994
|
|
|
41565
41995
|
// src/hooks/knowledge-migrator.ts
|
|
41566
41996
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
41567
|
-
import { existsSync as
|
|
41997
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
41568
41998
|
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
41569
|
-
import * as
|
|
41999
|
+
import * as path25 from "path";
|
|
41570
42000
|
async function migrateKnowledgeToExternal(_directory, _config) {
|
|
41571
42001
|
return {
|
|
41572
42002
|
migrated: false,
|
|
@@ -41577,10 +42007,10 @@ async function migrateKnowledgeToExternal(_directory, _config) {
|
|
|
41577
42007
|
};
|
|
41578
42008
|
}
|
|
41579
42009
|
async function migrateContextToKnowledge(directory, config3) {
|
|
41580
|
-
const sentinelPath =
|
|
41581
|
-
const contextPath =
|
|
42010
|
+
const sentinelPath = path25.join(directory, ".swarm", ".knowledge-migrated");
|
|
42011
|
+
const contextPath = path25.join(directory, ".swarm", "context.md");
|
|
41582
42012
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
41583
|
-
if (
|
|
42013
|
+
if (existsSync15(sentinelPath)) {
|
|
41584
42014
|
return {
|
|
41585
42015
|
migrated: false,
|
|
41586
42016
|
entriesMigrated: 0,
|
|
@@ -41589,7 +42019,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
41589
42019
|
skippedReason: "sentinel-exists"
|
|
41590
42020
|
};
|
|
41591
42021
|
}
|
|
41592
|
-
if (!
|
|
42022
|
+
if (!existsSync15(contextPath)) {
|
|
41593
42023
|
return {
|
|
41594
42024
|
migrated: false,
|
|
41595
42025
|
entriesMigrated: 0,
|
|
@@ -41774,16 +42204,16 @@ function truncateLesson(text) {
|
|
|
41774
42204
|
return `${text.slice(0, 277)}...`;
|
|
41775
42205
|
}
|
|
41776
42206
|
function inferProjectName(directory) {
|
|
41777
|
-
const packageJsonPath =
|
|
41778
|
-
if (
|
|
42207
|
+
const packageJsonPath = path25.join(directory, "package.json");
|
|
42208
|
+
if (existsSync15(packageJsonPath)) {
|
|
41779
42209
|
try {
|
|
41780
|
-
const pkg = JSON.parse(
|
|
42210
|
+
const pkg = JSON.parse(readFileSync11(packageJsonPath, "utf-8"));
|
|
41781
42211
|
if (pkg.name && typeof pkg.name === "string") {
|
|
41782
42212
|
return pkg.name;
|
|
41783
42213
|
}
|
|
41784
42214
|
} catch {}
|
|
41785
42215
|
}
|
|
41786
|
-
return
|
|
42216
|
+
return path25.basename(directory);
|
|
41787
42217
|
}
|
|
41788
42218
|
async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
41789
42219
|
const sentinel = {
|
|
@@ -41795,7 +42225,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
41795
42225
|
schema_version: 1,
|
|
41796
42226
|
migration_tool: "knowledge-migrator.ts"
|
|
41797
42227
|
};
|
|
41798
|
-
await mkdir5(
|
|
42228
|
+
await mkdir5(path25.dirname(sentinelPath), { recursive: true });
|
|
41799
42229
|
await writeFile6(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
41800
42230
|
}
|
|
41801
42231
|
var _internals14;
|
|
@@ -42316,8 +42746,8 @@ function containsControlChars(str) {
|
|
|
42316
42746
|
var init_path_security = () => {};
|
|
42317
42747
|
|
|
42318
42748
|
// src/tools/lint.ts
|
|
42319
|
-
import * as
|
|
42320
|
-
import * as
|
|
42749
|
+
import * as fs12 from "fs";
|
|
42750
|
+
import * as path26 from "path";
|
|
42321
42751
|
function validateArgs(args) {
|
|
42322
42752
|
if (typeof args !== "object" || args === null)
|
|
42323
42753
|
return false;
|
|
@@ -42328,9 +42758,9 @@ function validateArgs(args) {
|
|
|
42328
42758
|
}
|
|
42329
42759
|
function getLinterCommand(linter, mode, projectDir) {
|
|
42330
42760
|
const isWindows = process.platform === "win32";
|
|
42331
|
-
const binDir =
|
|
42332
|
-
const biomeBin = isWindows ?
|
|
42333
|
-
const eslintBin = isWindows ?
|
|
42761
|
+
const binDir = path26.join(projectDir, "node_modules", ".bin");
|
|
42762
|
+
const biomeBin = isWindows ? path26.join(binDir, "biome.EXE") : path26.join(binDir, "biome");
|
|
42763
|
+
const eslintBin = isWindows ? path26.join(binDir, "eslint.cmd") : path26.join(binDir, "eslint");
|
|
42334
42764
|
switch (linter) {
|
|
42335
42765
|
case "biome":
|
|
42336
42766
|
if (mode === "fix") {
|
|
@@ -42346,7 +42776,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
42346
42776
|
}
|
|
42347
42777
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
42348
42778
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
42349
|
-
const gradlew =
|
|
42779
|
+
const gradlew = fs12.existsSync(path26.join(cwd, gradlewName)) ? path26.join(cwd, gradlewName) : null;
|
|
42350
42780
|
switch (linter) {
|
|
42351
42781
|
case "ruff":
|
|
42352
42782
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -42380,12 +42810,12 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
42380
42810
|
}
|
|
42381
42811
|
}
|
|
42382
42812
|
function detectRuff(cwd) {
|
|
42383
|
-
if (
|
|
42813
|
+
if (fs12.existsSync(path26.join(cwd, "ruff.toml")))
|
|
42384
42814
|
return isCommandAvailable("ruff");
|
|
42385
42815
|
try {
|
|
42386
|
-
const pyproject =
|
|
42387
|
-
if (
|
|
42388
|
-
const content =
|
|
42816
|
+
const pyproject = path26.join(cwd, "pyproject.toml");
|
|
42817
|
+
if (fs12.existsSync(pyproject)) {
|
|
42818
|
+
const content = fs12.readFileSync(pyproject, "utf-8");
|
|
42389
42819
|
if (content.includes("[tool.ruff]"))
|
|
42390
42820
|
return isCommandAvailable("ruff");
|
|
42391
42821
|
}
|
|
@@ -42393,21 +42823,21 @@ function detectRuff(cwd) {
|
|
|
42393
42823
|
return false;
|
|
42394
42824
|
}
|
|
42395
42825
|
function detectClippy(cwd) {
|
|
42396
|
-
return
|
|
42826
|
+
return fs12.existsSync(path26.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
42397
42827
|
}
|
|
42398
42828
|
function detectGolangciLint(cwd) {
|
|
42399
|
-
return
|
|
42829
|
+
return fs12.existsSync(path26.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
42400
42830
|
}
|
|
42401
42831
|
function detectCheckstyle(cwd) {
|
|
42402
|
-
const hasMaven =
|
|
42403
|
-
const hasGradle =
|
|
42404
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (
|
|
42832
|
+
const hasMaven = fs12.existsSync(path26.join(cwd, "pom.xml"));
|
|
42833
|
+
const hasGradle = fs12.existsSync(path26.join(cwd, "build.gradle")) || fs12.existsSync(path26.join(cwd, "build.gradle.kts"));
|
|
42834
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(path26.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
42405
42835
|
return (hasMaven || hasGradle) && hasBinary;
|
|
42406
42836
|
}
|
|
42407
42837
|
function detectKtlint(cwd) {
|
|
42408
|
-
const hasKotlin =
|
|
42838
|
+
const hasKotlin = fs12.existsSync(path26.join(cwd, "build.gradle.kts")) || fs12.existsSync(path26.join(cwd, "build.gradle")) || (() => {
|
|
42409
42839
|
try {
|
|
42410
|
-
return
|
|
42840
|
+
return fs12.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
42411
42841
|
} catch {
|
|
42412
42842
|
return false;
|
|
42413
42843
|
}
|
|
@@ -42416,7 +42846,7 @@ function detectKtlint(cwd) {
|
|
|
42416
42846
|
}
|
|
42417
42847
|
function detectDotnetFormat(cwd) {
|
|
42418
42848
|
try {
|
|
42419
|
-
const files =
|
|
42849
|
+
const files = fs12.readdirSync(cwd);
|
|
42420
42850
|
const hasCsproj = files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"));
|
|
42421
42851
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
42422
42852
|
} catch {
|
|
@@ -42424,14 +42854,14 @@ function detectDotnetFormat(cwd) {
|
|
|
42424
42854
|
}
|
|
42425
42855
|
}
|
|
42426
42856
|
function detectCppcheck(cwd) {
|
|
42427
|
-
if (
|
|
42857
|
+
if (fs12.existsSync(path26.join(cwd, "CMakeLists.txt"))) {
|
|
42428
42858
|
return isCommandAvailable("cppcheck");
|
|
42429
42859
|
}
|
|
42430
42860
|
try {
|
|
42431
|
-
const dirsToCheck = [cwd,
|
|
42861
|
+
const dirsToCheck = [cwd, path26.join(cwd, "src")];
|
|
42432
42862
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
42433
42863
|
try {
|
|
42434
|
-
return
|
|
42864
|
+
return fs12.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
42435
42865
|
} catch {
|
|
42436
42866
|
return false;
|
|
42437
42867
|
}
|
|
@@ -42442,13 +42872,13 @@ function detectCppcheck(cwd) {
|
|
|
42442
42872
|
}
|
|
42443
42873
|
}
|
|
42444
42874
|
function detectSwiftlint(cwd) {
|
|
42445
|
-
return
|
|
42875
|
+
return fs12.existsSync(path26.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
42446
42876
|
}
|
|
42447
42877
|
function detectDartAnalyze(cwd) {
|
|
42448
|
-
return
|
|
42878
|
+
return fs12.existsSync(path26.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
42449
42879
|
}
|
|
42450
42880
|
function detectRubocop(cwd) {
|
|
42451
|
-
return (
|
|
42881
|
+
return (fs12.existsSync(path26.join(cwd, "Gemfile")) || fs12.existsSync(path26.join(cwd, "gems.rb")) || fs12.existsSync(path26.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
|
|
42452
42882
|
}
|
|
42453
42883
|
function detectAdditionalLinter(cwd) {
|
|
42454
42884
|
if (detectRuff(cwd))
|
|
@@ -42476,10 +42906,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
42476
42906
|
function findBinInAncestors(startDir, binName) {
|
|
42477
42907
|
let dir = startDir;
|
|
42478
42908
|
while (true) {
|
|
42479
|
-
const candidate =
|
|
42480
|
-
if (
|
|
42909
|
+
const candidate = path26.join(dir, "node_modules", ".bin", binName);
|
|
42910
|
+
if (fs12.existsSync(candidate))
|
|
42481
42911
|
return candidate;
|
|
42482
|
-
const parent =
|
|
42912
|
+
const parent = path26.dirname(dir);
|
|
42483
42913
|
if (parent === dir)
|
|
42484
42914
|
break;
|
|
42485
42915
|
dir = parent;
|
|
@@ -42488,11 +42918,11 @@ function findBinInAncestors(startDir, binName) {
|
|
|
42488
42918
|
}
|
|
42489
42919
|
function findBinInEnvPath(binName) {
|
|
42490
42920
|
const searchPath = process.env.PATH ?? "";
|
|
42491
|
-
for (const dir of searchPath.split(
|
|
42921
|
+
for (const dir of searchPath.split(path26.delimiter)) {
|
|
42492
42922
|
if (!dir)
|
|
42493
42923
|
continue;
|
|
42494
|
-
const candidate =
|
|
42495
|
-
if (
|
|
42924
|
+
const candidate = path26.join(dir, binName);
|
|
42925
|
+
if (fs12.existsSync(candidate))
|
|
42496
42926
|
return candidate;
|
|
42497
42927
|
}
|
|
42498
42928
|
return null;
|
|
@@ -42500,17 +42930,17 @@ function findBinInEnvPath(binName) {
|
|
|
42500
42930
|
async function detectAvailableLinter(directory) {
|
|
42501
42931
|
if (!directory)
|
|
42502
42932
|
return null;
|
|
42503
|
-
if (!
|
|
42933
|
+
if (!fs12.existsSync(directory))
|
|
42504
42934
|
return null;
|
|
42505
42935
|
const projectDir = directory;
|
|
42506
42936
|
const isWindows = process.platform === "win32";
|
|
42507
|
-
const biomeBin = isWindows ?
|
|
42508
|
-
const eslintBin = isWindows ?
|
|
42937
|
+
const biomeBin = isWindows ? path26.join(projectDir, "node_modules", ".bin", "biome.EXE") : path26.join(projectDir, "node_modules", ".bin", "biome");
|
|
42938
|
+
const eslintBin = isWindows ? path26.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path26.join(projectDir, "node_modules", ".bin", "eslint");
|
|
42509
42939
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
42510
42940
|
if (localResult)
|
|
42511
42941
|
return localResult;
|
|
42512
|
-
const biomeAncestor = findBinInAncestors(
|
|
42513
|
-
const eslintAncestor = findBinInAncestors(
|
|
42942
|
+
const biomeAncestor = findBinInAncestors(path26.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
42943
|
+
const eslintAncestor = findBinInAncestors(path26.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
42514
42944
|
if (biomeAncestor || eslintAncestor) {
|
|
42515
42945
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
42516
42946
|
}
|
|
@@ -42529,11 +42959,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
42529
42959
|
stderr: "pipe"
|
|
42530
42960
|
});
|
|
42531
42961
|
const biomeExit = biomeProc.exited;
|
|
42532
|
-
const timeout = new Promise((
|
|
42962
|
+
const timeout = new Promise((resolve10) => setTimeout(() => resolve10("timeout"), DETECT_TIMEOUT));
|
|
42533
42963
|
const result = await Promise.race([biomeExit, timeout]);
|
|
42534
42964
|
if (result === "timeout") {
|
|
42535
42965
|
biomeProc.kill();
|
|
42536
|
-
} else if (biomeProc.exitCode === 0 &&
|
|
42966
|
+
} else if (biomeProc.exitCode === 0 && fs12.existsSync(biomeBin)) {
|
|
42537
42967
|
return "biome";
|
|
42538
42968
|
}
|
|
42539
42969
|
} catch {}
|
|
@@ -42543,11 +42973,11 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
42543
42973
|
stderr: "pipe"
|
|
42544
42974
|
});
|
|
42545
42975
|
const eslintExit = eslintProc.exited;
|
|
42546
|
-
const timeout = new Promise((
|
|
42976
|
+
const timeout = new Promise((resolve10) => setTimeout(() => resolve10("timeout"), DETECT_TIMEOUT));
|
|
42547
42977
|
const result = await Promise.race([eslintExit, timeout]);
|
|
42548
42978
|
if (result === "timeout") {
|
|
42549
42979
|
eslintProc.kill();
|
|
42550
|
-
} else if (eslintProc.exitCode === 0 &&
|
|
42980
|
+
} else if (eslintProc.exitCode === 0 && fs12.existsSync(eslintBin)) {
|
|
42551
42981
|
return "eslint";
|
|
42552
42982
|
}
|
|
42553
42983
|
} catch {}
|
|
@@ -42732,8 +43162,8 @@ For Rust: rustup component add clippy`
|
|
|
42732
43162
|
});
|
|
42733
43163
|
|
|
42734
43164
|
// src/tools/secretscan.ts
|
|
42735
|
-
import * as
|
|
42736
|
-
import * as
|
|
43165
|
+
import * as fs13 from "fs";
|
|
43166
|
+
import * as path27 from "path";
|
|
42737
43167
|
function calculateShannonEntropy(str) {
|
|
42738
43168
|
if (str.length === 0)
|
|
42739
43169
|
return 0;
|
|
@@ -42781,11 +43211,11 @@ function isGlobOrPathPattern(pattern) {
|
|
|
42781
43211
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
42782
43212
|
}
|
|
42783
43213
|
function loadSecretScanIgnore(scanDir) {
|
|
42784
|
-
const ignorePath =
|
|
43214
|
+
const ignorePath = path27.join(scanDir, ".secretscanignore");
|
|
42785
43215
|
try {
|
|
42786
|
-
if (!
|
|
43216
|
+
if (!fs13.existsSync(ignorePath))
|
|
42787
43217
|
return [];
|
|
42788
|
-
const content =
|
|
43218
|
+
const content = fs13.readFileSync(ignorePath, "utf8");
|
|
42789
43219
|
const patterns = [];
|
|
42790
43220
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
42791
43221
|
const line = rawLine.trim();
|
|
@@ -42804,7 +43234,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
42804
43234
|
if (exactNames.has(entry))
|
|
42805
43235
|
return true;
|
|
42806
43236
|
for (const pattern of globPatterns) {
|
|
42807
|
-
if (
|
|
43237
|
+
if (path27.matchesGlob(relPath, pattern))
|
|
42808
43238
|
return true;
|
|
42809
43239
|
}
|
|
42810
43240
|
return false;
|
|
@@ -42825,7 +43255,7 @@ function validateDirectoryInput(dir) {
|
|
|
42825
43255
|
return null;
|
|
42826
43256
|
}
|
|
42827
43257
|
function isBinaryFile(filePath, buffer) {
|
|
42828
|
-
const ext =
|
|
43258
|
+
const ext = path27.extname(filePath).toLowerCase();
|
|
42829
43259
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
42830
43260
|
return true;
|
|
42831
43261
|
}
|
|
@@ -42903,7 +43333,7 @@ function createRedactedContext(line, findings) {
|
|
|
42903
43333
|
function scanFileForSecrets(filePath) {
|
|
42904
43334
|
const findings = [];
|
|
42905
43335
|
try {
|
|
42906
|
-
const lstat =
|
|
43336
|
+
const lstat = fs13.lstatSync(filePath);
|
|
42907
43337
|
if (lstat.isSymbolicLink()) {
|
|
42908
43338
|
return findings;
|
|
42909
43339
|
}
|
|
@@ -42912,14 +43342,14 @@ function scanFileForSecrets(filePath) {
|
|
|
42912
43342
|
}
|
|
42913
43343
|
let buffer;
|
|
42914
43344
|
if (O_NOFOLLOW !== undefined) {
|
|
42915
|
-
const fd =
|
|
43345
|
+
const fd = fs13.openSync(filePath, "r", O_NOFOLLOW);
|
|
42916
43346
|
try {
|
|
42917
|
-
buffer =
|
|
43347
|
+
buffer = fs13.readFileSync(fd);
|
|
42918
43348
|
} finally {
|
|
42919
|
-
|
|
43349
|
+
fs13.closeSync(fd);
|
|
42920
43350
|
}
|
|
42921
43351
|
} else {
|
|
42922
|
-
buffer =
|
|
43352
|
+
buffer = fs13.readFileSync(filePath);
|
|
42923
43353
|
}
|
|
42924
43354
|
if (isBinaryFile(filePath, buffer)) {
|
|
42925
43355
|
return findings;
|
|
@@ -42961,9 +43391,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
42961
43391
|
return false;
|
|
42962
43392
|
}
|
|
42963
43393
|
function isPathWithinScope(realPath, scanDir) {
|
|
42964
|
-
const resolvedScanDir =
|
|
42965
|
-
const resolvedRealPath =
|
|
42966
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
43394
|
+
const resolvedScanDir = path27.resolve(scanDir);
|
|
43395
|
+
const resolvedRealPath = path27.resolve(realPath);
|
|
43396
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path27.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
42967
43397
|
}
|
|
42968
43398
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
42969
43399
|
skippedDirs: 0,
|
|
@@ -42974,7 +43404,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
42974
43404
|
const files = [];
|
|
42975
43405
|
let entries;
|
|
42976
43406
|
try {
|
|
42977
|
-
entries =
|
|
43407
|
+
entries = fs13.readdirSync(dir);
|
|
42978
43408
|
} catch {
|
|
42979
43409
|
stats.fileErrors++;
|
|
42980
43410
|
return files;
|
|
@@ -42989,15 +43419,15 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
42989
43419
|
return a.localeCompare(b);
|
|
42990
43420
|
});
|
|
42991
43421
|
for (const entry of entries) {
|
|
42992
|
-
const fullPath =
|
|
42993
|
-
const relPath =
|
|
43422
|
+
const fullPath = path27.join(dir, entry);
|
|
43423
|
+
const relPath = path27.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
42994
43424
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
42995
43425
|
stats.skippedDirs++;
|
|
42996
43426
|
continue;
|
|
42997
43427
|
}
|
|
42998
43428
|
let lstat;
|
|
42999
43429
|
try {
|
|
43000
|
-
lstat =
|
|
43430
|
+
lstat = fs13.lstatSync(fullPath);
|
|
43001
43431
|
} catch {
|
|
43002
43432
|
stats.fileErrors++;
|
|
43003
43433
|
continue;
|
|
@@ -43009,7 +43439,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
43009
43439
|
if (lstat.isDirectory()) {
|
|
43010
43440
|
let realPath;
|
|
43011
43441
|
try {
|
|
43012
|
-
realPath =
|
|
43442
|
+
realPath = fs13.realpathSync(fullPath);
|
|
43013
43443
|
} catch {
|
|
43014
43444
|
stats.fileErrors++;
|
|
43015
43445
|
continue;
|
|
@@ -43025,7 +43455,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
43025
43455
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
43026
43456
|
files.push(...subFiles);
|
|
43027
43457
|
} else if (lstat.isFile()) {
|
|
43028
|
-
const ext =
|
|
43458
|
+
const ext = path27.extname(fullPath).toLowerCase();
|
|
43029
43459
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
43030
43460
|
files.push(fullPath);
|
|
43031
43461
|
} else {
|
|
@@ -43228,7 +43658,7 @@ var init_secretscan = __esm(() => {
|
|
|
43228
43658
|
redactTemplate: () => "SK[REDACTED]"
|
|
43229
43659
|
}
|
|
43230
43660
|
];
|
|
43231
|
-
O_NOFOLLOW = process.platform !== "win32" ?
|
|
43661
|
+
O_NOFOLLOW = process.platform !== "win32" ? fs13.constants.O_NOFOLLOW : undefined;
|
|
43232
43662
|
secretscan = createSwarmTool({
|
|
43233
43663
|
description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default. Supports glob patterns (e.g. **/.svelte-kit/**, **/*.test.ts) and reads .secretscanignore at the scan root.",
|
|
43234
43664
|
args: {
|
|
@@ -43285,15 +43715,15 @@ var init_secretscan = __esm(() => {
|
|
|
43285
43715
|
}
|
|
43286
43716
|
}
|
|
43287
43717
|
try {
|
|
43288
|
-
const _scanDirRaw =
|
|
43718
|
+
const _scanDirRaw = path27.resolve(directory);
|
|
43289
43719
|
const scanDir = (() => {
|
|
43290
43720
|
try {
|
|
43291
|
-
return
|
|
43721
|
+
return fs13.realpathSync(_scanDirRaw);
|
|
43292
43722
|
} catch {
|
|
43293
43723
|
return _scanDirRaw;
|
|
43294
43724
|
}
|
|
43295
43725
|
})();
|
|
43296
|
-
if (!
|
|
43726
|
+
if (!fs13.existsSync(scanDir)) {
|
|
43297
43727
|
const errorResult = {
|
|
43298
43728
|
error: "directory not found",
|
|
43299
43729
|
scan_dir: directory,
|
|
@@ -43304,7 +43734,7 @@ var init_secretscan = __esm(() => {
|
|
|
43304
43734
|
};
|
|
43305
43735
|
return JSON.stringify(errorResult, null, 2);
|
|
43306
43736
|
}
|
|
43307
|
-
const dirStat =
|
|
43737
|
+
const dirStat = fs13.statSync(scanDir);
|
|
43308
43738
|
if (!dirStat.isDirectory()) {
|
|
43309
43739
|
const errorResult = {
|
|
43310
43740
|
error: "target must be a directory, not a file",
|
|
@@ -43355,7 +43785,7 @@ var init_secretscan = __esm(() => {
|
|
|
43355
43785
|
break;
|
|
43356
43786
|
const fileFindings = scanFileForSecrets(filePath);
|
|
43357
43787
|
try {
|
|
43358
|
-
const stat3 =
|
|
43788
|
+
const stat3 = fs13.statSync(filePath);
|
|
43359
43789
|
if (stat3.size > MAX_FILE_SIZE_BYTES) {
|
|
43360
43790
|
skippedFiles++;
|
|
43361
43791
|
continue;
|
|
@@ -43431,15 +43861,15 @@ var init_secretscan = __esm(() => {
|
|
|
43431
43861
|
});
|
|
43432
43862
|
|
|
43433
43863
|
// src/test-impact/analyzer.ts
|
|
43434
|
-
import
|
|
43435
|
-
import
|
|
43864
|
+
import fs14 from "fs";
|
|
43865
|
+
import path28 from "path";
|
|
43436
43866
|
function normalizePath(p) {
|
|
43437
43867
|
return p.replace(/\\/g, "/");
|
|
43438
43868
|
}
|
|
43439
43869
|
function isCacheStale(impactMap, generatedAtMs) {
|
|
43440
43870
|
for (const sourcePath of Object.keys(impactMap)) {
|
|
43441
43871
|
try {
|
|
43442
|
-
const stat3 =
|
|
43872
|
+
const stat3 = fs14.statSync(sourcePath);
|
|
43443
43873
|
if (stat3.mtimeMs > generatedAtMs) {
|
|
43444
43874
|
return true;
|
|
43445
43875
|
}
|
|
@@ -43453,15 +43883,15 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
43453
43883
|
if (!importPath.startsWith(".")) {
|
|
43454
43884
|
return null;
|
|
43455
43885
|
}
|
|
43456
|
-
const resolved =
|
|
43457
|
-
if (
|
|
43458
|
-
if (
|
|
43886
|
+
const resolved = path28.resolve(fromDir, importPath);
|
|
43887
|
+
if (path28.extname(resolved)) {
|
|
43888
|
+
if (fs14.existsSync(resolved) && fs14.statSync(resolved).isFile()) {
|
|
43459
43889
|
return normalizePath(resolved);
|
|
43460
43890
|
}
|
|
43461
43891
|
} else {
|
|
43462
43892
|
for (const ext of EXTENSIONS_TO_TRY) {
|
|
43463
43893
|
const withExt = resolved + ext;
|
|
43464
|
-
if (
|
|
43894
|
+
if (fs14.existsSync(withExt) && fs14.statSync(withExt).isFile()) {
|
|
43465
43895
|
return normalizePath(withExt);
|
|
43466
43896
|
}
|
|
43467
43897
|
}
|
|
@@ -43480,13 +43910,13 @@ function findTestFilesSync(cwd) {
|
|
|
43480
43910
|
function walk(dir, visitedInodes) {
|
|
43481
43911
|
let entries;
|
|
43482
43912
|
try {
|
|
43483
|
-
entries =
|
|
43913
|
+
entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
43484
43914
|
} catch {
|
|
43485
43915
|
return;
|
|
43486
43916
|
}
|
|
43487
43917
|
let dirInode;
|
|
43488
43918
|
try {
|
|
43489
|
-
dirInode =
|
|
43919
|
+
dirInode = fs14.statSync(dir).ino;
|
|
43490
43920
|
} catch {
|
|
43491
43921
|
return;
|
|
43492
43922
|
}
|
|
@@ -43499,12 +43929,12 @@ function findTestFilesSync(cwd) {
|
|
|
43499
43929
|
for (const entry of entries) {
|
|
43500
43930
|
if (entry.isDirectory()) {
|
|
43501
43931
|
if (!skipDirs.has(entry.name)) {
|
|
43502
|
-
walk(
|
|
43932
|
+
walk(path28.join(dir, entry.name), visitedInodes);
|
|
43503
43933
|
}
|
|
43504
43934
|
} else if (entry.isFile()) {
|
|
43505
43935
|
const name = entry.name;
|
|
43506
43936
|
if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name)) {
|
|
43507
|
-
testFiles.push(normalizePath(
|
|
43937
|
+
testFiles.push(normalizePath(path28.join(dir, entry.name)));
|
|
43508
43938
|
}
|
|
43509
43939
|
}
|
|
43510
43940
|
}
|
|
@@ -43534,7 +43964,7 @@ async function buildImpactMapInternal(cwd) {
|
|
|
43534
43964
|
for (const testFile of testFiles) {
|
|
43535
43965
|
let content;
|
|
43536
43966
|
try {
|
|
43537
|
-
content =
|
|
43967
|
+
content = fs14.readFileSync(testFile, "utf-8");
|
|
43538
43968
|
} catch {
|
|
43539
43969
|
continue;
|
|
43540
43970
|
}
|
|
@@ -43542,7 +43972,7 @@ async function buildImpactMapInternal(cwd) {
|
|
|
43542
43972
|
continue;
|
|
43543
43973
|
}
|
|
43544
43974
|
const imports = extractImports(content);
|
|
43545
|
-
const testDir =
|
|
43975
|
+
const testDir = path28.dirname(testFile);
|
|
43546
43976
|
for (const importPath of imports) {
|
|
43547
43977
|
const resolvedSource = resolveRelativeImport(testDir, importPath);
|
|
43548
43978
|
if (resolvedSource === null) {
|
|
@@ -43564,10 +43994,10 @@ async function buildImpactMap(cwd) {
|
|
|
43564
43994
|
return impactMap;
|
|
43565
43995
|
}
|
|
43566
43996
|
async function loadImpactMap(cwd) {
|
|
43567
|
-
const cachePath =
|
|
43568
|
-
if (
|
|
43997
|
+
const cachePath = path28.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
43998
|
+
if (fs14.existsSync(cachePath)) {
|
|
43569
43999
|
try {
|
|
43570
|
-
const content =
|
|
44000
|
+
const content = fs14.readFileSync(cachePath, "utf-8");
|
|
43571
44001
|
const data = JSON.parse(content);
|
|
43572
44002
|
const map3 = data.map;
|
|
43573
44003
|
const generatedAt = new Date(data.generatedAt).getTime();
|
|
@@ -43579,17 +44009,17 @@ async function loadImpactMap(cwd) {
|
|
|
43579
44009
|
return _internals17.buildImpactMap(cwd);
|
|
43580
44010
|
}
|
|
43581
44011
|
async function saveImpactMap(cwd, impactMap) {
|
|
43582
|
-
const cacheDir2 =
|
|
43583
|
-
const cachePath =
|
|
43584
|
-
if (!
|
|
43585
|
-
|
|
44012
|
+
const cacheDir2 = path28.join(cwd, ".swarm", "cache");
|
|
44013
|
+
const cachePath = path28.join(cacheDir2, "impact-map.json");
|
|
44014
|
+
if (!fs14.existsSync(cacheDir2)) {
|
|
44015
|
+
fs14.mkdirSync(cacheDir2, { recursive: true });
|
|
43586
44016
|
}
|
|
43587
44017
|
const data = {
|
|
43588
44018
|
generatedAt: new Date().toISOString(),
|
|
43589
44019
|
fileCount: Object.keys(impactMap).length,
|
|
43590
44020
|
map: impactMap
|
|
43591
44021
|
};
|
|
43592
|
-
|
|
44022
|
+
fs14.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
43593
44023
|
}
|
|
43594
44024
|
async function analyzeImpact(changedFiles, cwd) {
|
|
43595
44025
|
if (!Array.isArray(changedFiles)) {
|
|
@@ -43606,7 +44036,7 @@ async function analyzeImpact(changedFiles, cwd) {
|
|
|
43606
44036
|
const impactedTestsSet = new Set;
|
|
43607
44037
|
const untestedFiles = [];
|
|
43608
44038
|
for (const changedFile of validFiles) {
|
|
43609
|
-
const normalizedChanged = normalizePath(
|
|
44039
|
+
const normalizedChanged = normalizePath(path28.resolve(changedFile));
|
|
43610
44040
|
const tests = impactMap[normalizedChanged];
|
|
43611
44041
|
if (tests && tests.length > 0) {
|
|
43612
44042
|
for (const test of tests) {
|
|
@@ -43869,10 +44299,10 @@ function detectFlakyTests(allHistory) {
|
|
|
43869
44299
|
var FLAKY_THRESHOLD = 0.3, MIN_RUNS_FOR_QUARANTINE = 5, MAX_HISTORY_RUNS = 20;
|
|
43870
44300
|
|
|
43871
44301
|
// src/test-impact/history-store.ts
|
|
43872
|
-
import
|
|
43873
|
-
import
|
|
44302
|
+
import fs15 from "fs";
|
|
44303
|
+
import path29 from "path";
|
|
43874
44304
|
function getHistoryPath(workingDir) {
|
|
43875
|
-
return
|
|
44305
|
+
return path29.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
|
|
43876
44306
|
}
|
|
43877
44307
|
function sanitizeErrorMessage(errorMessage) {
|
|
43878
44308
|
if (errorMessage === undefined) {
|
|
@@ -43927,9 +44357,9 @@ function appendTestRun(record3, workingDir) {
|
|
|
43927
44357
|
changedFiles: sanitizeChangedFiles(record3.changedFiles || [])
|
|
43928
44358
|
};
|
|
43929
44359
|
const historyPath = getHistoryPath(workingDir);
|
|
43930
|
-
const historyDir =
|
|
43931
|
-
if (!
|
|
43932
|
-
|
|
44360
|
+
const historyDir = path29.dirname(historyPath);
|
|
44361
|
+
if (!fs15.existsSync(historyDir)) {
|
|
44362
|
+
fs15.mkdirSync(historyDir, { recursive: true });
|
|
43933
44363
|
}
|
|
43934
44364
|
const existingRecords = readAllRecords(historyPath);
|
|
43935
44365
|
existingRecords.push(sanitizedRecord);
|
|
@@ -43954,24 +44384,24 @@ function appendTestRun(record3, workingDir) {
|
|
|
43954
44384
|
`)}
|
|
43955
44385
|
`;
|
|
43956
44386
|
const tempPath = `${historyPath}.tmp`;
|
|
43957
|
-
|
|
43958
|
-
|
|
44387
|
+
fs15.writeFileSync(tempPath, content, "utf-8");
|
|
44388
|
+
fs15.renameSync(tempPath, historyPath);
|
|
43959
44389
|
} catch (err) {
|
|
43960
44390
|
try {
|
|
43961
44391
|
const tempPath = `${historyPath}.tmp`;
|
|
43962
|
-
if (
|
|
43963
|
-
|
|
44392
|
+
if (fs15.existsSync(tempPath)) {
|
|
44393
|
+
fs15.unlinkSync(tempPath);
|
|
43964
44394
|
}
|
|
43965
44395
|
} catch {}
|
|
43966
44396
|
throw new Error(`Failed to write test history: ${err instanceof Error ? err.message : String(err)}`);
|
|
43967
44397
|
}
|
|
43968
44398
|
}
|
|
43969
44399
|
function readAllRecords(historyPath) {
|
|
43970
|
-
if (!
|
|
44400
|
+
if (!fs15.existsSync(historyPath)) {
|
|
43971
44401
|
return [];
|
|
43972
44402
|
}
|
|
43973
44403
|
try {
|
|
43974
|
-
const content =
|
|
44404
|
+
const content = fs15.readFileSync(historyPath, "utf-8");
|
|
43975
44405
|
const lines = content.split(`
|
|
43976
44406
|
`);
|
|
43977
44407
|
const records = [];
|
|
@@ -44008,8 +44438,8 @@ var init_history_store = __esm(() => {
|
|
|
44008
44438
|
});
|
|
44009
44439
|
|
|
44010
44440
|
// src/tools/resolve-working-directory.ts
|
|
44011
|
-
import * as
|
|
44012
|
-
import * as
|
|
44441
|
+
import * as fs16 from "fs";
|
|
44442
|
+
import * as path30 from "path";
|
|
44013
44443
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
44014
44444
|
if (workingDirectory == null || workingDirectory === "") {
|
|
44015
44445
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -44029,18 +44459,18 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
44029
44459
|
};
|
|
44030
44460
|
}
|
|
44031
44461
|
}
|
|
44032
|
-
const normalizedDir =
|
|
44033
|
-
const pathParts = normalizedDir.split(
|
|
44462
|
+
const normalizedDir = path30.normalize(workingDirectory);
|
|
44463
|
+
const pathParts = normalizedDir.split(path30.sep);
|
|
44034
44464
|
if (pathParts.includes("..")) {
|
|
44035
44465
|
return {
|
|
44036
44466
|
success: false,
|
|
44037
44467
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
44038
44468
|
};
|
|
44039
44469
|
}
|
|
44040
|
-
const resolvedDir =
|
|
44470
|
+
const resolvedDir = path30.resolve(normalizedDir);
|
|
44041
44471
|
let statResult;
|
|
44042
44472
|
try {
|
|
44043
|
-
statResult =
|
|
44473
|
+
statResult = fs16.statSync(resolvedDir);
|
|
44044
44474
|
} catch {
|
|
44045
44475
|
return {
|
|
44046
44476
|
success: false,
|
|
@@ -44053,17 +44483,17 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
44053
44483
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
44054
44484
|
};
|
|
44055
44485
|
}
|
|
44056
|
-
const resolvedFallback =
|
|
44486
|
+
const resolvedFallback = path30.resolve(fallbackDirectory);
|
|
44057
44487
|
let fallbackExists = false;
|
|
44058
44488
|
try {
|
|
44059
|
-
|
|
44489
|
+
fs16.statSync(resolvedFallback);
|
|
44060
44490
|
fallbackExists = true;
|
|
44061
44491
|
} catch {
|
|
44062
44492
|
fallbackExists = false;
|
|
44063
44493
|
}
|
|
44064
44494
|
if (workingDirectory != null && workingDirectory !== "") {
|
|
44065
44495
|
if (fallbackExists) {
|
|
44066
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
44496
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path30.sep);
|
|
44067
44497
|
if (isSubdirectory) {
|
|
44068
44498
|
return {
|
|
44069
44499
|
success: false,
|
|
@@ -44084,8 +44514,8 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
44084
44514
|
var init_resolve_working_directory = () => {};
|
|
44085
44515
|
|
|
44086
44516
|
// src/tools/test-runner.ts
|
|
44087
|
-
import * as
|
|
44088
|
-
import * as
|
|
44517
|
+
import * as fs17 from "fs";
|
|
44518
|
+
import * as path31 from "path";
|
|
44089
44519
|
function isAbsolutePath(str) {
|
|
44090
44520
|
if (str.startsWith("/"))
|
|
44091
44521
|
return true;
|
|
@@ -44150,19 +44580,19 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
44150
44580
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
44151
44581
|
}
|
|
44152
44582
|
function detectGoTest(cwd) {
|
|
44153
|
-
return
|
|
44583
|
+
return fs17.existsSync(path31.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
44154
44584
|
}
|
|
44155
44585
|
function detectJavaMaven(cwd) {
|
|
44156
|
-
return
|
|
44586
|
+
return fs17.existsSync(path31.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
44157
44587
|
}
|
|
44158
44588
|
function detectGradle(cwd) {
|
|
44159
|
-
const hasBuildFile =
|
|
44160
|
-
const hasGradlew =
|
|
44589
|
+
const hasBuildFile = fs17.existsSync(path31.join(cwd, "build.gradle")) || fs17.existsSync(path31.join(cwd, "build.gradle.kts"));
|
|
44590
|
+
const hasGradlew = fs17.existsSync(path31.join(cwd, "gradlew")) || fs17.existsSync(path31.join(cwd, "gradlew.bat"));
|
|
44161
44591
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
44162
44592
|
}
|
|
44163
44593
|
function detectDotnetTest(cwd) {
|
|
44164
44594
|
try {
|
|
44165
|
-
const files =
|
|
44595
|
+
const files = fs17.readdirSync(cwd);
|
|
44166
44596
|
const hasCsproj = files.some((f) => f.endsWith(".csproj"));
|
|
44167
44597
|
return hasCsproj && isCommandAvailable("dotnet");
|
|
44168
44598
|
} catch {
|
|
@@ -44170,32 +44600,32 @@ function detectDotnetTest(cwd) {
|
|
|
44170
44600
|
}
|
|
44171
44601
|
}
|
|
44172
44602
|
function detectCTest(cwd) {
|
|
44173
|
-
const hasSource =
|
|
44174
|
-
const hasBuildCache =
|
|
44603
|
+
const hasSource = fs17.existsSync(path31.join(cwd, "CMakeLists.txt"));
|
|
44604
|
+
const hasBuildCache = fs17.existsSync(path31.join(cwd, "CMakeCache.txt")) || fs17.existsSync(path31.join(cwd, "build", "CMakeCache.txt"));
|
|
44175
44605
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
44176
44606
|
}
|
|
44177
44607
|
function detectSwiftTest(cwd) {
|
|
44178
|
-
return
|
|
44608
|
+
return fs17.existsSync(path31.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
44179
44609
|
}
|
|
44180
44610
|
function detectDartTest(cwd) {
|
|
44181
|
-
return
|
|
44611
|
+
return fs17.existsSync(path31.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
44182
44612
|
}
|
|
44183
44613
|
function detectRSpec(cwd) {
|
|
44184
|
-
const hasRSpecFile =
|
|
44185
|
-
const hasGemfile =
|
|
44186
|
-
const hasSpecDir =
|
|
44614
|
+
const hasRSpecFile = fs17.existsSync(path31.join(cwd, ".rspec"));
|
|
44615
|
+
const hasGemfile = fs17.existsSync(path31.join(cwd, "Gemfile"));
|
|
44616
|
+
const hasSpecDir = fs17.existsSync(path31.join(cwd, "spec"));
|
|
44187
44617
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
44188
44618
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
44189
44619
|
}
|
|
44190
44620
|
function detectMinitest(cwd) {
|
|
44191
|
-
return
|
|
44621
|
+
return fs17.existsSync(path31.join(cwd, "test")) && (fs17.existsSync(path31.join(cwd, "Gemfile")) || fs17.existsSync(path31.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
44192
44622
|
}
|
|
44193
44623
|
async function detectTestFramework(cwd) {
|
|
44194
44624
|
const baseDir = cwd;
|
|
44195
44625
|
try {
|
|
44196
|
-
const packageJsonPath =
|
|
44197
|
-
if (
|
|
44198
|
-
const content =
|
|
44626
|
+
const packageJsonPath = path31.join(baseDir, "package.json");
|
|
44627
|
+
if (fs17.existsSync(packageJsonPath)) {
|
|
44628
|
+
const content = fs17.readFileSync(packageJsonPath, "utf-8");
|
|
44199
44629
|
const pkg = JSON.parse(content);
|
|
44200
44630
|
const _deps = pkg.dependencies || {};
|
|
44201
44631
|
const devDeps = pkg.devDependencies || {};
|
|
@@ -44214,38 +44644,38 @@ async function detectTestFramework(cwd) {
|
|
|
44214
44644
|
return "jest";
|
|
44215
44645
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
44216
44646
|
return "mocha";
|
|
44217
|
-
if (
|
|
44647
|
+
if (fs17.existsSync(path31.join(baseDir, "bun.lockb")) || fs17.existsSync(path31.join(baseDir, "bun.lock"))) {
|
|
44218
44648
|
if (scripts.test?.includes("bun"))
|
|
44219
44649
|
return "bun";
|
|
44220
44650
|
}
|
|
44221
44651
|
}
|
|
44222
44652
|
} catch {}
|
|
44223
44653
|
try {
|
|
44224
|
-
const pyprojectTomlPath =
|
|
44225
|
-
const setupCfgPath =
|
|
44226
|
-
const requirementsTxtPath =
|
|
44227
|
-
if (
|
|
44228
|
-
const content =
|
|
44654
|
+
const pyprojectTomlPath = path31.join(baseDir, "pyproject.toml");
|
|
44655
|
+
const setupCfgPath = path31.join(baseDir, "setup.cfg");
|
|
44656
|
+
const requirementsTxtPath = path31.join(baseDir, "requirements.txt");
|
|
44657
|
+
if (fs17.existsSync(pyprojectTomlPath)) {
|
|
44658
|
+
const content = fs17.readFileSync(pyprojectTomlPath, "utf-8");
|
|
44229
44659
|
if (content.includes("[tool.pytest"))
|
|
44230
44660
|
return "pytest";
|
|
44231
44661
|
if (content.includes("pytest"))
|
|
44232
44662
|
return "pytest";
|
|
44233
44663
|
}
|
|
44234
|
-
if (
|
|
44235
|
-
const content =
|
|
44664
|
+
if (fs17.existsSync(setupCfgPath)) {
|
|
44665
|
+
const content = fs17.readFileSync(setupCfgPath, "utf-8");
|
|
44236
44666
|
if (content.includes("[pytest]"))
|
|
44237
44667
|
return "pytest";
|
|
44238
44668
|
}
|
|
44239
|
-
if (
|
|
44240
|
-
const content =
|
|
44669
|
+
if (fs17.existsSync(requirementsTxtPath)) {
|
|
44670
|
+
const content = fs17.readFileSync(requirementsTxtPath, "utf-8");
|
|
44241
44671
|
if (content.includes("pytest"))
|
|
44242
44672
|
return "pytest";
|
|
44243
44673
|
}
|
|
44244
44674
|
} catch {}
|
|
44245
44675
|
try {
|
|
44246
|
-
const cargoTomlPath =
|
|
44247
|
-
if (
|
|
44248
|
-
const content =
|
|
44676
|
+
const cargoTomlPath = path31.join(baseDir, "Cargo.toml");
|
|
44677
|
+
if (fs17.existsSync(cargoTomlPath)) {
|
|
44678
|
+
const content = fs17.readFileSync(cargoTomlPath, "utf-8");
|
|
44249
44679
|
if (content.includes("[dev-dependencies]")) {
|
|
44250
44680
|
if (content.includes("tokio") || content.includes("mockall") || content.includes("pretty_assertions")) {
|
|
44251
44681
|
return "cargo";
|
|
@@ -44254,10 +44684,10 @@ async function detectTestFramework(cwd) {
|
|
|
44254
44684
|
}
|
|
44255
44685
|
} catch {}
|
|
44256
44686
|
try {
|
|
44257
|
-
const pesterConfigPath =
|
|
44258
|
-
const pesterConfigJsonPath =
|
|
44259
|
-
const pesterPs1Path =
|
|
44260
|
-
if (
|
|
44687
|
+
const pesterConfigPath = path31.join(baseDir, "pester.config.ps1");
|
|
44688
|
+
const pesterConfigJsonPath = path31.join(baseDir, "pester.config.ps1.json");
|
|
44689
|
+
const pesterPs1Path = path31.join(baseDir, "tests.ps1");
|
|
44690
|
+
if (fs17.existsSync(pesterConfigPath) || fs17.existsSync(pesterConfigJsonPath) || fs17.existsSync(pesterPs1Path)) {
|
|
44261
44691
|
return "pester";
|
|
44262
44692
|
}
|
|
44263
44693
|
} catch {}
|
|
@@ -44285,12 +44715,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
44285
44715
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
44286
44716
|
}
|
|
44287
44717
|
function resolveWorkspacePath(file3, workingDir) {
|
|
44288
|
-
return
|
|
44718
|
+
return path31.isAbsolute(file3) ? path31.resolve(file3) : path31.resolve(workingDir, file3);
|
|
44289
44719
|
}
|
|
44290
44720
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
44291
44721
|
if (!preferRelative)
|
|
44292
44722
|
return absolutePath;
|
|
44293
|
-
return
|
|
44723
|
+
return path31.relative(workingDir, absolutePath);
|
|
44294
44724
|
}
|
|
44295
44725
|
function dedupePush(target, value) {
|
|
44296
44726
|
if (!target.includes(value)) {
|
|
@@ -44327,18 +44757,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
44327
44757
|
}
|
|
44328
44758
|
}
|
|
44329
44759
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
44330
|
-
const relativeDir =
|
|
44760
|
+
const relativeDir = path31.dirname(relativePath);
|
|
44331
44761
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
44332
44762
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
44333
|
-
const rootDir =
|
|
44334
|
-
return nestedRelativeDir ? [rootDir,
|
|
44763
|
+
const rootDir = path31.join(workingDir, dirName);
|
|
44764
|
+
return nestedRelativeDir ? [rootDir, path31.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
44335
44765
|
});
|
|
44336
44766
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
44337
44767
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
44338
|
-
directories.push(
|
|
44768
|
+
directories.push(path31.join(workingDir, "src/test/java", path31.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
44339
44769
|
}
|
|
44340
44770
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
44341
|
-
directories.push(
|
|
44771
|
+
directories.push(path31.join(workingDir, "src/test/kotlin", path31.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
44342
44772
|
}
|
|
44343
44773
|
return [...new Set(directories)];
|
|
44344
44774
|
}
|
|
@@ -44366,23 +44796,23 @@ function isLanguageSpecificTestFile(basename5) {
|
|
|
44366
44796
|
}
|
|
44367
44797
|
function isConventionTestFilePath(filePath) {
|
|
44368
44798
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
44369
|
-
const basename5 =
|
|
44799
|
+
const basename5 = path31.basename(filePath);
|
|
44370
44800
|
return hasCompoundTestExtension(basename5) || basename5.includes(".spec.") || basename5.includes(".test.") || isLanguageSpecificTestFile(basename5) || isTestDirectoryPath(normalizedPath);
|
|
44371
44801
|
}
|
|
44372
44802
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
44373
44803
|
const testFiles = [];
|
|
44374
44804
|
for (const file3 of sourceFiles) {
|
|
44375
44805
|
const absoluteFile = resolveWorkspacePath(file3, workingDir);
|
|
44376
|
-
const relativeFile =
|
|
44377
|
-
const basename5 =
|
|
44378
|
-
const dirname13 =
|
|
44379
|
-
const preferRelativeOutput = !
|
|
44806
|
+
const relativeFile = path31.relative(workingDir, absoluteFile);
|
|
44807
|
+
const basename5 = path31.basename(absoluteFile);
|
|
44808
|
+
const dirname13 = path31.dirname(absoluteFile);
|
|
44809
|
+
const preferRelativeOutput = !path31.isAbsolute(file3);
|
|
44380
44810
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
|
|
44381
44811
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
44382
44812
|
continue;
|
|
44383
44813
|
}
|
|
44384
44814
|
const nameWithoutExt = basename5.replace(/\.[^.]+$/, "");
|
|
44385
|
-
const ext =
|
|
44815
|
+
const ext = path31.extname(basename5);
|
|
44386
44816
|
const genericTestNames = [
|
|
44387
44817
|
`${nameWithoutExt}.spec${ext}`,
|
|
44388
44818
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -44391,7 +44821,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
44391
44821
|
const colocatedCandidates = [
|
|
44392
44822
|
...genericTestNames,
|
|
44393
44823
|
...languageSpecificTestNames
|
|
44394
|
-
].map((candidateName) =>
|
|
44824
|
+
].map((candidateName) => path31.join(dirname13, candidateName));
|
|
44395
44825
|
const testDirectoryNames = [
|
|
44396
44826
|
basename5,
|
|
44397
44827
|
...genericTestNames,
|
|
@@ -44400,11 +44830,11 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
44400
44830
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
44401
44831
|
const possibleTestFiles = [
|
|
44402
44832
|
...colocatedCandidates,
|
|
44403
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
44404
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
44833
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path31.join(dirname13, dirName, candidateName))),
|
|
44834
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path31.join(candidateDir, candidateName)))
|
|
44405
44835
|
];
|
|
44406
44836
|
for (const testFile of possibleTestFiles) {
|
|
44407
|
-
if (
|
|
44837
|
+
if (fs17.existsSync(testFile)) {
|
|
44408
44838
|
dedupePush(testFiles, toWorkspaceOutputPath(testFile, workingDir, preferRelativeOutput));
|
|
44409
44839
|
}
|
|
44410
44840
|
}
|
|
@@ -44421,8 +44851,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
44421
44851
|
for (const testFile of candidateTestFiles) {
|
|
44422
44852
|
try {
|
|
44423
44853
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
44424
|
-
const content =
|
|
44425
|
-
const testDir =
|
|
44854
|
+
const content = fs17.readFileSync(absoluteTestFile, "utf-8");
|
|
44855
|
+
const testDir = path31.dirname(absoluteTestFile);
|
|
44426
44856
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
44427
44857
|
let match;
|
|
44428
44858
|
match = importRegex.exec(content);
|
|
@@ -44430,8 +44860,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
44430
44860
|
const importPath = match[1];
|
|
44431
44861
|
let resolvedImport;
|
|
44432
44862
|
if (importPath.startsWith(".")) {
|
|
44433
|
-
resolvedImport =
|
|
44434
|
-
const existingExt =
|
|
44863
|
+
resolvedImport = path31.resolve(testDir, importPath);
|
|
44864
|
+
const existingExt = path31.extname(resolvedImport);
|
|
44435
44865
|
if (!existingExt) {
|
|
44436
44866
|
for (const extToTry of [
|
|
44437
44867
|
".ts",
|
|
@@ -44442,7 +44872,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
44442
44872
|
".cjs"
|
|
44443
44873
|
]) {
|
|
44444
44874
|
const withExt = resolvedImport + extToTry;
|
|
44445
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
44875
|
+
if (absoluteSourceFiles.includes(withExt) || fs17.existsSync(withExt)) {
|
|
44446
44876
|
resolvedImport = withExt;
|
|
44447
44877
|
break;
|
|
44448
44878
|
}
|
|
@@ -44451,12 +44881,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
44451
44881
|
} else {
|
|
44452
44882
|
continue;
|
|
44453
44883
|
}
|
|
44454
|
-
const importBasename =
|
|
44455
|
-
const importDir =
|
|
44884
|
+
const importBasename = path31.basename(resolvedImport, path31.extname(resolvedImport));
|
|
44885
|
+
const importDir = path31.dirname(resolvedImport);
|
|
44456
44886
|
for (const sourceFile of absoluteSourceFiles) {
|
|
44457
|
-
const sourceDir =
|
|
44458
|
-
const sourceBasename =
|
|
44459
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
44887
|
+
const sourceDir = path31.dirname(sourceFile);
|
|
44888
|
+
const sourceBasename = path31.basename(sourceFile, path31.extname(sourceFile));
|
|
44889
|
+
const isRelatedDir = importDir === sourceDir || importDir === path31.join(sourceDir, "__tests__") || importDir === path31.join(sourceDir, "tests") || importDir === path31.join(sourceDir, "test") || importDir === path31.join(sourceDir, "spec");
|
|
44460
44890
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
44461
44891
|
dedupePush(testFiles, testFile);
|
|
44462
44892
|
break;
|
|
@@ -44469,8 +44899,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
44469
44899
|
while (match !== null) {
|
|
44470
44900
|
const importPath = match[1];
|
|
44471
44901
|
if (importPath.startsWith(".")) {
|
|
44472
|
-
let resolvedImport =
|
|
44473
|
-
const existingExt =
|
|
44902
|
+
let resolvedImport = path31.resolve(testDir, importPath);
|
|
44903
|
+
const existingExt = path31.extname(resolvedImport);
|
|
44474
44904
|
if (!existingExt) {
|
|
44475
44905
|
for (const extToTry of [
|
|
44476
44906
|
".ts",
|
|
@@ -44481,18 +44911,18 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
44481
44911
|
".cjs"
|
|
44482
44912
|
]) {
|
|
44483
44913
|
const withExt = resolvedImport + extToTry;
|
|
44484
|
-
if (absoluteSourceFiles.includes(withExt) ||
|
|
44914
|
+
if (absoluteSourceFiles.includes(withExt) || fs17.existsSync(withExt)) {
|
|
44485
44915
|
resolvedImport = withExt;
|
|
44486
44916
|
break;
|
|
44487
44917
|
}
|
|
44488
44918
|
}
|
|
44489
44919
|
}
|
|
44490
|
-
const importDir =
|
|
44491
|
-
const importBasename =
|
|
44920
|
+
const importDir = path31.dirname(resolvedImport);
|
|
44921
|
+
const importBasename = path31.basename(resolvedImport, path31.extname(resolvedImport));
|
|
44492
44922
|
for (const sourceFile of absoluteSourceFiles) {
|
|
44493
|
-
const sourceDir =
|
|
44494
|
-
const sourceBasename =
|
|
44495
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
44923
|
+
const sourceDir = path31.dirname(sourceFile);
|
|
44924
|
+
const sourceBasename = path31.basename(sourceFile, path31.extname(sourceFile));
|
|
44925
|
+
const isRelatedDir = importDir === sourceDir || importDir === path31.join(sourceDir, "__tests__") || importDir === path31.join(sourceDir, "tests") || importDir === path31.join(sourceDir, "test") || importDir === path31.join(sourceDir, "spec");
|
|
44496
44926
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
44497
44927
|
dedupePush(testFiles, testFile);
|
|
44498
44928
|
break;
|
|
@@ -44595,8 +45025,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
44595
45025
|
return ["mvn", "test"];
|
|
44596
45026
|
case "gradle": {
|
|
44597
45027
|
const isWindows = process.platform === "win32";
|
|
44598
|
-
const hasGradlewBat =
|
|
44599
|
-
const hasGradlew =
|
|
45028
|
+
const hasGradlewBat = fs17.existsSync(path31.join(baseDir, "gradlew.bat"));
|
|
45029
|
+
const hasGradlew = fs17.existsSync(path31.join(baseDir, "gradlew"));
|
|
44600
45030
|
if (hasGradlewBat && isWindows)
|
|
44601
45031
|
return ["gradlew.bat", "test"];
|
|
44602
45032
|
if (hasGradlew)
|
|
@@ -44613,7 +45043,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
44613
45043
|
"cmake-build-release",
|
|
44614
45044
|
"out"
|
|
44615
45045
|
];
|
|
44616
|
-
const actualBuildDir = buildDirCandidates.find((d) =>
|
|
45046
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs17.existsSync(path31.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
44617
45047
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
44618
45048
|
}
|
|
44619
45049
|
case "swift-test":
|
|
@@ -44934,9 +45364,9 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
44934
45364
|
stderr: "pipe",
|
|
44935
45365
|
cwd
|
|
44936
45366
|
});
|
|
44937
|
-
const timeoutPromise = new Promise((
|
|
45367
|
+
const timeoutPromise = new Promise((resolve13) => setTimeout(() => {
|
|
44938
45368
|
proc.kill();
|
|
44939
|
-
|
|
45369
|
+
resolve13(-1);
|
|
44940
45370
|
}, timeout_ms));
|
|
44941
45371
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
44942
45372
|
Promise.race([proc.exited, timeoutPromise]),
|
|
@@ -45266,7 +45696,7 @@ var init_test_runner = __esm(() => {
|
|
|
45266
45696
|
const sourceFiles = args.files.filter((file3) => {
|
|
45267
45697
|
if (directTestFiles.includes(file3))
|
|
45268
45698
|
return false;
|
|
45269
|
-
const ext =
|
|
45699
|
+
const ext = path31.extname(file3).toLowerCase();
|
|
45270
45700
|
return SOURCE_EXTENSIONS.has(ext);
|
|
45271
45701
|
});
|
|
45272
45702
|
const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
|
|
@@ -45301,7 +45731,7 @@ var init_test_runner = __esm(() => {
|
|
|
45301
45731
|
if (isConventionTestFilePath(f)) {
|
|
45302
45732
|
return false;
|
|
45303
45733
|
}
|
|
45304
|
-
const ext =
|
|
45734
|
+
const ext = path31.extname(f).toLowerCase();
|
|
45305
45735
|
return SOURCE_EXTENSIONS.has(ext);
|
|
45306
45736
|
});
|
|
45307
45737
|
if (sourceFiles.length === 0) {
|
|
@@ -45328,7 +45758,7 @@ var init_test_runner = __esm(() => {
|
|
|
45328
45758
|
if (isConventionTestFilePath(f)) {
|
|
45329
45759
|
return false;
|
|
45330
45760
|
}
|
|
45331
|
-
const ext =
|
|
45761
|
+
const ext = path31.extname(f).toLowerCase();
|
|
45332
45762
|
return SOURCE_EXTENSIONS.has(ext);
|
|
45333
45763
|
});
|
|
45334
45764
|
if (sourceFiles.length === 0) {
|
|
@@ -45346,8 +45776,8 @@ var init_test_runner = __esm(() => {
|
|
|
45346
45776
|
const impactResult = await analyzeImpact(sourceFiles, workingDir);
|
|
45347
45777
|
if (impactResult.impactedTests.length > 0) {
|
|
45348
45778
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
45349
|
-
const relativePath =
|
|
45350
|
-
return
|
|
45779
|
+
const relativePath = path31.relative(workingDir, absPath);
|
|
45780
|
+
return path31.isAbsolute(relativePath) ? absPath : relativePath;
|
|
45351
45781
|
});
|
|
45352
45782
|
} else {
|
|
45353
45783
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -45422,8 +45852,8 @@ var init_test_runner = __esm(() => {
|
|
|
45422
45852
|
});
|
|
45423
45853
|
|
|
45424
45854
|
// src/services/preflight-service.ts
|
|
45425
|
-
import * as
|
|
45426
|
-
import * as
|
|
45855
|
+
import * as fs18 from "fs";
|
|
45856
|
+
import * as path32 from "path";
|
|
45427
45857
|
function validateDirectoryPath(dir) {
|
|
45428
45858
|
if (!dir || typeof dir !== "string") {
|
|
45429
45859
|
throw new Error("Directory path is required");
|
|
@@ -45431,8 +45861,8 @@ function validateDirectoryPath(dir) {
|
|
|
45431
45861
|
if (dir.includes("..")) {
|
|
45432
45862
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
45433
45863
|
}
|
|
45434
|
-
const normalized =
|
|
45435
|
-
const absolutePath =
|
|
45864
|
+
const normalized = path32.normalize(dir);
|
|
45865
|
+
const absolutePath = path32.isAbsolute(normalized) ? normalized : path32.resolve(normalized);
|
|
45436
45866
|
return absolutePath;
|
|
45437
45867
|
}
|
|
45438
45868
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -45455,9 +45885,9 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
45455
45885
|
}
|
|
45456
45886
|
function getPackageVersion(dir) {
|
|
45457
45887
|
try {
|
|
45458
|
-
const packagePath =
|
|
45459
|
-
if (
|
|
45460
|
-
const content =
|
|
45888
|
+
const packagePath = path32.join(dir, "package.json");
|
|
45889
|
+
if (fs18.existsSync(packagePath)) {
|
|
45890
|
+
const content = fs18.readFileSync(packagePath, "utf-8");
|
|
45461
45891
|
const pkg = JSON.parse(content);
|
|
45462
45892
|
return pkg.version ?? null;
|
|
45463
45893
|
}
|
|
@@ -45466,9 +45896,9 @@ function getPackageVersion(dir) {
|
|
|
45466
45896
|
}
|
|
45467
45897
|
function getChangelogVersion(dir) {
|
|
45468
45898
|
try {
|
|
45469
|
-
const changelogPath =
|
|
45470
|
-
if (
|
|
45471
|
-
const content =
|
|
45899
|
+
const changelogPath = path32.join(dir, "CHANGELOG.md");
|
|
45900
|
+
if (fs18.existsSync(changelogPath)) {
|
|
45901
|
+
const content = fs18.readFileSync(changelogPath, "utf-8");
|
|
45472
45902
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
45473
45903
|
if (match) {
|
|
45474
45904
|
return match[1];
|
|
@@ -45480,10 +45910,10 @@ function getChangelogVersion(dir) {
|
|
|
45480
45910
|
function getVersionFileVersion(dir) {
|
|
45481
45911
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
45482
45912
|
for (const file3 of possibleFiles) {
|
|
45483
|
-
const filePath =
|
|
45484
|
-
if (
|
|
45913
|
+
const filePath = path32.join(dir, file3);
|
|
45914
|
+
if (fs18.existsSync(filePath)) {
|
|
45485
45915
|
try {
|
|
45486
|
-
const content =
|
|
45916
|
+
const content = fs18.readFileSync(filePath, "utf-8").trim();
|
|
45487
45917
|
const match = content.match(/(\d+\.\d+\.\d+)/);
|
|
45488
45918
|
if (match) {
|
|
45489
45919
|
return match[1];
|
|
@@ -45807,8 +46237,8 @@ async function runEvidenceCheck(dir) {
|
|
|
45807
46237
|
async function runRequirementCoverageCheck(dir, currentPhase) {
|
|
45808
46238
|
const startTime = Date.now();
|
|
45809
46239
|
try {
|
|
45810
|
-
const specPath =
|
|
45811
|
-
if (!
|
|
46240
|
+
const specPath = path32.join(dir, ".swarm", "spec.md");
|
|
46241
|
+
if (!fs18.existsSync(specPath)) {
|
|
45812
46242
|
return {
|
|
45813
46243
|
type: "req_coverage",
|
|
45814
46244
|
status: "skip",
|
|
@@ -46285,13 +46715,13 @@ class CircuitBreaker {
|
|
|
46285
46715
|
if (this.config.callTimeoutMs <= 0) {
|
|
46286
46716
|
return fn();
|
|
46287
46717
|
}
|
|
46288
|
-
return new Promise((
|
|
46718
|
+
return new Promise((resolve14, reject) => {
|
|
46289
46719
|
const timeout = setTimeout(() => {
|
|
46290
46720
|
reject(new Error(`Call timeout after ${this.config.callTimeoutMs}ms`));
|
|
46291
46721
|
}, this.config.callTimeoutMs);
|
|
46292
46722
|
fn().then((result) => {
|
|
46293
46723
|
clearTimeout(timeout);
|
|
46294
|
-
|
|
46724
|
+
resolve14(result);
|
|
46295
46725
|
}).catch((error93) => {
|
|
46296
46726
|
clearTimeout(timeout);
|
|
46297
46727
|
reject(error93);
|
|
@@ -46578,7 +47008,7 @@ var init_queue = __esm(() => {
|
|
|
46578
47008
|
|
|
46579
47009
|
// src/background/worker.ts
|
|
46580
47010
|
function sleep(ms) {
|
|
46581
|
-
return new Promise((
|
|
47011
|
+
return new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
46582
47012
|
}
|
|
46583
47013
|
|
|
46584
47014
|
class WorkerManager {
|
|
@@ -46923,8 +47353,8 @@ var init_manager3 = __esm(() => {
|
|
|
46923
47353
|
});
|
|
46924
47354
|
|
|
46925
47355
|
// src/commands/reset.ts
|
|
46926
|
-
import * as
|
|
46927
|
-
import * as
|
|
47356
|
+
import * as fs19 from "fs";
|
|
47357
|
+
import * as path33 from "path";
|
|
46928
47358
|
async function handleResetCommand(directory, args) {
|
|
46929
47359
|
const hasConfirm = args.includes("--confirm");
|
|
46930
47360
|
if (!hasConfirm) {
|
|
@@ -46952,8 +47382,8 @@ async function handleResetCommand(directory, args) {
|
|
|
46952
47382
|
for (const filename of filesToReset) {
|
|
46953
47383
|
try {
|
|
46954
47384
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
46955
|
-
if (
|
|
46956
|
-
|
|
47385
|
+
if (fs19.existsSync(resolvedPath)) {
|
|
47386
|
+
fs19.unlinkSync(resolvedPath);
|
|
46957
47387
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
46958
47388
|
} else {
|
|
46959
47389
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -46964,9 +47394,9 @@ async function handleResetCommand(directory, args) {
|
|
|
46964
47394
|
}
|
|
46965
47395
|
for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
|
|
46966
47396
|
try {
|
|
46967
|
-
const rootPath =
|
|
46968
|
-
if (
|
|
46969
|
-
|
|
47397
|
+
const rootPath = path33.join(directory, filename);
|
|
47398
|
+
if (fs19.existsSync(rootPath)) {
|
|
47399
|
+
fs19.unlinkSync(rootPath);
|
|
46970
47400
|
results.push(`- \u2705 Deleted ${filename} (root)`);
|
|
46971
47401
|
}
|
|
46972
47402
|
} catch {}
|
|
@@ -46979,8 +47409,8 @@ async function handleResetCommand(directory, args) {
|
|
|
46979
47409
|
}
|
|
46980
47410
|
try {
|
|
46981
47411
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
46982
|
-
if (
|
|
46983
|
-
|
|
47412
|
+
if (fs19.existsSync(summariesPath)) {
|
|
47413
|
+
fs19.rmSync(summariesPath, { recursive: true, force: true });
|
|
46984
47414
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
46985
47415
|
} else {
|
|
46986
47416
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -47003,14 +47433,14 @@ var init_reset = __esm(() => {
|
|
|
47003
47433
|
});
|
|
47004
47434
|
|
|
47005
47435
|
// src/commands/reset-session.ts
|
|
47006
|
-
import * as
|
|
47007
|
-
import * as
|
|
47436
|
+
import * as fs20 from "fs";
|
|
47437
|
+
import * as path34 from "path";
|
|
47008
47438
|
async function handleResetSessionCommand(directory, _args) {
|
|
47009
47439
|
const results = [];
|
|
47010
47440
|
try {
|
|
47011
47441
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
47012
|
-
if (
|
|
47013
|
-
|
|
47442
|
+
if (fs20.existsSync(statePath)) {
|
|
47443
|
+
fs20.unlinkSync(statePath);
|
|
47014
47444
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
47015
47445
|
} else {
|
|
47016
47446
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -47019,15 +47449,15 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
47019
47449
|
results.push("\u274C Failed to delete state.json");
|
|
47020
47450
|
}
|
|
47021
47451
|
try {
|
|
47022
|
-
const sessionDir =
|
|
47023
|
-
if (
|
|
47024
|
-
const files =
|
|
47452
|
+
const sessionDir = path34.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
47453
|
+
if (fs20.existsSync(sessionDir)) {
|
|
47454
|
+
const files = fs20.readdirSync(sessionDir);
|
|
47025
47455
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
47026
47456
|
let deletedCount = 0;
|
|
47027
47457
|
for (const file3 of otherFiles) {
|
|
47028
|
-
const filePath =
|
|
47029
|
-
if (
|
|
47030
|
-
|
|
47458
|
+
const filePath = path34.join(sessionDir, file3);
|
|
47459
|
+
if (fs20.lstatSync(filePath).isFile()) {
|
|
47460
|
+
fs20.unlinkSync(filePath);
|
|
47031
47461
|
deletedCount++;
|
|
47032
47462
|
}
|
|
47033
47463
|
}
|
|
@@ -47057,7 +47487,7 @@ var init_reset_session = __esm(() => {
|
|
|
47057
47487
|
});
|
|
47058
47488
|
|
|
47059
47489
|
// src/summaries/manager.ts
|
|
47060
|
-
import * as
|
|
47490
|
+
import * as path35 from "path";
|
|
47061
47491
|
function sanitizeSummaryId(id) {
|
|
47062
47492
|
if (!id || id.length === 0) {
|
|
47063
47493
|
throw new Error("Invalid summary ID: empty string");
|
|
@@ -47080,7 +47510,7 @@ function sanitizeSummaryId(id) {
|
|
|
47080
47510
|
}
|
|
47081
47511
|
async function loadFullOutput(directory, id) {
|
|
47082
47512
|
const sanitizedId = sanitizeSummaryId(id);
|
|
47083
|
-
const relativePath =
|
|
47513
|
+
const relativePath = path35.join("summaries", `${sanitizedId}.json`);
|
|
47084
47514
|
validateSwarmPath(directory, relativePath);
|
|
47085
47515
|
const content = await readSwarmFileAsync(directory, relativePath);
|
|
47086
47516
|
if (content === null) {
|
|
@@ -47142,18 +47572,18 @@ var init_retrieve = __esm(() => {
|
|
|
47142
47572
|
});
|
|
47143
47573
|
|
|
47144
47574
|
// src/commands/rollback.ts
|
|
47145
|
-
import * as
|
|
47146
|
-
import * as
|
|
47575
|
+
import * as fs21 from "fs";
|
|
47576
|
+
import * as path36 from "path";
|
|
47147
47577
|
async function handleRollbackCommand(directory, args) {
|
|
47148
47578
|
const phaseArg = args[0];
|
|
47149
47579
|
if (!phaseArg) {
|
|
47150
47580
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
47151
|
-
if (!
|
|
47581
|
+
if (!fs21.existsSync(manifestPath2)) {
|
|
47152
47582
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
47153
47583
|
}
|
|
47154
47584
|
let manifest2;
|
|
47155
47585
|
try {
|
|
47156
|
-
manifest2 = JSON.parse(
|
|
47586
|
+
manifest2 = JSON.parse(fs21.readFileSync(manifestPath2, "utf-8"));
|
|
47157
47587
|
} catch {
|
|
47158
47588
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
47159
47589
|
}
|
|
@@ -47175,12 +47605,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
47175
47605
|
return "Error: Phase number must be a positive integer.";
|
|
47176
47606
|
}
|
|
47177
47607
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
47178
|
-
if (!
|
|
47608
|
+
if (!fs21.existsSync(manifestPath)) {
|
|
47179
47609
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
47180
47610
|
}
|
|
47181
47611
|
let manifest;
|
|
47182
47612
|
try {
|
|
47183
|
-
manifest = JSON.parse(
|
|
47613
|
+
manifest = JSON.parse(fs21.readFileSync(manifestPath, "utf-8"));
|
|
47184
47614
|
} catch {
|
|
47185
47615
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
47186
47616
|
}
|
|
@@ -47190,10 +47620,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
47190
47620
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
47191
47621
|
}
|
|
47192
47622
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
47193
|
-
if (!
|
|
47623
|
+
if (!fs21.existsSync(checkpointDir)) {
|
|
47194
47624
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
47195
47625
|
}
|
|
47196
|
-
const checkpointFiles =
|
|
47626
|
+
const checkpointFiles = fs21.readdirSync(checkpointDir);
|
|
47197
47627
|
if (checkpointFiles.length === 0) {
|
|
47198
47628
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
47199
47629
|
}
|
|
@@ -47208,10 +47638,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
47208
47638
|
if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
|
|
47209
47639
|
continue;
|
|
47210
47640
|
}
|
|
47211
|
-
const src =
|
|
47212
|
-
const dest =
|
|
47641
|
+
const src = path36.join(checkpointDir, file3);
|
|
47642
|
+
const dest = path36.join(swarmDir, file3);
|
|
47213
47643
|
try {
|
|
47214
|
-
|
|
47644
|
+
fs21.cpSync(src, dest, { recursive: true, force: true });
|
|
47215
47645
|
successes.push(file3);
|
|
47216
47646
|
} catch (error93) {
|
|
47217
47647
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -47228,14 +47658,14 @@ async function handleRollbackCommand(directory, args) {
|
|
|
47228
47658
|
].join(`
|
|
47229
47659
|
`);
|
|
47230
47660
|
}
|
|
47231
|
-
const existingLedgerPath =
|
|
47232
|
-
if (
|
|
47233
|
-
|
|
47661
|
+
const existingLedgerPath = path36.join(swarmDir, "plan-ledger.jsonl");
|
|
47662
|
+
if (fs21.existsSync(existingLedgerPath)) {
|
|
47663
|
+
fs21.unlinkSync(existingLedgerPath);
|
|
47234
47664
|
}
|
|
47235
47665
|
try {
|
|
47236
|
-
const planJsonPath =
|
|
47237
|
-
if (
|
|
47238
|
-
const planRaw =
|
|
47666
|
+
const planJsonPath = path36.join(swarmDir, "plan.json");
|
|
47667
|
+
if (fs21.existsSync(planJsonPath)) {
|
|
47668
|
+
const planRaw = fs21.readFileSync(planJsonPath, "utf-8");
|
|
47239
47669
|
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
47240
47670
|
const planId = derivePlanId(plan);
|
|
47241
47671
|
const planHash = computePlanHash(plan);
|
|
@@ -47262,7 +47692,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
47262
47692
|
timestamp: new Date().toISOString()
|
|
47263
47693
|
};
|
|
47264
47694
|
try {
|
|
47265
|
-
|
|
47695
|
+
fs21.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
47266
47696
|
`);
|
|
47267
47697
|
} catch (error93) {
|
|
47268
47698
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -47310,11 +47740,11 @@ async function handleSimulateCommand(directory, args) {
|
|
|
47310
47740
|
];
|
|
47311
47741
|
const report = reportLines.filter(Boolean).join(`
|
|
47312
47742
|
`);
|
|
47313
|
-
const
|
|
47314
|
-
const
|
|
47315
|
-
const reportPath =
|
|
47316
|
-
await
|
|
47317
|
-
await
|
|
47743
|
+
const fs22 = await import("fs/promises");
|
|
47744
|
+
const path37 = await import("path");
|
|
47745
|
+
const reportPath = path37.join(directory, ".swarm", "simulate-report.md");
|
|
47746
|
+
await fs22.mkdir(path37.dirname(reportPath), { recursive: true });
|
|
47747
|
+
await fs22.writeFile(reportPath, report, "utf-8");
|
|
47318
47748
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
47319
47749
|
}
|
|
47320
47750
|
var init_simulate = __esm(() => {
|
|
@@ -47667,8 +48097,8 @@ __export(exports_commands, {
|
|
|
47667
48097
|
VALID_COMMANDS: () => VALID_COMMANDS,
|
|
47668
48098
|
COMMAND_REGISTRY: () => COMMAND_REGISTRY
|
|
47669
48099
|
});
|
|
47670
|
-
import
|
|
47671
|
-
import
|
|
48100
|
+
import fs22 from "fs";
|
|
48101
|
+
import path37 from "path";
|
|
47672
48102
|
function buildHelpText() {
|
|
47673
48103
|
const lines = ["## Swarm Commands", ""];
|
|
47674
48104
|
const CATEGORIES = [
|
|
@@ -47777,11 +48207,11 @@ function createSwarmCommandHandler(directory, agents) {
|
|
|
47777
48207
|
return;
|
|
47778
48208
|
}
|
|
47779
48209
|
let isFirstRun = false;
|
|
47780
|
-
const sentinelPath =
|
|
48210
|
+
const sentinelPath = path37.join(directory, ".swarm", ".first-run-complete");
|
|
47781
48211
|
try {
|
|
47782
|
-
const swarmDir =
|
|
47783
|
-
|
|
47784
|
-
|
|
48212
|
+
const swarmDir = path37.join(directory, ".swarm");
|
|
48213
|
+
fs22.mkdirSync(swarmDir, { recursive: true });
|
|
48214
|
+
fs22.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
|
|
47785
48215
|
`, { flag: "wx" });
|
|
47786
48216
|
isFirstRun = true;
|
|
47787
48217
|
} catch (_err) {}
|
|
@@ -47964,24 +48394,24 @@ function validateAliases() {
|
|
|
47964
48394
|
}
|
|
47965
48395
|
aliasTargets.get(target).push(name);
|
|
47966
48396
|
const visited = new Set;
|
|
47967
|
-
const
|
|
48397
|
+
const path38 = [];
|
|
47968
48398
|
let current = target;
|
|
47969
48399
|
while (current) {
|
|
47970
48400
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
47971
48401
|
if (!currentEntry)
|
|
47972
48402
|
break;
|
|
47973
48403
|
if (visited.has(current)) {
|
|
47974
|
-
const cycleStart =
|
|
48404
|
+
const cycleStart = path38.indexOf(current);
|
|
47975
48405
|
const fullChain = [
|
|
47976
48406
|
name,
|
|
47977
|
-
...
|
|
48407
|
+
...path38.slice(0, cycleStart > 0 ? cycleStart : path38.length),
|
|
47978
48408
|
current
|
|
47979
48409
|
].join(" \u2192 ");
|
|
47980
48410
|
errors5.push(`Circular alias detected: ${fullChain}`);
|
|
47981
48411
|
break;
|
|
47982
48412
|
}
|
|
47983
48413
|
visited.add(current);
|
|
47984
|
-
|
|
48414
|
+
path38.push(current);
|
|
47985
48415
|
current = currentEntry.aliasOf || "";
|
|
47986
48416
|
}
|
|
47987
48417
|
}
|
|
@@ -48439,68 +48869,68 @@ init_package();
|
|
|
48439
48869
|
init_registry();
|
|
48440
48870
|
init_cache_paths();
|
|
48441
48871
|
init_constants();
|
|
48442
|
-
import * as
|
|
48872
|
+
import * as fs23 from "fs";
|
|
48443
48873
|
import * as os7 from "os";
|
|
48444
|
-
import * as
|
|
48874
|
+
import * as path38 from "path";
|
|
48445
48875
|
var { version: version4 } = package_default;
|
|
48446
48876
|
var CONFIG_DIR = getPluginConfigDir();
|
|
48447
|
-
var OPENCODE_CONFIG_PATH =
|
|
48448
|
-
var PLUGIN_CONFIG_PATH =
|
|
48449
|
-
var PROMPTS_DIR =
|
|
48877
|
+
var OPENCODE_CONFIG_PATH = path38.join(CONFIG_DIR, "opencode.json");
|
|
48878
|
+
var PLUGIN_CONFIG_PATH = path38.join(CONFIG_DIR, "opencode-swarm.json");
|
|
48879
|
+
var PROMPTS_DIR = path38.join(CONFIG_DIR, "opencode-swarm");
|
|
48450
48880
|
var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
|
|
48451
48881
|
var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
|
|
48452
48882
|
function isSafeCachePath(p) {
|
|
48453
|
-
const resolved =
|
|
48454
|
-
const home =
|
|
48883
|
+
const resolved = path38.resolve(p);
|
|
48884
|
+
const home = path38.resolve(os7.homedir());
|
|
48455
48885
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
48456
48886
|
return false;
|
|
48457
48887
|
}
|
|
48458
|
-
const segments = resolved.split(
|
|
48888
|
+
const segments = resolved.split(path38.sep).filter((s) => s.length > 0);
|
|
48459
48889
|
if (segments.length < 4) {
|
|
48460
48890
|
return false;
|
|
48461
48891
|
}
|
|
48462
|
-
const leaf =
|
|
48892
|
+
const leaf = path38.basename(resolved);
|
|
48463
48893
|
if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
|
|
48464
48894
|
return false;
|
|
48465
48895
|
}
|
|
48466
|
-
const parent =
|
|
48896
|
+
const parent = path38.basename(path38.dirname(resolved));
|
|
48467
48897
|
if (parent !== "packages" && parent !== "node_modules") {
|
|
48468
48898
|
return false;
|
|
48469
48899
|
}
|
|
48470
|
-
const grandparent =
|
|
48900
|
+
const grandparent = path38.basename(path38.dirname(path38.dirname(resolved)));
|
|
48471
48901
|
if (grandparent !== "opencode") {
|
|
48472
48902
|
return false;
|
|
48473
48903
|
}
|
|
48474
48904
|
return true;
|
|
48475
48905
|
}
|
|
48476
48906
|
function isSafeLockFilePath(p) {
|
|
48477
|
-
const resolved =
|
|
48478
|
-
const home =
|
|
48907
|
+
const resolved = path38.resolve(p);
|
|
48908
|
+
const home = path38.resolve(os7.homedir());
|
|
48479
48909
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
48480
48910
|
return false;
|
|
48481
48911
|
}
|
|
48482
|
-
const segments = resolved.split(
|
|
48912
|
+
const segments = resolved.split(path38.sep).filter((s) => s.length > 0);
|
|
48483
48913
|
if (segments.length < 4) {
|
|
48484
48914
|
return false;
|
|
48485
48915
|
}
|
|
48486
|
-
const leaf =
|
|
48916
|
+
const leaf = path38.basename(resolved);
|
|
48487
48917
|
if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
|
|
48488
48918
|
return false;
|
|
48489
48919
|
}
|
|
48490
|
-
const parent =
|
|
48920
|
+
const parent = path38.basename(path38.dirname(resolved));
|
|
48491
48921
|
if (parent !== "opencode") {
|
|
48492
48922
|
return false;
|
|
48493
48923
|
}
|
|
48494
48924
|
return true;
|
|
48495
48925
|
}
|
|
48496
48926
|
function ensureDir(dir) {
|
|
48497
|
-
if (!
|
|
48498
|
-
|
|
48927
|
+
if (!fs23.existsSync(dir)) {
|
|
48928
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
48499
48929
|
}
|
|
48500
48930
|
}
|
|
48501
48931
|
function loadJson(filepath) {
|
|
48502
48932
|
try {
|
|
48503
|
-
const content =
|
|
48933
|
+
const content = fs23.readFileSync(filepath, "utf-8");
|
|
48504
48934
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
48505
48935
|
return JSON.parse(stripped);
|
|
48506
48936
|
} catch {
|
|
@@ -48508,14 +48938,14 @@ function loadJson(filepath) {
|
|
|
48508
48938
|
}
|
|
48509
48939
|
}
|
|
48510
48940
|
function saveJson(filepath, data) {
|
|
48511
|
-
|
|
48941
|
+
fs23.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
48512
48942
|
`, "utf-8");
|
|
48513
48943
|
}
|
|
48514
48944
|
function writeProjectConfigIfMissing(cwd) {
|
|
48515
48945
|
try {
|
|
48516
|
-
const opencodeDir =
|
|
48517
|
-
const projectConfigPath =
|
|
48518
|
-
if (
|
|
48946
|
+
const opencodeDir = path38.join(cwd, ".opencode");
|
|
48947
|
+
const projectConfigPath = path38.join(opencodeDir, "opencode-swarm.json");
|
|
48948
|
+
if (fs23.existsSync(projectConfigPath)) {
|
|
48519
48949
|
return;
|
|
48520
48950
|
}
|
|
48521
48951
|
ensureDir(opencodeDir);
|
|
@@ -48535,7 +48965,7 @@ async function install() {
|
|
|
48535
48965
|
`);
|
|
48536
48966
|
ensureDir(CONFIG_DIR);
|
|
48537
48967
|
ensureDir(PROMPTS_DIR);
|
|
48538
|
-
const LEGACY_CONFIG_PATH =
|
|
48968
|
+
const LEGACY_CONFIG_PATH = path38.join(CONFIG_DIR, "config.json");
|
|
48539
48969
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
48540
48970
|
if (!opencodeConfig) {
|
|
48541
48971
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -48582,7 +49012,7 @@ async function install() {
|
|
|
48582
49012
|
console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
|
|
48583
49013
|
${failed}`);
|
|
48584
49014
|
}
|
|
48585
|
-
if (!
|
|
49015
|
+
if (!fs23.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
48586
49016
|
const defaultConfig = {
|
|
48587
49017
|
agents: { ...DEFAULT_AGENT_CONFIGS },
|
|
48588
49018
|
max_iterations: 5
|
|
@@ -48661,14 +49091,14 @@ function evictPluginCaches() {
|
|
|
48661
49091
|
const cleared = [];
|
|
48662
49092
|
const failed = [];
|
|
48663
49093
|
for (const cachePath of OPENCODE_PLUGIN_CACHE_PATHS) {
|
|
48664
|
-
if (!
|
|
49094
|
+
if (!fs23.existsSync(cachePath))
|
|
48665
49095
|
continue;
|
|
48666
49096
|
if (!isSafeCachePath(cachePath)) {
|
|
48667
49097
|
failed.push(`${cachePath} (refused: failed safety check)`);
|
|
48668
49098
|
continue;
|
|
48669
49099
|
}
|
|
48670
49100
|
try {
|
|
48671
|
-
|
|
49101
|
+
fs23.rmSync(cachePath, { recursive: true, force: true });
|
|
48672
49102
|
cleared.push(cachePath);
|
|
48673
49103
|
} catch (err) {
|
|
48674
49104
|
failed.push(`${cachePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -48680,14 +49110,14 @@ function evictLockFiles() {
|
|
|
48680
49110
|
const cleared = [];
|
|
48681
49111
|
const failed = [];
|
|
48682
49112
|
for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
|
|
48683
|
-
if (!
|
|
49113
|
+
if (!fs23.existsSync(lockPath))
|
|
48684
49114
|
continue;
|
|
48685
49115
|
if (!isSafeLockFilePath(lockPath)) {
|
|
48686
49116
|
failed.push(`${lockPath} (refused: failed safety check)`);
|
|
48687
49117
|
continue;
|
|
48688
49118
|
}
|
|
48689
49119
|
try {
|
|
48690
|
-
|
|
49120
|
+
fs23.unlinkSync(lockPath);
|
|
48691
49121
|
cleared.push(lockPath);
|
|
48692
49122
|
} catch (err) {
|
|
48693
49123
|
const code = err?.code;
|
|
@@ -48706,7 +49136,7 @@ async function uninstall() {
|
|
|
48706
49136
|
`);
|
|
48707
49137
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
48708
49138
|
if (!opencodeConfig) {
|
|
48709
|
-
if (
|
|
49139
|
+
if (fs23.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
48710
49140
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
48711
49141
|
return 1;
|
|
48712
49142
|
} else {
|
|
@@ -48738,13 +49168,13 @@ async function uninstall() {
|
|
|
48738
49168
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
48739
49169
|
if (process.argv.includes("--clean")) {
|
|
48740
49170
|
let cleaned = false;
|
|
48741
|
-
if (
|
|
48742
|
-
|
|
49171
|
+
if (fs23.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
49172
|
+
fs23.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
48743
49173
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
48744
49174
|
cleaned = true;
|
|
48745
49175
|
}
|
|
48746
|
-
if (
|
|
48747
|
-
|
|
49176
|
+
if (fs23.existsSync(PROMPTS_DIR)) {
|
|
49177
|
+
fs23.rmSync(PROMPTS_DIR, { recursive: true });
|
|
48748
49178
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
48749
49179
|
cleaned = true;
|
|
48750
49180
|
}
|