@opencode_weave/weave 0.7.4 → 0.7.5
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/config/loader.d.ts
CHANGED
|
@@ -1,2 +1,21 @@
|
|
|
1
1
|
import { type WeaveConfig } from "./schema";
|
|
2
|
+
export interface ConfigDiagnostic {
|
|
3
|
+
level: "warn" | "error";
|
|
4
|
+
section: string;
|
|
5
|
+
message: string;
|
|
6
|
+
/** Individual field-level issues within the section */
|
|
7
|
+
fields?: Array<{
|
|
8
|
+
path: string;
|
|
9
|
+
message: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export interface ConfigLoadResult {
|
|
13
|
+
config: WeaveConfig;
|
|
14
|
+
/** Config files that were found and loaded (may be empty) */
|
|
15
|
+
loadedFiles: string[];
|
|
16
|
+
/** Validation diagnostics — empty when config is fully valid */
|
|
17
|
+
diagnostics: ConfigDiagnostic[];
|
|
18
|
+
}
|
|
19
|
+
/** Retrieve the most recent config load result (for /weave-health command). */
|
|
20
|
+
export declare function getLastConfigLoadResult(): ConfigLoadResult | null;
|
|
2
21
|
export declare function loadWeaveConfig(directory: string, _ctx?: unknown, _homeDir?: string): WeaveConfig;
|
|
@@ -13,4 +13,4 @@ export interface BuiltinCommand {
|
|
|
13
13
|
/** Hint shown for the argument (e.g., "[plan-name]") */
|
|
14
14
|
argumentHint?: string;
|
|
15
15
|
}
|
|
16
|
-
export type BuiltinCommandName = "start-work" | "token-report" | "metrics" | "run-workflow";
|
|
16
|
+
export type BuiltinCommandName = "start-work" | "token-report" | "metrics" | "run-workflow" | "weave-health";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConfigLoadResult } from "../config/loader";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a human-readable health report from the config load result.
|
|
4
|
+
* Surfaced via the /weave-health command so the user can diagnose
|
|
5
|
+
* config issues directly in the TUI.
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateHealthReport(loadResult: ConfigLoadResult | null, agents: Record<string, unknown>): string;
|
package/dist/index.js
CHANGED
|
@@ -1007,6 +1007,10 @@ function logDelegation(event) {
|
|
|
1007
1007
|
}
|
|
1008
1008
|
|
|
1009
1009
|
// src/config/loader.ts
|
|
1010
|
+
var lastLoadResult = null;
|
|
1011
|
+
function getLastConfigLoadResult() {
|
|
1012
|
+
return lastLoadResult;
|
|
1013
|
+
}
|
|
1010
1014
|
function readJsoncFile(filePath) {
|
|
1011
1015
|
try {
|
|
1012
1016
|
const text = readFileSync(filePath, "utf-8");
|
|
@@ -1039,13 +1043,37 @@ function loadWeaveConfig(directory, _ctx, _homeDir) {
|
|
|
1039
1043
|
userConfig: userConfigPath ?? "(none)",
|
|
1040
1044
|
projectConfig: projectConfigPath ?? "(none)"
|
|
1041
1045
|
});
|
|
1046
|
+
const loadedFiles = [];
|
|
1047
|
+
if (userConfigPath)
|
|
1048
|
+
loadedFiles.push(userConfigPath);
|
|
1049
|
+
if (projectConfigPath)
|
|
1050
|
+
loadedFiles.push(projectConfigPath);
|
|
1042
1051
|
const userRaw = userConfigPath ? readJsoncFile(userConfigPath) : {};
|
|
1043
1052
|
const projectRaw = projectConfigPath ? readJsoncFile(projectConfigPath) : {};
|
|
1044
1053
|
const merged = mergeConfigs(userRaw, projectRaw);
|
|
1045
1054
|
const result = WeaveConfigSchema.safeParse(merged);
|
|
1046
1055
|
if (!result.success) {
|
|
1047
|
-
|
|
1048
|
-
|
|
1056
|
+
const recovery = recoverValidSections(merged, result.error.issues);
|
|
1057
|
+
if (recovery) {
|
|
1058
|
+
lastLoadResult = { config: recovery.config, loadedFiles, diagnostics: recovery.diagnostics };
|
|
1059
|
+
return recovery.config;
|
|
1060
|
+
}
|
|
1061
|
+
const diagnostics = [{
|
|
1062
|
+
level: "error",
|
|
1063
|
+
section: "(root)",
|
|
1064
|
+
message: "Config validation failed entirely — using defaults",
|
|
1065
|
+
fields: result.error.issues.map((i) => ({
|
|
1066
|
+
path: i.path.join(".") || "(root)",
|
|
1067
|
+
message: i.message
|
|
1068
|
+
}))
|
|
1069
|
+
}];
|
|
1070
|
+
error("WeaveConfig validation errors — using defaults. Fix the issues below and restart.", result.error.issues.map((i) => ({
|
|
1071
|
+
path: i.path.join(".") || "(root)",
|
|
1072
|
+
message: i.message
|
|
1073
|
+
})));
|
|
1074
|
+
const fallback = WeaveConfigSchema.parse({});
|
|
1075
|
+
lastLoadResult = { config: fallback, loadedFiles, diagnostics };
|
|
1076
|
+
return fallback;
|
|
1049
1077
|
}
|
|
1050
1078
|
debug("Weave config loaded successfully", {
|
|
1051
1079
|
hasAgentOverrides: !!result.data.agents && Object.keys(result.data.agents).length > 0,
|
|
@@ -1054,8 +1082,52 @@ function loadWeaveConfig(directory, _ctx, _homeDir) {
|
|
|
1054
1082
|
logLevel: result.data.log_level ?? "(default)",
|
|
1055
1083
|
analyticsEnabled: result.data.analytics?.enabled ?? false
|
|
1056
1084
|
});
|
|
1085
|
+
lastLoadResult = { config: result.data, loadedFiles, diagnostics: [] };
|
|
1057
1086
|
return result.data;
|
|
1058
1087
|
}
|
|
1088
|
+
function recoverValidSections(merged, issues) {
|
|
1089
|
+
const failingKeys = new Set;
|
|
1090
|
+
for (const issue of issues) {
|
|
1091
|
+
if (issue.path.length > 0) {
|
|
1092
|
+
failingKeys.add(String(issue.path[0]));
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
if (failingKeys.size === 0)
|
|
1096
|
+
return null;
|
|
1097
|
+
const diagnostics = [];
|
|
1098
|
+
for (const key of failingKeys) {
|
|
1099
|
+
const sectionIssues = issues.filter((i) => i.path.length > 0 && String(i.path[0]) === key);
|
|
1100
|
+
const fields = sectionIssues.map((i) => ({
|
|
1101
|
+
path: i.path.slice(1).join("."),
|
|
1102
|
+
message: i.message
|
|
1103
|
+
}));
|
|
1104
|
+
const details = fields.map((f) => f.path ? ` → ${f.path}: ${f.message}` : ` → ${f.message}`);
|
|
1105
|
+
diagnostics.push({
|
|
1106
|
+
level: "warn",
|
|
1107
|
+
section: key,
|
|
1108
|
+
message: `Section "${key}" was dropped due to validation errors`,
|
|
1109
|
+
fields
|
|
1110
|
+
});
|
|
1111
|
+
warn(`Config section "${key}" has validation errors and was dropped:
|
|
1112
|
+
${details.join(`
|
|
1113
|
+
`)}
|
|
1114
|
+
Remaining config sections are preserved. Fix the errors above and restart.`);
|
|
1115
|
+
}
|
|
1116
|
+
const stripped = { ...merged };
|
|
1117
|
+
for (const key of failingKeys) {
|
|
1118
|
+
delete stripped[key];
|
|
1119
|
+
}
|
|
1120
|
+
const retry = WeaveConfigSchema.safeParse(stripped);
|
|
1121
|
+
if (retry.success) {
|
|
1122
|
+
debug("Config recovery succeeded", {
|
|
1123
|
+
droppedSections: [...failingKeys],
|
|
1124
|
+
hasAgentOverrides: !!retry.data.agents && Object.keys(retry.data.agents).length > 0,
|
|
1125
|
+
customAgents: retry.data.custom_agents ? Object.keys(retry.data.custom_agents) : []
|
|
1126
|
+
});
|
|
1127
|
+
return { config: retry.data, diagnostics };
|
|
1128
|
+
}
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1059
1131
|
|
|
1060
1132
|
// src/shared/agent-display-names.ts
|
|
1061
1133
|
var AGENT_DISPLAY_NAMES = {
|
|
@@ -1218,6 +1290,16 @@ ${RUN_WORKFLOW_TEMPLATE}
|
|
|
1218
1290
|
<session-context>Session ID: $SESSION_ID Timestamp: $TIMESTAMP</session-context>
|
|
1219
1291
|
<user-request>$ARGUMENTS</user-request>`,
|
|
1220
1292
|
argumentHint: '<workflow-name> ["goal"]'
|
|
1293
|
+
},
|
|
1294
|
+
"weave-health": {
|
|
1295
|
+
name: "weave-health",
|
|
1296
|
+
description: "Show Weave config health and any validation issues",
|
|
1297
|
+
agent: "loom",
|
|
1298
|
+
template: `<command-instruction>
|
|
1299
|
+
Display the Weave health report below to the user. Present warnings and errors prominently.
|
|
1300
|
+
If there are no issues, confirm that Weave config is healthy.
|
|
1301
|
+
</command-instruction>
|
|
1302
|
+
<weave-health>$ARGUMENTS</weave-health>`
|
|
1221
1303
|
}
|
|
1222
1304
|
};
|
|
1223
1305
|
// src/managers/config-handler.ts
|
|
@@ -6028,6 +6110,76 @@ function generateMetricsReport(directory, state) {
|
|
|
6028
6110
|
}
|
|
6029
6111
|
}
|
|
6030
6112
|
|
|
6113
|
+
// src/features/health-report.ts
|
|
6114
|
+
function generateHealthReport(loadResult, agents) {
|
|
6115
|
+
if (!loadResult) {
|
|
6116
|
+
return "⚠ No config load result available — Weave may not have initialized properly.";
|
|
6117
|
+
}
|
|
6118
|
+
const lines = [];
|
|
6119
|
+
const { config, loadedFiles, diagnostics } = loadResult;
|
|
6120
|
+
const hasIssues = diagnostics.length > 0;
|
|
6121
|
+
lines.push(hasIssues ? "## ⚠ Weave Config Health: Issues Found" : "## ✅ Weave Config Health: OK");
|
|
6122
|
+
lines.push("");
|
|
6123
|
+
lines.push("### Config Files");
|
|
6124
|
+
if (loadedFiles.length === 0) {
|
|
6125
|
+
lines.push("No config files found (using defaults)");
|
|
6126
|
+
} else {
|
|
6127
|
+
for (const f of loadedFiles) {
|
|
6128
|
+
lines.push(`- \`${f}\``);
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6131
|
+
lines.push("");
|
|
6132
|
+
if (diagnostics.length > 0) {
|
|
6133
|
+
lines.push("### Validation Issues");
|
|
6134
|
+
lines.push("");
|
|
6135
|
+
for (const d of diagnostics) {
|
|
6136
|
+
const icon = d.level === "error" ? "\uD83D\uDD34" : "\uD83D\uDFE1";
|
|
6137
|
+
lines.push(`${icon} **${d.section}**: ${d.message}`);
|
|
6138
|
+
if (d.fields?.length) {
|
|
6139
|
+
for (const f of d.fields) {
|
|
6140
|
+
const fieldLabel = f.path || "(root)";
|
|
6141
|
+
lines.push(` - \`${fieldLabel}\`: ${f.message}`);
|
|
6142
|
+
}
|
|
6143
|
+
}
|
|
6144
|
+
lines.push("");
|
|
6145
|
+
}
|
|
6146
|
+
lines.push("Fix the issues above in your config file and restart opencode.");
|
|
6147
|
+
lines.push("");
|
|
6148
|
+
}
|
|
6149
|
+
lines.push("### Loaded Agents");
|
|
6150
|
+
const builtinKeys = ["loom", "tapestry", "shuttle", "pattern", "thread", "spindle", "warp", "weft"];
|
|
6151
|
+
const builtinDisplayNames = new Set(builtinKeys.map((k) => getAgentDisplayName(k)));
|
|
6152
|
+
const agentNames = Object.keys(agents);
|
|
6153
|
+
const builtinAgents = agentNames.filter((n) => builtinDisplayNames.has(n));
|
|
6154
|
+
const customAgents = agentNames.filter((n) => !builtinDisplayNames.has(n));
|
|
6155
|
+
lines.push(`- Builtin: ${builtinAgents.length}/8 (${builtinAgents.join(", ")})`);
|
|
6156
|
+
if (customAgents.length > 0) {
|
|
6157
|
+
lines.push(`- Custom: ${customAgents.length} (${customAgents.join(", ")})`);
|
|
6158
|
+
} else {
|
|
6159
|
+
lines.push("- Custom: 0");
|
|
6160
|
+
}
|
|
6161
|
+
lines.push("");
|
|
6162
|
+
if (config.custom_agents && Object.keys(config.custom_agents).length > 0) {
|
|
6163
|
+
lines.push("### Custom Agent Config");
|
|
6164
|
+
for (const [name, agentConfig] of Object.entries(config.custom_agents)) {
|
|
6165
|
+
const mode = agentConfig.mode ?? "subagent";
|
|
6166
|
+
const model = agentConfig.model ?? "(default)";
|
|
6167
|
+
lines.push(`- **${agentConfig.display_name ?? name}** — mode: ${mode}, model: ${model}`);
|
|
6168
|
+
}
|
|
6169
|
+
lines.push("");
|
|
6170
|
+
}
|
|
6171
|
+
const disabled = config.disabled_agents ?? [];
|
|
6172
|
+
if (disabled.length > 0) {
|
|
6173
|
+
lines.push(`### Disabled Agents: ${disabled.join(", ")}`);
|
|
6174
|
+
lines.push("");
|
|
6175
|
+
}
|
|
6176
|
+
lines.push("### Logs");
|
|
6177
|
+
lines.push("Detailed logs: `~/.local/share/opencode/log/` (grep for `service=weave`)");
|
|
6178
|
+
lines.push("Real-time: `opencode --print-logs --log-level WARN`");
|
|
6179
|
+
return lines.join(`
|
|
6180
|
+
`);
|
|
6181
|
+
}
|
|
6182
|
+
|
|
6031
6183
|
// src/plugin/plugin-interface.ts
|
|
6032
6184
|
function createPluginInterface(args) {
|
|
6033
6185
|
const { pluginConfig, hooks, tools, configHandler, agents, client: client2, directory = "", tracker } = args;
|
|
@@ -6462,6 +6614,11 @@ ${cmdResult.contextInjection}`;
|
|
|
6462
6614
|
const metricsMarkdown = formatMetricsMarkdown(reports, summaries, args2);
|
|
6463
6615
|
parts.push({ type: "text", text: metricsMarkdown });
|
|
6464
6616
|
}
|
|
6617
|
+
if (command === "weave-health") {
|
|
6618
|
+
const loadResult = getLastConfigLoadResult();
|
|
6619
|
+
const reportText = generateHealthReport(loadResult, agents);
|
|
6620
|
+
parts.push({ type: "text", text: reportText });
|
|
6621
|
+
}
|
|
6465
6622
|
},
|
|
6466
6623
|
"tool.definition": async (input, output) => {
|
|
6467
6624
|
if (hooks.todoDescriptionOverride) {
|
|
@@ -6890,8 +7047,8 @@ function createAnalytics(directory, fingerprint) {
|
|
|
6890
7047
|
|
|
6891
7048
|
// src/index.ts
|
|
6892
7049
|
var WeavePlugin = async (ctx) => {
|
|
6893
|
-
const pluginConfig = loadWeaveConfig(ctx.directory, ctx);
|
|
6894
7050
|
setClient(ctx.client);
|
|
7051
|
+
const pluginConfig = loadWeaveConfig(ctx.directory, ctx);
|
|
6895
7052
|
if (pluginConfig.log_level) {
|
|
6896
7053
|
setLogLevel(pluginConfig.log_level);
|
|
6897
7054
|
}
|