@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.
@@ -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
- error("WeaveConfig validation errors — using defaults", result.error.issues);
1048
- return WeaveConfigSchema.parse({});
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencode_weave/weave",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Weave — lean OpenCode plugin with multi-agent orchestration",
5
5
  "author": "Weave",
6
6
  "license": "MIT",