@tudeorangbiasa/sdd-multiagent-opencode 0.1.3 → 0.1.4

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.
@@ -0,0 +1,141 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const pluginDir = path.dirname(fileURLToPath(import.meta.url));
6
+ const LEVELS = new Set(["low", "medium", "high"]);
7
+ const pendingOverrides = new Map();
8
+ const recentCommands = new Map();
9
+
10
+ function readJson(filePath) {
11
+ if (!fs.existsSync(filePath)) return null;
12
+
13
+ try {
14
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ function readProfile() {
21
+ const candidates = [
22
+ path.join(process.cwd(), ".sdd", "reasoning-profile.json"),
23
+ path.join(pluginDir, "..", "..", ".sdd", "reasoning-profile.json"),
24
+ path.join(pluginDir, "..", ".sdd", "reasoning-profile.json"),
25
+ ];
26
+
27
+ for (const candidate of candidates) {
28
+ const profile = readJson(candidate);
29
+ if (profile) return profile;
30
+ }
31
+
32
+ return {
33
+ default: "low",
34
+ applyProviderOptions: true,
35
+ resetToolOverrideAfterNextRequest: true,
36
+ agents: {},
37
+ commands: {},
38
+ };
39
+ }
40
+
41
+ function normalizeLevel(level, fallback = "low") {
42
+ return LEVELS.has(level) ? level : fallback;
43
+ }
44
+
45
+ function applyReasoningOptions(output, level) {
46
+ if (!output.options) output.options = {};
47
+
48
+ output.options.reasoningEffort = level;
49
+ output.options.reasoning_effort = level;
50
+ output.options.reasoning = {
51
+ ...(output.options.reasoning ?? {}),
52
+ effort: level,
53
+ };
54
+
55
+ output.options.providerOptions = {
56
+ ...(output.options.providerOptions ?? {}),
57
+ openai: {
58
+ ...(output.options.providerOptions?.openai ?? {}),
59
+ reasoningEffort: level,
60
+ reasoning: {
61
+ ...(output.options.providerOptions?.openai?.reasoning ?? {}),
62
+ effort: level,
63
+ },
64
+ },
65
+ };
66
+ }
67
+
68
+ function resolveLevel(profile, input) {
69
+ const override = pendingOverrides.get(input.sessionID);
70
+ if (override) return override;
71
+
72
+ const command = recentCommands.get(input.sessionID);
73
+ if (command && profile.commands?.[command]) {
74
+ return normalizeLevel(profile.commands[command], profile.default);
75
+ }
76
+
77
+ if (profile.agents?.[input.agent]) {
78
+ return normalizeLevel(profile.agents[input.agent], profile.default);
79
+ }
80
+
81
+ return normalizeLevel(profile.default, "low");
82
+ }
83
+
84
+ export default async () => {
85
+ let toolHooks = {};
86
+
87
+ try {
88
+ const { tool } = await import("@opencode-ai/plugin");
89
+ toolHooks = {
90
+ tool: {
91
+ change_reasoning: tool({
92
+ description: "Set reasoning effort for the next model request in this session.",
93
+ args: {
94
+ level: tool.schema.enum(["low", "medium", "high"]).describe("Reasoning level to use: low, medium, or high."),
95
+ },
96
+ async execute(args, context) {
97
+ const level = normalizeLevel(args.level, "low");
98
+ pendingOverrides.set(context.sessionID, level);
99
+ context.metadata({ title: `Reasoning: ${level}`, metadata: { level } });
100
+
101
+ return {
102
+ output: `Reasoning level set to ${level} for the next model request in this session.`,
103
+ metadata: { level },
104
+ };
105
+ },
106
+ }),
107
+ },
108
+ };
109
+ } catch {
110
+ toolHooks = {};
111
+ }
112
+
113
+ return {
114
+ ...toolHooks,
115
+
116
+ "command.execute.before": async (input) => {
117
+ recentCommands.set(input.sessionID, input.command);
118
+ },
119
+
120
+ "chat.params": async (input, output) => {
121
+ const profile = readProfile();
122
+ const level = resolveLevel(profile, input);
123
+
124
+ if (profile.applyProviderOptions !== false) {
125
+ applyReasoningOptions(output, level);
126
+ }
127
+
128
+ output.options.sddReasoningLevel = level;
129
+
130
+ if (profile.resetToolOverrideAfterNextRequest !== false) {
131
+ pendingOverrides.delete(input.sessionID);
132
+ }
133
+ },
134
+
135
+ "experimental.chat.system.transform": async (_input, output) => {
136
+ output.system.push(
137
+ "SDD auto reasoning is enabled. Use change_reasoning sparingly when the current task needs a different reasoning level. Use low for simple edits, medium for implementation/review, and high for architecture, planning, multi-agent orchestration, and hard debugging."
138
+ );
139
+ },
140
+ };
141
+ };
@@ -0,0 +1,25 @@
1
+ {
2
+ "default": "low",
3
+ "applyProviderOptions": true,
4
+ "resetToolOverrideAfterNextRequest": true,
5
+ "agents": {
6
+ "general": "medium",
7
+ "plan": "high",
8
+ "sdd-orchestrator": "high",
9
+ "sdd-planner": "high",
10
+ "sdd-explorer": "low",
11
+ "sdd-implementer": "medium",
12
+ "sdd-verifier": "medium",
13
+ "sdd-reviewer": "medium"
14
+ },
15
+ "commands": {
16
+ "brief": "medium",
17
+ "research": "medium",
18
+ "specify": "high",
19
+ "plan": "high",
20
+ "tasks": "high",
21
+ "implement": "medium",
22
+ "execute-parallel": "high",
23
+ "audit": "medium"
24
+ }
25
+ }
package/README.md CHANGED
@@ -84,6 +84,31 @@ Default profile avoids paid GPT models:
84
84
 
85
85
  `.opencode/plugins/sdd-model-router.js` reads this file at OpenCode startup and applies the model settings. Restart OpenCode after editing it.
86
86
 
87
+ ### Auto Reasoning
88
+
89
+ The installer also creates `.sdd/reasoning-profile.json` and installs `.opencode/plugins/sdd-auto-reasoning.js`.
90
+
91
+ The plugin reverse-engineers the behavior of `@howaboua/pi-auto-reasoning-tool` for OpenCode:
92
+ - sets reasoning effort automatically per agent and command
93
+ - exposes `change_reasoning` when `@opencode-ai/plugin` is available
94
+ - resets tool overrides after the next model request by default
95
+
96
+ Default routing:
97
+
98
+ ```json
99
+ {
100
+ "default": "low",
101
+ "agents": {
102
+ "sdd-orchestrator": "high",
103
+ "sdd-planner": "high",
104
+ "sdd-implementer": "medium",
105
+ "sdd-reviewer": "medium"
106
+ }
107
+ }
108
+ ```
109
+
110
+ Restart OpenCode after editing `.sdd/reasoning-profile.json`.
111
+
87
112
  ### 1. Install (Manual)
88
113
 
89
114
  #### Option A: Using Commands (Recommended)
@@ -260,6 +260,20 @@ function installSdd(ctx) {
260
260
  count++;
261
261
  }
262
262
 
263
+ const reasoningProfileDest = path.join(sddDir, "reasoning-profile.json");
264
+ const reasoningProfileTemplateSrc = path.join(
265
+ sddDir,
266
+ "templates",
267
+ "reasoning-profile-template.json"
268
+ );
269
+
270
+ if (fs.existsSync(reasoningProfileDest) && !ctx.force) {
271
+ ctx.skipped.push(".sdd/reasoning-profile.json (exists)");
272
+ } else if (fs.existsSync(reasoningProfileTemplateSrc)) {
273
+ copyFileSafe(reasoningProfileTemplateSrc, reasoningProfileDest, ctx);
274
+ count++;
275
+ }
276
+
263
277
  const specsDir = path.join(ctx.targetRoot, "specs");
264
278
  ensureDir(path.join(specsDir, "active"));
265
279
  ensureDir(path.join(specsDir, "backlog"));
@@ -303,6 +317,21 @@ function patchOpencodeJson(ctx) {
303
317
  if (fs.existsSync(pkgJsonPath)) {
304
318
  const pkgConfig = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
305
319
 
320
+ if (Array.isArray(pkgConfig.plugin)) {
321
+ if (!Array.isArray(targetConfig.plugin)) {
322
+ targetConfig.plugin = [];
323
+ }
324
+
325
+ for (const pluginEntry of pkgConfig.plugin) {
326
+ const key = JSON.stringify(pluginEntry);
327
+ const exists = targetConfig.plugin.some((existing) => JSON.stringify(existing) === key);
328
+ if (!exists) {
329
+ targetConfig.plugin.push(pluginEntry);
330
+ ctx.installed.push(`opencode.json (plugin: ${Array.isArray(pluginEntry) ? pluginEntry[0] : pluginEntry})`);
331
+ }
332
+ }
333
+ }
334
+
306
335
  if (pkgConfig.model) {
307
336
  targetConfig.model = pkgConfig.model;
308
337
  ctx.installed.push("opencode.json (model patch)");
package/opencode.json CHANGED
@@ -2,6 +2,10 @@
2
2
  "$schema": "https://opencode.ai/config.json",
3
3
  "model": "opencode/minimax-m2.5-free",
4
4
  "small_model": "opencode/deepseek-v4-flash-free",
5
+ "plugin": [
6
+ "./plugins/sdd-model-router.js",
7
+ "./plugins/sdd-auto-reasoning.js"
8
+ ],
5
9
  "agent": {
6
10
  "sdd-orchestrator": {
7
11
  "description": "Parallel task coordination and DAG-based execution for SDD workflows. Use for /execute-parallel, --until-finish automation, and coordinating multiple subagents across complex projects.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tudeorangbiasa/sdd-multiagent-opencode",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Spec-Driven Development multi-agent workflow for OpenCode",
5
5
  "type": "module",
6
6
  "bin": {