opencode-swarm 7.74.2 → 7.75.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 +771 -128
- package/dist/commands/command-dispatch.d.ts +0 -3
- package/dist/commands/full-auto.d.ts +15 -3
- package/dist/commands/registry.d.ts +3 -3
- package/dist/config/loader.d.ts +6 -11
- package/dist/config/schema.d.ts +22 -0
- package/dist/full-auto/state.d.ts +12 -0
- package/dist/hooks/auto-review.d.ts +82 -0
- package/dist/hooks/review-receipt-collector.d.ts +58 -0
- package/dist/hooks/review-receipt.d.ts +7 -0
- package/dist/index.js +2754 -1631
- package/dist/services/config-doctor.d.ts +15 -0
- package/dist/session/snapshot-reader.d.ts +1 -1
- package/dist/turbo/lean/reviewer.d.ts +3 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "opencode-swarm",
|
|
55
|
-
version: "7.
|
|
55
|
+
version: "7.75.0",
|
|
56
56
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
57
57
|
main: "dist/index.js",
|
|
58
58
|
types: "dist/index.d.ts",
|
|
@@ -18015,6 +18015,7 @@ __export(exports_schema, {
|
|
|
18015
18015
|
AutomationModeSchema: () => AutomationModeSchema,
|
|
18016
18016
|
AutomationConfigSchema: () => AutomationConfigSchema,
|
|
18017
18017
|
AutomationCapabilitiesSchema: () => AutomationCapabilitiesSchema,
|
|
18018
|
+
AutoReviewConfigSchema: () => AutoReviewConfigSchema,
|
|
18018
18019
|
AuthorityConfigSchema: () => AuthorityConfigSchema,
|
|
18019
18020
|
ArchitecturalSupervisionConfigSchema: () => ArchitecturalSupervisionConfigSchema,
|
|
18020
18021
|
AgentThinkingConfigSchema: () => AgentThinkingConfigSchema,
|
|
@@ -18100,7 +18101,7 @@ function resolveExternalSkillsConfig(input) {
|
|
|
18100
18101
|
};
|
|
18101
18102
|
return merged;
|
|
18102
18103
|
}
|
|
18103
|
-
var _internals5, SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentReasoningConfigSchema, AgentThinkingConfigSchema, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, PrMonitorConfigSchema, ParallelizationConfigSchema, WorktreeIsolationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, ExternalSkillCandidateSourceTypeSchema, ExternalSkillCandidateEvaluationVerdictSchema, DiscoverySourceSchema, ExternalSkillCandidateSchema, ExternalSkillsConfigSchema, DEFAULT_EXTERNAL_SKILLS_CONFIG, PluginConfigSchema;
|
|
18104
|
+
var _internals5, SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentReasoningConfigSchema, AgentThinkingConfigSchema, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AutoReviewConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, PrMonitorConfigSchema, ParallelizationConfigSchema, WorktreeIsolationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, ExternalSkillCandidateSourceTypeSchema, ExternalSkillCandidateEvaluationVerdictSchema, DiscoverySourceSchema, ExternalSkillCandidateSchema, ExternalSkillsConfigSchema, DEFAULT_EXTERNAL_SKILLS_CONFIG, PluginConfigSchema;
|
|
18104
18105
|
var init_schema = __esm(() => {
|
|
18105
18106
|
init_zod();
|
|
18106
18107
|
init_constants();
|
|
@@ -18288,6 +18289,12 @@ var init_schema = __esm(() => {
|
|
|
18288
18289
|
"**/token/**"
|
|
18289
18290
|
])
|
|
18290
18291
|
});
|
|
18292
|
+
AutoReviewConfigSchema = exports_external.object({
|
|
18293
|
+
enabled: exports_external.boolean().default(false),
|
|
18294
|
+
trigger: exports_external.enum(["task_completion", "phase_boundary", "both"]).default("phase_boundary"),
|
|
18295
|
+
timeout_ms: exports_external.number().int().min(1e4).max(1800000).default(300000),
|
|
18296
|
+
max_diff_kb: exports_external.number().int().min(16).max(2048).default(256)
|
|
18297
|
+
});
|
|
18291
18298
|
AdversarialDetectionConfigSchema = exports_external.object({
|
|
18292
18299
|
enabled: exports_external.boolean().default(true),
|
|
18293
18300
|
policy: exports_external.enum(["warn", "gate", "ignore"]).default("warn"),
|
|
@@ -18971,6 +18978,7 @@ var init_schema = __esm(() => {
|
|
|
18971
18978
|
guardrails: GuardrailsConfigSchema.optional(),
|
|
18972
18979
|
watchdog: WatchdogConfigSchema.optional(),
|
|
18973
18980
|
self_review: SelfReviewConfigSchema.optional(),
|
|
18981
|
+
auto_review: AutoReviewConfigSchema.optional(),
|
|
18974
18982
|
tool_filter: ToolFilterConfigSchema.optional(),
|
|
18975
18983
|
authority: AuthorityConfigSchema.optional(),
|
|
18976
18984
|
plan_cursor: PlanCursorConfigSchema.optional(),
|
|
@@ -19021,6 +19029,7 @@ var init_schema = __esm(() => {
|
|
|
19021
19029
|
version_check: exports_external.boolean().default(true).optional(),
|
|
19022
19030
|
full_auto: exports_external.object({
|
|
19023
19031
|
enabled: exports_external.boolean().default(false),
|
|
19032
|
+
locked: exports_external.boolean().default(false),
|
|
19024
19033
|
critic_model: exports_external.string().optional(),
|
|
19025
19034
|
max_interactions_per_phase: exports_external.number().int().min(5).max(200).default(50),
|
|
19026
19035
|
deadlock_threshold: exports_external.number().int().min(2).max(10).default(3),
|
|
@@ -19034,6 +19043,7 @@ var init_schema = __esm(() => {
|
|
|
19034
19043
|
protected_paths: exports_external.array(exports_external.string()).default([
|
|
19035
19044
|
".git",
|
|
19036
19045
|
".github/workflows",
|
|
19046
|
+
".opencode",
|
|
19037
19047
|
".swarm",
|
|
19038
19048
|
"package.json",
|
|
19039
19049
|
"package-lock.json",
|
|
@@ -19061,6 +19071,7 @@ var init_schema = __esm(() => {
|
|
|
19061
19071
|
protected_paths: [
|
|
19062
19072
|
".git",
|
|
19063
19073
|
".github/workflows",
|
|
19074
|
+
".opencode",
|
|
19064
19075
|
".swarm",
|
|
19065
19076
|
"package.json",
|
|
19066
19077
|
"package-lock.json",
|
|
@@ -19112,6 +19123,7 @@ var init_schema = __esm(() => {
|
|
|
19112
19123
|
}))
|
|
19113
19124
|
}).optional().default(() => ({
|
|
19114
19125
|
enabled: false,
|
|
19126
|
+
locked: false,
|
|
19115
19127
|
max_interactions_per_phase: 50,
|
|
19116
19128
|
deadlock_threshold: 3,
|
|
19117
19129
|
escalation_mode: "pause",
|
|
@@ -19124,6 +19136,7 @@ var init_schema = __esm(() => {
|
|
|
19124
19136
|
protected_paths: [
|
|
19125
19137
|
".git",
|
|
19126
19138
|
".github/workflows",
|
|
19139
|
+
".opencode",
|
|
19127
19140
|
".swarm",
|
|
19128
19141
|
"package.json",
|
|
19129
19142
|
"package-lock.json",
|
|
@@ -19319,6 +19332,15 @@ function sanitizeExternalSkillsConfig(raw) {
|
|
|
19319
19332
|
delete cleaned.external_skills;
|
|
19320
19333
|
return cleaned;
|
|
19321
19334
|
}
|
|
19335
|
+
function rawFullAutoLocked(raw) {
|
|
19336
|
+
if (!raw || typeof raw !== "object")
|
|
19337
|
+
return false;
|
|
19338
|
+
const fullAuto = raw.full_auto;
|
|
19339
|
+
if (!fullAuto || typeof fullAuto !== "object" || Array.isArray(fullAuto)) {
|
|
19340
|
+
return false;
|
|
19341
|
+
}
|
|
19342
|
+
return fullAuto.locked === true;
|
|
19343
|
+
}
|
|
19322
19344
|
function loadPluginConfig(directory) {
|
|
19323
19345
|
const userConfigPath = path7.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
|
|
19324
19346
|
const projectConfigPath = path7.join(directory, ".opencode", CONFIG_FILENAME);
|
|
@@ -19332,6 +19354,13 @@ function loadPluginConfig(directory) {
|
|
|
19332
19354
|
if (rawProjectConfig) {
|
|
19333
19355
|
mergedRaw = deepMerge(mergedRaw, rawProjectConfig);
|
|
19334
19356
|
}
|
|
19357
|
+
if (rawFullAutoLocked(rawUserConfig) || rawFullAutoLocked(rawProjectConfig)) {
|
|
19358
|
+
const fullAutoRaw = mergedRaw.full_auto && typeof mergedRaw.full_auto === "object" && !Array.isArray(mergedRaw.full_auto) ? mergedRaw.full_auto : {};
|
|
19359
|
+
mergedRaw = {
|
|
19360
|
+
...mergedRaw,
|
|
19361
|
+
full_auto: { ...fullAutoRaw, locked: true }
|
|
19362
|
+
};
|
|
19363
|
+
}
|
|
19335
19364
|
mergedRaw = migratePresetsConfig(mergedRaw);
|
|
19336
19365
|
mergedRaw = sanitizeExternalSkillsConfig(mergedRaw);
|
|
19337
19366
|
const result = PluginConfigSchema.safeParse(mergedRaw);
|
|
@@ -19364,8 +19393,9 @@ function loadPluginConfigWithMeta(directory) {
|
|
|
19364
19393
|
const userResult = loadRawConfigFromPath(userConfigPath);
|
|
19365
19394
|
const projectResult = loadRawConfigFromPath(projectConfigPath);
|
|
19366
19395
|
const loadedFromFile = userResult.fileExisted || projectResult.fileExisted;
|
|
19396
|
+
const configHadErrors = userResult.hadError || projectResult.hadError;
|
|
19367
19397
|
const config2 = loadPluginConfig(directory);
|
|
19368
|
-
return { config: config2, loadedFromFile };
|
|
19398
|
+
return { config: config2, loadedFromFile, configHadErrors };
|
|
19369
19399
|
}
|
|
19370
19400
|
var CONFIG_FILENAME = "opencode-swarm.json", MAX_CONFIG_FILE_BYTES = 102400;
|
|
19371
19401
|
var init_loader = __esm(() => {
|
|
@@ -47713,6 +47743,7 @@ __export(exports_config_doctor, {
|
|
|
47713
47743
|
runConfigDoctor: () => runConfigDoctor,
|
|
47714
47744
|
restoreFromBackup: () => restoreFromBackup,
|
|
47715
47745
|
removeStraySwarmDir: () => removeStraySwarmDir,
|
|
47746
|
+
readDoctorArtifact: () => readDoctorArtifact,
|
|
47716
47747
|
getConfigPaths: () => getConfigPaths,
|
|
47717
47748
|
detectStraySwarmDirs: () => detectStraySwarmDirs,
|
|
47718
47749
|
createConfigBackup: () => createConfigBackup,
|
|
@@ -47722,6 +47753,37 @@ import * as crypto4 from "crypto";
|
|
|
47722
47753
|
import * as fs13 from "fs";
|
|
47723
47754
|
import * as os7 from "os";
|
|
47724
47755
|
import * as path35 from "path";
|
|
47756
|
+
function levenshteinDistance(a, b) {
|
|
47757
|
+
const al = a.length;
|
|
47758
|
+
const bl = b.length;
|
|
47759
|
+
const matrix = [];
|
|
47760
|
+
for (let i = 0;i <= al; i++) {
|
|
47761
|
+
matrix[i] = [i];
|
|
47762
|
+
}
|
|
47763
|
+
for (let j = 0;j <= bl; j++) {
|
|
47764
|
+
matrix[0][j] = j;
|
|
47765
|
+
}
|
|
47766
|
+
for (let i = 1;i <= al; i++) {
|
|
47767
|
+
for (let j = 1;j <= bl; j++) {
|
|
47768
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
47769
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
|
|
47770
|
+
}
|
|
47771
|
+
}
|
|
47772
|
+
return matrix[al][bl];
|
|
47773
|
+
}
|
|
47774
|
+
function emitObjectTypeMismatch(key, value, findings) {
|
|
47775
|
+
if (value !== undefined && (typeof value !== "object" || Array.isArray(value) || value === null)) {
|
|
47776
|
+
findings.push({
|
|
47777
|
+
id: `invalid-${key}-type`,
|
|
47778
|
+
title: `Invalid ${key} type`,
|
|
47779
|
+
description: `"${key}" must be an object, got ${typeof value}`,
|
|
47780
|
+
severity: "error",
|
|
47781
|
+
path: key,
|
|
47782
|
+
currentValue: value,
|
|
47783
|
+
autoFixable: false
|
|
47784
|
+
});
|
|
47785
|
+
}
|
|
47786
|
+
}
|
|
47725
47787
|
function getUserConfigDir3() {
|
|
47726
47788
|
return process.env.XDG_CONFIG_HOME || path35.join(os7.homedir(), ".config");
|
|
47727
47789
|
}
|
|
@@ -47743,23 +47805,39 @@ function isValidConfigPath(configPath, directory) {
|
|
|
47743
47805
|
}
|
|
47744
47806
|
}
|
|
47745
47807
|
}
|
|
47746
|
-
for (const pattern of VALID_CONFIG_PATTERNS) {
|
|
47747
|
-
if (pattern.test(normalizedPath)) {
|
|
47748
|
-
return true;
|
|
47749
|
-
}
|
|
47750
|
-
}
|
|
47751
47808
|
const { userConfigPath, projectConfigPath } = getConfigPaths(directory);
|
|
47752
|
-
const normalizedUser = userConfigPath.replace(/\\/g, "/");
|
|
47753
|
-
const normalizedProject = projectConfigPath.replace(/\\/g, "/");
|
|
47754
47809
|
try {
|
|
47755
47810
|
const resolvedConfig = path35.resolve(configPath);
|
|
47756
|
-
const resolvedUser = path35.resolve(
|
|
47757
|
-
const resolvedProject = path35.resolve(
|
|
47758
|
-
|
|
47811
|
+
const resolvedUser = path35.resolve(userConfigPath);
|
|
47812
|
+
const resolvedProject = path35.resolve(projectConfigPath);
|
|
47813
|
+
if (resolvedConfig !== resolvedUser && resolvedConfig !== resolvedProject) {
|
|
47814
|
+
return false;
|
|
47815
|
+
}
|
|
47816
|
+
try {
|
|
47817
|
+
if (fs13.existsSync(resolvedConfig)) {
|
|
47818
|
+
const realConfig = fs13.realpathSync(resolvedConfig);
|
|
47819
|
+
if (realConfig !== resolvedConfig) {
|
|
47820
|
+
return false;
|
|
47821
|
+
}
|
|
47822
|
+
}
|
|
47823
|
+
} catch {}
|
|
47824
|
+
return true;
|
|
47759
47825
|
} catch {
|
|
47760
47826
|
return false;
|
|
47761
47827
|
}
|
|
47762
47828
|
}
|
|
47829
|
+
function atomicWriteFileSync(filePath, content) {
|
|
47830
|
+
const tmpPath = `${filePath}.tmp.${process.pid}`;
|
|
47831
|
+
fs13.writeFileSync(tmpPath, content, "utf-8");
|
|
47832
|
+
try {
|
|
47833
|
+
fs13.renameSync(tmpPath, filePath);
|
|
47834
|
+
} catch {
|
|
47835
|
+
try {
|
|
47836
|
+
fs13.unlinkSync(filePath);
|
|
47837
|
+
} catch {}
|
|
47838
|
+
fs13.renameSync(tmpPath, filePath);
|
|
47839
|
+
}
|
|
47840
|
+
}
|
|
47763
47841
|
function createConfigBackup(directory) {
|
|
47764
47842
|
const { userConfigPath, projectConfigPath } = getConfigPaths(directory);
|
|
47765
47843
|
let configPath = projectConfigPath;
|
|
@@ -47807,13 +47885,18 @@ function writeBackupArtifact(directory, backup) {
|
|
|
47807
47885
|
content: backup.content,
|
|
47808
47886
|
preview: backup.content.substring(0, 500) + (backup.content.length > 500 ? "..." : "")
|
|
47809
47887
|
};
|
|
47810
|
-
|
|
47888
|
+
atomicWriteFileSync(backupPath, JSON.stringify(artifact, null, 2));
|
|
47811
47889
|
return backupPath;
|
|
47812
47890
|
}
|
|
47813
47891
|
function restoreFromBackup(backupPath, directory) {
|
|
47814
47892
|
if (!fs13.existsSync(backupPath)) {
|
|
47815
47893
|
return null;
|
|
47816
47894
|
}
|
|
47895
|
+
const swarmDir = path35.resolve(path35.join(directory, ".swarm"));
|
|
47896
|
+
const resolvedBackup = path35.resolve(backupPath);
|
|
47897
|
+
if (!resolvedBackup.startsWith(swarmDir + path35.sep) && resolvedBackup !== swarmDir) {
|
|
47898
|
+
return null;
|
|
47899
|
+
}
|
|
47817
47900
|
try {
|
|
47818
47901
|
const artifact = JSON.parse(fs13.readFileSync(backupPath, "utf-8"));
|
|
47819
47902
|
if (!artifact.content || !artifact.configPath || !artifact.contentHash) {
|
|
@@ -47828,12 +47911,13 @@ function restoreFromBackup(backupPath, directory) {
|
|
|
47828
47911
|
if (!isLegacyHash && computedHash !== storedHash) {
|
|
47829
47912
|
return null;
|
|
47830
47913
|
}
|
|
47914
|
+
log("[ConfigDoctor] Warning: restoring from backup with legacy numeric hash (pre-SHA-256). Consider re-backing up.", {});
|
|
47831
47915
|
const targetPath = artifact.configPath;
|
|
47832
47916
|
const targetDir = path35.dirname(targetPath);
|
|
47833
47917
|
if (!fs13.existsSync(targetDir)) {
|
|
47834
47918
|
fs13.mkdirSync(targetDir, { recursive: true });
|
|
47835
47919
|
}
|
|
47836
|
-
|
|
47920
|
+
atomicWriteFileSync(targetPath, artifact.content);
|
|
47837
47921
|
return targetPath;
|
|
47838
47922
|
} catch {
|
|
47839
47923
|
return null;
|
|
@@ -47856,12 +47940,28 @@ function readConfigFromFile(directory) {
|
|
|
47856
47940
|
try {
|
|
47857
47941
|
const config3 = JSON.parse(configContent);
|
|
47858
47942
|
return { config: config3, configPath };
|
|
47859
|
-
} catch {
|
|
47943
|
+
} catch (error93) {
|
|
47944
|
+
log(`[ConfigDoctor] Failed to parse config file: ${configPath}`, {
|
|
47945
|
+
error: error93 instanceof Error ? error93.message : String(error93)
|
|
47946
|
+
});
|
|
47860
47947
|
return null;
|
|
47861
47948
|
}
|
|
47862
47949
|
}
|
|
47863
|
-
function validateConfigKey(path36, value
|
|
47950
|
+
function validateConfigKey(path36, value) {
|
|
47864
47951
|
const findings = [];
|
|
47952
|
+
for (const [depPath, depInfo] of DEPRECATED_FIELDS) {
|
|
47953
|
+
if (path36 === depPath && !depInfo.isDefaultValue(value)) {
|
|
47954
|
+
findings.push({
|
|
47955
|
+
id: "deprecated-field",
|
|
47956
|
+
title: `Deprecated config field: ${depPath}`,
|
|
47957
|
+
description: `Config field "${depPath}" is deprecated. Replacement: ${depInfo.replacement}.`,
|
|
47958
|
+
severity: "info",
|
|
47959
|
+
path: depPath,
|
|
47960
|
+
currentValue: value,
|
|
47961
|
+
autoFixable: false
|
|
47962
|
+
});
|
|
47963
|
+
}
|
|
47964
|
+
}
|
|
47865
47965
|
switch (path36) {
|
|
47866
47966
|
case "agents": {
|
|
47867
47967
|
if (value !== undefined) {
|
|
@@ -47979,8 +48079,9 @@ function validateConfigKey(path36, value, _config) {
|
|
|
47979
48079
|
break;
|
|
47980
48080
|
}
|
|
47981
48081
|
case "hooks": {
|
|
47982
|
-
|
|
47983
|
-
if (
|
|
48082
|
+
emitObjectTypeMismatch("hooks", value, findings);
|
|
48083
|
+
if (value !== undefined && typeof value === "object" && !Array.isArray(value) && value !== null) {
|
|
48084
|
+
const hooks = value;
|
|
47984
48085
|
const validHooks = [
|
|
47985
48086
|
"system_enhancer",
|
|
47986
48087
|
"compaction",
|
|
@@ -48061,8 +48162,42 @@ function validateConfigKey(path36, value, _config) {
|
|
|
48061
48162
|
break;
|
|
48062
48163
|
}
|
|
48063
48164
|
case "swarms": {
|
|
48064
|
-
|
|
48065
|
-
|
|
48165
|
+
if (value !== undefined) {
|
|
48166
|
+
if (typeof value !== "object" || Array.isArray(value) || value === null) {
|
|
48167
|
+
findings.push({
|
|
48168
|
+
id: "invalid-swarms-type",
|
|
48169
|
+
title: "Invalid swarms type",
|
|
48170
|
+
description: `"swarms" must be an object, got ${typeof value}`,
|
|
48171
|
+
severity: "error",
|
|
48172
|
+
path: "swarms",
|
|
48173
|
+
currentValue: value,
|
|
48174
|
+
autoFixable: false
|
|
48175
|
+
});
|
|
48176
|
+
break;
|
|
48177
|
+
}
|
|
48178
|
+
const swarms = value;
|
|
48179
|
+
if (Object.keys(swarms).length === 0) {
|
|
48180
|
+
findings.push({
|
|
48181
|
+
id: "empty-swarms",
|
|
48182
|
+
title: "Empty swarms configuration",
|
|
48183
|
+
description: 'The "swarms" field is an empty object. No swarm configurations are defined.',
|
|
48184
|
+
severity: "info",
|
|
48185
|
+
path: "swarms",
|
|
48186
|
+
autoFixable: false
|
|
48187
|
+
});
|
|
48188
|
+
}
|
|
48189
|
+
for (const swarmId of Object.keys(swarms)) {
|
|
48190
|
+
if (swarmId.includes("..") || swarmId.includes("/") || swarmId.includes("\\") || swarmId.includes("\x00")) {
|
|
48191
|
+
findings.push({
|
|
48192
|
+
id: "swarm-id-path-traversal",
|
|
48193
|
+
title: "Path traversal in swarm ID",
|
|
48194
|
+
description: `Swarm ID "${swarmId}" contains path traversal characters.`,
|
|
48195
|
+
severity: "error",
|
|
48196
|
+
path: `swarms.${swarmId}`,
|
|
48197
|
+
autoFixable: false
|
|
48198
|
+
});
|
|
48199
|
+
}
|
|
48200
|
+
}
|
|
48066
48201
|
const validAgents = new Set(ALL_AGENT_NAMES);
|
|
48067
48202
|
for (const [swarmId, swarmConfig] of Object.entries(swarms)) {
|
|
48068
48203
|
const swarm = swarmConfig;
|
|
@@ -48096,36 +48231,376 @@ function validateConfigKey(path36, value, _config) {
|
|
|
48096
48231
|
}
|
|
48097
48232
|
break;
|
|
48098
48233
|
}
|
|
48234
|
+
case "default_agent": {
|
|
48235
|
+
if (value !== undefined && typeof value !== "string") {
|
|
48236
|
+
findings.push({
|
|
48237
|
+
id: "invalid-default_agent-type",
|
|
48238
|
+
title: "Invalid default_agent type",
|
|
48239
|
+
description: `"default_agent" must be a string, got ${typeof value}`,
|
|
48240
|
+
severity: "error",
|
|
48241
|
+
path: "default_agent",
|
|
48242
|
+
currentValue: value,
|
|
48243
|
+
autoFixable: false
|
|
48244
|
+
});
|
|
48245
|
+
}
|
|
48246
|
+
break;
|
|
48247
|
+
}
|
|
48248
|
+
case "auto_select_architect": {
|
|
48249
|
+
if (value !== undefined && typeof value !== "boolean" && typeof value !== "string") {
|
|
48250
|
+
findings.push({
|
|
48251
|
+
id: "invalid-auto_select_architect-type",
|
|
48252
|
+
title: "Invalid auto_select_architect type",
|
|
48253
|
+
description: `"auto_select_architect" must be a boolean or string, got ${typeof value}`,
|
|
48254
|
+
severity: "error",
|
|
48255
|
+
path: "auto_select_architect",
|
|
48256
|
+
currentValue: value,
|
|
48257
|
+
autoFixable: false
|
|
48258
|
+
});
|
|
48259
|
+
}
|
|
48260
|
+
break;
|
|
48261
|
+
}
|
|
48262
|
+
case "pipeline": {
|
|
48263
|
+
emitObjectTypeMismatch("pipeline", value, findings);
|
|
48264
|
+
break;
|
|
48265
|
+
}
|
|
48266
|
+
case "phase_complete": {
|
|
48267
|
+
emitObjectTypeMismatch("phase_complete", value, findings);
|
|
48268
|
+
break;
|
|
48269
|
+
}
|
|
48270
|
+
case "execution_mode": {
|
|
48271
|
+
const validModes = ["strict", "balanced", "fast"];
|
|
48272
|
+
if (value !== undefined && !validModes.includes(value)) {
|
|
48273
|
+
findings.push({
|
|
48274
|
+
id: "invalid-execution_mode-type",
|
|
48275
|
+
title: "Invalid execution_mode",
|
|
48276
|
+
description: `"execution_mode" must be one of: ${validModes.join(", ")}, got "${value}"`,
|
|
48277
|
+
severity: "error",
|
|
48278
|
+
path: "execution_mode",
|
|
48279
|
+
currentValue: value,
|
|
48280
|
+
autoFixable: false
|
|
48281
|
+
});
|
|
48282
|
+
}
|
|
48283
|
+
break;
|
|
48284
|
+
}
|
|
48285
|
+
case "inject_phase_reminders": {
|
|
48286
|
+
if (value !== undefined && typeof value !== "boolean") {
|
|
48287
|
+
findings.push({
|
|
48288
|
+
id: "invalid-inject_phase_reminders-type",
|
|
48289
|
+
title: "Invalid inject_phase_reminders type",
|
|
48290
|
+
description: `"inject_phase_reminders" must be a boolean, got ${typeof value}`,
|
|
48291
|
+
severity: "error",
|
|
48292
|
+
path: "inject_phase_reminders",
|
|
48293
|
+
currentValue: value,
|
|
48294
|
+
autoFixable: false
|
|
48295
|
+
});
|
|
48296
|
+
}
|
|
48297
|
+
break;
|
|
48298
|
+
}
|
|
48299
|
+
case "gates": {
|
|
48300
|
+
emitObjectTypeMismatch("gates", value, findings);
|
|
48301
|
+
break;
|
|
48302
|
+
}
|
|
48303
|
+
case "context_budget": {
|
|
48304
|
+
emitObjectTypeMismatch("context_budget", value, findings);
|
|
48305
|
+
break;
|
|
48306
|
+
}
|
|
48307
|
+
case "guardrails": {
|
|
48308
|
+
emitObjectTypeMismatch("guardrails", value, findings);
|
|
48309
|
+
break;
|
|
48310
|
+
}
|
|
48311
|
+
case "watchdog": {
|
|
48312
|
+
emitObjectTypeMismatch("watchdog", value, findings);
|
|
48313
|
+
break;
|
|
48314
|
+
}
|
|
48315
|
+
case "self_review": {
|
|
48316
|
+
emitObjectTypeMismatch("self_review", value, findings);
|
|
48317
|
+
break;
|
|
48318
|
+
}
|
|
48319
|
+
case "tool_filter": {
|
|
48320
|
+
emitObjectTypeMismatch("tool_filter", value, findings);
|
|
48321
|
+
break;
|
|
48322
|
+
}
|
|
48323
|
+
case "authority": {
|
|
48324
|
+
emitObjectTypeMismatch("authority", value, findings);
|
|
48325
|
+
break;
|
|
48326
|
+
}
|
|
48327
|
+
case "plan_cursor": {
|
|
48328
|
+
emitObjectTypeMismatch("plan_cursor", value, findings);
|
|
48329
|
+
break;
|
|
48330
|
+
}
|
|
48331
|
+
case "context_map": {
|
|
48332
|
+
emitObjectTypeMismatch("context_map", value, findings);
|
|
48333
|
+
break;
|
|
48334
|
+
}
|
|
48335
|
+
case "evidence": {
|
|
48336
|
+
emitObjectTypeMismatch("evidence", value, findings);
|
|
48337
|
+
break;
|
|
48338
|
+
}
|
|
48339
|
+
case "summaries": {
|
|
48340
|
+
emitObjectTypeMismatch("summaries", value, findings);
|
|
48341
|
+
break;
|
|
48342
|
+
}
|
|
48343
|
+
case "review_passes": {
|
|
48344
|
+
emitObjectTypeMismatch("review_passes", value, findings);
|
|
48345
|
+
break;
|
|
48346
|
+
}
|
|
48347
|
+
case "adversarial_detection": {
|
|
48348
|
+
emitObjectTypeMismatch("adversarial_detection", value, findings);
|
|
48349
|
+
break;
|
|
48350
|
+
}
|
|
48351
|
+
case "adversarial_testing": {
|
|
48352
|
+
emitObjectTypeMismatch("adversarial_testing", value, findings);
|
|
48353
|
+
break;
|
|
48354
|
+
}
|
|
48355
|
+
case "integration_analysis": {
|
|
48356
|
+
emitObjectTypeMismatch("integration_analysis", value, findings);
|
|
48357
|
+
break;
|
|
48358
|
+
}
|
|
48359
|
+
case "docs": {
|
|
48360
|
+
emitObjectTypeMismatch("docs", value, findings);
|
|
48361
|
+
break;
|
|
48362
|
+
}
|
|
48363
|
+
case "design_docs": {
|
|
48364
|
+
emitObjectTypeMismatch("design_docs", value, findings);
|
|
48365
|
+
break;
|
|
48366
|
+
}
|
|
48367
|
+
case "ui_review": {
|
|
48368
|
+
emitObjectTypeMismatch("ui_review", value, findings);
|
|
48369
|
+
break;
|
|
48370
|
+
}
|
|
48371
|
+
case "compaction_advisory": {
|
|
48372
|
+
emitObjectTypeMismatch("compaction_advisory", value, findings);
|
|
48373
|
+
break;
|
|
48374
|
+
}
|
|
48375
|
+
case "lint": {
|
|
48376
|
+
emitObjectTypeMismatch("lint", value, findings);
|
|
48377
|
+
break;
|
|
48378
|
+
}
|
|
48379
|
+
case "secretscan": {
|
|
48380
|
+
emitObjectTypeMismatch("secretscan", value, findings);
|
|
48381
|
+
break;
|
|
48382
|
+
}
|
|
48383
|
+
case "checkpoint": {
|
|
48384
|
+
emitObjectTypeMismatch("checkpoint", value, findings);
|
|
48385
|
+
break;
|
|
48386
|
+
}
|
|
48387
|
+
case "automation": {
|
|
48388
|
+
emitObjectTypeMismatch("automation", value, findings);
|
|
48389
|
+
break;
|
|
48390
|
+
}
|
|
48391
|
+
case "knowledge": {
|
|
48392
|
+
emitObjectTypeMismatch("knowledge", value, findings);
|
|
48393
|
+
break;
|
|
48394
|
+
}
|
|
48395
|
+
case "memory": {
|
|
48396
|
+
emitObjectTypeMismatch("memory", value, findings);
|
|
48397
|
+
break;
|
|
48398
|
+
}
|
|
48399
|
+
case "curator": {
|
|
48400
|
+
emitObjectTypeMismatch("curator", value, findings);
|
|
48401
|
+
break;
|
|
48402
|
+
}
|
|
48403
|
+
case "architectural_supervision": {
|
|
48404
|
+
emitObjectTypeMismatch("architectural_supervision", value, findings);
|
|
48405
|
+
break;
|
|
48406
|
+
}
|
|
48407
|
+
case "knowledge_application": {
|
|
48408
|
+
emitObjectTypeMismatch("knowledge_application", value, findings);
|
|
48409
|
+
break;
|
|
48410
|
+
}
|
|
48411
|
+
case "skillPropagation": {
|
|
48412
|
+
emitObjectTypeMismatch("skillPropagation", value, findings);
|
|
48413
|
+
break;
|
|
48414
|
+
}
|
|
48415
|
+
case "skill_improver": {
|
|
48416
|
+
emitObjectTypeMismatch("skill_improver", value, findings);
|
|
48417
|
+
break;
|
|
48418
|
+
}
|
|
48419
|
+
case "spec_writer": {
|
|
48420
|
+
emitObjectTypeMismatch("spec_writer", value, findings);
|
|
48421
|
+
break;
|
|
48422
|
+
}
|
|
48423
|
+
case "tool_output": {
|
|
48424
|
+
emitObjectTypeMismatch("tool_output", value, findings);
|
|
48425
|
+
break;
|
|
48426
|
+
}
|
|
48427
|
+
case "slop_detector": {
|
|
48428
|
+
emitObjectTypeMismatch("slop_detector", value, findings);
|
|
48429
|
+
break;
|
|
48430
|
+
}
|
|
48431
|
+
case "todo_gate": {
|
|
48432
|
+
emitObjectTypeMismatch("todo_gate", value, findings);
|
|
48433
|
+
break;
|
|
48434
|
+
}
|
|
48435
|
+
case "incremental_verify": {
|
|
48436
|
+
emitObjectTypeMismatch("incremental_verify", value, findings);
|
|
48437
|
+
break;
|
|
48438
|
+
}
|
|
48439
|
+
case "compaction_service": {
|
|
48440
|
+
emitObjectTypeMismatch("compaction_service", value, findings);
|
|
48441
|
+
break;
|
|
48442
|
+
}
|
|
48443
|
+
case "prm": {
|
|
48444
|
+
emitObjectTypeMismatch("prm", value, findings);
|
|
48445
|
+
break;
|
|
48446
|
+
}
|
|
48447
|
+
case "council": {
|
|
48448
|
+
emitObjectTypeMismatch("council", value, findings);
|
|
48449
|
+
break;
|
|
48450
|
+
}
|
|
48451
|
+
case "parallelization": {
|
|
48452
|
+
emitObjectTypeMismatch("parallelization", value, findings);
|
|
48453
|
+
break;
|
|
48454
|
+
}
|
|
48455
|
+
case "worktree": {
|
|
48456
|
+
emitObjectTypeMismatch("worktree", value, findings);
|
|
48457
|
+
break;
|
|
48458
|
+
}
|
|
48459
|
+
case "turbo": {
|
|
48460
|
+
emitObjectTypeMismatch("turbo", value, findings);
|
|
48461
|
+
break;
|
|
48462
|
+
}
|
|
48463
|
+
case "turbo_mode": {
|
|
48464
|
+
if (value !== undefined && typeof value !== "boolean") {
|
|
48465
|
+
findings.push({
|
|
48466
|
+
id: "invalid-turbo_mode-type",
|
|
48467
|
+
title: "Invalid turbo_mode type",
|
|
48468
|
+
description: `"turbo_mode" must be a boolean, got ${typeof value}`,
|
|
48469
|
+
severity: "error",
|
|
48470
|
+
path: "turbo_mode",
|
|
48471
|
+
currentValue: value,
|
|
48472
|
+
autoFixable: false
|
|
48473
|
+
});
|
|
48474
|
+
}
|
|
48475
|
+
break;
|
|
48476
|
+
}
|
|
48477
|
+
case "quiet": {
|
|
48478
|
+
if (value !== undefined && typeof value !== "boolean") {
|
|
48479
|
+
findings.push({
|
|
48480
|
+
id: "invalid-quiet-type",
|
|
48481
|
+
title: "Invalid quiet type",
|
|
48482
|
+
description: `"quiet" must be a boolean, got ${typeof value}`,
|
|
48483
|
+
severity: "error",
|
|
48484
|
+
path: "quiet",
|
|
48485
|
+
currentValue: value,
|
|
48486
|
+
autoFixable: false
|
|
48487
|
+
});
|
|
48488
|
+
}
|
|
48489
|
+
break;
|
|
48490
|
+
}
|
|
48491
|
+
case "version_check": {
|
|
48492
|
+
if (value !== undefined && typeof value !== "boolean") {
|
|
48493
|
+
findings.push({
|
|
48494
|
+
id: "invalid-version_check-type",
|
|
48495
|
+
title: "Invalid version_check type",
|
|
48496
|
+
description: `"version_check" must be a boolean, got ${typeof value}`,
|
|
48497
|
+
severity: "error",
|
|
48498
|
+
path: "version_check",
|
|
48499
|
+
currentValue: value,
|
|
48500
|
+
autoFixable: false
|
|
48501
|
+
});
|
|
48502
|
+
}
|
|
48503
|
+
break;
|
|
48504
|
+
}
|
|
48505
|
+
case "full_auto": {
|
|
48506
|
+
emitObjectTypeMismatch("full_auto", value, findings);
|
|
48507
|
+
break;
|
|
48508
|
+
}
|
|
48509
|
+
case "pr_monitor": {
|
|
48510
|
+
emitObjectTypeMismatch("pr_monitor", value, findings);
|
|
48511
|
+
break;
|
|
48512
|
+
}
|
|
48513
|
+
case "external_skills": {
|
|
48514
|
+
emitObjectTypeMismatch("external_skills", value, findings);
|
|
48515
|
+
break;
|
|
48516
|
+
}
|
|
48517
|
+
default: {
|
|
48518
|
+
const topLevel = path36.split(".")[0];
|
|
48519
|
+
if (KNOWN_TOP_LEVEL_KEYS.has(topLevel)) {
|
|
48520
|
+
break;
|
|
48521
|
+
}
|
|
48522
|
+
const MAX_SUGGESTION_KEY_LENGTH = 100;
|
|
48523
|
+
const lowerTopLevel = topLevel.toLowerCase();
|
|
48524
|
+
let suggestion;
|
|
48525
|
+
let matchCount = 0;
|
|
48526
|
+
if (lowerTopLevel.length <= MAX_SUGGESTION_KEY_LENGTH) {
|
|
48527
|
+
for (const knownKey of KNOWN_TOP_LEVEL_KEYS) {
|
|
48528
|
+
if (levenshteinDistance(lowerTopLevel, knownKey.toLowerCase()) <= 2) {
|
|
48529
|
+
matchCount++;
|
|
48530
|
+
if (matchCount === 1) {
|
|
48531
|
+
suggestion = knownKey;
|
|
48532
|
+
}
|
|
48533
|
+
}
|
|
48534
|
+
}
|
|
48535
|
+
}
|
|
48536
|
+
if (matchCount === 1 && suggestion) {
|
|
48537
|
+
findings.push({
|
|
48538
|
+
id: "unknown-config-key",
|
|
48539
|
+
title: `Unknown config key: ${topLevel}`,
|
|
48540
|
+
description: `Unknown config key "${path36}" is not in the schema. Did you mean "${suggestion}"?`,
|
|
48541
|
+
severity: "warn",
|
|
48542
|
+
path: path36,
|
|
48543
|
+
currentValue: value,
|
|
48544
|
+
autoFixable: false
|
|
48545
|
+
});
|
|
48546
|
+
} else {
|
|
48547
|
+
findings.push({
|
|
48548
|
+
id: "unknown-config-key",
|
|
48549
|
+
title: `Unknown config key: ${topLevel}`,
|
|
48550
|
+
description: `Unknown config key "${path36}" is not in the schema.`,
|
|
48551
|
+
severity: "warn",
|
|
48552
|
+
path: path36,
|
|
48553
|
+
currentValue: value,
|
|
48554
|
+
autoFixable: false
|
|
48555
|
+
});
|
|
48556
|
+
}
|
|
48557
|
+
break;
|
|
48558
|
+
}
|
|
48099
48559
|
}
|
|
48100
48560
|
return findings;
|
|
48101
48561
|
}
|
|
48102
|
-
function walkConfigAndValidate(obj, path36,
|
|
48562
|
+
function walkConfigAndValidate(obj, path36, findings, visited = new WeakSet) {
|
|
48103
48563
|
if (obj === null || obj === undefined) {
|
|
48104
48564
|
return;
|
|
48105
48565
|
}
|
|
48106
48566
|
if (path36 && typeof obj === "object" && !Array.isArray(obj)) {
|
|
48107
|
-
const keyFindings = validateConfigKey(path36, obj
|
|
48567
|
+
const keyFindings = validateConfigKey(path36, obj);
|
|
48108
48568
|
findings.push(...keyFindings);
|
|
48109
48569
|
}
|
|
48110
48570
|
if (typeof obj !== "object") {
|
|
48111
|
-
const keyFindings = validateConfigKey(path36, obj
|
|
48571
|
+
const keyFindings = validateConfigKey(path36, obj);
|
|
48112
48572
|
findings.push(...keyFindings);
|
|
48113
48573
|
return;
|
|
48114
48574
|
}
|
|
48575
|
+
if (visited.has(obj)) {
|
|
48576
|
+
findings.push({
|
|
48577
|
+
id: "circular-reference",
|
|
48578
|
+
title: `Circular reference detected at ${path36}`,
|
|
48579
|
+
description: `Config value at "${path36}" contains a circular reference. Validation stopped at this path to prevent stack overflow.`,
|
|
48580
|
+
severity: "error",
|
|
48581
|
+
path: path36,
|
|
48582
|
+
currentValue: "[circular]",
|
|
48583
|
+
autoFixable: false
|
|
48584
|
+
});
|
|
48585
|
+
return;
|
|
48586
|
+
}
|
|
48587
|
+
visited.add(obj);
|
|
48115
48588
|
if (Array.isArray(obj)) {
|
|
48589
|
+
const arrayFindings = validateConfigKey(path36, obj);
|
|
48590
|
+
findings.push(...arrayFindings);
|
|
48116
48591
|
obj.forEach((item, index) => {
|
|
48117
|
-
walkConfigAndValidate(item, `${path36}[${index}]`,
|
|
48592
|
+
walkConfigAndValidate(item, `${path36}[${index}]`, findings, visited);
|
|
48118
48593
|
});
|
|
48119
48594
|
return;
|
|
48120
48595
|
}
|
|
48121
48596
|
for (const [key, value] of Object.entries(obj)) {
|
|
48122
48597
|
const newPath = path36 ? `${path36}.${key}` : key;
|
|
48123
|
-
walkConfigAndValidate(value, newPath,
|
|
48598
|
+
walkConfigAndValidate(value, newPath, findings, visited);
|
|
48124
48599
|
}
|
|
48125
48600
|
}
|
|
48126
48601
|
function runConfigDoctor(config3, directory) {
|
|
48127
48602
|
const findings = [];
|
|
48128
|
-
walkConfigAndValidate(config3, "",
|
|
48603
|
+
walkConfigAndValidate(config3, "", findings);
|
|
48129
48604
|
const summary = {
|
|
48130
48605
|
info: findings.filter((f) => f.severity === "info").length,
|
|
48131
48606
|
warn: findings.filter((f) => f.severity === "warn").length,
|
|
@@ -48243,11 +48718,45 @@ function applySafeAutoFixes(directory, result) {
|
|
|
48243
48718
|
if (!fs13.existsSync(configDir)) {
|
|
48244
48719
|
fs13.mkdirSync(configDir, { recursive: true });
|
|
48245
48720
|
}
|
|
48246
|
-
|
|
48721
|
+
atomicWriteFileSync(configPath, JSON.stringify(config3, null, 2));
|
|
48247
48722
|
updatedConfigPath = configPath;
|
|
48248
48723
|
}
|
|
48249
48724
|
return { appliedFixes, updatedConfigPath };
|
|
48250
48725
|
}
|
|
48726
|
+
function readDoctorArtifact(directory) {
|
|
48727
|
+
try {
|
|
48728
|
+
const artifactPath = path35.join(directory, ".swarm", "config-doctor.json");
|
|
48729
|
+
if (!fs13.existsSync(artifactPath)) {
|
|
48730
|
+
return null;
|
|
48731
|
+
}
|
|
48732
|
+
const content = fs13.readFileSync(artifactPath, "utf-8");
|
|
48733
|
+
const artifact = JSON.parse(content);
|
|
48734
|
+
const summary = artifact.summary;
|
|
48735
|
+
if (!summary || typeof summary !== "object") {
|
|
48736
|
+
return null;
|
|
48737
|
+
}
|
|
48738
|
+
const infoVal = summary.info;
|
|
48739
|
+
const warnVal = summary.warn;
|
|
48740
|
+
const errorVal = summary.error;
|
|
48741
|
+
if (typeof infoVal !== "number" || !Number.isFinite(infoVal) || typeof warnVal !== "number" || !Number.isFinite(warnVal) || typeof errorVal !== "number" || !Number.isFinite(errorVal)) {
|
|
48742
|
+
return null;
|
|
48743
|
+
}
|
|
48744
|
+
const ts = artifact.timestamp;
|
|
48745
|
+
if (typeof ts !== "number" || !Number.isFinite(ts)) {
|
|
48746
|
+
return null;
|
|
48747
|
+
}
|
|
48748
|
+
const findingsCount = infoVal + warnVal + errorVal;
|
|
48749
|
+
const findings = artifact.findings;
|
|
48750
|
+
const autoFixableCount = Array.isArray(findings) ? findings.filter((f) => f.autoFixable === true).length : 0;
|
|
48751
|
+
return {
|
|
48752
|
+
timestamp: new Date(ts).toISOString(),
|
|
48753
|
+
findingsCount,
|
|
48754
|
+
autoFixableCount
|
|
48755
|
+
};
|
|
48756
|
+
} catch {
|
|
48757
|
+
return null;
|
|
48758
|
+
}
|
|
48759
|
+
}
|
|
48251
48760
|
function writeDoctorArtifact(directory, result) {
|
|
48252
48761
|
const swarmDir = path35.join(directory, ".swarm");
|
|
48253
48762
|
if (!fs13.existsSync(swarmDir)) {
|
|
@@ -48275,7 +48784,7 @@ function writeDoctorArtifact(directory, result) {
|
|
|
48275
48784
|
} : null
|
|
48276
48785
|
}))
|
|
48277
48786
|
};
|
|
48278
|
-
|
|
48787
|
+
atomicWriteFileSync(artifactPath, JSON.stringify(guiOutput, null, 2));
|
|
48279
48788
|
return artifactPath;
|
|
48280
48789
|
}
|
|
48281
48790
|
function shouldRunOnStartup(automationConfig) {
|
|
@@ -48428,15 +48937,46 @@ function removeStraySwarmDir(projectRoot, strayPath) {
|
|
|
48428
48937
|
};
|
|
48429
48938
|
}
|
|
48430
48939
|
}
|
|
48431
|
-
var
|
|
48940
|
+
var KNOWN_TOP_LEVEL_KEYS, DEPRECATED_FIELDS, DANGEROUS_PATH_SEGMENTS;
|
|
48432
48941
|
var init_config_doctor = __esm(() => {
|
|
48433
48942
|
init_constants();
|
|
48434
48943
|
init_schema();
|
|
48435
48944
|
init_utils();
|
|
48436
|
-
|
|
48437
|
-
|
|
48438
|
-
|
|
48439
|
-
|
|
48945
|
+
KNOWN_TOP_LEVEL_KEYS = new Set(Object.keys(PluginConfigSchema.shape));
|
|
48946
|
+
DEPRECATED_FIELDS = new Map([
|
|
48947
|
+
[
|
|
48948
|
+
"skill_improver.model",
|
|
48949
|
+
{
|
|
48950
|
+
message: "deprecated",
|
|
48951
|
+
replacement: "agents.skill_improver.model",
|
|
48952
|
+
isDefaultValue: (v) => v === null
|
|
48953
|
+
}
|
|
48954
|
+
],
|
|
48955
|
+
[
|
|
48956
|
+
"skill_improver.fallback_models",
|
|
48957
|
+
{
|
|
48958
|
+
message: "deprecated",
|
|
48959
|
+
replacement: "agents.skill_improver.fallback_models",
|
|
48960
|
+
isDefaultValue: (v) => Array.isArray(v) && v.length === 0
|
|
48961
|
+
}
|
|
48962
|
+
],
|
|
48963
|
+
[
|
|
48964
|
+
"spec_writer.model",
|
|
48965
|
+
{
|
|
48966
|
+
message: "deprecated",
|
|
48967
|
+
replacement: "agents.spec_writer.model",
|
|
48968
|
+
isDefaultValue: (v) => v === null
|
|
48969
|
+
}
|
|
48970
|
+
],
|
|
48971
|
+
[
|
|
48972
|
+
"spec_writer.fallback_models",
|
|
48973
|
+
{
|
|
48974
|
+
message: "deprecated",
|
|
48975
|
+
replacement: "agents.spec_writer.fallback_models",
|
|
48976
|
+
isDefaultValue: (v) => Array.isArray(v) && v.length === 0
|
|
48977
|
+
}
|
|
48978
|
+
]
|
|
48979
|
+
]);
|
|
48440
48980
|
DANGEROUS_PATH_SEGMENTS = new Set([
|
|
48441
48981
|
"__proto__",
|
|
48442
48982
|
"constructor",
|
|
@@ -50099,7 +50639,14 @@ async function handleDoctorCommand(directory, args) {
|
|
|
50099
50639
|
const fixResult = await runConfigDoctorWithFixes2(directory, config3, true);
|
|
50100
50640
|
output = formatDoctorMarkdown(fixResult.result);
|
|
50101
50641
|
} else {
|
|
50102
|
-
|
|
50642
|
+
const lastRun = readDoctorArtifact(directory);
|
|
50643
|
+
let markdown = formatDoctorMarkdown(result);
|
|
50644
|
+
if (lastRun) {
|
|
50645
|
+
markdown = `Last run: ${lastRun.timestamp}, ${lastRun.findingsCount} findings (${lastRun.autoFixableCount} auto-fixable)
|
|
50646
|
+
|
|
50647
|
+
` + markdown;
|
|
50648
|
+
}
|
|
50649
|
+
output = markdown;
|
|
50103
50650
|
}
|
|
50104
50651
|
const strayDirs = detectStraySwarmDirs(directory);
|
|
50105
50652
|
if (strayDirs.length > 0) {
|
|
@@ -50725,6 +51272,25 @@ function emptyCounters() {
|
|
|
50725
51272
|
consecutiveNoProgressTurns: 0
|
|
50726
51273
|
};
|
|
50727
51274
|
}
|
|
51275
|
+
function sanitizeRunState(raw) {
|
|
51276
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
51277
|
+
return null;
|
|
51278
|
+
const r = raw;
|
|
51279
|
+
if (typeof r.sessionID !== "string" || !r.sessionID)
|
|
51280
|
+
return null;
|
|
51281
|
+
const mode = VALID_RUN_MODES.has(r.mode) ? r.mode : "supervised";
|
|
51282
|
+
const status = VALID_RUN_STATUSES.has(r.status) ? r.status : "idle";
|
|
51283
|
+
return { ...raw, mode, status };
|
|
51284
|
+
}
|
|
51285
|
+
function sanitizeSessions(raw) {
|
|
51286
|
+
const result = {};
|
|
51287
|
+
for (const [id, session] of Object.entries(raw)) {
|
|
51288
|
+
const sanitized = sanitizeRunState(session);
|
|
51289
|
+
if (sanitized)
|
|
51290
|
+
result[id] = sanitized;
|
|
51291
|
+
}
|
|
51292
|
+
return result;
|
|
51293
|
+
}
|
|
50728
51294
|
function emptyState(sessionID, mode = "supervised") {
|
|
50729
51295
|
const now = nowISO();
|
|
50730
51296
|
return {
|
|
@@ -50789,26 +51355,45 @@ function clearStateUnreadable() {
|
|
|
50789
51355
|
stateUnreadable = false;
|
|
50790
51356
|
stateUnreadableReason = "";
|
|
50791
51357
|
}
|
|
51358
|
+
function isFullAutoStateUnreadable() {
|
|
51359
|
+
return { unreadable: stateUnreadable, reason: stateUnreadableReason };
|
|
51360
|
+
}
|
|
50792
51361
|
function readPersisted(directory) {
|
|
50793
51362
|
try {
|
|
50794
51363
|
const filePath = validateSwarmPath(directory, STATE_FILE);
|
|
50795
|
-
|
|
51364
|
+
let stats;
|
|
51365
|
+
try {
|
|
51366
|
+
stats = fs15.statSync(filePath);
|
|
51367
|
+
} catch {
|
|
50796
51368
|
clearStateUnreadable();
|
|
51369
|
+
readCache.delete(filePath);
|
|
50797
51370
|
return emptyPersisted();
|
|
50798
51371
|
}
|
|
51372
|
+
const cached3 = readCache.get(filePath);
|
|
51373
|
+
if (cached3 && cached3.mtimeMs === stats.mtimeMs && cached3.size === stats.size) {
|
|
51374
|
+
clearStateUnreadable();
|
|
51375
|
+
return structuredClone(cached3.state);
|
|
51376
|
+
}
|
|
50799
51377
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
50800
51378
|
const parsed = JSON.parse(raw);
|
|
50801
51379
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 2 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
50802
51380
|
markStateUnreadable(`malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
51381
|
+
readCache.delete(filePath);
|
|
50803
51382
|
return emptyPersisted();
|
|
50804
51383
|
}
|
|
50805
51384
|
clearStateUnreadable();
|
|
50806
|
-
|
|
51385
|
+
const state = {
|
|
50807
51386
|
version: 2,
|
|
50808
51387
|
updatedAt: parsed.updatedAt ?? nowISO(),
|
|
50809
51388
|
oversightSequence: typeof parsed.oversightSequence === "number" ? parsed.oversightSequence : 0,
|
|
50810
|
-
sessions: parsed.sessions
|
|
51389
|
+
sessions: sanitizeSessions(parsed.sessions)
|
|
50811
51390
|
};
|
|
51391
|
+
readCache.set(filePath, {
|
|
51392
|
+
mtimeMs: stats.mtimeMs,
|
|
51393
|
+
size: stats.size,
|
|
51394
|
+
state: structuredClone(state)
|
|
51395
|
+
});
|
|
51396
|
+
return state;
|
|
50812
51397
|
} catch (error93) {
|
|
50813
51398
|
const reason = error93 instanceof Error ? error93.message : String(error93);
|
|
50814
51399
|
error(`[full-auto/state] Failed to read ${STATE_FILE}: ${reason} \u2014 attempting .bak recovery`);
|
|
@@ -50824,7 +51409,7 @@ function readPersisted(directory) {
|
|
|
50824
51409
|
version: 2,
|
|
50825
51410
|
updatedAt: parsed.updatedAt ?? nowISO(),
|
|
50826
51411
|
oversightSequence: typeof parsed.oversightSequence === "number" ? parsed.oversightSequence : 0,
|
|
50827
|
-
sessions: parsed.sessions
|
|
51412
|
+
sessions: sanitizeSessions(parsed.sessions)
|
|
50828
51413
|
};
|
|
50829
51414
|
}
|
|
50830
51415
|
}
|
|
@@ -50832,6 +51417,7 @@ function readPersisted(directory) {
|
|
|
50832
51417
|
error(`[full-auto/state] .bak recovery also failed: ${bakError instanceof Error ? bakError.message : String(bakError)}`);
|
|
50833
51418
|
}
|
|
50834
51419
|
markStateUnreadable(`canonical=${reason}; .bak=missing-or-corrupt`);
|
|
51420
|
+
readCache.clear();
|
|
50835
51421
|
return emptyPersisted();
|
|
50836
51422
|
}
|
|
50837
51423
|
}
|
|
@@ -50869,6 +51455,7 @@ function writePersisted(directory, persisted) {
|
|
|
50869
51455
|
}
|
|
50870
51456
|
} catch {}
|
|
50871
51457
|
fs15.renameSync(tmpPath, filePath);
|
|
51458
|
+
readCache.delete(filePath);
|
|
50872
51459
|
const readback = fs15.readFileSync(filePath, "utf-8");
|
|
50873
51460
|
const parsed = JSON.parse(readback);
|
|
50874
51461
|
if (parsed?.version !== 2) {
|
|
@@ -50880,6 +51467,10 @@ function writePersisted(directory, persisted) {
|
|
|
50880
51467
|
throw new Error(`Full-Auto state persistence failed: ${msg}`);
|
|
50881
51468
|
}
|
|
50882
51469
|
}
|
|
51470
|
+
function loadFullAutoRunState(directory, sessionID) {
|
|
51471
|
+
const persisted = readPersisted(directory);
|
|
51472
|
+
return persisted.sessions[sessionID];
|
|
51473
|
+
}
|
|
50883
51474
|
function startFullAutoRun(directory, sessionID, config3, options = {}) {
|
|
50884
51475
|
return withStateLock(directory, () => {
|
|
50885
51476
|
const persisted = readPersisted(directory);
|
|
@@ -50911,14 +51502,15 @@ function startFullAutoRun(directory, sessionID, config3, options = {}) {
|
|
|
50911
51502
|
return state;
|
|
50912
51503
|
});
|
|
50913
51504
|
}
|
|
50914
|
-
function
|
|
51505
|
+
function disarmFullAutoRun(directory, sessionID, reason) {
|
|
50915
51506
|
return withStateLock(directory, () => {
|
|
50916
51507
|
const persisted = readPersisted(directory);
|
|
50917
51508
|
const state = persisted.sessions[sessionID];
|
|
50918
51509
|
if (!state)
|
|
50919
51510
|
return;
|
|
50920
|
-
state.status = "
|
|
51511
|
+
state.status = "idle";
|
|
50921
51512
|
state.pauseReason = reason;
|
|
51513
|
+
state.terminateReason = undefined;
|
|
50922
51514
|
state.updatedAt = nowISO();
|
|
50923
51515
|
persisted.sessions[sessionID] = state;
|
|
50924
51516
|
writePersisted(directory, persisted);
|
|
@@ -50939,15 +51531,26 @@ function terminateFullAutoRun(directory, sessionID, reason) {
|
|
|
50939
51531
|
return state;
|
|
50940
51532
|
});
|
|
50941
51533
|
}
|
|
50942
|
-
var import_proper_lockfile8, lockfile8, STATE_FILE = "full-auto-state.json", stateUnreadable = false, stateUnreadableReason = "";
|
|
51534
|
+
var import_proper_lockfile8, lockfile8, STATE_FILE = "full-auto-state.json", VALID_RUN_MODES, VALID_RUN_STATUSES, stateUnreadable = false, stateUnreadableReason = "", readCache;
|
|
50943
51535
|
var init_state2 = __esm(() => {
|
|
50944
51536
|
init_utils2();
|
|
50945
51537
|
init_logger();
|
|
50946
51538
|
import_proper_lockfile8 = __toESM(require_proper_lockfile(), 1);
|
|
50947
51539
|
lockfile8 = import_proper_lockfile8.default;
|
|
51540
|
+
VALID_RUN_MODES = new Set(["assisted", "supervised", "strict"]);
|
|
51541
|
+
VALID_RUN_STATUSES = new Set([
|
|
51542
|
+
"idle",
|
|
51543
|
+
"running",
|
|
51544
|
+
"paused",
|
|
51545
|
+
"terminated"
|
|
51546
|
+
]);
|
|
51547
|
+
readCache = new Map;
|
|
50948
51548
|
});
|
|
50949
51549
|
|
|
50950
51550
|
// src/commands/full-auto.ts
|
|
51551
|
+
function isValidMode(value) {
|
|
51552
|
+
return VALID_MODES.includes(value);
|
|
51553
|
+
}
|
|
50951
51554
|
async function handleFullAutoCommand(directory, args, sessionID) {
|
|
50952
51555
|
if (!sessionID || sessionID.trim() === "") {
|
|
50953
51556
|
return "Error: No active session context. Full-Auto Mode requires an active session. Use /swarm-full-auto from within an OpenCode session, or start a session first.";
|
|
@@ -50957,39 +51560,63 @@ async function handleFullAutoCommand(directory, args, sessionID) {
|
|
|
50957
51560
|
return "Error: No active session. Full-Auto Mode requires an active session to operate.";
|
|
50958
51561
|
}
|
|
50959
51562
|
const arg = args[0]?.toLowerCase();
|
|
51563
|
+
if (arg === "status") {
|
|
51564
|
+
return buildStatusReport(directory, sessionID, session.fullAutoMode);
|
|
51565
|
+
}
|
|
50960
51566
|
let newFullAutoMode;
|
|
51567
|
+
let modeOverride;
|
|
50961
51568
|
if (arg === "on") {
|
|
50962
51569
|
newFullAutoMode = true;
|
|
51570
|
+
const modeArg = args[1]?.toLowerCase();
|
|
51571
|
+
if (modeArg) {
|
|
51572
|
+
if (!isValidMode(modeArg)) {
|
|
51573
|
+
return `Error: invalid Full-Auto mode '${args[1]}'. Valid modes: ${VALID_MODES.join(", ")}.`;
|
|
51574
|
+
}
|
|
51575
|
+
modeOverride = modeArg;
|
|
51576
|
+
}
|
|
50963
51577
|
} else if (arg === "off") {
|
|
50964
51578
|
newFullAutoMode = false;
|
|
51579
|
+
} else if (arg && isValidMode(arg)) {
|
|
51580
|
+
newFullAutoMode = true;
|
|
51581
|
+
modeOverride = arg;
|
|
50965
51582
|
} else {
|
|
50966
51583
|
newFullAutoMode = !session.fullAutoMode;
|
|
50967
51584
|
}
|
|
50968
|
-
if (newFullAutoMode && !swarmState.fullAutoEnabledInConfig) {
|
|
50969
|
-
return "Error: Full-Auto Mode cannot be enabled because full_auto.enabled is not set to true in the swarm plugin config. The autonomous oversight hook is inactive without config-level enablement. Set full_auto.enabled = true in your opencode-swarm config and restart.";
|
|
50970
|
-
}
|
|
50971
51585
|
let v2Status = "unavailable";
|
|
50972
51586
|
let modeLabel = "supervised";
|
|
50973
51587
|
let denialMaxConsecutive = 3;
|
|
50974
51588
|
let denialMaxTotal = 20;
|
|
50975
51589
|
let failClosed = true;
|
|
50976
51590
|
let durableError;
|
|
51591
|
+
let criticModelAdvisory = "";
|
|
50977
51592
|
try {
|
|
50978
|
-
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
51593
|
+
const { config: config3, configHadErrors } = loadPluginConfigWithMeta(directory);
|
|
50979
51594
|
const fullAutoConfig = config3.full_auto;
|
|
50980
|
-
|
|
51595
|
+
if (newFullAutoMode && configHadErrors) {
|
|
51596
|
+
return "Error: Full-Auto Mode cannot be enabled \u2014 a swarm plugin config file exists but could not be loaded, so full_auto.locked cannot be verified. Fix the config file (see warnings above) and retry.";
|
|
51597
|
+
}
|
|
51598
|
+
if (newFullAutoMode && fullAutoConfig?.locked === true) {
|
|
51599
|
+
return "Error: Full-Auto Mode is locked for this project (full_auto.locked is true in the swarm plugin config). Runtime activation is disabled by configuration; remove the lock to use /swarm full-auto on.";
|
|
51600
|
+
}
|
|
51601
|
+
const effectiveMode = modeOverride ?? fullAutoConfig?.mode ?? "supervised";
|
|
51602
|
+
modeLabel = effectiveMode;
|
|
50981
51603
|
denialMaxConsecutive = fullAutoConfig?.denials?.max_consecutive ?? 3;
|
|
50982
51604
|
denialMaxTotal = fullAutoConfig?.denials?.max_total ?? 20;
|
|
50983
51605
|
failClosed = fullAutoConfig?.fail_closed !== false;
|
|
50984
51606
|
if (newFullAutoMode) {
|
|
50985
|
-
startFullAutoRun(directory, sessionID, fullAutoConfig);
|
|
51607
|
+
startFullAutoRun(directory, sessionID, fullAutoConfig ? { ...fullAutoConfig, mode: effectiveMode } : undefined);
|
|
50986
51608
|
v2Status = "running";
|
|
51609
|
+
const criticModel = fullAutoConfig?.critic_model ?? config3.agents?.critic?.model;
|
|
51610
|
+
const architectModel = config3.agents?.architect?.model;
|
|
51611
|
+
if (criticModel && architectModel && criticModel === architectModel) {
|
|
51612
|
+
criticModelAdvisory = " WARNING: critic model matches architect model \u2014 set full_auto.critic_model (or agents.critic.model) to a different model for independent oversight.";
|
|
51613
|
+
}
|
|
50987
51614
|
} else {
|
|
50988
|
-
const
|
|
50989
|
-
if (!
|
|
51615
|
+
const disarmed = disarmFullAutoRun(directory, sessionID, "/swarm full-auto off");
|
|
51616
|
+
if (!disarmed) {
|
|
50990
51617
|
terminateFullAutoRun(directory, sessionID, "never started");
|
|
50991
51618
|
}
|
|
50992
|
-
v2Status = "
|
|
51619
|
+
v2Status = "idle";
|
|
50993
51620
|
}
|
|
50994
51621
|
} catch (error93) {
|
|
50995
51622
|
durableError = error93 instanceof Error ? error93.message : String(error93);
|
|
@@ -51018,13 +51645,54 @@ async function handleFullAutoCommand(directory, args, sessionID) {
|
|
|
51018
51645
|
"Full-Auto Mode enabled",
|
|
51019
51646
|
`(v2 mode=${modeLabel}, fail_closed=${failClosed},`,
|
|
51020
51647
|
`denials max ${denialMaxConsecutive} consecutive / ${denialMaxTotal} total)`
|
|
51021
|
-
].join(" ");
|
|
51648
|
+
].join(" ") + criticModelAdvisory;
|
|
51649
|
+
}
|
|
51650
|
+
function buildStatusReport(directory, sessionID, sessionFlag) {
|
|
51651
|
+
const lines = [];
|
|
51652
|
+
lines.push(`Full-Auto session flag: ${sessionFlag ? "on" : "off"}`);
|
|
51653
|
+
try {
|
|
51654
|
+
const { config: config3, configHadErrors } = loadPluginConfigWithMeta(directory);
|
|
51655
|
+
if (configHadErrors) {
|
|
51656
|
+
lines.push("Config: UNREADABLE (a config file exists but could not be loaded; `full_auto.locked` cannot be verified, so runtime activation refuses by fail-closed default). Fix the config file to restore normal status.");
|
|
51657
|
+
}
|
|
51658
|
+
if (config3.full_auto?.locked === true) {
|
|
51659
|
+
lines.push("Config: locked (runtime activation disabled via full_auto.locked)");
|
|
51660
|
+
}
|
|
51661
|
+
} catch {}
|
|
51662
|
+
try {
|
|
51663
|
+
const runState = loadFullAutoRunState(directory, sessionID);
|
|
51664
|
+
const stateHealth = isFullAutoStateUnreadable();
|
|
51665
|
+
if (stateHealth.unreadable) {
|
|
51666
|
+
lines.push(`Durable run-state: UNREADABLE (${stateHealth.reason}). Non-read-only tools are blocked fail-closed until .swarm/full-auto-state.json (or .bak) is restored or deleted.`);
|
|
51667
|
+
} else if (!runState) {
|
|
51668
|
+
lines.push("Durable run-state: none (no Full-Auto run for this session)");
|
|
51669
|
+
} else {
|
|
51670
|
+
lines.push(`Durable run-state: ${runState.status} (mode=${runState.mode})`);
|
|
51671
|
+
if (runState.pauseReason) {
|
|
51672
|
+
lines.push(`Pause reason: ${runState.pauseReason}`);
|
|
51673
|
+
}
|
|
51674
|
+
if (runState.terminateReason) {
|
|
51675
|
+
lines.push(`Terminate reason: ${runState.terminateReason}`);
|
|
51676
|
+
}
|
|
51677
|
+
lines.push(`Counters: ${runState.counters.toolCalls} tool calls, ${runState.counters.architectTurns} architect turns, ${runState.counters.oversightChecks} oversight checks`);
|
|
51678
|
+
lines.push(`Denials: ${runState.denialCounters.consecutive} consecutive / ${runState.denialCounters.total} total`);
|
|
51679
|
+
if (runState.lastOversightVerdict) {
|
|
51680
|
+
lines.push(`Last oversight verdict: ${runState.lastOversightVerdict}${runState.lastOversightAt ? ` at ${runState.lastOversightAt}` : ""}`);
|
|
51681
|
+
}
|
|
51682
|
+
}
|
|
51683
|
+
} catch (error93) {
|
|
51684
|
+
lines.push(`Durable run-state: unreadable (${error93 instanceof Error ? error93.message : String(error93)})`);
|
|
51685
|
+
}
|
|
51686
|
+
return lines.join(`
|
|
51687
|
+
`);
|
|
51022
51688
|
}
|
|
51689
|
+
var VALID_MODES;
|
|
51023
51690
|
var init_full_auto = __esm(() => {
|
|
51024
51691
|
init_config();
|
|
51025
51692
|
init_state2();
|
|
51026
51693
|
init_state();
|
|
51027
51694
|
init_logger();
|
|
51695
|
+
VALID_MODES = ["assisted", "supervised", "strict"];
|
|
51028
51696
|
});
|
|
51029
51697
|
|
|
51030
51698
|
// src/services/handoff-service.ts
|
|
@@ -51408,7 +52076,7 @@ var init_handoff_service = __esm(() => {
|
|
|
51408
52076
|
});
|
|
51409
52077
|
|
|
51410
52078
|
// src/session/snapshot-writer.ts
|
|
51411
|
-
import { closeSync as closeSync5, fsyncSync as fsyncSync2, mkdirSync as mkdirSync15, openSync as openSync5, renameSync as
|
|
52079
|
+
import { closeSync as closeSync5, fsyncSync as fsyncSync2, mkdirSync as mkdirSync15, openSync as openSync5, renameSync as renameSync10 } from "fs";
|
|
51412
52080
|
import * as path38 from "path";
|
|
51413
52081
|
function serializeAgentSession(s) {
|
|
51414
52082
|
const gateLog = {};
|
|
@@ -51527,7 +52195,7 @@ async function writeSnapshot(directory, state) {
|
|
|
51527
52195
|
closeSync5(fd);
|
|
51528
52196
|
}
|
|
51529
52197
|
} catch {}
|
|
51530
|
-
|
|
52198
|
+
renameSync10(tempPath, resolvedPath);
|
|
51531
52199
|
} catch (error93) {
|
|
51532
52200
|
log("[snapshot-writer] write failed", {
|
|
51533
52201
|
error: error93 instanceof Error ? error93.message : String(error93)
|
|
@@ -51560,7 +52228,7 @@ var init_snapshot_writer = __esm(() => {
|
|
|
51560
52228
|
|
|
51561
52229
|
// src/commands/handoff.ts
|
|
51562
52230
|
import crypto5 from "crypto";
|
|
51563
|
-
import { renameSync as
|
|
52231
|
+
import { renameSync as renameSync11, unlinkSync as unlinkSync7 } from "fs";
|
|
51564
52232
|
async function handleHandoffCommand(directory, _args) {
|
|
51565
52233
|
const handoffData = await getHandoffData(directory);
|
|
51566
52234
|
const markdown = formatHandoffMarkdown(handoffData);
|
|
@@ -51569,10 +52237,10 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
51569
52237
|
const tempPath = `${resolvedPath}.tmp.${crypto5.randomUUID()}`;
|
|
51570
52238
|
await bunWrite(tempPath, markdown);
|
|
51571
52239
|
try {
|
|
51572
|
-
|
|
52240
|
+
renameSync11(tempPath, resolvedPath);
|
|
51573
52241
|
} catch (renameErr) {
|
|
51574
52242
|
try {
|
|
51575
|
-
|
|
52243
|
+
unlinkSync7(tempPath);
|
|
51576
52244
|
} catch {}
|
|
51577
52245
|
throw renameErr;
|
|
51578
52246
|
}
|
|
@@ -51581,10 +52249,10 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
51581
52249
|
const promptTempPath = `${promptPath}.tmp.${crypto5.randomUUID()}`;
|
|
51582
52250
|
await bunWrite(promptTempPath, continuationPrompt);
|
|
51583
52251
|
try {
|
|
51584
|
-
|
|
52252
|
+
renameSync11(promptTempPath, promptPath);
|
|
51585
52253
|
} catch (renameErr) {
|
|
51586
52254
|
try {
|
|
51587
|
-
|
|
52255
|
+
unlinkSync7(promptTempPath);
|
|
51588
52256
|
} catch {}
|
|
51589
52257
|
throw renameErr;
|
|
51590
52258
|
}
|
|
@@ -64742,8 +65410,6 @@ var init_write_retro2 = __esm(() => {
|
|
|
64742
65410
|
});
|
|
64743
65411
|
|
|
64744
65412
|
// src/commands/command-dispatch.ts
|
|
64745
|
-
import fs35 from "fs";
|
|
64746
|
-
import path65 from "path";
|
|
64747
65413
|
function normalizeSwarmCommandInput(command, argumentText) {
|
|
64748
65414
|
if (command !== "swarm" && !command.startsWith("swarm-")) {
|
|
64749
65415
|
return { isSwarmCommand: false, tokens: [] };
|
|
@@ -64778,25 +65444,6 @@ ${similar.map((cmd) => ` - /swarm ${cmd}`).join(`
|
|
|
64778
65444
|
|
|
64779
65445
|
`);
|
|
64780
65446
|
}
|
|
64781
|
-
function maybeMarkFirstRun(directory) {
|
|
64782
|
-
const sentinelPath = path65.join(directory, ".swarm", ".first-run-complete");
|
|
64783
|
-
try {
|
|
64784
|
-
const swarmDir = path65.join(directory, ".swarm");
|
|
64785
|
-
fs35.mkdirSync(swarmDir, { recursive: true });
|
|
64786
|
-
fs35.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
|
|
64787
|
-
`, { flag: "wx" });
|
|
64788
|
-
return true;
|
|
64789
|
-
} catch {
|
|
64790
|
-
return false;
|
|
64791
|
-
}
|
|
64792
|
-
}
|
|
64793
|
-
function prependWelcome(text) {
|
|
64794
|
-
const welcomeMessage = `Welcome to OpenCode Swarm!
|
|
64795
|
-
` + `
|
|
64796
|
-
` + `Run \`/swarm help\` to see all available commands, or \`/swarm config\` to review your configuration.
|
|
64797
|
-
`;
|
|
64798
|
-
return welcomeMessage + text;
|
|
64799
|
-
}
|
|
64800
65447
|
async function executeSwarmCommand(args) {
|
|
64801
65448
|
const {
|
|
64802
65449
|
directory,
|
|
@@ -64804,7 +65451,6 @@ async function executeSwarmCommand(args) {
|
|
|
64804
65451
|
sessionID,
|
|
64805
65452
|
tokens,
|
|
64806
65453
|
packageRoot,
|
|
64807
|
-
includeWelcome = false,
|
|
64808
65454
|
buildHelpText,
|
|
64809
65455
|
policy
|
|
64810
65456
|
} = args;
|
|
@@ -64838,9 +65484,6 @@ ${text}`;
|
|
|
64838
65484
|
}
|
|
64839
65485
|
}
|
|
64840
65486
|
}
|
|
64841
|
-
if (includeWelcome && maybeMarkFirstRun(directory)) {
|
|
64842
|
-
text = prependWelcome(text);
|
|
64843
|
-
}
|
|
64844
65487
|
return {
|
|
64845
65488
|
text,
|
|
64846
65489
|
resolved: resolved ?? undefined,
|
|
@@ -65463,7 +66106,7 @@ var init_commands = __esm(() => {
|
|
|
65463
66106
|
});
|
|
65464
66107
|
|
|
65465
66108
|
// src/commands/registry.ts
|
|
65466
|
-
function
|
|
66109
|
+
function levenshteinDistance2(a, b) {
|
|
65467
66110
|
const matrix = [];
|
|
65468
66111
|
for (let i = 0;i <= b.length; i++) {
|
|
65469
66112
|
matrix[i] = [i];
|
|
@@ -65590,24 +66233,24 @@ function validateAliases() {
|
|
|
65590
66233
|
}
|
|
65591
66234
|
aliasTargets.get(target).push(name);
|
|
65592
66235
|
const visited = new Set;
|
|
65593
|
-
const
|
|
66236
|
+
const path65 = [];
|
|
65594
66237
|
let current = target;
|
|
65595
66238
|
while (current) {
|
|
65596
66239
|
const currentEntry = COMMAND_REGISTRY[current];
|
|
65597
66240
|
if (!currentEntry)
|
|
65598
66241
|
break;
|
|
65599
66242
|
if (visited.has(current)) {
|
|
65600
|
-
const cycleStart =
|
|
66243
|
+
const cycleStart = path65.indexOf(current);
|
|
65601
66244
|
const fullChain = [
|
|
65602
66245
|
name,
|
|
65603
|
-
...
|
|
66246
|
+
...path65.slice(0, cycleStart > 0 ? cycleStart : path65.length),
|
|
65604
66247
|
current
|
|
65605
66248
|
].join(" \u2192 ");
|
|
65606
66249
|
errors5.push(`Circular alias detected: ${fullChain}`);
|
|
65607
66250
|
break;
|
|
65608
66251
|
}
|
|
65609
66252
|
visited.add(current);
|
|
65610
|
-
|
|
66253
|
+
path65.push(current);
|
|
65611
66254
|
current = currentEntry.aliasOf || "";
|
|
65612
66255
|
}
|
|
65613
66256
|
}
|
|
@@ -66184,9 +66827,9 @@ Subcommands:
|
|
|
66184
66827
|
},
|
|
66185
66828
|
"full-auto": {
|
|
66186
66829
|
handler: (ctx) => handleFullAutoCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
66187
|
-
description: "Toggle Full-Auto Mode for the active session [on|off]",
|
|
66188
|
-
args: "on, off",
|
|
66189
|
-
details: '
|
|
66830
|
+
description: "Toggle Full-Auto Mode for the active session [on [mode]|off|status]",
|
|
66831
|
+
args: "on [assisted|supervised|strict], off, status",
|
|
66832
|
+
details: 'First-class toggle for Full-Auto Mode \u2014 autonomous execution with the critic reviewing escalations on your behalf. No config-level enablement is required: "on" activates immediately (unless full_auto.locked is true in config), "off" disarms the run and returns the session to normal interactive operation, "status" reports the durable run state. ' + 'An optional mode after "on" overrides full_auto.mode for this run: assisted (critic consulted only on policy escalations), supervised (default \u2014 risky/high-impact actions reviewed by the critic), strict (ALL plan mutations reviewed by the critic). ' + "While active, the critic answers architect questions and reviews phase boundaries, delegations, and risky actions on your behalf; only ESCALATE_TO_HUMAN verdicts halt the run for your input. The run state is durable (.swarm/full-auto-state.json) and survives restarts; toggle with no argument flips the current state.",
|
|
66190
66833
|
category: "utility"
|
|
66191
66834
|
},
|
|
66192
66835
|
"auto-proceed": {
|
|
@@ -66329,7 +66972,7 @@ Subcommands:
|
|
|
66329
66972
|
handleHelpCommand,
|
|
66330
66973
|
validateAliases,
|
|
66331
66974
|
resolveCommand,
|
|
66332
|
-
levenshteinDistance,
|
|
66975
|
+
levenshteinDistance: levenshteinDistance2,
|
|
66333
66976
|
findSimilarCommands,
|
|
66334
66977
|
buildDetailedHelp
|
|
66335
66978
|
};
|
|
@@ -66351,72 +66994,72 @@ init_package();
|
|
|
66351
66994
|
init_registry();
|
|
66352
66995
|
init_cache_paths();
|
|
66353
66996
|
init_constants();
|
|
66354
|
-
import * as
|
|
66997
|
+
import * as fs35 from "fs";
|
|
66355
66998
|
import * as os10 from "os";
|
|
66356
|
-
import * as
|
|
66999
|
+
import * as path65 from "path";
|
|
66357
67000
|
var { version: version5 } = package_default;
|
|
66358
67001
|
var CONFIG_DIR = getPluginConfigDir();
|
|
66359
|
-
var OPENCODE_CONFIG_PATH =
|
|
66360
|
-
var PLUGIN_CONFIG_PATH =
|
|
66361
|
-
var PROMPTS_DIR =
|
|
67002
|
+
var OPENCODE_CONFIG_PATH = path65.join(CONFIG_DIR, "opencode.json");
|
|
67003
|
+
var PLUGIN_CONFIG_PATH = path65.join(CONFIG_DIR, "opencode-swarm.json");
|
|
67004
|
+
var PROMPTS_DIR = path65.join(CONFIG_DIR, "opencode-swarm");
|
|
66362
67005
|
var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
|
|
66363
67006
|
var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
|
|
66364
67007
|
function isSafeCachePath(p) {
|
|
66365
|
-
const resolved =
|
|
66366
|
-
const home =
|
|
67008
|
+
const resolved = path65.resolve(p);
|
|
67009
|
+
const home = path65.resolve(os10.homedir());
|
|
66367
67010
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
66368
67011
|
return false;
|
|
66369
67012
|
}
|
|
66370
|
-
const segments = resolved.split(
|
|
67013
|
+
const segments = resolved.split(path65.sep).filter((s) => s.length > 0);
|
|
66371
67014
|
if (segments.length < 4) {
|
|
66372
67015
|
return false;
|
|
66373
67016
|
}
|
|
66374
|
-
const leaf =
|
|
67017
|
+
const leaf = path65.basename(resolved);
|
|
66375
67018
|
if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
|
|
66376
67019
|
return false;
|
|
66377
67020
|
}
|
|
66378
|
-
const parent =
|
|
67021
|
+
const parent = path65.basename(path65.dirname(resolved));
|
|
66379
67022
|
if (parent !== "packages" && parent !== "node_modules") {
|
|
66380
67023
|
return false;
|
|
66381
67024
|
}
|
|
66382
|
-
const grandparent =
|
|
67025
|
+
const grandparent = path65.basename(path65.dirname(path65.dirname(resolved)));
|
|
66383
67026
|
if (grandparent !== "opencode") {
|
|
66384
67027
|
return false;
|
|
66385
67028
|
}
|
|
66386
67029
|
return true;
|
|
66387
67030
|
}
|
|
66388
67031
|
function isSafeLockFilePath(p) {
|
|
66389
|
-
const resolved =
|
|
66390
|
-
const home =
|
|
67032
|
+
const resolved = path65.resolve(p);
|
|
67033
|
+
const home = path65.resolve(os10.homedir());
|
|
66391
67034
|
if (resolved === "/" || resolved === home || resolved.length <= home.length) {
|
|
66392
67035
|
return false;
|
|
66393
67036
|
}
|
|
66394
|
-
const segments = resolved.split(
|
|
67037
|
+
const segments = resolved.split(path65.sep).filter((s) => s.length > 0);
|
|
66395
67038
|
if (segments.length < 4) {
|
|
66396
67039
|
return false;
|
|
66397
67040
|
}
|
|
66398
|
-
const leaf =
|
|
67041
|
+
const leaf = path65.basename(resolved);
|
|
66399
67042
|
if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
|
|
66400
67043
|
return false;
|
|
66401
67044
|
}
|
|
66402
|
-
const parent =
|
|
67045
|
+
const parent = path65.basename(path65.dirname(resolved));
|
|
66403
67046
|
if (parent !== "opencode") {
|
|
66404
67047
|
return false;
|
|
66405
67048
|
}
|
|
66406
|
-
const grandparent =
|
|
67049
|
+
const grandparent = path65.basename(path65.dirname(path65.dirname(resolved)));
|
|
66407
67050
|
if (grandparent === "opencode") {
|
|
66408
67051
|
return false;
|
|
66409
67052
|
}
|
|
66410
67053
|
return true;
|
|
66411
67054
|
}
|
|
66412
67055
|
function ensureDir(dir) {
|
|
66413
|
-
if (!
|
|
66414
|
-
|
|
67056
|
+
if (!fs35.existsSync(dir)) {
|
|
67057
|
+
fs35.mkdirSync(dir, { recursive: true });
|
|
66415
67058
|
}
|
|
66416
67059
|
}
|
|
66417
67060
|
function loadJson(filepath) {
|
|
66418
67061
|
try {
|
|
66419
|
-
const content =
|
|
67062
|
+
const content = fs35.readFileSync(filepath, "utf-8");
|
|
66420
67063
|
const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
|
|
66421
67064
|
return JSON.parse(stripped);
|
|
66422
67065
|
} catch {
|
|
@@ -66424,14 +67067,14 @@ function loadJson(filepath) {
|
|
|
66424
67067
|
}
|
|
66425
67068
|
}
|
|
66426
67069
|
function saveJson(filepath, data) {
|
|
66427
|
-
|
|
67070
|
+
fs35.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
|
|
66428
67071
|
`, "utf-8");
|
|
66429
67072
|
}
|
|
66430
67073
|
function writeProjectConfigIfMissing(cwd) {
|
|
66431
67074
|
try {
|
|
66432
|
-
const opencodeDir =
|
|
66433
|
-
const projectConfigPath =
|
|
66434
|
-
if (
|
|
67075
|
+
const opencodeDir = path65.join(cwd, ".opencode");
|
|
67076
|
+
const projectConfigPath = path65.join(opencodeDir, "opencode-swarm.json");
|
|
67077
|
+
if (fs35.existsSync(projectConfigPath)) {
|
|
66435
67078
|
return;
|
|
66436
67079
|
}
|
|
66437
67080
|
ensureDir(opencodeDir);
|
|
@@ -66447,7 +67090,7 @@ async function install() {
|
|
|
66447
67090
|
`);
|
|
66448
67091
|
ensureDir(CONFIG_DIR);
|
|
66449
67092
|
ensureDir(PROMPTS_DIR);
|
|
66450
|
-
const LEGACY_CONFIG_PATH =
|
|
67093
|
+
const LEGACY_CONFIG_PATH = path65.join(CONFIG_DIR, "config.json");
|
|
66451
67094
|
let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
66452
67095
|
if (!opencodeConfig) {
|
|
66453
67096
|
const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
|
|
@@ -66494,7 +67137,7 @@ async function install() {
|
|
|
66494
67137
|
console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
|
|
66495
67138
|
${failed}`);
|
|
66496
67139
|
}
|
|
66497
|
-
if (!
|
|
67140
|
+
if (!fs35.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
66498
67141
|
const defaultConfig = {
|
|
66499
67142
|
agents: { ...DEFAULT_AGENT_CONFIGS },
|
|
66500
67143
|
max_iterations: 5
|
|
@@ -66573,14 +67216,14 @@ function evictPluginCaches() {
|
|
|
66573
67216
|
const cleared = [];
|
|
66574
67217
|
const failed = [];
|
|
66575
67218
|
for (const cachePath of OPENCODE_PLUGIN_CACHE_PATHS) {
|
|
66576
|
-
if (!
|
|
67219
|
+
if (!fs35.existsSync(cachePath))
|
|
66577
67220
|
continue;
|
|
66578
67221
|
if (!isSafeCachePath(cachePath)) {
|
|
66579
67222
|
failed.push(`${cachePath} (refused: failed safety check)`);
|
|
66580
67223
|
continue;
|
|
66581
67224
|
}
|
|
66582
67225
|
try {
|
|
66583
|
-
|
|
67226
|
+
fs35.rmSync(cachePath, { recursive: true, force: true });
|
|
66584
67227
|
cleared.push(cachePath);
|
|
66585
67228
|
} catch (err) {
|
|
66586
67229
|
failed.push(`${cachePath} (${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -66592,14 +67235,14 @@ function evictLockFiles() {
|
|
|
66592
67235
|
const cleared = [];
|
|
66593
67236
|
const failed = [];
|
|
66594
67237
|
for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
|
|
66595
|
-
if (!
|
|
67238
|
+
if (!fs35.existsSync(lockPath))
|
|
66596
67239
|
continue;
|
|
66597
67240
|
if (!isSafeLockFilePath(lockPath)) {
|
|
66598
67241
|
failed.push(`${lockPath} (refused: failed safety check)`);
|
|
66599
67242
|
continue;
|
|
66600
67243
|
}
|
|
66601
67244
|
try {
|
|
66602
|
-
|
|
67245
|
+
fs35.unlinkSync(lockPath);
|
|
66603
67246
|
cleared.push(lockPath);
|
|
66604
67247
|
} catch (err) {
|
|
66605
67248
|
const code = err?.code;
|
|
@@ -66618,7 +67261,7 @@ async function uninstall() {
|
|
|
66618
67261
|
`);
|
|
66619
67262
|
const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
|
|
66620
67263
|
if (!opencodeConfig) {
|
|
66621
|
-
if (
|
|
67264
|
+
if (fs35.existsSync(OPENCODE_CONFIG_PATH)) {
|
|
66622
67265
|
console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
|
|
66623
67266
|
return 1;
|
|
66624
67267
|
} else {
|
|
@@ -66650,13 +67293,13 @@ async function uninstall() {
|
|
|
66650
67293
|
console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
|
|
66651
67294
|
if (process.argv.includes("--clean")) {
|
|
66652
67295
|
let cleaned = false;
|
|
66653
|
-
if (
|
|
66654
|
-
|
|
67296
|
+
if (fs35.existsSync(PLUGIN_CONFIG_PATH)) {
|
|
67297
|
+
fs35.unlinkSync(PLUGIN_CONFIG_PATH);
|
|
66655
67298
|
console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
|
|
66656
67299
|
cleaned = true;
|
|
66657
67300
|
}
|
|
66658
|
-
if (
|
|
66659
|
-
|
|
67301
|
+
if (fs35.existsSync(PROMPTS_DIR)) {
|
|
67302
|
+
fs35.rmSync(PROMPTS_DIR, { recursive: true });
|
|
66660
67303
|
console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
|
|
66661
67304
|
cleaned = true;
|
|
66662
67305
|
}
|