opencode-swarm 6.85.3 → 6.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +357 -408
- package/dist/config/schema.d.ts +4 -0
- package/dist/index.js +1040 -1081
- package/dist/tools/batch-symbols.d.ts +1 -1
- package/dist/tools/build-check.d.ts +1 -1
- package/dist/tools/check-gate-status.d.ts +1 -1
- package/dist/tools/checkpoint.d.ts +1 -1
- package/dist/tools/co-change-analyzer.d.ts +1 -1
- package/dist/tools/completion-verify.d.ts +1 -1
- package/dist/tools/complexity-hotspots.d.ts +1 -1
- package/dist/tools/convene-council.d.ts +1 -1
- package/dist/tools/convene-general-council.d.ts +1 -1
- package/dist/tools/create-tool.d.ts +9 -1
- package/dist/tools/declare-council-criteria.d.ts +1 -1
- package/dist/tools/declare-scope.d.ts +1 -1
- package/dist/tools/domain-detector.d.ts +2 -2
- package/dist/tools/evidence-check.d.ts +1 -1
- package/dist/tools/file-extractor.d.ts +1 -1
- package/dist/tools/get-approved-plan.d.ts +1 -1
- package/dist/tools/knowledge-query.d.ts +1 -1
- package/dist/tools/lint.d.ts +1 -1
- package/dist/tools/pkg-audit.d.ts +1 -1
- package/dist/tools/placeholder-scan.d.ts +1 -1
- package/dist/tools/pre-check-batch.d.ts +1 -1
- package/dist/tools/quality-budget.d.ts +1 -1
- package/dist/tools/req-coverage.d.ts +1 -1
- package/dist/tools/save-plan.d.ts +1 -1
- package/dist/tools/sbom-generate.d.ts +1 -1
- package/dist/tools/schema-drift.d.ts +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/set-qa-gates.d.ts +1 -1
- package/dist/tools/suggest-patch.d.ts +1 -1
- package/dist/tools/symbols.d.ts +1 -1
- package/dist/tools/syntax-check.d.ts +1 -1
- package/dist/tools/test-runner.d.ts +1 -1
- package/dist/tools/todo-extract.d.ts +1 -1
- package/dist/tools/update-task-status.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools/write-drift-evidence.d.ts +1 -1
- package/dist/tools/write-hallucination-evidence.d.ts +1 -1
- package/dist/tools/write-mutation-evidence.d.ts +1 -1
- package/dist/tools/write-retro.d.ts +1 -1
- package/package.json +1 -1
- package/dist/knowledge/hive-promoter.d.ts +0 -23
package/dist/cli/index.js
CHANGED
|
@@ -17611,13 +17611,13 @@ __export(exports_config_doctor, {
|
|
|
17611
17611
|
import * as crypto3 from "crypto";
|
|
17612
17612
|
import * as fs9 from "fs";
|
|
17613
17613
|
import * as os5 from "os";
|
|
17614
|
-
import * as
|
|
17614
|
+
import * as path19 from "path";
|
|
17615
17615
|
function getUserConfigDir3() {
|
|
17616
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
17616
|
+
return process.env.XDG_CONFIG_HOME || path19.join(os5.homedir(), ".config");
|
|
17617
17617
|
}
|
|
17618
17618
|
function getConfigPaths(directory) {
|
|
17619
|
-
const userConfigPath =
|
|
17620
|
-
const projectConfigPath =
|
|
17619
|
+
const userConfigPath = path19.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
|
|
17620
|
+
const projectConfigPath = path19.join(directory, ".opencode", "opencode-swarm.json");
|
|
17621
17621
|
return { userConfigPath, projectConfigPath };
|
|
17622
17622
|
}
|
|
17623
17623
|
function computeHash(content) {
|
|
@@ -17642,9 +17642,9 @@ function isValidConfigPath(configPath, directory) {
|
|
|
17642
17642
|
const normalizedUser = userConfigPath.replace(/\\/g, "/");
|
|
17643
17643
|
const normalizedProject = projectConfigPath.replace(/\\/g, "/");
|
|
17644
17644
|
try {
|
|
17645
|
-
const resolvedConfig =
|
|
17646
|
-
const resolvedUser =
|
|
17647
|
-
const resolvedProject =
|
|
17645
|
+
const resolvedConfig = path19.resolve(configPath);
|
|
17646
|
+
const resolvedUser = path19.resolve(normalizedUser);
|
|
17647
|
+
const resolvedProject = path19.resolve(normalizedProject);
|
|
17648
17648
|
return resolvedConfig === resolvedUser || resolvedConfig === resolvedProject;
|
|
17649
17649
|
} catch {
|
|
17650
17650
|
return false;
|
|
@@ -17684,12 +17684,12 @@ function createConfigBackup(directory) {
|
|
|
17684
17684
|
};
|
|
17685
17685
|
}
|
|
17686
17686
|
function writeBackupArtifact(directory, backup) {
|
|
17687
|
-
const swarmDir =
|
|
17687
|
+
const swarmDir = path19.join(directory, ".swarm");
|
|
17688
17688
|
if (!fs9.existsSync(swarmDir)) {
|
|
17689
17689
|
fs9.mkdirSync(swarmDir, { recursive: true });
|
|
17690
17690
|
}
|
|
17691
17691
|
const backupFilename = `config-backup-${backup.createdAt}.json`;
|
|
17692
|
-
const backupPath =
|
|
17692
|
+
const backupPath = path19.join(swarmDir, backupFilename);
|
|
17693
17693
|
const artifact = {
|
|
17694
17694
|
createdAt: backup.createdAt,
|
|
17695
17695
|
configPath: backup.configPath,
|
|
@@ -17719,7 +17719,7 @@ function restoreFromBackup(backupPath, directory) {
|
|
|
17719
17719
|
return null;
|
|
17720
17720
|
}
|
|
17721
17721
|
const targetPath = artifact.configPath;
|
|
17722
|
-
const targetDir =
|
|
17722
|
+
const targetDir = path19.dirname(targetPath);
|
|
17723
17723
|
if (!fs9.existsSync(targetDir)) {
|
|
17724
17724
|
fs9.mkdirSync(targetDir, { recursive: true });
|
|
17725
17725
|
}
|
|
@@ -17750,9 +17750,9 @@ function readConfigFromFile(directory) {
|
|
|
17750
17750
|
return null;
|
|
17751
17751
|
}
|
|
17752
17752
|
}
|
|
17753
|
-
function validateConfigKey(
|
|
17753
|
+
function validateConfigKey(path20, value, _config) {
|
|
17754
17754
|
const findings = [];
|
|
17755
|
-
switch (
|
|
17755
|
+
switch (path20) {
|
|
17756
17756
|
case "agents": {
|
|
17757
17757
|
if (value !== undefined) {
|
|
17758
17758
|
findings.push({
|
|
@@ -17999,27 +17999,27 @@ function validateConfigKey(path19, value, _config) {
|
|
|
17999
17999
|
}
|
|
18000
18000
|
return findings;
|
|
18001
18001
|
}
|
|
18002
|
-
function walkConfigAndValidate(obj,
|
|
18002
|
+
function walkConfigAndValidate(obj, path20, config3, findings) {
|
|
18003
18003
|
if (obj === null || obj === undefined) {
|
|
18004
18004
|
return;
|
|
18005
18005
|
}
|
|
18006
|
-
if (
|
|
18007
|
-
const keyFindings = validateConfigKey(
|
|
18006
|
+
if (path20 && typeof obj === "object" && !Array.isArray(obj)) {
|
|
18007
|
+
const keyFindings = validateConfigKey(path20, obj, config3);
|
|
18008
18008
|
findings.push(...keyFindings);
|
|
18009
18009
|
}
|
|
18010
18010
|
if (typeof obj !== "object") {
|
|
18011
|
-
const keyFindings = validateConfigKey(
|
|
18011
|
+
const keyFindings = validateConfigKey(path20, obj, config3);
|
|
18012
18012
|
findings.push(...keyFindings);
|
|
18013
18013
|
return;
|
|
18014
18014
|
}
|
|
18015
18015
|
if (Array.isArray(obj)) {
|
|
18016
18016
|
obj.forEach((item, index) => {
|
|
18017
|
-
walkConfigAndValidate(item, `${
|
|
18017
|
+
walkConfigAndValidate(item, `${path20}[${index}]`, config3, findings);
|
|
18018
18018
|
});
|
|
18019
18019
|
return;
|
|
18020
18020
|
}
|
|
18021
18021
|
for (const [key, value] of Object.entries(obj)) {
|
|
18022
|
-
const newPath =
|
|
18022
|
+
const newPath = path20 ? `${path20}.${key}` : key;
|
|
18023
18023
|
walkConfigAndValidate(value, newPath, config3, findings);
|
|
18024
18024
|
}
|
|
18025
18025
|
}
|
|
@@ -18139,7 +18139,7 @@ function applySafeAutoFixes(directory, result) {
|
|
|
18139
18139
|
}
|
|
18140
18140
|
}
|
|
18141
18141
|
if (appliedFixes.length > 0) {
|
|
18142
|
-
const configDir =
|
|
18142
|
+
const configDir = path19.dirname(configPath);
|
|
18143
18143
|
if (!fs9.existsSync(configDir)) {
|
|
18144
18144
|
fs9.mkdirSync(configDir, { recursive: true });
|
|
18145
18145
|
}
|
|
@@ -18149,12 +18149,12 @@ function applySafeAutoFixes(directory, result) {
|
|
|
18149
18149
|
return { appliedFixes, updatedConfigPath };
|
|
18150
18150
|
}
|
|
18151
18151
|
function writeDoctorArtifact(directory, result) {
|
|
18152
|
-
const swarmDir =
|
|
18152
|
+
const swarmDir = path19.join(directory, ".swarm");
|
|
18153
18153
|
if (!fs9.existsSync(swarmDir)) {
|
|
18154
18154
|
fs9.mkdirSync(swarmDir, { recursive: true });
|
|
18155
18155
|
}
|
|
18156
18156
|
const artifactFilename = "config-doctor.json";
|
|
18157
|
-
const artifactPath =
|
|
18157
|
+
const artifactPath = path19.join(swarmDir, artifactFilename);
|
|
18158
18158
|
const guiOutput = {
|
|
18159
18159
|
timestamp: result.timestamp,
|
|
18160
18160
|
summary: result.summary,
|
|
@@ -18515,8 +18515,8 @@ var init_evidence_summary_service = __esm(() => {
|
|
|
18515
18515
|
});
|
|
18516
18516
|
|
|
18517
18517
|
// src/cli/index.ts
|
|
18518
|
-
import * as
|
|
18519
|
-
import * as
|
|
18518
|
+
import * as fs22 from "fs";
|
|
18519
|
+
import * as os6 from "os";
|
|
18520
18520
|
import * as path33 from "path";
|
|
18521
18521
|
|
|
18522
18522
|
// src/commands/acknowledge-spec-drift.ts
|
|
@@ -18973,6 +18973,7 @@ for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
|
|
|
18973
18973
|
// src/config/schema.ts
|
|
18974
18974
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
18975
18975
|
model: exports_external.string().optional(),
|
|
18976
|
+
variant: exports_external.string().min(1).optional(),
|
|
18976
18977
|
temperature: exports_external.number().min(0).max(2).optional(),
|
|
18977
18978
|
disabled: exports_external.boolean().optional(),
|
|
18978
18979
|
fallback_models: exports_external.array(exports_external.string()).max(3).optional()
|
|
@@ -20842,6 +20843,7 @@ async function handleBrainstormCommand(_directory, args) {
|
|
|
20842
20843
|
init_zod();
|
|
20843
20844
|
|
|
20844
20845
|
// src/tools/checkpoint.ts
|
|
20846
|
+
init_zod();
|
|
20845
20847
|
import * as child_process from "child_process";
|
|
20846
20848
|
import * as fs6 from "fs";
|
|
20847
20849
|
import * as path8 from "path";
|
|
@@ -33184,7 +33186,8 @@ function createSwarmTool(opts) {
|
|
|
33184
33186
|
execute: async (args, ctx) => {
|
|
33185
33187
|
const directory = ctx?.directory ?? process.cwd();
|
|
33186
33188
|
try {
|
|
33187
|
-
|
|
33189
|
+
const result = await opts.execute(args, directory, ctx);
|
|
33190
|
+
return result;
|
|
33188
33191
|
} catch (error93) {
|
|
33189
33192
|
const message = error93 instanceof Error ? error93.message : String(error93);
|
|
33190
33193
|
return JSON.stringify({
|
|
@@ -33410,8 +33413,8 @@ function handleDelete(label, directory) {
|
|
|
33410
33413
|
var checkpoint = createSwarmTool({
|
|
33411
33414
|
description: "Save, restore, list, and delete git checkpoints. " + "Use save to create a named snapshot, restore to return to a checkpoint (soft reset), " + "list to see all checkpoints, and delete to remove a checkpoint from the log. " + "Git commits are preserved on delete.",
|
|
33412
33415
|
args: {
|
|
33413
|
-
action:
|
|
33414
|
-
label:
|
|
33416
|
+
action: exports_external.string().describe("Action to perform: save, restore, list, or delete"),
|
|
33417
|
+
label: exports_external.string().optional().describe("Checkpoint label (required for save, restore, delete)")
|
|
33415
33418
|
},
|
|
33416
33419
|
execute: async (args, directory) => {
|
|
33417
33420
|
if (!isGitRepo()) {
|
|
@@ -33485,7 +33488,8 @@ var CheckpointResultSchema = exports_external.object({
|
|
|
33485
33488
|
checkpoints: exports_external.array(exports_external.unknown()).optional()
|
|
33486
33489
|
}).passthrough();
|
|
33487
33490
|
function safeParseResult(result) {
|
|
33488
|
-
const
|
|
33491
|
+
const jsonStr = typeof result === "string" ? result : result.output;
|
|
33492
|
+
const parsed = CheckpointResultSchema.safeParse(JSON.parse(jsonStr));
|
|
33489
33493
|
if (!parsed.success) {
|
|
33490
33494
|
return {
|
|
33491
33495
|
success: false,
|
|
@@ -33671,7 +33675,7 @@ function resolveSwarmRejectedPath(directory) {
|
|
|
33671
33675
|
}
|
|
33672
33676
|
function resolveHiveKnowledgePath() {
|
|
33673
33677
|
const platform = process.platform;
|
|
33674
|
-
const home = os3.homedir();
|
|
33678
|
+
const home = process.env.HOME || os3.homedir();
|
|
33675
33679
|
let dataDir;
|
|
33676
33680
|
if (platform === "win32") {
|
|
33677
33681
|
dataDir = path9.join(process.env.LOCALAPPDATA || path9.join(home, "AppData", "Local"), "opencode-swarm", "Data");
|
|
@@ -34429,6 +34433,7 @@ async function flushPendingSnapshot(directory) {
|
|
|
34429
34433
|
}
|
|
34430
34434
|
|
|
34431
34435
|
// src/tools/write-retro.ts
|
|
34436
|
+
init_zod();
|
|
34432
34437
|
init_evidence_schema();
|
|
34433
34438
|
init_manager2();
|
|
34434
34439
|
async function executeWriteRetro(args, directory) {
|
|
@@ -34734,22 +34739,22 @@ async function executeWriteRetro(args, directory) {
|
|
|
34734
34739
|
var write_retro = createSwarmTool({
|
|
34735
34740
|
description: "Write a retrospective evidence bundle for a completed phase. " + "Accepts flat retro fields and writes a correctly-wrapped EvidenceBundle to " + ".swarm/evidence/retro-{phase}/evidence.json. " + "Use this instead of manually writing retro JSON to avoid schema validation failures in phase_complete.",
|
|
34736
34741
|
args: {
|
|
34737
|
-
phase:
|
|
34738
|
-
summary:
|
|
34739
|
-
task_count:
|
|
34740
|
-
task_complexity:
|
|
34741
|
-
total_tool_calls:
|
|
34742
|
-
coder_revisions:
|
|
34743
|
-
reviewer_rejections:
|
|
34744
|
-
loop_detections:
|
|
34745
|
-
circuit_breaker_trips:
|
|
34746
|
-
test_failures:
|
|
34747
|
-
security_findings:
|
|
34748
|
-
integration_issues:
|
|
34749
|
-
lessons_learned:
|
|
34750
|
-
top_rejection_reasons:
|
|
34751
|
-
task_id:
|
|
34752
|
-
metadata:
|
|
34742
|
+
phase: exports_external.number().int().min(1).max(99).describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
34743
|
+
summary: exports_external.string().describe("Human-readable summary of the phase"),
|
|
34744
|
+
task_count: exports_external.number().int().min(1).max(9999).describe("Count of tasks completed in this phase"),
|
|
34745
|
+
task_complexity: exports_external.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
|
|
34746
|
+
total_tool_calls: exports_external.number().int().min(0).max(9999).describe("Total number of tool calls in this phase"),
|
|
34747
|
+
coder_revisions: exports_external.number().int().min(0).max(999).describe("Number of coder revisions made"),
|
|
34748
|
+
reviewer_rejections: exports_external.number().int().min(0).max(999).describe("Number of reviewer rejections received"),
|
|
34749
|
+
loop_detections: exports_external.number().int().min(0).max(9999).optional().describe("Number of loop detection events in this phase"),
|
|
34750
|
+
circuit_breaker_trips: exports_external.number().int().min(0).max(9999).optional().describe("Number of circuit breaker trips in this phase"),
|
|
34751
|
+
test_failures: exports_external.number().int().min(0).max(9999).describe("Number of test failures encountered"),
|
|
34752
|
+
security_findings: exports_external.number().int().min(0).max(999).describe("Number of security findings"),
|
|
34753
|
+
integration_issues: exports_external.number().int().min(0).max(999).describe("Number of integration issues"),
|
|
34754
|
+
lessons_learned: exports_external.array(exports_external.string()).max(5).optional().describe("Key lessons learned from this phase (max 5)"),
|
|
34755
|
+
top_rejection_reasons: exports_external.array(exports_external.string()).optional().describe("Top reasons for reviewer rejections"),
|
|
34756
|
+
task_id: exports_external.string().optional().describe("Optional custom task ID (defaults to retro-{phase})"),
|
|
34757
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Optional additional metadata")
|
|
34753
34758
|
},
|
|
34754
34759
|
execute: async (args, directory) => {
|
|
34755
34760
|
const rawPhase = args.phase !== undefined ? Number(args.phase) : 0;
|
|
@@ -35346,6 +35351,9 @@ async function handleCouncilCommand(_directory, args) {
|
|
|
35346
35351
|
return `[${tokens.join(" ")}] ${question}`;
|
|
35347
35352
|
}
|
|
35348
35353
|
|
|
35354
|
+
// src/hooks/hive-promoter.ts
|
|
35355
|
+
import path15 from "path";
|
|
35356
|
+
|
|
35349
35357
|
// src/background/event-bus.ts
|
|
35350
35358
|
init_utils();
|
|
35351
35359
|
|
|
@@ -35567,6 +35575,86 @@ async function checkHivePromotions(swarmEntries, config3) {
|
|
|
35567
35575
|
total_hive_entries: hiveEntries.length
|
|
35568
35576
|
};
|
|
35569
35577
|
}
|
|
35578
|
+
async function promoteToHive(directory, lesson, category) {
|
|
35579
|
+
const trimmedLesson = lesson.trim();
|
|
35580
|
+
const hiveEntries = await readKnowledge(resolveHiveKnowledgePath());
|
|
35581
|
+
const validationResult = validateLesson(trimmedLesson, hiveEntries.map((e) => e.lesson), {
|
|
35582
|
+
category: category || "process",
|
|
35583
|
+
scope: "global",
|
|
35584
|
+
confidence: 1
|
|
35585
|
+
});
|
|
35586
|
+
if (validationResult.severity === "error") {
|
|
35587
|
+
throw new Error(`Lesson rejected by validator: ${validationResult.reason}`);
|
|
35588
|
+
}
|
|
35589
|
+
if (findNearDuplicate(trimmedLesson, hiveEntries, 0.6)) {
|
|
35590
|
+
return `Lesson already exists in hive (near-duplicate).`;
|
|
35591
|
+
}
|
|
35592
|
+
const newHiveEntry = {
|
|
35593
|
+
id: crypto.randomUUID(),
|
|
35594
|
+
tier: "hive",
|
|
35595
|
+
lesson: trimmedLesson,
|
|
35596
|
+
category: category || "process",
|
|
35597
|
+
tags: [],
|
|
35598
|
+
scope: "global",
|
|
35599
|
+
confidence: 1,
|
|
35600
|
+
status: "promoted",
|
|
35601
|
+
confirmed_by: [],
|
|
35602
|
+
retrieval_outcomes: {
|
|
35603
|
+
applied_count: 0,
|
|
35604
|
+
succeeded_after_count: 0,
|
|
35605
|
+
failed_after_count: 0
|
|
35606
|
+
},
|
|
35607
|
+
schema_version: 1,
|
|
35608
|
+
created_at: new Date().toISOString(),
|
|
35609
|
+
updated_at: new Date().toISOString(),
|
|
35610
|
+
source_project: path15.basename(directory) || "unknown",
|
|
35611
|
+
encounter_score: 1
|
|
35612
|
+
};
|
|
35613
|
+
await appendKnowledge(resolveHiveKnowledgePath(), newHiveEntry);
|
|
35614
|
+
return `Promoted to hive: "${trimmedLesson.slice(0, 50)}${trimmedLesson.length > 50 ? "..." : ""}" (confidence: 1.0, source: manual)`;
|
|
35615
|
+
}
|
|
35616
|
+
async function promoteFromSwarm(directory, lessonId) {
|
|
35617
|
+
const swarmEntries = await readKnowledge(resolveSwarmKnowledgePath(directory));
|
|
35618
|
+
const swarmEntry = swarmEntries.find((e) => e.id === lessonId);
|
|
35619
|
+
if (!swarmEntry) {
|
|
35620
|
+
throw new Error(`Lesson ${lessonId} not found in .swarm/knowledge.jsonl`);
|
|
35621
|
+
}
|
|
35622
|
+
const hiveEntries = await readKnowledge(resolveHiveKnowledgePath());
|
|
35623
|
+
const validationResult = validateLesson(swarmEntry.lesson, hiveEntries.map((e) => e.lesson), {
|
|
35624
|
+
category: swarmEntry.category,
|
|
35625
|
+
scope: swarmEntry.scope,
|
|
35626
|
+
confidence: swarmEntry.confidence
|
|
35627
|
+
});
|
|
35628
|
+
if (validationResult.severity === "error") {
|
|
35629
|
+
throw new Error(`Lesson rejected by validator: ${validationResult.reason}`);
|
|
35630
|
+
}
|
|
35631
|
+
if (findNearDuplicate(swarmEntry.lesson, hiveEntries, 0.6)) {
|
|
35632
|
+
return `Lesson already exists in hive (near-duplicate).`;
|
|
35633
|
+
}
|
|
35634
|
+
const newHiveEntry = {
|
|
35635
|
+
id: crypto.randomUUID(),
|
|
35636
|
+
tier: "hive",
|
|
35637
|
+
lesson: swarmEntry.lesson,
|
|
35638
|
+
category: swarmEntry.category,
|
|
35639
|
+
tags: swarmEntry.tags,
|
|
35640
|
+
scope: swarmEntry.scope,
|
|
35641
|
+
confidence: 1,
|
|
35642
|
+
status: "promoted",
|
|
35643
|
+
confirmed_by: [],
|
|
35644
|
+
retrieval_outcomes: {
|
|
35645
|
+
applied_count: 0,
|
|
35646
|
+
succeeded_after_count: 0,
|
|
35647
|
+
failed_after_count: 0
|
|
35648
|
+
},
|
|
35649
|
+
schema_version: 1,
|
|
35650
|
+
created_at: new Date().toISOString(),
|
|
35651
|
+
updated_at: new Date().toISOString(),
|
|
35652
|
+
source_project: swarmEntry.project_name,
|
|
35653
|
+
encounter_score: 1
|
|
35654
|
+
};
|
|
35655
|
+
await appendKnowledge(resolveHiveKnowledgePath(), newHiveEntry);
|
|
35656
|
+
return `Promoted lesson ${lessonId} from swarm to hive: "${swarmEntry.lesson.slice(0, 50)}${swarmEntry.lesson.length > 50 ? "..." : ""}"`;
|
|
35657
|
+
}
|
|
35570
35658
|
|
|
35571
35659
|
// src/commands/curate.ts
|
|
35572
35660
|
async function handleCurateCommand(directory, _args) {
|
|
@@ -35597,13 +35685,14 @@ function formatCurationSummary(summary) {
|
|
|
35597
35685
|
}
|
|
35598
35686
|
|
|
35599
35687
|
// src/commands/dark-matter.ts
|
|
35600
|
-
import
|
|
35688
|
+
import path17 from "path";
|
|
35601
35689
|
|
|
35602
35690
|
// src/tools/co-change-analyzer.ts
|
|
35691
|
+
init_zod();
|
|
35603
35692
|
import * as child_process3 from "child_process";
|
|
35604
35693
|
import { randomUUID } from "crypto";
|
|
35605
35694
|
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
35606
|
-
import * as
|
|
35695
|
+
import * as path16 from "path";
|
|
35607
35696
|
import { promisify } from "util";
|
|
35608
35697
|
function getExecFileAsync() {
|
|
35609
35698
|
return promisify(child_process3.execFile);
|
|
@@ -35705,7 +35794,7 @@ async function scanSourceFiles(dir) {
|
|
|
35705
35794
|
try {
|
|
35706
35795
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
35707
35796
|
for (const entry of entries) {
|
|
35708
|
-
const fullPath =
|
|
35797
|
+
const fullPath = path16.join(dir, entry.name);
|
|
35709
35798
|
if (entry.isDirectory()) {
|
|
35710
35799
|
if (skipDirs.has(entry.name)) {
|
|
35711
35800
|
continue;
|
|
@@ -35713,7 +35802,7 @@ async function scanSourceFiles(dir) {
|
|
|
35713
35802
|
const subFiles = await scanSourceFiles(fullPath);
|
|
35714
35803
|
results.push(...subFiles);
|
|
35715
35804
|
} else if (entry.isFile()) {
|
|
35716
|
-
const ext =
|
|
35805
|
+
const ext = path16.extname(entry.name);
|
|
35717
35806
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
|
|
35718
35807
|
results.push(fullPath);
|
|
35719
35808
|
}
|
|
@@ -35735,8 +35824,8 @@ async function getStaticEdges(directory) {
|
|
|
35735
35824
|
continue;
|
|
35736
35825
|
}
|
|
35737
35826
|
try {
|
|
35738
|
-
const sourceDir =
|
|
35739
|
-
const resolvedPath =
|
|
35827
|
+
const sourceDir = path16.dirname(sourceFile);
|
|
35828
|
+
const resolvedPath = path16.resolve(sourceDir, importPath);
|
|
35740
35829
|
const extensions = [
|
|
35741
35830
|
"",
|
|
35742
35831
|
".ts",
|
|
@@ -35761,8 +35850,8 @@ async function getStaticEdges(directory) {
|
|
|
35761
35850
|
if (!targetFile) {
|
|
35762
35851
|
continue;
|
|
35763
35852
|
}
|
|
35764
|
-
const relSource =
|
|
35765
|
-
const relTarget =
|
|
35853
|
+
const relSource = path16.relative(directory, sourceFile).replace(/\\/g, "/");
|
|
35854
|
+
const relTarget = path16.relative(directory, targetFile).replace(/\\/g, "/");
|
|
35766
35855
|
const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
|
|
35767
35856
|
edges.add(key);
|
|
35768
35857
|
} catch {}
|
|
@@ -35774,7 +35863,7 @@ async function getStaticEdges(directory) {
|
|
|
35774
35863
|
function isTestImplementationPair(fileA, fileB) {
|
|
35775
35864
|
const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
|
|
35776
35865
|
const getBaseName = (filePath) => {
|
|
35777
|
-
const base =
|
|
35866
|
+
const base = path16.basename(filePath);
|
|
35778
35867
|
for (const pattern of testPatterns) {
|
|
35779
35868
|
if (base.endsWith(pattern)) {
|
|
35780
35869
|
return base.slice(0, -pattern.length);
|
|
@@ -35784,16 +35873,16 @@ function isTestImplementationPair(fileA, fileB) {
|
|
|
35784
35873
|
};
|
|
35785
35874
|
const baseA = getBaseName(fileA);
|
|
35786
35875
|
const baseB = getBaseName(fileB);
|
|
35787
|
-
return baseA === baseB && baseA !==
|
|
35876
|
+
return baseA === baseB && baseA !== path16.basename(fileA) && baseA !== path16.basename(fileB);
|
|
35788
35877
|
}
|
|
35789
35878
|
function hasSharedPrefix(fileA, fileB) {
|
|
35790
|
-
const dirA =
|
|
35791
|
-
const dirB =
|
|
35879
|
+
const dirA = path16.dirname(fileA);
|
|
35880
|
+
const dirB = path16.dirname(fileB);
|
|
35792
35881
|
if (dirA !== dirB) {
|
|
35793
35882
|
return false;
|
|
35794
35883
|
}
|
|
35795
|
-
const baseA =
|
|
35796
|
-
const baseB =
|
|
35884
|
+
const baseA = path16.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
35885
|
+
const baseB = path16.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
|
|
35797
35886
|
if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
|
|
35798
35887
|
return true;
|
|
35799
35888
|
}
|
|
@@ -35847,8 +35936,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
|
|
|
35847
35936
|
const entries = [];
|
|
35848
35937
|
const now = new Date().toISOString();
|
|
35849
35938
|
for (const pair of pairs.slice(0, 10)) {
|
|
35850
|
-
const baseA =
|
|
35851
|
-
const baseB =
|
|
35939
|
+
const baseA = path16.basename(pair.fileA);
|
|
35940
|
+
const baseB = path16.basename(pair.fileB);
|
|
35852
35941
|
let lesson = `Files ${pair.fileA} and ${pair.fileB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
|
|
35853
35942
|
if (lesson.length > 280) {
|
|
35854
35943
|
lesson = `Files ${baseA} and ${baseB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
|
|
@@ -35906,10 +35995,10 @@ Consider adding explicit documentation or extracting the shared concern.`;
|
|
|
35906
35995
|
var co_change_analyzer = createSwarmTool({
|
|
35907
35996
|
description: "Detects hidden couplings (dark matter) by analyzing git history to find file pairs that frequently co-change but have no import relationship. Useful for identifying architectural concerns that are not explicitly documented.",
|
|
35908
35997
|
args: {
|
|
35909
|
-
min_commits:
|
|
35910
|
-
min_co_changes:
|
|
35911
|
-
threshold:
|
|
35912
|
-
max_commits:
|
|
35998
|
+
min_commits: exports_external.number().optional().describe("Minimum commit count to analyze (default: 20)"),
|
|
35999
|
+
min_co_changes: exports_external.number().optional().describe("Minimum co-change count to consider (default: 3)"),
|
|
36000
|
+
threshold: exports_external.number().optional().describe("NPMI threshold for filtering (default: 0.5)"),
|
|
36001
|
+
max_commits: exports_external.number().optional().describe("Maximum commits to analyze (default: 500)")
|
|
35913
36002
|
},
|
|
35914
36003
|
async execute(args, directory) {
|
|
35915
36004
|
let minCommits;
|
|
@@ -35958,7 +36047,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
35958
36047
|
const output = formatDarkMatterOutput(pairs);
|
|
35959
36048
|
if (pairs.length > 0) {
|
|
35960
36049
|
try {
|
|
35961
|
-
const projectName =
|
|
36050
|
+
const projectName = path17.basename(path17.resolve(directory));
|
|
35962
36051
|
const entries = darkMatterToKnowledgeEntries(pairs, projectName);
|
|
35963
36052
|
if (entries.length > 0) {
|
|
35964
36053
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
@@ -35980,7 +36069,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
35980
36069
|
// src/services/diagnose-service.ts
|
|
35981
36070
|
import * as child_process4 from "child_process";
|
|
35982
36071
|
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync5 } from "fs";
|
|
35983
|
-
import
|
|
36072
|
+
import path18 from "path";
|
|
35984
36073
|
import { fileURLToPath } from "url";
|
|
35985
36074
|
init_manager2();
|
|
35986
36075
|
init_utils2();
|
|
@@ -36280,7 +36369,7 @@ async function checkSpecStaleness(directory, plan) {
|
|
|
36280
36369
|
};
|
|
36281
36370
|
}
|
|
36282
36371
|
async function checkConfigParseability(directory) {
|
|
36283
|
-
const configPath =
|
|
36372
|
+
const configPath = path18.join(directory, ".opencode/opencode-swarm.json");
|
|
36284
36373
|
if (!existsSync8(configPath)) {
|
|
36285
36374
|
return {
|
|
36286
36375
|
name: "Config Parseability",
|
|
@@ -36309,7 +36398,7 @@ function resolveGrammarDir(thisDir) {
|
|
|
36309
36398
|
const normalized = thisDir.replace(/\\/g, "/");
|
|
36310
36399
|
const isSource = normalized.endsWith("/src/services");
|
|
36311
36400
|
const isCliBundle = normalized.endsWith("/cli");
|
|
36312
|
-
return isSource || isCliBundle ?
|
|
36401
|
+
return isSource || isCliBundle ? path18.join(thisDir, "..", "lang", "grammars") : path18.join(thisDir, "lang", "grammars");
|
|
36313
36402
|
}
|
|
36314
36403
|
async function checkGrammarWasmFiles() {
|
|
36315
36404
|
const grammarFiles = [
|
|
@@ -36333,14 +36422,14 @@ async function checkGrammarWasmFiles() {
|
|
|
36333
36422
|
"tree-sitter-ini.wasm",
|
|
36334
36423
|
"tree-sitter-regex.wasm"
|
|
36335
36424
|
];
|
|
36336
|
-
const thisDir =
|
|
36425
|
+
const thisDir = path18.dirname(fileURLToPath(import.meta.url));
|
|
36337
36426
|
const grammarDir = resolveGrammarDir(thisDir);
|
|
36338
36427
|
const missing = [];
|
|
36339
|
-
if (!existsSync8(
|
|
36428
|
+
if (!existsSync8(path18.join(grammarDir, "tree-sitter.wasm"))) {
|
|
36340
36429
|
missing.push("tree-sitter.wasm (core runtime)");
|
|
36341
36430
|
}
|
|
36342
36431
|
for (const file3 of grammarFiles) {
|
|
36343
|
-
if (!existsSync8(
|
|
36432
|
+
if (!existsSync8(path18.join(grammarDir, file3))) {
|
|
36344
36433
|
missing.push(file3);
|
|
36345
36434
|
}
|
|
36346
36435
|
}
|
|
@@ -36358,7 +36447,7 @@ async function checkGrammarWasmFiles() {
|
|
|
36358
36447
|
};
|
|
36359
36448
|
}
|
|
36360
36449
|
async function checkCheckpointManifest(directory) {
|
|
36361
|
-
const manifestPath =
|
|
36450
|
+
const manifestPath = path18.join(directory, ".swarm/checkpoints.json");
|
|
36362
36451
|
if (!existsSync8(manifestPath)) {
|
|
36363
36452
|
return {
|
|
36364
36453
|
name: "Checkpoint Manifest",
|
|
@@ -36410,7 +36499,7 @@ async function checkCheckpointManifest(directory) {
|
|
|
36410
36499
|
}
|
|
36411
36500
|
}
|
|
36412
36501
|
async function checkEventStreamIntegrity(directory) {
|
|
36413
|
-
const eventsPath =
|
|
36502
|
+
const eventsPath = path18.join(directory, ".swarm/events.jsonl");
|
|
36414
36503
|
if (!existsSync8(eventsPath)) {
|
|
36415
36504
|
return {
|
|
36416
36505
|
name: "Event Stream",
|
|
@@ -36451,7 +36540,7 @@ async function checkEventStreamIntegrity(directory) {
|
|
|
36451
36540
|
}
|
|
36452
36541
|
}
|
|
36453
36542
|
async function checkSteeringDirectives(directory) {
|
|
36454
|
-
const eventsPath =
|
|
36543
|
+
const eventsPath = path18.join(directory, ".swarm/events.jsonl");
|
|
36455
36544
|
if (!existsSync8(eventsPath)) {
|
|
36456
36545
|
return {
|
|
36457
36546
|
name: "Steering Directives",
|
|
@@ -36507,7 +36596,7 @@ async function checkCurator(directory) {
|
|
|
36507
36596
|
detail: "Disabled (enable via curator.enabled)"
|
|
36508
36597
|
};
|
|
36509
36598
|
}
|
|
36510
|
-
const summaryPath =
|
|
36599
|
+
const summaryPath = path18.join(directory, ".swarm/curator-summary.json");
|
|
36511
36600
|
if (!existsSync8(summaryPath)) {
|
|
36512
36601
|
return {
|
|
36513
36602
|
name: "Curator",
|
|
@@ -36655,7 +36744,7 @@ async function getDiagnoseData(directory) {
|
|
|
36655
36744
|
checks5.push(await checkSteeringDirectives(directory));
|
|
36656
36745
|
checks5.push(await checkCurator(directory));
|
|
36657
36746
|
try {
|
|
36658
|
-
const evidenceDir =
|
|
36747
|
+
const evidenceDir = path18.join(directory, ".swarm", "evidence");
|
|
36659
36748
|
const snapshotFiles = existsSync8(evidenceDir) ? readdirSync4(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
|
|
36660
36749
|
if (snapshotFiles.length > 0) {
|
|
36661
36750
|
const latest = snapshotFiles.sort().pop();
|
|
@@ -36708,11 +36797,11 @@ init_config_doctor();
|
|
|
36708
36797
|
|
|
36709
36798
|
// src/services/tool-doctor.ts
|
|
36710
36799
|
import * as fs11 from "fs";
|
|
36711
|
-
import * as
|
|
36800
|
+
import * as path21 from "path";
|
|
36712
36801
|
|
|
36713
36802
|
// src/build/discovery.ts
|
|
36714
36803
|
import * as fs10 from "fs";
|
|
36715
|
-
import * as
|
|
36804
|
+
import * as path20 from "path";
|
|
36716
36805
|
|
|
36717
36806
|
// src/lang/detector.ts
|
|
36718
36807
|
import { access as access2, readdir as readdir2 } from "fs/promises";
|
|
@@ -37876,11 +37965,11 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
37876
37965
|
const regex = simpleGlobToRegex(pattern);
|
|
37877
37966
|
const matches = files.filter((f) => regex.test(f));
|
|
37878
37967
|
if (matches.length > 0) {
|
|
37879
|
-
return
|
|
37968
|
+
return path20.join(dir, matches[0]);
|
|
37880
37969
|
}
|
|
37881
37970
|
} catch {}
|
|
37882
37971
|
} else {
|
|
37883
|
-
const filePath =
|
|
37972
|
+
const filePath = path20.join(workingDir, pattern);
|
|
37884
37973
|
if (fs10.existsSync(filePath)) {
|
|
37885
37974
|
return filePath;
|
|
37886
37975
|
}
|
|
@@ -37889,7 +37978,7 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
37889
37978
|
return null;
|
|
37890
37979
|
}
|
|
37891
37980
|
function getRepoDefinedScripts(workingDir, scripts) {
|
|
37892
|
-
const packageJsonPath =
|
|
37981
|
+
const packageJsonPath = path20.join(workingDir, "package.json");
|
|
37893
37982
|
if (!fs10.existsSync(packageJsonPath)) {
|
|
37894
37983
|
return [];
|
|
37895
37984
|
}
|
|
@@ -37930,7 +38019,7 @@ function findAllBuildFiles(workingDir) {
|
|
|
37930
38019
|
const regex = simpleGlobToRegex(pattern);
|
|
37931
38020
|
findFilesRecursive(workingDir, regex, allBuildFiles);
|
|
37932
38021
|
} else {
|
|
37933
|
-
const filePath =
|
|
38022
|
+
const filePath = path20.join(workingDir, pattern);
|
|
37934
38023
|
if (fs10.existsSync(filePath)) {
|
|
37935
38024
|
allBuildFiles.add(filePath);
|
|
37936
38025
|
}
|
|
@@ -37943,7 +38032,7 @@ function findFilesRecursive(dir, regex, results) {
|
|
|
37943
38032
|
try {
|
|
37944
38033
|
const entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
37945
38034
|
for (const entry of entries) {
|
|
37946
|
-
const fullPath =
|
|
38035
|
+
const fullPath = path20.join(dir, entry.name);
|
|
37947
38036
|
if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
|
|
37948
38037
|
findFilesRecursive(fullPath, regex, results);
|
|
37949
38038
|
} else if (entry.isFile() && regex.test(entry.name)) {
|
|
@@ -37966,7 +38055,7 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
|
|
|
37966
38055
|
let foundCommand = false;
|
|
37967
38056
|
for (const cmd of sortedCommands) {
|
|
37968
38057
|
if (cmd.detectFile) {
|
|
37969
|
-
const detectFilePath =
|
|
38058
|
+
const detectFilePath = path20.join(workingDir, cmd.detectFile);
|
|
37970
38059
|
if (!fs10.existsSync(detectFilePath)) {
|
|
37971
38060
|
continue;
|
|
37972
38061
|
}
|
|
@@ -38141,8 +38230,8 @@ function checkBinaryReadiness() {
|
|
|
38141
38230
|
}
|
|
38142
38231
|
function runToolDoctor(_directory, pluginRoot) {
|
|
38143
38232
|
const findings = [];
|
|
38144
|
-
const resolvedPluginRoot = pluginRoot ??
|
|
38145
|
-
const indexPath =
|
|
38233
|
+
const resolvedPluginRoot = pluginRoot ?? path21.resolve(import.meta.dir, "..", "..");
|
|
38234
|
+
const indexPath = path21.join(resolvedPluginRoot, "src", "index.ts");
|
|
38146
38235
|
if (!fs11.existsSync(indexPath)) {
|
|
38147
38236
|
return {
|
|
38148
38237
|
findings: [
|
|
@@ -39065,10 +39154,10 @@ async function handleHistoryCommand(directory, _args) {
|
|
|
39065
39154
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
39066
39155
|
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
39067
39156
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
39068
|
-
import * as
|
|
39157
|
+
import * as path22 from "path";
|
|
39069
39158
|
async function migrateContextToKnowledge(directory, config3) {
|
|
39070
|
-
const sentinelPath =
|
|
39071
|
-
const contextPath =
|
|
39159
|
+
const sentinelPath = path22.join(directory, ".swarm", ".knowledge-migrated");
|
|
39160
|
+
const contextPath = path22.join(directory, ".swarm", "context.md");
|
|
39072
39161
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
39073
39162
|
if (existsSync12(sentinelPath)) {
|
|
39074
39163
|
return {
|
|
@@ -39264,7 +39353,7 @@ function truncateLesson(text) {
|
|
|
39264
39353
|
return `${text.slice(0, 277)}...`;
|
|
39265
39354
|
}
|
|
39266
39355
|
function inferProjectName(directory) {
|
|
39267
|
-
const packageJsonPath =
|
|
39356
|
+
const packageJsonPath = path22.join(directory, "package.json");
|
|
39268
39357
|
if (existsSync12(packageJsonPath)) {
|
|
39269
39358
|
try {
|
|
39270
39359
|
const pkg = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
|
|
@@ -39273,7 +39362,7 @@ function inferProjectName(directory) {
|
|
|
39273
39362
|
}
|
|
39274
39363
|
} catch {}
|
|
39275
39364
|
}
|
|
39276
|
-
return
|
|
39365
|
+
return path22.basename(directory);
|
|
39277
39366
|
}
|
|
39278
39367
|
async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
39279
39368
|
const sentinel = {
|
|
@@ -39285,7 +39374,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
|
|
|
39285
39374
|
schema_version: 1,
|
|
39286
39375
|
migration_tool: "knowledge-migrator.ts"
|
|
39287
39376
|
};
|
|
39288
|
-
await mkdir3(
|
|
39377
|
+
await mkdir3(path22.dirname(sentinelPath), { recursive: true });
|
|
39289
39378
|
await writeFile4(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
39290
39379
|
}
|
|
39291
39380
|
|
|
@@ -39522,11 +39611,12 @@ async function handlePlanCommand(directory, args) {
|
|
|
39522
39611
|
init_manager2();
|
|
39523
39612
|
init_manager();
|
|
39524
39613
|
import * as fs18 from "fs";
|
|
39525
|
-
import * as
|
|
39614
|
+
import * as path29 from "path";
|
|
39526
39615
|
|
|
39527
39616
|
// src/tools/lint.ts
|
|
39617
|
+
init_zod();
|
|
39528
39618
|
import * as fs12 from "fs";
|
|
39529
|
-
import * as
|
|
39619
|
+
import * as path23 from "path";
|
|
39530
39620
|
init_utils();
|
|
39531
39621
|
|
|
39532
39622
|
// src/utils/path-security.ts
|
|
@@ -39572,9 +39662,9 @@ function validateArgs(args) {
|
|
|
39572
39662
|
}
|
|
39573
39663
|
function getLinterCommand(linter, mode, projectDir) {
|
|
39574
39664
|
const isWindows = process.platform === "win32";
|
|
39575
|
-
const binDir =
|
|
39576
|
-
const biomeBin = isWindows ?
|
|
39577
|
-
const eslintBin = isWindows ?
|
|
39665
|
+
const binDir = path23.join(projectDir, "node_modules", ".bin");
|
|
39666
|
+
const biomeBin = isWindows ? path23.join(binDir, "biome.EXE") : path23.join(binDir, "biome");
|
|
39667
|
+
const eslintBin = isWindows ? path23.join(binDir, "eslint.cmd") : path23.join(binDir, "eslint");
|
|
39578
39668
|
switch (linter) {
|
|
39579
39669
|
case "biome":
|
|
39580
39670
|
if (mode === "fix") {
|
|
@@ -39590,7 +39680,7 @@ function getLinterCommand(linter, mode, projectDir) {
|
|
|
39590
39680
|
}
|
|
39591
39681
|
function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
39592
39682
|
const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
|
|
39593
|
-
const gradlew = fs12.existsSync(
|
|
39683
|
+
const gradlew = fs12.existsSync(path23.join(cwd, gradlewName)) ? path23.join(cwd, gradlewName) : null;
|
|
39594
39684
|
switch (linter) {
|
|
39595
39685
|
case "ruff":
|
|
39596
39686
|
return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
|
|
@@ -39624,10 +39714,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
|
|
|
39624
39714
|
}
|
|
39625
39715
|
}
|
|
39626
39716
|
function detectRuff(cwd) {
|
|
39627
|
-
if (fs12.existsSync(
|
|
39717
|
+
if (fs12.existsSync(path23.join(cwd, "ruff.toml")))
|
|
39628
39718
|
return isCommandAvailable("ruff");
|
|
39629
39719
|
try {
|
|
39630
|
-
const pyproject =
|
|
39720
|
+
const pyproject = path23.join(cwd, "pyproject.toml");
|
|
39631
39721
|
if (fs12.existsSync(pyproject)) {
|
|
39632
39722
|
const content = fs12.readFileSync(pyproject, "utf-8");
|
|
39633
39723
|
if (content.includes("[tool.ruff]"))
|
|
@@ -39637,19 +39727,19 @@ function detectRuff(cwd) {
|
|
|
39637
39727
|
return false;
|
|
39638
39728
|
}
|
|
39639
39729
|
function detectClippy(cwd) {
|
|
39640
|
-
return fs12.existsSync(
|
|
39730
|
+
return fs12.existsSync(path23.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
39641
39731
|
}
|
|
39642
39732
|
function detectGolangciLint(cwd) {
|
|
39643
|
-
return fs12.existsSync(
|
|
39733
|
+
return fs12.existsSync(path23.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
39644
39734
|
}
|
|
39645
39735
|
function detectCheckstyle(cwd) {
|
|
39646
|
-
const hasMaven = fs12.existsSync(
|
|
39647
|
-
const hasGradle = fs12.existsSync(
|
|
39648
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(
|
|
39736
|
+
const hasMaven = fs12.existsSync(path23.join(cwd, "pom.xml"));
|
|
39737
|
+
const hasGradle = fs12.existsSync(path23.join(cwd, "build.gradle")) || fs12.existsSync(path23.join(cwd, "build.gradle.kts"));
|
|
39738
|
+
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(path23.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
|
|
39649
39739
|
return (hasMaven || hasGradle) && hasBinary;
|
|
39650
39740
|
}
|
|
39651
39741
|
function detectKtlint(cwd) {
|
|
39652
|
-
const hasKotlin = fs12.existsSync(
|
|
39742
|
+
const hasKotlin = fs12.existsSync(path23.join(cwd, "build.gradle.kts")) || fs12.existsSync(path23.join(cwd, "build.gradle")) || (() => {
|
|
39653
39743
|
try {
|
|
39654
39744
|
return fs12.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
|
|
39655
39745
|
} catch {
|
|
@@ -39668,11 +39758,11 @@ function detectDotnetFormat(cwd) {
|
|
|
39668
39758
|
}
|
|
39669
39759
|
}
|
|
39670
39760
|
function detectCppcheck(cwd) {
|
|
39671
|
-
if (fs12.existsSync(
|
|
39761
|
+
if (fs12.existsSync(path23.join(cwd, "CMakeLists.txt"))) {
|
|
39672
39762
|
return isCommandAvailable("cppcheck");
|
|
39673
39763
|
}
|
|
39674
39764
|
try {
|
|
39675
|
-
const dirsToCheck = [cwd,
|
|
39765
|
+
const dirsToCheck = [cwd, path23.join(cwd, "src")];
|
|
39676
39766
|
const hasCpp = dirsToCheck.some((dir) => {
|
|
39677
39767
|
try {
|
|
39678
39768
|
return fs12.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
|
|
@@ -39686,13 +39776,13 @@ function detectCppcheck(cwd) {
|
|
|
39686
39776
|
}
|
|
39687
39777
|
}
|
|
39688
39778
|
function detectSwiftlint(cwd) {
|
|
39689
|
-
return fs12.existsSync(
|
|
39779
|
+
return fs12.existsSync(path23.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
39690
39780
|
}
|
|
39691
39781
|
function detectDartAnalyze(cwd) {
|
|
39692
|
-
return fs12.existsSync(
|
|
39782
|
+
return fs12.existsSync(path23.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
39693
39783
|
}
|
|
39694
39784
|
function detectRubocop(cwd) {
|
|
39695
|
-
return (fs12.existsSync(
|
|
39785
|
+
return (fs12.existsSync(path23.join(cwd, "Gemfile")) || fs12.existsSync(path23.join(cwd, "gems.rb")) || fs12.existsSync(path23.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
|
|
39696
39786
|
}
|
|
39697
39787
|
function detectAdditionalLinter(cwd) {
|
|
39698
39788
|
if (detectRuff(cwd))
|
|
@@ -39720,10 +39810,10 @@ function detectAdditionalLinter(cwd) {
|
|
|
39720
39810
|
function findBinInAncestors(startDir, binName) {
|
|
39721
39811
|
let dir = startDir;
|
|
39722
39812
|
while (true) {
|
|
39723
|
-
const candidate =
|
|
39813
|
+
const candidate = path23.join(dir, "node_modules", ".bin", binName);
|
|
39724
39814
|
if (fs12.existsSync(candidate))
|
|
39725
39815
|
return candidate;
|
|
39726
|
-
const parent =
|
|
39816
|
+
const parent = path23.dirname(dir);
|
|
39727
39817
|
if (parent === dir)
|
|
39728
39818
|
break;
|
|
39729
39819
|
dir = parent;
|
|
@@ -39732,10 +39822,10 @@ function findBinInAncestors(startDir, binName) {
|
|
|
39732
39822
|
}
|
|
39733
39823
|
function findBinInEnvPath(binName) {
|
|
39734
39824
|
const searchPath = process.env.PATH ?? "";
|
|
39735
|
-
for (const dir of searchPath.split(
|
|
39825
|
+
for (const dir of searchPath.split(path23.delimiter)) {
|
|
39736
39826
|
if (!dir)
|
|
39737
39827
|
continue;
|
|
39738
|
-
const candidate =
|
|
39828
|
+
const candidate = path23.join(dir, binName);
|
|
39739
39829
|
if (fs12.existsSync(candidate))
|
|
39740
39830
|
return candidate;
|
|
39741
39831
|
}
|
|
@@ -39748,13 +39838,13 @@ async function detectAvailableLinter(directory) {
|
|
|
39748
39838
|
return null;
|
|
39749
39839
|
const projectDir = directory;
|
|
39750
39840
|
const isWindows = process.platform === "win32";
|
|
39751
|
-
const biomeBin = isWindows ?
|
|
39752
|
-
const eslintBin = isWindows ?
|
|
39841
|
+
const biomeBin = isWindows ? path23.join(projectDir, "node_modules", ".bin", "biome.EXE") : path23.join(projectDir, "node_modules", ".bin", "biome");
|
|
39842
|
+
const eslintBin = isWindows ? path23.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path23.join(projectDir, "node_modules", ".bin", "eslint");
|
|
39753
39843
|
const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
39754
39844
|
if (localResult)
|
|
39755
39845
|
return localResult;
|
|
39756
|
-
const biomeAncestor = findBinInAncestors(
|
|
39757
|
-
const eslintAncestor = findBinInAncestors(
|
|
39846
|
+
const biomeAncestor = findBinInAncestors(path23.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
|
|
39847
|
+
const eslintAncestor = findBinInAncestors(path23.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
|
|
39758
39848
|
if (biomeAncestor || eslintAncestor) {
|
|
39759
39849
|
return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
|
|
39760
39850
|
}
|
|
@@ -39916,7 +40006,7 @@ async function runAdditionalLint(linter, mode, cwd) {
|
|
|
39916
40006
|
var lint = createSwarmTool({
|
|
39917
40007
|
description: "Run project linter in check or fix mode. Supports biome, eslint (JS/TS), ruff (Python), clippy (Rust), golangci-lint (Go), checkstyle (Java), ktlint (Kotlin), dotnet-format (C#), cppcheck (C/C++), swiftlint (Swift), dart analyze (Dart), and rubocop (Ruby). Returns JSON with success status, exit code, and output for architect pre-reviewer gate. Use check mode for CI/linting and fix mode to automatically apply fixes.",
|
|
39918
40008
|
args: {
|
|
39919
|
-
mode:
|
|
40009
|
+
mode: exports_external.enum(["fix", "check"]).describe('Linting mode: "check" for read-only lint check, "fix" to automatically apply fixes')
|
|
39920
40010
|
},
|
|
39921
40011
|
async execute(args, directory) {
|
|
39922
40012
|
if (!validateArgs(args)) {
|
|
@@ -39961,8 +40051,9 @@ For Rust: rustup component add clippy`
|
|
|
39961
40051
|
});
|
|
39962
40052
|
|
|
39963
40053
|
// src/tools/secretscan.ts
|
|
40054
|
+
init_zod();
|
|
39964
40055
|
import * as fs13 from "fs";
|
|
39965
|
-
import * as
|
|
40056
|
+
import * as path24 from "path";
|
|
39966
40057
|
var MAX_FILE_PATH_LENGTH = 500;
|
|
39967
40058
|
var MAX_FILE_SIZE_BYTES = 512 * 1024;
|
|
39968
40059
|
var MAX_FILES_SCANNED = 1000;
|
|
@@ -40189,7 +40280,7 @@ function isGlobOrPathPattern(pattern) {
|
|
|
40189
40280
|
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
40190
40281
|
}
|
|
40191
40282
|
function loadSecretScanIgnore(scanDir) {
|
|
40192
|
-
const ignorePath =
|
|
40283
|
+
const ignorePath = path24.join(scanDir, ".secretscanignore");
|
|
40193
40284
|
try {
|
|
40194
40285
|
if (!fs13.existsSync(ignorePath))
|
|
40195
40286
|
return [];
|
|
@@ -40212,7 +40303,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
40212
40303
|
if (exactNames.has(entry))
|
|
40213
40304
|
return true;
|
|
40214
40305
|
for (const pattern of globPatterns) {
|
|
40215
|
-
if (
|
|
40306
|
+
if (path24.matchesGlob(relPath, pattern))
|
|
40216
40307
|
return true;
|
|
40217
40308
|
}
|
|
40218
40309
|
return false;
|
|
@@ -40233,7 +40324,7 @@ function validateDirectoryInput(dir) {
|
|
|
40233
40324
|
return null;
|
|
40234
40325
|
}
|
|
40235
40326
|
function isBinaryFile(filePath, buffer) {
|
|
40236
|
-
const ext =
|
|
40327
|
+
const ext = path24.extname(filePath).toLowerCase();
|
|
40237
40328
|
if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
40238
40329
|
return true;
|
|
40239
40330
|
}
|
|
@@ -40370,9 +40461,9 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
40370
40461
|
return false;
|
|
40371
40462
|
}
|
|
40372
40463
|
function isPathWithinScope(realPath, scanDir) {
|
|
40373
|
-
const resolvedScanDir =
|
|
40374
|
-
const resolvedRealPath =
|
|
40375
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir +
|
|
40464
|
+
const resolvedScanDir = path24.resolve(scanDir);
|
|
40465
|
+
const resolvedRealPath = path24.resolve(realPath);
|
|
40466
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path24.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
40376
40467
|
}
|
|
40377
40468
|
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
40378
40469
|
skippedDirs: 0,
|
|
@@ -40398,8 +40489,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
40398
40489
|
return a.localeCompare(b);
|
|
40399
40490
|
});
|
|
40400
40491
|
for (const entry of entries) {
|
|
40401
|
-
const fullPath =
|
|
40402
|
-
const relPath =
|
|
40492
|
+
const fullPath = path24.join(dir, entry);
|
|
40493
|
+
const relPath = path24.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
40403
40494
|
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
40404
40495
|
stats.skippedDirs++;
|
|
40405
40496
|
continue;
|
|
@@ -40434,7 +40525,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
40434
40525
|
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
40435
40526
|
files.push(...subFiles);
|
|
40436
40527
|
} else if (lstat.isFile()) {
|
|
40437
|
-
const ext =
|
|
40528
|
+
const ext = path24.extname(fullPath).toLowerCase();
|
|
40438
40529
|
if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
|
|
40439
40530
|
files.push(fullPath);
|
|
40440
40531
|
} else {
|
|
@@ -40447,8 +40538,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
|
|
|
40447
40538
|
var secretscan = createSwarmTool({
|
|
40448
40539
|
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.",
|
|
40449
40540
|
args: {
|
|
40450
|
-
directory:
|
|
40451
|
-
exclude:
|
|
40541
|
+
directory: exports_external.string().describe('Directory to scan for secrets (e.g., "." or "./src")'),
|
|
40542
|
+
exclude: exports_external.array(exports_external.string()).optional().describe("Patterns to exclude: plain directory names (e.g. node_modules), relative paths, or globs (e.g. **/.svelte-kit/**, **/*.test.ts). Added to default exclusions.")
|
|
40452
40543
|
},
|
|
40453
40544
|
async execute(args, _directory, _ctx) {
|
|
40454
40545
|
const typedArgs = args;
|
|
@@ -40500,7 +40591,7 @@ var secretscan = createSwarmTool({
|
|
|
40500
40591
|
}
|
|
40501
40592
|
}
|
|
40502
40593
|
try {
|
|
40503
|
-
const _scanDirRaw =
|
|
40594
|
+
const _scanDirRaw = path24.resolve(directory);
|
|
40504
40595
|
const scanDir = (() => {
|
|
40505
40596
|
try {
|
|
40506
40597
|
return fs13.realpathSync(_scanDirRaw);
|
|
@@ -40642,7 +40733,8 @@ var secretscan = createSwarmTool({
|
|
|
40642
40733
|
async function runSecretscan(directory) {
|
|
40643
40734
|
try {
|
|
40644
40735
|
const result = await secretscan.execute({ directory }, {});
|
|
40645
|
-
|
|
40736
|
+
const jsonStr = typeof result === "string" ? result : result.output;
|
|
40737
|
+
return JSON.parse(jsonStr);
|
|
40646
40738
|
} catch (e) {
|
|
40647
40739
|
const errorResult = {
|
|
40648
40740
|
error: e instanceof Error ? `scan failed: ${e.message}` : "scan failed: unknown error",
|
|
@@ -40657,12 +40749,13 @@ async function runSecretscan(directory) {
|
|
|
40657
40749
|
}
|
|
40658
40750
|
|
|
40659
40751
|
// src/tools/test-runner.ts
|
|
40752
|
+
init_zod();
|
|
40660
40753
|
import * as fs17 from "fs";
|
|
40661
|
-
import * as
|
|
40754
|
+
import * as path28 from "path";
|
|
40662
40755
|
|
|
40663
40756
|
// src/test-impact/analyzer.ts
|
|
40664
40757
|
import fs14 from "fs";
|
|
40665
|
-
import
|
|
40758
|
+
import path25 from "path";
|
|
40666
40759
|
var IMPORT_REGEX_ES = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
40667
40760
|
var IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
40668
40761
|
var IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
@@ -40687,8 +40780,8 @@ function resolveRelativeImport(fromDir, importPath) {
|
|
|
40687
40780
|
if (!importPath.startsWith(".")) {
|
|
40688
40781
|
return null;
|
|
40689
40782
|
}
|
|
40690
|
-
const resolved =
|
|
40691
|
-
if (
|
|
40783
|
+
const resolved = path25.resolve(fromDir, importPath);
|
|
40784
|
+
if (path25.extname(resolved)) {
|
|
40692
40785
|
if (fs14.existsSync(resolved) && fs14.statSync(resolved).isFile()) {
|
|
40693
40786
|
return normalizePath(resolved);
|
|
40694
40787
|
}
|
|
@@ -40733,12 +40826,12 @@ function findTestFilesSync(cwd) {
|
|
|
40733
40826
|
for (const entry of entries) {
|
|
40734
40827
|
if (entry.isDirectory()) {
|
|
40735
40828
|
if (!skipDirs.has(entry.name)) {
|
|
40736
|
-
walk(
|
|
40829
|
+
walk(path25.join(dir, entry.name), visitedInodes);
|
|
40737
40830
|
}
|
|
40738
40831
|
} else if (entry.isFile()) {
|
|
40739
40832
|
const name = entry.name;
|
|
40740
40833
|
if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name)) {
|
|
40741
|
-
testFiles.push(normalizePath(
|
|
40834
|
+
testFiles.push(normalizePath(path25.join(dir, entry.name)));
|
|
40742
40835
|
}
|
|
40743
40836
|
}
|
|
40744
40837
|
}
|
|
@@ -40776,7 +40869,7 @@ async function buildImpactMapInternal(cwd) {
|
|
|
40776
40869
|
continue;
|
|
40777
40870
|
}
|
|
40778
40871
|
const imports = extractImports(content);
|
|
40779
|
-
const testDir =
|
|
40872
|
+
const testDir = path25.dirname(testFile);
|
|
40780
40873
|
for (const importPath of imports) {
|
|
40781
40874
|
const resolvedSource = resolveRelativeImport(testDir, importPath);
|
|
40782
40875
|
if (resolvedSource === null) {
|
|
@@ -40798,7 +40891,7 @@ async function buildImpactMap(cwd) {
|
|
|
40798
40891
|
return impactMap;
|
|
40799
40892
|
}
|
|
40800
40893
|
async function loadImpactMap(cwd) {
|
|
40801
|
-
const cachePath =
|
|
40894
|
+
const cachePath = path25.join(cwd, ".swarm", "cache", "impact-map.json");
|
|
40802
40895
|
if (fs14.existsSync(cachePath)) {
|
|
40803
40896
|
try {
|
|
40804
40897
|
const content = fs14.readFileSync(cachePath, "utf-8");
|
|
@@ -40813,8 +40906,8 @@ async function loadImpactMap(cwd) {
|
|
|
40813
40906
|
return buildImpactMap(cwd);
|
|
40814
40907
|
}
|
|
40815
40908
|
async function saveImpactMap(cwd, impactMap) {
|
|
40816
|
-
const cacheDir =
|
|
40817
|
-
const cachePath =
|
|
40909
|
+
const cacheDir = path25.join(cwd, ".swarm", "cache");
|
|
40910
|
+
const cachePath = path25.join(cacheDir, "impact-map.json");
|
|
40818
40911
|
if (!fs14.existsSync(cacheDir)) {
|
|
40819
40912
|
fs14.mkdirSync(cacheDir, { recursive: true });
|
|
40820
40913
|
}
|
|
@@ -40840,7 +40933,7 @@ async function analyzeImpact(changedFiles, cwd) {
|
|
|
40840
40933
|
const impactedTestsSet = new Set;
|
|
40841
40934
|
const untestedFiles = [];
|
|
40842
40935
|
for (const changedFile of validFiles) {
|
|
40843
|
-
const normalizedChanged = normalizePath(
|
|
40936
|
+
const normalizedChanged = normalizePath(path25.resolve(changedFile));
|
|
40844
40937
|
const tests = impactMap[normalizedChanged];
|
|
40845
40938
|
if (tests && tests.length > 0) {
|
|
40846
40939
|
for (const test of tests) {
|
|
@@ -41087,13 +41180,13 @@ function detectFlakyTests(allHistory) {
|
|
|
41087
41180
|
|
|
41088
41181
|
// src/test-impact/history-store.ts
|
|
41089
41182
|
import fs15 from "fs";
|
|
41090
|
-
import
|
|
41183
|
+
import path26 from "path";
|
|
41091
41184
|
var MAX_HISTORY_PER_TEST = 20;
|
|
41092
41185
|
var MAX_ERROR_LENGTH = 500;
|
|
41093
41186
|
var MAX_STACK_LENGTH = 200;
|
|
41094
41187
|
var MAX_CHANGED_FILES = 50;
|
|
41095
41188
|
function getHistoryPath(workingDir) {
|
|
41096
|
-
return
|
|
41189
|
+
return path26.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
|
|
41097
41190
|
}
|
|
41098
41191
|
function sanitizeErrorMessage(errorMessage) {
|
|
41099
41192
|
if (errorMessage === undefined) {
|
|
@@ -41153,7 +41246,7 @@ function appendTestRun(record3, workingDir) {
|
|
|
41153
41246
|
changedFiles: sanitizeChangedFiles(record3.changedFiles || [])
|
|
41154
41247
|
};
|
|
41155
41248
|
const historyPath = getHistoryPath(workingDir);
|
|
41156
|
-
const historyDir =
|
|
41249
|
+
const historyDir = path26.dirname(historyPath);
|
|
41157
41250
|
if (!fs15.existsSync(historyDir)) {
|
|
41158
41251
|
fs15.mkdirSync(historyDir, { recursive: true });
|
|
41159
41252
|
}
|
|
@@ -41227,7 +41320,7 @@ function getAllHistory(workingDir) {
|
|
|
41227
41320
|
|
|
41228
41321
|
// src/tools/resolve-working-directory.ts
|
|
41229
41322
|
import * as fs16 from "fs";
|
|
41230
|
-
import * as
|
|
41323
|
+
import * as path27 from "path";
|
|
41231
41324
|
function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
41232
41325
|
if (workingDirectory == null || workingDirectory === "") {
|
|
41233
41326
|
return { success: true, directory: fallbackDirectory };
|
|
@@ -41247,15 +41340,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
41247
41340
|
};
|
|
41248
41341
|
}
|
|
41249
41342
|
}
|
|
41250
|
-
const normalizedDir =
|
|
41251
|
-
const pathParts = normalizedDir.split(
|
|
41343
|
+
const normalizedDir = path27.normalize(workingDirectory);
|
|
41344
|
+
const pathParts = normalizedDir.split(path27.sep);
|
|
41252
41345
|
if (pathParts.includes("..")) {
|
|
41253
41346
|
return {
|
|
41254
41347
|
success: false,
|
|
41255
41348
|
message: "Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
41256
41349
|
};
|
|
41257
41350
|
}
|
|
41258
|
-
const resolvedDir =
|
|
41351
|
+
const resolvedDir = path27.resolve(normalizedDir);
|
|
41259
41352
|
let statResult;
|
|
41260
41353
|
try {
|
|
41261
41354
|
statResult = fs16.statSync(resolvedDir);
|
|
@@ -41271,7 +41364,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
41271
41364
|
message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
|
|
41272
41365
|
};
|
|
41273
41366
|
}
|
|
41274
|
-
const resolvedFallback =
|
|
41367
|
+
const resolvedFallback = path27.resolve(fallbackDirectory);
|
|
41275
41368
|
let fallbackExists = false;
|
|
41276
41369
|
try {
|
|
41277
41370
|
fs16.statSync(resolvedFallback);
|
|
@@ -41281,7 +41374,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
|
|
|
41281
41374
|
}
|
|
41282
41375
|
if (workingDirectory != null && workingDirectory !== "") {
|
|
41283
41376
|
if (fallbackExists) {
|
|
41284
|
-
const isSubdirectory = resolvedDir.startsWith(resolvedFallback +
|
|
41377
|
+
const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path27.sep);
|
|
41285
41378
|
if (isSubdirectory) {
|
|
41286
41379
|
return {
|
|
41287
41380
|
success: false,
|
|
@@ -41371,14 +41464,14 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
41371
41464
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
41372
41465
|
}
|
|
41373
41466
|
function detectGoTest(cwd) {
|
|
41374
|
-
return fs17.existsSync(
|
|
41467
|
+
return fs17.existsSync(path28.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
41375
41468
|
}
|
|
41376
41469
|
function detectJavaMaven(cwd) {
|
|
41377
|
-
return fs17.existsSync(
|
|
41470
|
+
return fs17.existsSync(path28.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
41378
41471
|
}
|
|
41379
41472
|
function detectGradle(cwd) {
|
|
41380
|
-
const hasBuildFile = fs17.existsSync(
|
|
41381
|
-
const hasGradlew = fs17.existsSync(
|
|
41473
|
+
const hasBuildFile = fs17.existsSync(path28.join(cwd, "build.gradle")) || fs17.existsSync(path28.join(cwd, "build.gradle.kts"));
|
|
41474
|
+
const hasGradlew = fs17.existsSync(path28.join(cwd, "gradlew")) || fs17.existsSync(path28.join(cwd, "gradlew.bat"));
|
|
41382
41475
|
return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
|
|
41383
41476
|
}
|
|
41384
41477
|
function detectDotnetTest(cwd) {
|
|
@@ -41391,30 +41484,30 @@ function detectDotnetTest(cwd) {
|
|
|
41391
41484
|
}
|
|
41392
41485
|
}
|
|
41393
41486
|
function detectCTest(cwd) {
|
|
41394
|
-
const hasSource = fs17.existsSync(
|
|
41395
|
-
const hasBuildCache = fs17.existsSync(
|
|
41487
|
+
const hasSource = fs17.existsSync(path28.join(cwd, "CMakeLists.txt"));
|
|
41488
|
+
const hasBuildCache = fs17.existsSync(path28.join(cwd, "CMakeCache.txt")) || fs17.existsSync(path28.join(cwd, "build", "CMakeCache.txt"));
|
|
41396
41489
|
return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
|
|
41397
41490
|
}
|
|
41398
41491
|
function detectSwiftTest(cwd) {
|
|
41399
|
-
return fs17.existsSync(
|
|
41492
|
+
return fs17.existsSync(path28.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
41400
41493
|
}
|
|
41401
41494
|
function detectDartTest(cwd) {
|
|
41402
|
-
return fs17.existsSync(
|
|
41495
|
+
return fs17.existsSync(path28.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
41403
41496
|
}
|
|
41404
41497
|
function detectRSpec(cwd) {
|
|
41405
|
-
const hasRSpecFile = fs17.existsSync(
|
|
41406
|
-
const hasGemfile = fs17.existsSync(
|
|
41407
|
-
const hasSpecDir = fs17.existsSync(
|
|
41498
|
+
const hasRSpecFile = fs17.existsSync(path28.join(cwd, ".rspec"));
|
|
41499
|
+
const hasGemfile = fs17.existsSync(path28.join(cwd, "Gemfile"));
|
|
41500
|
+
const hasSpecDir = fs17.existsSync(path28.join(cwd, "spec"));
|
|
41408
41501
|
const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
|
|
41409
41502
|
return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
|
|
41410
41503
|
}
|
|
41411
41504
|
function detectMinitest(cwd) {
|
|
41412
|
-
return fs17.existsSync(
|
|
41505
|
+
return fs17.existsSync(path28.join(cwd, "test")) && (fs17.existsSync(path28.join(cwd, "Gemfile")) || fs17.existsSync(path28.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
41413
41506
|
}
|
|
41414
41507
|
async function detectTestFramework(cwd) {
|
|
41415
41508
|
const baseDir = cwd;
|
|
41416
41509
|
try {
|
|
41417
|
-
const packageJsonPath =
|
|
41510
|
+
const packageJsonPath = path28.join(baseDir, "package.json");
|
|
41418
41511
|
if (fs17.existsSync(packageJsonPath)) {
|
|
41419
41512
|
const content = fs17.readFileSync(packageJsonPath, "utf-8");
|
|
41420
41513
|
const pkg = JSON.parse(content);
|
|
@@ -41435,16 +41528,16 @@ async function detectTestFramework(cwd) {
|
|
|
41435
41528
|
return "jest";
|
|
41436
41529
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
41437
41530
|
return "mocha";
|
|
41438
|
-
if (fs17.existsSync(
|
|
41531
|
+
if (fs17.existsSync(path28.join(baseDir, "bun.lockb")) || fs17.existsSync(path28.join(baseDir, "bun.lock"))) {
|
|
41439
41532
|
if (scripts.test?.includes("bun"))
|
|
41440
41533
|
return "bun";
|
|
41441
41534
|
}
|
|
41442
41535
|
}
|
|
41443
41536
|
} catch {}
|
|
41444
41537
|
try {
|
|
41445
|
-
const pyprojectTomlPath =
|
|
41446
|
-
const setupCfgPath =
|
|
41447
|
-
const requirementsTxtPath =
|
|
41538
|
+
const pyprojectTomlPath = path28.join(baseDir, "pyproject.toml");
|
|
41539
|
+
const setupCfgPath = path28.join(baseDir, "setup.cfg");
|
|
41540
|
+
const requirementsTxtPath = path28.join(baseDir, "requirements.txt");
|
|
41448
41541
|
if (fs17.existsSync(pyprojectTomlPath)) {
|
|
41449
41542
|
const content = fs17.readFileSync(pyprojectTomlPath, "utf-8");
|
|
41450
41543
|
if (content.includes("[tool.pytest"))
|
|
@@ -41464,7 +41557,7 @@ async function detectTestFramework(cwd) {
|
|
|
41464
41557
|
}
|
|
41465
41558
|
} catch {}
|
|
41466
41559
|
try {
|
|
41467
|
-
const cargoTomlPath =
|
|
41560
|
+
const cargoTomlPath = path28.join(baseDir, "Cargo.toml");
|
|
41468
41561
|
if (fs17.existsSync(cargoTomlPath)) {
|
|
41469
41562
|
const content = fs17.readFileSync(cargoTomlPath, "utf-8");
|
|
41470
41563
|
if (content.includes("[dev-dependencies]")) {
|
|
@@ -41475,9 +41568,9 @@ async function detectTestFramework(cwd) {
|
|
|
41475
41568
|
}
|
|
41476
41569
|
} catch {}
|
|
41477
41570
|
try {
|
|
41478
|
-
const pesterConfigPath =
|
|
41479
|
-
const pesterConfigJsonPath =
|
|
41480
|
-
const pesterPs1Path =
|
|
41571
|
+
const pesterConfigPath = path28.join(baseDir, "pester.config.ps1");
|
|
41572
|
+
const pesterConfigJsonPath = path28.join(baseDir, "pester.config.ps1.json");
|
|
41573
|
+
const pesterPs1Path = path28.join(baseDir, "tests.ps1");
|
|
41481
41574
|
if (fs17.existsSync(pesterConfigPath) || fs17.existsSync(pesterConfigJsonPath) || fs17.existsSync(pesterPs1Path)) {
|
|
41482
41575
|
return "pester";
|
|
41483
41576
|
}
|
|
@@ -41520,12 +41613,12 @@ function isTestDirectoryPath(normalizedPath) {
|
|
|
41520
41613
|
return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
|
|
41521
41614
|
}
|
|
41522
41615
|
function resolveWorkspacePath(file3, workingDir) {
|
|
41523
|
-
return
|
|
41616
|
+
return path28.isAbsolute(file3) ? path28.resolve(file3) : path28.resolve(workingDir, file3);
|
|
41524
41617
|
}
|
|
41525
41618
|
function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
|
|
41526
41619
|
if (!preferRelative)
|
|
41527
41620
|
return absolutePath;
|
|
41528
|
-
return
|
|
41621
|
+
return path28.relative(workingDir, absolutePath);
|
|
41529
41622
|
}
|
|
41530
41623
|
function dedupePush(target, value) {
|
|
41531
41624
|
if (!target.includes(value)) {
|
|
@@ -41562,18 +41655,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
|
|
|
41562
41655
|
}
|
|
41563
41656
|
}
|
|
41564
41657
|
function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
|
|
41565
|
-
const relativeDir =
|
|
41658
|
+
const relativeDir = path28.dirname(relativePath);
|
|
41566
41659
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
41567
41660
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
41568
|
-
const rootDir =
|
|
41569
|
-
return nestedRelativeDir ? [rootDir,
|
|
41661
|
+
const rootDir = path28.join(workingDir, dirName);
|
|
41662
|
+
return nestedRelativeDir ? [rootDir, path28.join(rootDir, nestedRelativeDir)] : [rootDir];
|
|
41570
41663
|
});
|
|
41571
41664
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
41572
41665
|
if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
|
|
41573
|
-
directories.push(
|
|
41666
|
+
directories.push(path28.join(workingDir, "src/test/java", path28.dirname(normalizedRelativePath.slice("src/main/java/".length))));
|
|
41574
41667
|
}
|
|
41575
41668
|
if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
|
|
41576
|
-
directories.push(
|
|
41669
|
+
directories.push(path28.join(workingDir, "src/test/kotlin", path28.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
|
|
41577
41670
|
}
|
|
41578
41671
|
return [...new Set(directories)];
|
|
41579
41672
|
}
|
|
@@ -41601,23 +41694,23 @@ function isLanguageSpecificTestFile(basename4) {
|
|
|
41601
41694
|
}
|
|
41602
41695
|
function isConventionTestFilePath(filePath) {
|
|
41603
41696
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
41604
|
-
const basename4 =
|
|
41697
|
+
const basename4 = path28.basename(filePath);
|
|
41605
41698
|
return hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || isLanguageSpecificTestFile(basename4) || isTestDirectoryPath(normalizedPath);
|
|
41606
41699
|
}
|
|
41607
41700
|
function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
41608
41701
|
const testFiles = [];
|
|
41609
41702
|
for (const file3 of sourceFiles) {
|
|
41610
41703
|
const absoluteFile = resolveWorkspacePath(file3, workingDir);
|
|
41611
|
-
const relativeFile =
|
|
41612
|
-
const basename4 =
|
|
41613
|
-
const dirname11 =
|
|
41614
|
-
const preferRelativeOutput = !
|
|
41704
|
+
const relativeFile = path28.relative(workingDir, absoluteFile);
|
|
41705
|
+
const basename4 = path28.basename(absoluteFile);
|
|
41706
|
+
const dirname11 = path28.dirname(absoluteFile);
|
|
41707
|
+
const preferRelativeOutput = !path28.isAbsolute(file3);
|
|
41615
41708
|
if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
|
|
41616
41709
|
dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
|
|
41617
41710
|
continue;
|
|
41618
41711
|
}
|
|
41619
41712
|
const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
|
|
41620
|
-
const ext =
|
|
41713
|
+
const ext = path28.extname(basename4);
|
|
41621
41714
|
const genericTestNames = [
|
|
41622
41715
|
`${nameWithoutExt}.spec${ext}`,
|
|
41623
41716
|
`${nameWithoutExt}.test${ext}`
|
|
@@ -41626,7 +41719,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
41626
41719
|
const colocatedCandidates = [
|
|
41627
41720
|
...genericTestNames,
|
|
41628
41721
|
...languageSpecificTestNames
|
|
41629
|
-
].map((candidateName) =>
|
|
41722
|
+
].map((candidateName) => path28.join(dirname11, candidateName));
|
|
41630
41723
|
const testDirectoryNames = [
|
|
41631
41724
|
basename4,
|
|
41632
41725
|
...genericTestNames,
|
|
@@ -41635,8 +41728,8 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
|
|
|
41635
41728
|
const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
|
|
41636
41729
|
const possibleTestFiles = [
|
|
41637
41730
|
...colocatedCandidates,
|
|
41638
|
-
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) =>
|
|
41639
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) =>
|
|
41731
|
+
...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path28.join(dirname11, dirName, candidateName))),
|
|
41732
|
+
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path28.join(candidateDir, candidateName)))
|
|
41640
41733
|
];
|
|
41641
41734
|
for (const testFile of possibleTestFiles) {
|
|
41642
41735
|
if (fs17.existsSync(testFile)) {
|
|
@@ -41657,7 +41750,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
41657
41750
|
try {
|
|
41658
41751
|
const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
|
|
41659
41752
|
const content = fs17.readFileSync(absoluteTestFile, "utf-8");
|
|
41660
|
-
const testDir =
|
|
41753
|
+
const testDir = path28.dirname(absoluteTestFile);
|
|
41661
41754
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
41662
41755
|
let match;
|
|
41663
41756
|
match = importRegex.exec(content);
|
|
@@ -41665,8 +41758,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
41665
41758
|
const importPath = match[1];
|
|
41666
41759
|
let resolvedImport;
|
|
41667
41760
|
if (importPath.startsWith(".")) {
|
|
41668
|
-
resolvedImport =
|
|
41669
|
-
const existingExt =
|
|
41761
|
+
resolvedImport = path28.resolve(testDir, importPath);
|
|
41762
|
+
const existingExt = path28.extname(resolvedImport);
|
|
41670
41763
|
if (!existingExt) {
|
|
41671
41764
|
for (const extToTry of [
|
|
41672
41765
|
".ts",
|
|
@@ -41686,12 +41779,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
41686
41779
|
} else {
|
|
41687
41780
|
continue;
|
|
41688
41781
|
}
|
|
41689
|
-
const importBasename =
|
|
41690
|
-
const importDir =
|
|
41782
|
+
const importBasename = path28.basename(resolvedImport, path28.extname(resolvedImport));
|
|
41783
|
+
const importDir = path28.dirname(resolvedImport);
|
|
41691
41784
|
for (const sourceFile of absoluteSourceFiles) {
|
|
41692
|
-
const sourceDir =
|
|
41693
|
-
const sourceBasename =
|
|
41694
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
41785
|
+
const sourceDir = path28.dirname(sourceFile);
|
|
41786
|
+
const sourceBasename = path28.basename(sourceFile, path28.extname(sourceFile));
|
|
41787
|
+
const isRelatedDir = importDir === sourceDir || importDir === path28.join(sourceDir, "__tests__") || importDir === path28.join(sourceDir, "tests") || importDir === path28.join(sourceDir, "test") || importDir === path28.join(sourceDir, "spec");
|
|
41695
41788
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
41696
41789
|
dedupePush(testFiles, testFile);
|
|
41697
41790
|
break;
|
|
@@ -41704,8 +41797,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
41704
41797
|
while (match !== null) {
|
|
41705
41798
|
const importPath = match[1];
|
|
41706
41799
|
if (importPath.startsWith(".")) {
|
|
41707
|
-
let resolvedImport =
|
|
41708
|
-
const existingExt =
|
|
41800
|
+
let resolvedImport = path28.resolve(testDir, importPath);
|
|
41801
|
+
const existingExt = path28.extname(resolvedImport);
|
|
41709
41802
|
if (!existingExt) {
|
|
41710
41803
|
for (const extToTry of [
|
|
41711
41804
|
".ts",
|
|
@@ -41722,12 +41815,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
|
|
|
41722
41815
|
}
|
|
41723
41816
|
}
|
|
41724
41817
|
}
|
|
41725
|
-
const importDir =
|
|
41726
|
-
const importBasename =
|
|
41818
|
+
const importDir = path28.dirname(resolvedImport);
|
|
41819
|
+
const importBasename = path28.basename(resolvedImport, path28.extname(resolvedImport));
|
|
41727
41820
|
for (const sourceFile of absoluteSourceFiles) {
|
|
41728
|
-
const sourceDir =
|
|
41729
|
-
const sourceBasename =
|
|
41730
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
41821
|
+
const sourceDir = path28.dirname(sourceFile);
|
|
41822
|
+
const sourceBasename = path28.basename(sourceFile, path28.extname(sourceFile));
|
|
41823
|
+
const isRelatedDir = importDir === sourceDir || importDir === path28.join(sourceDir, "__tests__") || importDir === path28.join(sourceDir, "tests") || importDir === path28.join(sourceDir, "test") || importDir === path28.join(sourceDir, "spec");
|
|
41731
41824
|
if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
|
|
41732
41825
|
dedupePush(testFiles, testFile);
|
|
41733
41826
|
break;
|
|
@@ -41830,8 +41923,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
41830
41923
|
return ["mvn", "test"];
|
|
41831
41924
|
case "gradle": {
|
|
41832
41925
|
const isWindows = process.platform === "win32";
|
|
41833
|
-
const hasGradlewBat = fs17.existsSync(
|
|
41834
|
-
const hasGradlew = fs17.existsSync(
|
|
41926
|
+
const hasGradlewBat = fs17.existsSync(path28.join(baseDir, "gradlew.bat"));
|
|
41927
|
+
const hasGradlew = fs17.existsSync(path28.join(baseDir, "gradlew"));
|
|
41835
41928
|
if (hasGradlewBat && isWindows)
|
|
41836
41929
|
return ["gradlew.bat", "test"];
|
|
41837
41930
|
if (hasGradlew)
|
|
@@ -41848,7 +41941,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
|
|
|
41848
41941
|
"cmake-build-release",
|
|
41849
41942
|
"out"
|
|
41850
41943
|
];
|
|
41851
|
-
const actualBuildDir = buildDirCandidates.find((d) => fs17.existsSync(
|
|
41944
|
+
const actualBuildDir = buildDirCandidates.find((d) => fs17.existsSync(path28.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
|
|
41852
41945
|
return ["ctest", "--test-dir", actualBuildDir];
|
|
41853
41946
|
}
|
|
41854
41947
|
case "swift-test":
|
|
@@ -42346,12 +42439,12 @@ function analyzeFailures(workingDir) {
|
|
|
42346
42439
|
var test_runner = createSwarmTool({
|
|
42347
42440
|
description: 'Run project tests with framework detection. Supports bun, vitest, jest, mocha, pytest, cargo, pester, go-test, maven, gradle, dotnet-test, ctest, swift-test, dart-test, rspec, and minitest. Returns deterministic normalized JSON with framework, scope, command, totals, coverage, duration, success status, and failures. Use scope "all" for full suite, "convention" to accept direct test files or map source files to test files, "graph" to find related tests via imports from source files, or "impact" to find tests covering changed source files using test-impact analysis.',
|
|
42348
42441
|
args: {
|
|
42349
|
-
scope:
|
|
42350
|
-
files:
|
|
42351
|
-
coverage:
|
|
42352
|
-
timeout_ms:
|
|
42353
|
-
allow_full_suite:
|
|
42354
|
-
working_directory:
|
|
42442
|
+
scope: exports_external.enum(["all", "convention", "graph", "impact"]).optional().describe('Test scope: "all" runs full suite, "convention" accepts direct test files or maps source files to tests by naming, "graph" finds related tests via imports from source files, "impact" finds tests covering changed source files via test-impact analysis'),
|
|
42443
|
+
files: exports_external.array(exports_external.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
|
|
42444
|
+
coverage: exports_external.boolean().optional().describe("Enable coverage reporting if supported"),
|
|
42445
|
+
timeout_ms: exports_external.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
|
|
42446
|
+
allow_full_suite: exports_external.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
|
|
42447
|
+
working_directory: exports_external.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
|
|
42355
42448
|
},
|
|
42356
42449
|
async execute(args, directory) {
|
|
42357
42450
|
let workingDirInput;
|
|
@@ -42476,7 +42569,7 @@ var test_runner = createSwarmTool({
|
|
|
42476
42569
|
const sourceFiles = args.files.filter((file3) => {
|
|
42477
42570
|
if (directTestFiles.includes(file3))
|
|
42478
42571
|
return false;
|
|
42479
|
-
const ext =
|
|
42572
|
+
const ext = path28.extname(file3).toLowerCase();
|
|
42480
42573
|
return SOURCE_EXTENSIONS.has(ext);
|
|
42481
42574
|
});
|
|
42482
42575
|
const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
|
|
@@ -42511,7 +42604,7 @@ var test_runner = createSwarmTool({
|
|
|
42511
42604
|
if (isConventionTestFilePath(f)) {
|
|
42512
42605
|
return false;
|
|
42513
42606
|
}
|
|
42514
|
-
const ext =
|
|
42607
|
+
const ext = path28.extname(f).toLowerCase();
|
|
42515
42608
|
return SOURCE_EXTENSIONS.has(ext);
|
|
42516
42609
|
});
|
|
42517
42610
|
if (sourceFiles.length === 0) {
|
|
@@ -42538,7 +42631,7 @@ var test_runner = createSwarmTool({
|
|
|
42538
42631
|
if (isConventionTestFilePath(f)) {
|
|
42539
42632
|
return false;
|
|
42540
42633
|
}
|
|
42541
|
-
const ext =
|
|
42634
|
+
const ext = path28.extname(f).toLowerCase();
|
|
42542
42635
|
return SOURCE_EXTENSIONS.has(ext);
|
|
42543
42636
|
});
|
|
42544
42637
|
if (sourceFiles.length === 0) {
|
|
@@ -42556,8 +42649,8 @@ var test_runner = createSwarmTool({
|
|
|
42556
42649
|
const impactResult = await analyzeImpact(sourceFiles, workingDir);
|
|
42557
42650
|
if (impactResult.impactedTests.length > 0) {
|
|
42558
42651
|
testFiles = impactResult.impactedTests.map((absPath) => {
|
|
42559
|
-
const relativePath =
|
|
42560
|
-
return
|
|
42652
|
+
const relativePath = path28.relative(workingDir, absPath);
|
|
42653
|
+
return path28.isAbsolute(relativePath) ? absPath : relativePath;
|
|
42561
42654
|
});
|
|
42562
42655
|
} else {
|
|
42563
42656
|
graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
|
|
@@ -42650,8 +42743,8 @@ function validateDirectoryPath(dir) {
|
|
|
42650
42743
|
if (dir.includes("..")) {
|
|
42651
42744
|
throw new Error("Directory path must not contain path traversal sequences");
|
|
42652
42745
|
}
|
|
42653
|
-
const normalized =
|
|
42654
|
-
const absolutePath =
|
|
42746
|
+
const normalized = path29.normalize(dir);
|
|
42747
|
+
const absolutePath = path29.isAbsolute(normalized) ? normalized : path29.resolve(normalized);
|
|
42655
42748
|
return absolutePath;
|
|
42656
42749
|
}
|
|
42657
42750
|
function validateTimeout(timeoutMs, defaultValue) {
|
|
@@ -42674,7 +42767,7 @@ function validateTimeout(timeoutMs, defaultValue) {
|
|
|
42674
42767
|
}
|
|
42675
42768
|
function getPackageVersion(dir) {
|
|
42676
42769
|
try {
|
|
42677
|
-
const packagePath =
|
|
42770
|
+
const packagePath = path29.join(dir, "package.json");
|
|
42678
42771
|
if (fs18.existsSync(packagePath)) {
|
|
42679
42772
|
const content = fs18.readFileSync(packagePath, "utf-8");
|
|
42680
42773
|
const pkg = JSON.parse(content);
|
|
@@ -42685,7 +42778,7 @@ function getPackageVersion(dir) {
|
|
|
42685
42778
|
}
|
|
42686
42779
|
function getChangelogVersion(dir) {
|
|
42687
42780
|
try {
|
|
42688
|
-
const changelogPath =
|
|
42781
|
+
const changelogPath = path29.join(dir, "CHANGELOG.md");
|
|
42689
42782
|
if (fs18.existsSync(changelogPath)) {
|
|
42690
42783
|
const content = fs18.readFileSync(changelogPath, "utf-8");
|
|
42691
42784
|
const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
|
|
@@ -42699,7 +42792,7 @@ function getChangelogVersion(dir) {
|
|
|
42699
42792
|
function getVersionFileVersion(dir) {
|
|
42700
42793
|
const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
|
|
42701
42794
|
for (const file3 of possibleFiles) {
|
|
42702
|
-
const filePath =
|
|
42795
|
+
const filePath = path29.join(dir, file3);
|
|
42703
42796
|
if (fs18.existsSync(filePath)) {
|
|
42704
42797
|
try {
|
|
42705
42798
|
const content = fs18.readFileSync(filePath, "utf-8").trim();
|
|
@@ -43026,7 +43119,7 @@ async function runEvidenceCheck(dir) {
|
|
|
43026
43119
|
async function runRequirementCoverageCheck(dir, currentPhase) {
|
|
43027
43120
|
const startTime = Date.now();
|
|
43028
43121
|
try {
|
|
43029
|
-
const specPath =
|
|
43122
|
+
const specPath = path29.join(dir, ".swarm", "spec.md");
|
|
43030
43123
|
if (!fs18.existsSync(specPath)) {
|
|
43031
43124
|
return {
|
|
43032
43125
|
type: "req_coverage",
|
|
@@ -43239,144 +43332,6 @@ async function handlePreflightCommand(directory, _args) {
|
|
|
43239
43332
|
const report = await runPreflight(directory, phase);
|
|
43240
43333
|
return formatPreflightMarkdown(report);
|
|
43241
43334
|
}
|
|
43242
|
-
// src/knowledge/hive-promoter.ts
|
|
43243
|
-
import * as fs19 from "fs";
|
|
43244
|
-
import * as os6 from "os";
|
|
43245
|
-
import * as path29 from "path";
|
|
43246
|
-
var DANGEROUS_PATTERNS = [
|
|
43247
|
-
[/rm\s+-rf/, "rm\\s+-rf"],
|
|
43248
|
-
[/:\s*!\s*\|/, ":\\s*!\\s*\\|"],
|
|
43249
|
-
[/\|\s*sh\b/, "\\|\\s*sh\\b"],
|
|
43250
|
-
[/`[^`]*`/, "`[^`]*`"],
|
|
43251
|
-
[/\$\(/, "\\$\\("],
|
|
43252
|
-
[/;\s*rm\s+\//, ";\\s*rm\\s+\\/"],
|
|
43253
|
-
[/>\s*\/dev\//, ">\\s*\\/dev\\/"],
|
|
43254
|
-
[/\bmkfs\b/, "\\bmkfs\\b"],
|
|
43255
|
-
[/\bdd\s+if=/, "\\bdd\\s+if="],
|
|
43256
|
-
[/chmod\s+[0-7]*7[0-7]{2}/, "chmod\\s+[0-7]*7[0-7]\\{2\\}"],
|
|
43257
|
-
[/\bchown\s+-R\b/, "\\bchown\\s+-R\\b"],
|
|
43258
|
-
[/(?<!\.)\beval\s*\(/, "(?<!\\.)\\beval\\s*\\("],
|
|
43259
|
-
[/(?<!\.)\bexec\s*\(/, "(?<!\\.)\\bexec\\s*\\("]
|
|
43260
|
-
];
|
|
43261
|
-
var SHELL_COMMAND_START = /^(grep|find|ls|cat|sed|awk|curl|wget|ssh|scp|git|mv|cp|mkdir|touch|echo|printf|python|python3|node|bash|sh|zsh|apt|yum|brew)\s/;
|
|
43262
|
-
function validateLesson2(text) {
|
|
43263
|
-
if (!text || !text.trim()) {
|
|
43264
|
-
return { valid: false, reason: "Lesson text cannot be empty" };
|
|
43265
|
-
}
|
|
43266
|
-
for (const [pattern, patternSource] of DANGEROUS_PATTERNS) {
|
|
43267
|
-
if (pattern.test(text)) {
|
|
43268
|
-
return {
|
|
43269
|
-
valid: false,
|
|
43270
|
-
reason: `Dangerous pattern detected: ${patternSource}`
|
|
43271
|
-
};
|
|
43272
|
-
}
|
|
43273
|
-
}
|
|
43274
|
-
const trimmed = text.trim();
|
|
43275
|
-
if (SHELL_COMMAND_START.test(trimmed)) {
|
|
43276
|
-
const lastChar = trimmed[trimmed.length - 1];
|
|
43277
|
-
if (![".", "!", "?", ";"].includes(lastChar)) {
|
|
43278
|
-
return {
|
|
43279
|
-
valid: false,
|
|
43280
|
-
reason: "Lesson appears to contain raw shell commands"
|
|
43281
|
-
};
|
|
43282
|
-
}
|
|
43283
|
-
}
|
|
43284
|
-
return { valid: true };
|
|
43285
|
-
}
|
|
43286
|
-
function getHiveFilePath() {
|
|
43287
|
-
const platform = process.platform;
|
|
43288
|
-
const home = os6.homedir();
|
|
43289
|
-
let dataDir;
|
|
43290
|
-
if (platform === "win32") {
|
|
43291
|
-
dataDir = path29.join(process.env.LOCALAPPDATA || path29.join(home, "AppData", "Local"), "opencode-swarm", "Data");
|
|
43292
|
-
} else if (platform === "darwin") {
|
|
43293
|
-
dataDir = path29.join(home, "Library", "Application Support", "opencode-swarm");
|
|
43294
|
-
} else {
|
|
43295
|
-
dataDir = path29.join(process.env.XDG_DATA_HOME || path29.join(home, ".local", "share"), "opencode-swarm");
|
|
43296
|
-
}
|
|
43297
|
-
return path29.join(dataDir, "hive-knowledge.jsonl");
|
|
43298
|
-
}
|
|
43299
|
-
async function promoteToHive(_directory, lesson, category) {
|
|
43300
|
-
const trimmed = (lesson ?? "").trim();
|
|
43301
|
-
if (!trimmed) {
|
|
43302
|
-
throw new Error("Lesson text required");
|
|
43303
|
-
}
|
|
43304
|
-
const validation = validateLesson2(trimmed);
|
|
43305
|
-
if (!validation.valid) {
|
|
43306
|
-
throw new Error(`Lesson rejected by validator: ${validation.reason}`);
|
|
43307
|
-
}
|
|
43308
|
-
const hivePath = getHiveFilePath();
|
|
43309
|
-
const hiveDir = path29.dirname(hivePath);
|
|
43310
|
-
if (!fs19.existsSync(hiveDir)) {
|
|
43311
|
-
fs19.mkdirSync(hiveDir, { recursive: true });
|
|
43312
|
-
}
|
|
43313
|
-
const now = new Date;
|
|
43314
|
-
const entry = {
|
|
43315
|
-
id: `hive-manual-${now.getTime()}`,
|
|
43316
|
-
lesson: trimmed,
|
|
43317
|
-
category: category || "process",
|
|
43318
|
-
scope_tag: "global",
|
|
43319
|
-
confidence: 1,
|
|
43320
|
-
status: "promoted",
|
|
43321
|
-
promotion_source: "manual",
|
|
43322
|
-
promotedAt: now.toISOString(),
|
|
43323
|
-
retrievalOutcomes: { applied: 0, succeededAfter: 0, failedAfter: 0 }
|
|
43324
|
-
};
|
|
43325
|
-
fs19.appendFileSync(hivePath, `${JSON.stringify(entry)}
|
|
43326
|
-
`, "utf-8");
|
|
43327
|
-
const preview = `${trimmed.slice(0, 50)}${trimmed.length > 50 ? "..." : ""}`;
|
|
43328
|
-
return `Promoted to hive: "${preview}" (confidence: 1.0, source: manual)`;
|
|
43329
|
-
}
|
|
43330
|
-
async function promoteFromSwarm(directory, lessonId) {
|
|
43331
|
-
const knowledgePath = path29.join(directory, ".swarm", "knowledge.jsonl");
|
|
43332
|
-
const entries = [];
|
|
43333
|
-
if (fs19.existsSync(knowledgePath)) {
|
|
43334
|
-
const content = fs19.readFileSync(knowledgePath, "utf-8");
|
|
43335
|
-
for (const line of content.split(`
|
|
43336
|
-
`)) {
|
|
43337
|
-
const t = line.trim();
|
|
43338
|
-
if (!t)
|
|
43339
|
-
continue;
|
|
43340
|
-
try {
|
|
43341
|
-
entries.push(JSON.parse(t));
|
|
43342
|
-
} catch {}
|
|
43343
|
-
}
|
|
43344
|
-
}
|
|
43345
|
-
const swarmEntry = entries.find((e) => e.id === lessonId);
|
|
43346
|
-
if (!swarmEntry) {
|
|
43347
|
-
throw new Error(`Lesson ${lessonId} not found in .swarm/knowledge.jsonl`);
|
|
43348
|
-
}
|
|
43349
|
-
const lessonText = typeof swarmEntry.lesson === "string" ? swarmEntry.lesson.trim() : "";
|
|
43350
|
-
if (!lessonText) {
|
|
43351
|
-
throw new Error("Lesson text required");
|
|
43352
|
-
}
|
|
43353
|
-
const validation = validateLesson2(lessonText);
|
|
43354
|
-
if (!validation.valid) {
|
|
43355
|
-
throw new Error(`Lesson rejected by validator: ${validation.reason}`);
|
|
43356
|
-
}
|
|
43357
|
-
const hivePath = getHiveFilePath();
|
|
43358
|
-
const hiveDir = path29.dirname(hivePath);
|
|
43359
|
-
if (!fs19.existsSync(hiveDir)) {
|
|
43360
|
-
fs19.mkdirSync(hiveDir, { recursive: true });
|
|
43361
|
-
}
|
|
43362
|
-
const now = new Date;
|
|
43363
|
-
const hiveEntry = {
|
|
43364
|
-
id: `hive-manual-${now.getTime()}`,
|
|
43365
|
-
lesson: lessonText,
|
|
43366
|
-
category: typeof swarmEntry.category === "string" ? swarmEntry.category : "process",
|
|
43367
|
-
scope_tag: typeof swarmEntry.scope === "string" ? swarmEntry.scope : "global",
|
|
43368
|
-
confidence: 1,
|
|
43369
|
-
status: "promoted",
|
|
43370
|
-
promotion_source: "manual",
|
|
43371
|
-
promotedAt: now.toISOString(),
|
|
43372
|
-
retrievalOutcomes: { applied: 0, succeededAfter: 0, failedAfter: 0 }
|
|
43373
|
-
};
|
|
43374
|
-
fs19.appendFileSync(hivePath, `${JSON.stringify(hiveEntry)}
|
|
43375
|
-
`, "utf-8");
|
|
43376
|
-
const preview = `${lessonText.slice(0, 50)}${lessonText.length > 50 ? "..." : ""}`;
|
|
43377
|
-
return `Promoted to hive: "${preview}" (confidence: 1.0, source: manual)`;
|
|
43378
|
-
}
|
|
43379
|
-
|
|
43380
43335
|
// src/commands/promote.ts
|
|
43381
43336
|
async function handlePromoteCommand(directory, args) {
|
|
43382
43337
|
let category;
|
|
@@ -43398,12 +43353,6 @@ async function handlePromoteCommand(directory, args) {
|
|
|
43398
43353
|
if (!lessonText && !lessonId) {
|
|
43399
43354
|
return `Usage: /swarm promote "<lesson text>" or /swarm promote --from-swarm <id>`;
|
|
43400
43355
|
}
|
|
43401
|
-
if (lessonText) {
|
|
43402
|
-
const validation = validateLesson2(lessonText);
|
|
43403
|
-
if (!validation.valid) {
|
|
43404
|
-
return `Lesson rejected by validator: ${validation.reason}`;
|
|
43405
|
-
}
|
|
43406
|
-
}
|
|
43407
43356
|
if (lessonId) {
|
|
43408
43357
|
try {
|
|
43409
43358
|
return await promoteFromSwarm(directory, lessonId);
|
|
@@ -43552,7 +43501,7 @@ async function handleQaGatesCommand(directory, args, sessionID) {
|
|
|
43552
43501
|
}
|
|
43553
43502
|
|
|
43554
43503
|
// src/commands/reset.ts
|
|
43555
|
-
import * as
|
|
43504
|
+
import * as fs19 from "fs";
|
|
43556
43505
|
|
|
43557
43506
|
// src/background/manager.ts
|
|
43558
43507
|
init_utils();
|
|
@@ -44253,8 +44202,8 @@ async function handleResetCommand(directory, args) {
|
|
|
44253
44202
|
for (const filename of filesToReset) {
|
|
44254
44203
|
try {
|
|
44255
44204
|
const resolvedPath = validateSwarmPath(directory, filename);
|
|
44256
|
-
if (
|
|
44257
|
-
|
|
44205
|
+
if (fs19.existsSync(resolvedPath)) {
|
|
44206
|
+
fs19.unlinkSync(resolvedPath);
|
|
44258
44207
|
results.push(`- \u2705 Deleted ${filename}`);
|
|
44259
44208
|
} else {
|
|
44260
44209
|
results.push(`- \u23ED\uFE0F ${filename} not found (skipped)`);
|
|
@@ -44271,8 +44220,8 @@ async function handleResetCommand(directory, args) {
|
|
|
44271
44220
|
}
|
|
44272
44221
|
try {
|
|
44273
44222
|
const summariesPath = validateSwarmPath(directory, "summaries");
|
|
44274
|
-
if (
|
|
44275
|
-
|
|
44223
|
+
if (fs19.existsSync(summariesPath)) {
|
|
44224
|
+
fs19.rmSync(summariesPath, { recursive: true, force: true });
|
|
44276
44225
|
results.push("- \u2705 Deleted summaries/ directory");
|
|
44277
44226
|
} else {
|
|
44278
44227
|
results.push("- \u23ED\uFE0F summaries/ not found (skipped)");
|
|
@@ -44292,14 +44241,14 @@ async function handleResetCommand(directory, args) {
|
|
|
44292
44241
|
|
|
44293
44242
|
// src/commands/reset-session.ts
|
|
44294
44243
|
init_utils2();
|
|
44295
|
-
import * as
|
|
44244
|
+
import * as fs20 from "fs";
|
|
44296
44245
|
import * as path30 from "path";
|
|
44297
44246
|
async function handleResetSessionCommand(directory, _args) {
|
|
44298
44247
|
const results = [];
|
|
44299
44248
|
try {
|
|
44300
44249
|
const statePath = validateSwarmPath(directory, "session/state.json");
|
|
44301
|
-
if (
|
|
44302
|
-
|
|
44250
|
+
if (fs20.existsSync(statePath)) {
|
|
44251
|
+
fs20.unlinkSync(statePath);
|
|
44303
44252
|
results.push("\u2705 Deleted .swarm/session/state.json");
|
|
44304
44253
|
} else {
|
|
44305
44254
|
results.push("\u23ED\uFE0F state.json not found (already clean)");
|
|
@@ -44309,14 +44258,14 @@ async function handleResetSessionCommand(directory, _args) {
|
|
|
44309
44258
|
}
|
|
44310
44259
|
try {
|
|
44311
44260
|
const sessionDir = path30.dirname(validateSwarmPath(directory, "session/state.json"));
|
|
44312
|
-
if (
|
|
44313
|
-
const files =
|
|
44261
|
+
if (fs20.existsSync(sessionDir)) {
|
|
44262
|
+
const files = fs20.readdirSync(sessionDir);
|
|
44314
44263
|
const otherFiles = files.filter((f) => f !== "state.json");
|
|
44315
44264
|
let deletedCount = 0;
|
|
44316
44265
|
for (const file3 of otherFiles) {
|
|
44317
44266
|
const filePath = path30.join(sessionDir, file3);
|
|
44318
|
-
if (
|
|
44319
|
-
|
|
44267
|
+
if (fs20.lstatSync(filePath).isFile()) {
|
|
44268
|
+
fs20.unlinkSync(filePath);
|
|
44320
44269
|
deletedCount++;
|
|
44321
44270
|
}
|
|
44322
44271
|
}
|
|
@@ -44421,18 +44370,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
44421
44370
|
|
|
44422
44371
|
// src/commands/rollback.ts
|
|
44423
44372
|
init_utils2();
|
|
44424
|
-
import * as
|
|
44373
|
+
import * as fs21 from "fs";
|
|
44425
44374
|
import * as path32 from "path";
|
|
44426
44375
|
async function handleRollbackCommand(directory, args) {
|
|
44427
44376
|
const phaseArg = args[0];
|
|
44428
44377
|
if (!phaseArg) {
|
|
44429
44378
|
const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
44430
|
-
if (!
|
|
44379
|
+
if (!fs21.existsSync(manifestPath2)) {
|
|
44431
44380
|
return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
|
|
44432
44381
|
}
|
|
44433
44382
|
let manifest2;
|
|
44434
44383
|
try {
|
|
44435
|
-
manifest2 = JSON.parse(
|
|
44384
|
+
manifest2 = JSON.parse(fs21.readFileSync(manifestPath2, "utf-8"));
|
|
44436
44385
|
} catch {
|
|
44437
44386
|
return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
|
|
44438
44387
|
}
|
|
@@ -44454,12 +44403,12 @@ async function handleRollbackCommand(directory, args) {
|
|
|
44454
44403
|
return "Error: Phase number must be a positive integer.";
|
|
44455
44404
|
}
|
|
44456
44405
|
const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
|
|
44457
|
-
if (!
|
|
44406
|
+
if (!fs21.existsSync(manifestPath)) {
|
|
44458
44407
|
return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
|
|
44459
44408
|
}
|
|
44460
44409
|
let manifest;
|
|
44461
44410
|
try {
|
|
44462
|
-
manifest = JSON.parse(
|
|
44411
|
+
manifest = JSON.parse(fs21.readFileSync(manifestPath, "utf-8"));
|
|
44463
44412
|
} catch {
|
|
44464
44413
|
return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
|
|
44465
44414
|
}
|
|
@@ -44469,10 +44418,10 @@ async function handleRollbackCommand(directory, args) {
|
|
|
44469
44418
|
return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
|
|
44470
44419
|
}
|
|
44471
44420
|
const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
|
|
44472
|
-
if (!
|
|
44421
|
+
if (!fs21.existsSync(checkpointDir)) {
|
|
44473
44422
|
return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
|
|
44474
44423
|
}
|
|
44475
|
-
const checkpointFiles =
|
|
44424
|
+
const checkpointFiles = fs21.readdirSync(checkpointDir);
|
|
44476
44425
|
if (checkpointFiles.length === 0) {
|
|
44477
44426
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
44478
44427
|
}
|
|
@@ -44483,7 +44432,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
44483
44432
|
const src = path32.join(checkpointDir, file3);
|
|
44484
44433
|
const dest = path32.join(swarmDir, file3);
|
|
44485
44434
|
try {
|
|
44486
|
-
|
|
44435
|
+
fs21.cpSync(src, dest, { recursive: true, force: true });
|
|
44487
44436
|
successes.push(file3);
|
|
44488
44437
|
} catch (error93) {
|
|
44489
44438
|
failures.push({ file: file3, error: error93.message });
|
|
@@ -44500,7 +44449,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
44500
44449
|
timestamp: new Date().toISOString()
|
|
44501
44450
|
};
|
|
44502
44451
|
try {
|
|
44503
|
-
|
|
44452
|
+
fs21.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
44504
44453
|
`);
|
|
44505
44454
|
} catch (error93) {
|
|
44506
44455
|
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
@@ -44543,11 +44492,11 @@ async function handleSimulateCommand(directory, args) {
|
|
|
44543
44492
|
];
|
|
44544
44493
|
const report = reportLines.filter(Boolean).join(`
|
|
44545
44494
|
`);
|
|
44546
|
-
const
|
|
44495
|
+
const fs22 = await import("fs/promises");
|
|
44547
44496
|
const path33 = await import("path");
|
|
44548
44497
|
const reportPath = path33.join(directory, ".swarm", "simulate-report.md");
|
|
44549
|
-
await
|
|
44550
|
-
await
|
|
44498
|
+
await fs22.mkdir(path33.dirname(reportPath), { recursive: true });
|
|
44499
|
+
await fs22.writeFile(reportPath, report, "utf-8");
|
|
44551
44500
|
return `${darkMatterPairs.length} hidden coupling pairs detected`;
|
|
44552
44501
|
}
|
|
44553
44502
|
|
|
@@ -45080,19 +45029,19 @@ function resolveCommand(tokens) {
|
|
|
45080
45029
|
}
|
|
45081
45030
|
|
|
45082
45031
|
// src/cli/index.ts
|
|
45083
|
-
var CONFIG_DIR = path33.join(process.env.XDG_CONFIG_HOME || path33.join(
|
|
45032
|
+
var CONFIG_DIR = path33.join(process.env.XDG_CONFIG_HOME || path33.join(os6.homedir(), ".config"), "opencode");
|
|
45084
45033
|
var OPENCODE_CONFIG_PATH = path33.join(CONFIG_DIR, "opencode.json");
|
|
45085
45034
|
var PLUGIN_CONFIG_PATH = path33.join(CONFIG_DIR, "opencode-swarm.json");
|
|
45086
45035
|
var PROMPTS_DIR = path33.join(CONFIG_DIR, "opencode-swarm");
|
|
45087
|
-
var OPENCODE_PLUGIN_CACHE_PATH = path33.join(process.env.XDG_CACHE_HOME || path33.join(
|
|
45036
|
+
var OPENCODE_PLUGIN_CACHE_PATH = path33.join(process.env.XDG_CACHE_HOME || path33.join(os6.homedir(), ".cache"), "opencode", "packages", "opencode-swarm@latest");
|
|
45088
45037
|
function ensureDir(dir) {
|
|
45089
|
-
if (!
|
|
45090
|
-
|
|
45038
|
+
if (!fs22.existsSync(dir)) {
|
|
45039
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
45091
45040
|
}
|
|
45092
45041
|
}
|
|
45093
45042
|
function loadJson(filepath) {
|
|
45094
45043
|
try {
|
|
45095
|
-
const content =
|
|
45044
|
+
const content = fs22.readFileSync(filepath, "utf-8");
|
|
45096
45045
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
45097
45046
|
return JSON.parse(stripped);
|
|
45098
45047
|
} catch {
|
|
@@ -45100,7 +45049,7 @@ function loadJson(filepath) {
|
|
|
45100
45049
|
}
|
|
45101
45050
|
}
|
|
45102
45051
|
function saveJson(filepath, data) {
|
|
45103
|
-
|
|
45052
|
+
fs22.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
45104
45053
|
`, "utf-8");
|
|
45105
45054
|
}
|
|
45106
45055
|
async function install() {
|
|
@@ -45140,15 +45089,15 @@ async function install() {
|
|
|
45140
45089
|
console.log("\u2713 Added opencode-swarm to OpenCode plugins");
|
|
45141
45090
|
console.log("\u2713 Disabled default OpenCode agents (explore, general)");
|
|
45142
45091
|
try {
|
|
45143
|
-
if (
|
|
45144
|
-
|
|
45092
|
+
if (fs22.existsSync(OPENCODE_PLUGIN_CACHE_PATH)) {
|
|
45093
|
+
fs22.rmSync(OPENCODE_PLUGIN_CACHE_PATH, { recursive: true, force: true });
|
|
45145
45094
|
console.log("\u2713 Cleared opencode plugin cache (next start will fetch latest)");
|
|
45146
45095
|
}
|
|
45147
45096
|
} catch {
|
|
45148
45097
|
console.warn("\u26A0 Could not clear opencode plugin cache \u2014 you may need to delete it manually:");
|
|
45149
45098
|
console.warn(` ${OPENCODE_PLUGIN_CACHE_PATH}`);
|
|
45150
45099
|
}
|
|
45151
|
-
if (!
|
|
45100
|
+
if (!fs22.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
45152
45101
|
const defaultConfig = {
|
|
45153
45102
|
agents: {
|
|
45154
45103
|
coder: {
|
|
@@ -45251,7 +45200,7 @@ async function uninstall() {
|
|
|
45251
45200
|
`);
|
|
45252
45201
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
45253
45202
|
if (!opencodeConfig) {
|
|
45254
|
-
if (
|
|
45203
|
+
if (fs22.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
45255
45204
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
45256
45205
|
return 1;
|
|
45257
45206
|
} else {
|
|
@@ -45283,13 +45232,13 @@ async function uninstall() {
|
|
|
45283
45232
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
45284
45233
|
if (process.argv.includes("--clean")) {
|
|
45285
45234
|
let cleaned = false;
|
|
45286
|
-
if (
|
|
45287
|
-
|
|
45235
|
+
if (fs22.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
45236
|
+
fs22.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
45288
45237
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
45289
45238
|
cleaned = true;
|
|
45290
45239
|
}
|
|
45291
|
-
if (
|
|
45292
|
-
|
|
45240
|
+
if (fs22.existsSync(PROMPTS_DIR)) {
|
|
45241
|
+
fs22.rmSync(PROMPTS_DIR, { recursive: true });
|
|
45293
45242
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
45294
45243
|
cleaned = true;
|
|
45295
45244
|
}
|