@tudeorangbiasa/sdd-multiagent-opencode 0.1.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/.opencode/agents/sdd-explorer.md +75 -0
- package/.opencode/agents/sdd-implementer.md +72 -0
- package/.opencode/agents/sdd-orchestrator.md +152 -0
- package/.opencode/agents/sdd-planner.md +58 -0
- package/.opencode/agents/sdd-reviewer.md +74 -0
- package/.opencode/agents/sdd-verifier.md +90 -0
- package/.opencode/commands/audit.md +75 -0
- package/.opencode/commands/brief.md +190 -0
- package/.opencode/commands/evolve.md +87 -0
- package/.opencode/commands/execute-parallel.md +116 -0
- package/.opencode/commands/execute-task.md +81 -0
- package/.opencode/commands/generate-prd.md +82 -0
- package/.opencode/commands/generate-rules.md +67 -0
- package/.opencode/commands/grill-me.md +99 -0
- package/.opencode/commands/implement.md +149 -0
- package/.opencode/commands/init-sdd.md +141 -0
- package/.opencode/commands/plan.md +96 -0
- package/.opencode/commands/refine.md +115 -0
- package/.opencode/commands/research.md +194 -0
- package/.opencode/commands/sdd-full-plan.md +91 -0
- package/.opencode/commands/specify.md +124 -0
- package/.opencode/commands/tasks.md +110 -0
- package/.opencode/commands/upgrade.md +107 -0
- package/.opencode/skills/sdd-audit/SKILL.md +59 -0
- package/.opencode/skills/sdd-evolve/SKILL.md +95 -0
- package/.opencode/skills/sdd-implementation/SKILL.md +88 -0
- package/.opencode/skills/sdd-planning/SKILL.md +59 -0
- package/.opencode/skills/sdd-research/SKILL.md +44 -0
- package/.sdd/config.json +36 -0
- package/.sdd/templates/feature-brief-v2.md +65 -0
- package/.sdd/templates/plan-compact.md +50 -0
- package/.sdd/templates/project-profile-template.json +33 -0
- package/.sdd/templates/research-compact.md +114 -0
- package/.sdd/templates/roadmap-template.json +29 -0
- package/.sdd/templates/roadmap-template.md +66 -0
- package/.sdd/templates/spec-compact.md +71 -0
- package/.sdd/templates/tasks-compact.md +48 -0
- package/.sdd/templates/todo-compact.md +30 -0
- package/README.md +272 -0
- package/bin/sdd-opencode.js +387 -0
- package/opencode.json +98 -0
- package/package.json +28 -0
- package/vendor/opencode-agent-rules/.opencode/plugins/context-guard.js +7 -0
- package/vendor/opencode-agent-rules/.opencode/rules/cli-first.md +31 -0
- package/vendor/opencode-agent-rules/.opencode/rules/context-budget.md +39 -0
- package/vendor/opencode-agent-rules/.opencode/rules/core-behavior.md +44 -0
- package/vendor/opencode-agent-rules/.opencode/rules/status-verification.md +36 -0
- package/vendor/opencode-agent-rules/AGENTS.md +64 -0
- package/vendor/opencode-agent-rules/opencode.json +9 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
10
|
+
|
|
11
|
+
function parseArgs(argv) {
|
|
12
|
+
const args = {
|
|
13
|
+
command: null,
|
|
14
|
+
flags: {
|
|
15
|
+
sddOnly: false,
|
|
16
|
+
rulesOnly: false,
|
|
17
|
+
force: false,
|
|
18
|
+
dryRun: false,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
for (let i = 2; i < argv.length; i++) {
|
|
23
|
+
const arg = argv[i];
|
|
24
|
+
if (arg === "init" || arg === "help" || arg === "test") {
|
|
25
|
+
args.command = arg;
|
|
26
|
+
} else if (arg === "--sdd-only") {
|
|
27
|
+
args.flags.sddOnly = true;
|
|
28
|
+
} else if (arg === "--rules-only") {
|
|
29
|
+
args.flags.rulesOnly = true;
|
|
30
|
+
} else if (arg === "--force") {
|
|
31
|
+
args.flags.force = true;
|
|
32
|
+
} else if (arg === "--dry-run") {
|
|
33
|
+
args.flags.dryRun = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!args.command) {
|
|
38
|
+
args.command = "help";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return args;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function usage() {
|
|
45
|
+
console.log(`SDD Multi-Agent OpenCode Installer
|
|
46
|
+
|
|
47
|
+
Usage:
|
|
48
|
+
sdd-opencode init [flags]
|
|
49
|
+
sdd-opencode help
|
|
50
|
+
|
|
51
|
+
Flags:
|
|
52
|
+
--sdd-only Install only SDD workflow (skip base rules)
|
|
53
|
+
--rules-only Install only base rules (skip SDD)
|
|
54
|
+
--force Overwrite existing managed files
|
|
55
|
+
--dry-run Show what would be installed, no writes
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
sdd-opencode init # Install everything
|
|
59
|
+
sdd-opencode init --sdd-only # SDD only
|
|
60
|
+
sdd-opencode init --rules-only # Rules only
|
|
61
|
+
sdd-opencode init --force # Overwrite existing
|
|
62
|
+
sdd-opencode init --dry-run # Preview only
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ensureDir(dir) {
|
|
67
|
+
if (!fs.existsSync(dir)) {
|
|
68
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function copyFileSafe(src, dest, ctx, options = {}) {
|
|
73
|
+
const { force, dryRun } = ctx;
|
|
74
|
+
|
|
75
|
+
if (fs.existsSync(dest)) {
|
|
76
|
+
if (!force) {
|
|
77
|
+
ctx.skipped.push(path.relative(ctx.targetRoot, dest));
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (dryRun) {
|
|
83
|
+
ctx.dryRunFiles.push(path.relative(ctx.targetRoot, dest));
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const content = fs.readFileSync(src, "utf-8");
|
|
88
|
+
fs.writeFileSync(dest, content);
|
|
89
|
+
ctx.installed.push(path.relative(ctx.targetRoot, dest));
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function copyDirFlat(srcDir, destDir, ctx) {
|
|
94
|
+
if (!fs.existsSync(srcDir)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
ensureDir(destDir);
|
|
99
|
+
|
|
100
|
+
const files = fs.readdirSync(srcDir);
|
|
101
|
+
for (const file of files) {
|
|
102
|
+
const srcPath = path.join(srcDir, file);
|
|
103
|
+
const destPath = path.join(destDir, file);
|
|
104
|
+
|
|
105
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
106
|
+
copyDirFlat(srcPath, destPath, ctx);
|
|
107
|
+
} else {
|
|
108
|
+
copyFileSafe(srcPath, destPath, ctx);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function copyOpencodeDir(srcRelative, destRelative, ctx) {
|
|
114
|
+
const srcPath = path.join(packageRoot, srcRelative);
|
|
115
|
+
const destPath = path.join(ctx.targetRoot, destRelative);
|
|
116
|
+
|
|
117
|
+
if (!fs.existsSync(srcPath)) {
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let count = 0;
|
|
122
|
+
const items = fs.readdirSync(srcPath);
|
|
123
|
+
|
|
124
|
+
for (const item of items) {
|
|
125
|
+
const srcItem = path.join(srcPath, item);
|
|
126
|
+
const destItem = path.join(destPath, item);
|
|
127
|
+
|
|
128
|
+
if (fs.statSync(srcItem).isDirectory()) {
|
|
129
|
+
ensureDir(destItem);
|
|
130
|
+
const subCount = copyOpencodeDir(
|
|
131
|
+
`${srcRelative}/${item}`,
|
|
132
|
+
`${destRelative}/${item}`,
|
|
133
|
+
ctx
|
|
134
|
+
);
|
|
135
|
+
count += subCount;
|
|
136
|
+
} else {
|
|
137
|
+
if (copyFileSafe(srcItem, destItem, ctx)) {
|
|
138
|
+
count++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return count;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function installRules(ctx) {
|
|
147
|
+
const vendorRules = path.join(packageRoot, "vendor/opencode-agent-rules");
|
|
148
|
+
|
|
149
|
+
if (!fs.existsSync(vendorRules)) {
|
|
150
|
+
console.warn("⚠️ vendor/opencode-agent-rules not found, skipping rules");
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let count = 0;
|
|
155
|
+
|
|
156
|
+
const targetOpencode = path.join(ctx.targetRoot, ".opencode");
|
|
157
|
+
ensureDir(path.join(targetOpencode, "rules"));
|
|
158
|
+
ensureDir(path.join(targetOpencode, "plugins"));
|
|
159
|
+
|
|
160
|
+
const agentsSrc = path.join(vendorRules, "AGENTS.md");
|
|
161
|
+
const agentsDest = path.join(ctx.targetRoot, "AGENTS.md");
|
|
162
|
+
|
|
163
|
+
if (fs.existsSync(agentsSrc)) {
|
|
164
|
+
copyFileSafe(agentsSrc, agentsDest, ctx);
|
|
165
|
+
count++;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const rulesSrc = path.join(vendorRules, ".opencode/rules");
|
|
169
|
+
const rulesDest = path.join(targetOpencode, "rules");
|
|
170
|
+
count += copyOpencodeDir(".opencode/rules", ".opencode/rules", ctx);
|
|
171
|
+
|
|
172
|
+
const pluginsSrc = path.join(vendorRules, ".opencode/plugins");
|
|
173
|
+
if (fs.existsSync(pluginsSrc)) {
|
|
174
|
+
count += copyOpencodeDir(".opencode/plugins", ".opencode/plugins", ctx);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return count;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function installSdd(ctx) {
|
|
181
|
+
let count = 0;
|
|
182
|
+
|
|
183
|
+
const targetOpencode = path.join(ctx.targetRoot, ".opencode");
|
|
184
|
+
|
|
185
|
+
ensureDir(path.join(targetOpencode, "agents"));
|
|
186
|
+
ensureDir(path.join(targetOpencode, "commands"));
|
|
187
|
+
ensureDir(path.join(targetOpencode, "skills"));
|
|
188
|
+
|
|
189
|
+
count += copyOpencodeDir(".opencode/agents", ".opencode/agents", ctx);
|
|
190
|
+
count += copyOpencodeDir(".opencode/commands", ".opencode/commands", ctx);
|
|
191
|
+
count += copyOpencodeDir(".opencode/skills", ".opencode/skills", ctx);
|
|
192
|
+
|
|
193
|
+
const sddDir = path.join(ctx.targetRoot, ".sdd");
|
|
194
|
+
ensureDir(sddDir);
|
|
195
|
+
ensureDir(path.join(sddDir, "templates"));
|
|
196
|
+
|
|
197
|
+
const configSrc = path.join(packageRoot, ".sdd/config.json");
|
|
198
|
+
const configDest = path.join(sddDir, "config.json");
|
|
199
|
+
if (copyFileSafe(configSrc, configDest, ctx)) {
|
|
200
|
+
count++;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const templatesSrc = path.join(packageRoot, ".sdd/templates");
|
|
204
|
+
if (fs.existsSync(templatesSrc)) {
|
|
205
|
+
const templateFiles = fs.readdirSync(templatesSrc);
|
|
206
|
+
for (const file of templateFiles) {
|
|
207
|
+
const src = path.join(templatesSrc, file);
|
|
208
|
+
const dest = path.join(sddDir, "templates", file);
|
|
209
|
+
if (copyFileSafe(src, dest, ctx)) {
|
|
210
|
+
count++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const projectProfileDest = path.join(sddDir, "project-profile.json");
|
|
216
|
+
const profileTemplateSrc = path.join(
|
|
217
|
+
sddDir,
|
|
218
|
+
"templates",
|
|
219
|
+
"project-profile-template.json"
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
if (fs.existsSync(projectProfileDest) && !ctx.force) {
|
|
223
|
+
ctx.skipped.push(".sdd/project-profile.json (exists)");
|
|
224
|
+
} else if (fs.existsSync(profileTemplateSrc)) {
|
|
225
|
+
const profile = JSON.parse(fs.readFileSync(profileTemplateSrc, "utf-8"));
|
|
226
|
+
profile.initialized = new Date().toISOString();
|
|
227
|
+
profile.lastUpdated = new Date().toISOString();
|
|
228
|
+
|
|
229
|
+
if (ctx.dryRun) {
|
|
230
|
+
ctx.dryRunFiles.push(".sdd/project-profile.json");
|
|
231
|
+
} else {
|
|
232
|
+
fs.writeFileSync(
|
|
233
|
+
projectProfileDest,
|
|
234
|
+
JSON.stringify(profile, null, 2)
|
|
235
|
+
);
|
|
236
|
+
ctx.installed.push(".sdd/project-profile.json");
|
|
237
|
+
}
|
|
238
|
+
count++;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const specsDir = path.join(ctx.targetRoot, "specs");
|
|
242
|
+
ensureDir(path.join(specsDir, "active"));
|
|
243
|
+
ensureDir(path.join(specsDir, "backlog"));
|
|
244
|
+
ensureDir(path.join(specsDir, "completed"));
|
|
245
|
+
ensureDir(path.join(specsDir, "todo-roadmap"));
|
|
246
|
+
|
|
247
|
+
return count;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function patchOpencodeJson(ctx) {
|
|
251
|
+
const targetJsonPath = path.join(ctx.targetRoot, "opencode.json");
|
|
252
|
+
|
|
253
|
+
let targetConfig = {};
|
|
254
|
+
|
|
255
|
+
if (fs.existsSync(targetJsonPath)) {
|
|
256
|
+
try {
|
|
257
|
+
targetConfig = JSON.parse(fs.readFileSync(targetJsonPath, "utf-8"));
|
|
258
|
+
} catch (e) {
|
|
259
|
+
ctx.skipped.push("opencode.json (parse error, skipping patch)");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (!targetConfig.agent) {
|
|
265
|
+
targetConfig.agent = {};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const vendorRulesJson = path.join(
|
|
269
|
+
packageRoot,
|
|
270
|
+
"vendor/opencode-agent-rules/opencode.json"
|
|
271
|
+
);
|
|
272
|
+
if (fs.existsSync(vendorRulesJson)) {
|
|
273
|
+
const rulesConfig = JSON.parse(fs.readFileSync(vendorRulesJson, "utf-8"));
|
|
274
|
+
if (rulesConfig.agent?.compaction && !targetConfig.agent.compaction) {
|
|
275
|
+
targetConfig.agent.compaction = rulesConfig.agent.compaction;
|
|
276
|
+
ctx.installed.push("opencode.json (compaction patch)");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const pkgJsonPath = path.join(packageRoot, "opencode.json");
|
|
281
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
282
|
+
const pkgConfig = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
283
|
+
|
|
284
|
+
if (pkgConfig.agents && !targetConfig.agents) {
|
|
285
|
+
targetConfig.agents = pkgConfig.agents;
|
|
286
|
+
ctx.installed.push("opencode.json (agents patch)");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (pkgConfig.agent) {
|
|
290
|
+
for (const [key, value] of Object.entries(pkgConfig.agent)) {
|
|
291
|
+
if (!targetConfig.agent[key]) {
|
|
292
|
+
targetConfig.agent[key] = value;
|
|
293
|
+
ctx.installed.push(`opencode.json (agent: ${key})`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (pkgConfig.permission && !targetConfig.permission) {
|
|
299
|
+
targetConfig.permission = pkgConfig.permission;
|
|
300
|
+
ctx.installed.push("opencode.json (permission patch)");
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (ctx.dryRun) {
|
|
305
|
+
ctx.dryRunFiles.push("opencode.json (patch)");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fs.writeFileSync(targetJsonPath, JSON.stringify(targetConfig, null, 2));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function runInit(args) {
|
|
313
|
+
const targetRoot = process.cwd();
|
|
314
|
+
|
|
315
|
+
if (args.flags.sddOnly && args.flags.rulesOnly) {
|
|
316
|
+
console.error("❌ Cannot use --sdd-only and --rules-only together.");
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const ctx = {
|
|
321
|
+
targetRoot,
|
|
322
|
+
force: args.flags.force,
|
|
323
|
+
dryRun: args.flags.dryRun,
|
|
324
|
+
installed: [],
|
|
325
|
+
skipped: [],
|
|
326
|
+
dryRunFiles: [],
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
console.log(`\n📦 SDD Multi-Agent OpenCode Installer\n`);
|
|
330
|
+
console.log(`Target: ${targetRoot}`);
|
|
331
|
+
|
|
332
|
+
if (ctx.dryRun) {
|
|
333
|
+
console.log(`Mode: DRY RUN (no files will be written)\n`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let rulesCount = 0;
|
|
337
|
+
let sddCount = 0;
|
|
338
|
+
|
|
339
|
+
if (!args.flags.sddOnly) {
|
|
340
|
+
console.log(`Installing base rules...`);
|
|
341
|
+
rulesCount = installRules(ctx);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!args.flags.rulesOnly) {
|
|
345
|
+
console.log(`Installing SDD workflow...`);
|
|
346
|
+
sddCount = installSdd(ctx);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
patchOpencodeJson(ctx);
|
|
350
|
+
|
|
351
|
+
console.log(`\n✅ Done!\n`);
|
|
352
|
+
|
|
353
|
+
if (ctx.dryRun) {
|
|
354
|
+
console.log(`Would install (${ctx.dryRunFiles.length} files):`);
|
|
355
|
+
for (const f of ctx.dryRunFiles) {
|
|
356
|
+
console.log(` + ${f}`);
|
|
357
|
+
}
|
|
358
|
+
} else {
|
|
359
|
+
console.log(`Installed:`);
|
|
360
|
+
console.log(` - rules: ${rulesCount} files`);
|
|
361
|
+
console.log(` - sdd: ${sddCount} files`);
|
|
362
|
+
|
|
363
|
+
if (ctx.skipped.length > 0) {
|
|
364
|
+
console.log(`\nSkipped (exists, use --force):`);
|
|
365
|
+
for (const f of ctx.skipped) {
|
|
366
|
+
console.log(` - ${f}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
console.log(`\nNext steps:`);
|
|
371
|
+
console.log(` 1. Edit .sdd/project-profile.json with your stack`);
|
|
372
|
+
console.log(` 2. Run: /brief my-feature "Feature description"`);
|
|
373
|
+
console.log(` 3. Optional TDD: /tasks my-feature --tdd`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const args = parseArgs(process.argv);
|
|
378
|
+
|
|
379
|
+
if (args.command === "help") {
|
|
380
|
+
usage();
|
|
381
|
+
} else if (args.command === "init") {
|
|
382
|
+
runInit(args);
|
|
383
|
+
} else {
|
|
384
|
+
console.error(`Unknown command: ${args.command}`);
|
|
385
|
+
usage();
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
package/opencode.json
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"agent": {
|
|
4
|
+
"sdd-orchestrator": {
|
|
5
|
+
"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.",
|
|
6
|
+
"mode": "subagent",
|
|
7
|
+
"model": "opencode/gpt-5.5",
|
|
8
|
+
"temperature": 0.3,
|
|
9
|
+
"permission": {
|
|
10
|
+
"edit": "allow",
|
|
11
|
+
"bash": "allow",
|
|
12
|
+
"task": {
|
|
13
|
+
"*": "allow"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"color": "accent"
|
|
17
|
+
},
|
|
18
|
+
"sdd-planner": {
|
|
19
|
+
"description": "Architecture design and technical planning for SDD workflows. Use when generating plans from specifications, designing system architecture, or breaking down features into tasks.",
|
|
20
|
+
"mode": "subagent",
|
|
21
|
+
"model": "opencode/gpt-5.5",
|
|
22
|
+
"temperature": 0.3,
|
|
23
|
+
"permission": {
|
|
24
|
+
"edit": "allow",
|
|
25
|
+
"bash": "allow",
|
|
26
|
+
"task": {
|
|
27
|
+
"*": "allow"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"color": "info"
|
|
31
|
+
},
|
|
32
|
+
"sdd-explorer": {
|
|
33
|
+
"description": "Deep codebase exploration for SDD workflows using codebase-memory-mcp. Use proactively when technical approach is unclear, before /research or /specify, or when investigating existing patterns.",
|
|
34
|
+
"mode": "subagent",
|
|
35
|
+
"model": "opencode/deepseek-v4-flash-free",
|
|
36
|
+
"temperature": 0.1,
|
|
37
|
+
"permission": {
|
|
38
|
+
"edit": "deny",
|
|
39
|
+
"bash": {
|
|
40
|
+
"*": "ask",
|
|
41
|
+
"git status*": "allow",
|
|
42
|
+
"git log*": "allow",
|
|
43
|
+
"ls*": "allow",
|
|
44
|
+
"find*": "allow"
|
|
45
|
+
},
|
|
46
|
+
"skill": "allow"
|
|
47
|
+
},
|
|
48
|
+
"color": "secondary"
|
|
49
|
+
},
|
|
50
|
+
"sdd-implementer": {
|
|
51
|
+
"description": "Systematic code implementation following SDD plans and todo-lists. Use for executing planned implementations, code generation, and building features according to specifications.",
|
|
52
|
+
"mode": "subagent",
|
|
53
|
+
"model": "opencode/deepseek-v4-flash-free",
|
|
54
|
+
"temperature": 0.3,
|
|
55
|
+
"permission": {
|
|
56
|
+
"edit": "allow",
|
|
57
|
+
"bash": "allow",
|
|
58
|
+
"task": {
|
|
59
|
+
"sdd-verifier": "allow"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"color": "success"
|
|
63
|
+
},
|
|
64
|
+
"sdd-verifier": {
|
|
65
|
+
"description": "Independent validation that implementations are actually complete. Uses Chrome DevTools for visual/UI verification and codebase-memory-mcp for structural checks. Spawns after implementation phases.",
|
|
66
|
+
"mode": "subagent",
|
|
67
|
+
"model": "opencode/qwen3.6-plus-free",
|
|
68
|
+
"temperature": 0.1,
|
|
69
|
+
"permission": {
|
|
70
|
+
"edit": "deny",
|
|
71
|
+
"bash": "allow",
|
|
72
|
+
"skill": "allow"
|
|
73
|
+
},
|
|
74
|
+
"color": "warning"
|
|
75
|
+
},
|
|
76
|
+
"sdd-reviewer": {
|
|
77
|
+
"description": "Code review specialist for security, performance, and spec compliance. Use before marking tasks complete, during pull request reviews, or for quality assurance audits.",
|
|
78
|
+
"mode": "subagent",
|
|
79
|
+
"model": "opencode/minimax-m2.5-free",
|
|
80
|
+
"temperature": 0.2,
|
|
81
|
+
"permission": {
|
|
82
|
+
"edit": "deny",
|
|
83
|
+
"bash": {
|
|
84
|
+
"*": "ask",
|
|
85
|
+
"git diff*": "allow",
|
|
86
|
+
"git log*": "allow",
|
|
87
|
+
"grep*": "allow"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"color": "error"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"permission": {
|
|
94
|
+
"skill": {
|
|
95
|
+
"sdd-*": "allow"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tudeorangbiasa/sdd-multiagent-opencode",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Spec-Driven Development multi-agent workflow for OpenCode",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sdd-multiagent-opencode": "bin/sdd-opencode.js",
|
|
8
|
+
"sdd-opencode": "bin/sdd-opencode.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
".opencode",
|
|
13
|
+
".sdd",
|
|
14
|
+
"vendor",
|
|
15
|
+
"README.md",
|
|
16
|
+
"opencode.json"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"smoke": "node bin/sdd-opencode.js init --dry-run"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"opencode",
|
|
23
|
+
"sdd",
|
|
24
|
+
"spec-driven-development",
|
|
25
|
+
"multi-agent"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const ContextGuard = async () => {
|
|
2
|
+
return {
|
|
3
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
4
|
+
output.context.push(`## Context Guard\n\nWhen compacting this session, preserve the project operating state rather than a chat transcript.\n\nRequired structure:\n\n### Current Goal\n- What the user is trying to accomplish now.\n\n### Active Workflow\n- Workflow type: SDD / maintenance / bootstrap / general engineering.\n- Current command or phase.\n- Task ID or project ID if present.\n\n### Source Of Truth\n- AGENTS.md, DESIGN.md, PRODUCT.md, .sdd/project-profile.json, specs, plans, tasks, or roadmap paths that matter.\n\n### Decisions Made\n- Durable decisions and their rationale.\n\n### Files Touched\n- Created, modified, or intentionally avoided files.\n\n### Verification State\n- changed: what exists.\n- verified: exact proof run.\n- unverified: what was not checked.\n- blocked: blockers and evidence.\n\n### Next Action\n- The smallest concrete next step.\n\nDiscard resolved discussion, duplicate context, raw logs after summarization, and old alternatives no longer under consideration.`)
|
|
5
|
+
},
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# CLI-First Scaffolding
|
|
2
|
+
|
|
3
|
+
For new projects, always use the official framework CLI when one exists.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
- Do not manually create boilerplate files when an official CLI exists.
|
|
8
|
+
- Do not hand-write manifests, generated config, IDE metadata, or framework scaffolds from memory.
|
|
9
|
+
- Use non-interactive flags when possible.
|
|
10
|
+
- If required choices are missing, ask before running the CLI.
|
|
11
|
+
- After any scaffold or generator runs, inspect the created directory structure before continuing.
|
|
12
|
+
- For existing projects, never rerun scaffold commands unless the user explicitly asks.
|
|
13
|
+
|
|
14
|
+
## Examples
|
|
15
|
+
|
|
16
|
+
Use official commands such as:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm create next-app@latest
|
|
20
|
+
pnpm create vite@latest
|
|
21
|
+
pnpm dlx nuxi@latest init
|
|
22
|
+
pnpm create astro@latest
|
|
23
|
+
composer create-project laravel/laravel
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Never fabricate complex generated files such as:
|
|
27
|
+
|
|
28
|
+
- `.xcodeproj`
|
|
29
|
+
- `project.pbxproj`
|
|
30
|
+
- `.xcworkspace`
|
|
31
|
+
- complex `.sln` metadata
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Context Budget
|
|
2
|
+
|
|
3
|
+
Keep active context below 80%.
|
|
4
|
+
|
|
5
|
+
## Thresholds
|
|
6
|
+
|
|
7
|
+
| Threshold | Behavior |
|
|
8
|
+
|-----------|----------|
|
|
9
|
+
| 60% | Reduce context loading, checkpoint current state, pass minimal prompts to subagents |
|
|
10
|
+
| 80% | Prepare for compaction; preserve only source-of-truth state and active next action |
|
|
11
|
+
|
|
12
|
+
## Policy
|
|
13
|
+
|
|
14
|
+
- Prefer source-of-truth files over conversation memory.
|
|
15
|
+
- Use codebase search and snippets before reading large files.
|
|
16
|
+
- Do not inline entire roadmaps, specs, logs, or task objects into subagent prompts.
|
|
17
|
+
- For multi-agent work, pass only task ID, task title, linked spec path, command to execute, and constraints.
|
|
18
|
+
- Write checkpoints after each meaningful batch.
|
|
19
|
+
- Before long work, summarize current goal, files touched, decisions, verification state, blockers, and next action.
|
|
20
|
+
|
|
21
|
+
## Compaction Must Preserve
|
|
22
|
+
|
|
23
|
+
- Current goal.
|
|
24
|
+
- Active workflow and command.
|
|
25
|
+
- Task ID or project ID.
|
|
26
|
+
- Source-of-truth files.
|
|
27
|
+
- Key decisions made.
|
|
28
|
+
- Files touched.
|
|
29
|
+
- Open blockers.
|
|
30
|
+
- Verification state: `changed`, `verified`, `unverified`, `blocked`.
|
|
31
|
+
- Next action.
|
|
32
|
+
|
|
33
|
+
## Compaction Should Discard
|
|
34
|
+
|
|
35
|
+
- Resolved discussions.
|
|
36
|
+
- Duplicate context.
|
|
37
|
+
- Failed hypotheses after evidence has been recorded.
|
|
38
|
+
- Raw logs once the important result has been summarized.
|
|
39
|
+
- Old alternatives no longer under consideration.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Core Behavior
|
|
2
|
+
|
|
3
|
+
Use operational guidance, not persona text.
|
|
4
|
+
|
|
5
|
+
## Default Posture
|
|
6
|
+
|
|
7
|
+
- Act before explaining when tools can ground the answer.
|
|
8
|
+
- Read before editing and verify after meaningful changes.
|
|
9
|
+
- Match effort to task complexity and risk.
|
|
10
|
+
- Prefer the smallest safe change that solves the real problem.
|
|
11
|
+
- Reuse existing patterns before inventing new abstractions.
|
|
12
|
+
- Separate observation, inference, and assumption in reports.
|
|
13
|
+
|
|
14
|
+
## Solver Loop
|
|
15
|
+
|
|
16
|
+
For non-trivial work:
|
|
17
|
+
|
|
18
|
+
1. Define the outcome in operational terms.
|
|
19
|
+
2. Inspect the repo and current environment before choosing an approach.
|
|
20
|
+
3. Find the spine: entry points, data flow, state boundaries, persistence, and user-visible behavior.
|
|
21
|
+
4. Build the smallest vertical slice that proves the solution works.
|
|
22
|
+
5. Verify at the surface where the user experiences the change.
|
|
23
|
+
6. Expand scope only after the core slice is working.
|
|
24
|
+
|
|
25
|
+
## Scope Control
|
|
26
|
+
|
|
27
|
+
- Do exactly the slice the user asked for.
|
|
28
|
+
- Do not turn planning into implementation or explanation into edits.
|
|
29
|
+
- Do not broaden scope with opportunistic cleanup, refactors, or polish unless needed for the requested outcome.
|
|
30
|
+
- If scope changes during the work, tell the user what changed and why before continuing further than the original slice.
|
|
31
|
+
- If unrelated or unexpected edits appear, stop and ask before proceeding.
|
|
32
|
+
|
|
33
|
+
## Stuck Loop Policy
|
|
34
|
+
|
|
35
|
+
- After two failed verification attempts on the same hypothesis, stop repeating the same fix.
|
|
36
|
+
- Document evidence from those attempts, then switch strategy: smaller patch, wider read, or one concrete forked question.
|
|
37
|
+
- Do not loop on identical reasoning without changing inputs.
|
|
38
|
+
|
|
39
|
+
## Communication
|
|
40
|
+
|
|
41
|
+
- Lead with actions, findings, and results.
|
|
42
|
+
- Keep progress updates short and high signal.
|
|
43
|
+
- Prefer milestone updates over step-by-step narration.
|
|
44
|
+
- When blocked, state the blocker, evidence, and smallest next step.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Status And Verification
|
|
2
|
+
|
|
3
|
+
Code is not complete until it has been proved at the surface the user cares about.
|
|
4
|
+
|
|
5
|
+
## Allowed Status Labels
|
|
6
|
+
|
|
7
|
+
- `changed`: you edited or produced something.
|
|
8
|
+
- `verified`: you proved a claim with a relevant check.
|
|
9
|
+
- `unverified`: work exists but required proof was not run.
|
|
10
|
+
- `blocked`: required progress or proof failed and the task cannot honestly be called complete.
|
|
11
|
+
- `assumption`: a choice or statement depends on inference rather than direct evidence.
|
|
12
|
+
|
|
13
|
+
Do not use `done`, `fixed`, `working`, or `resolved` without naming proof immediately after.
|
|
14
|
+
|
|
15
|
+
## Minimum Proof By Change Type
|
|
16
|
+
|
|
17
|
+
| Change type | Minimum proof |
|
|
18
|
+
|-------------|---------------|
|
|
19
|
+
| Localized edit | Re-read or one targeted static check |
|
|
20
|
+
| Backend, logic, or API change | Targeted test, command, script, or runtime request |
|
|
21
|
+
| UI or interaction change | Browser or user-surface verification, plus static checks as needed |
|
|
22
|
+
| Integration-sensitive change | Build or typecheck plus one focused behavior check |
|
|
23
|
+
| New app or scaffold | Setup/install succeeds, startup or health check succeeds, production build succeeds, one primary happy-path flow works |
|
|
24
|
+
|
|
25
|
+
Build, lint, and typecheck support completion claims, but they do not replace runtime or surface proof for interaction, styling, navigation, or integration promises.
|
|
26
|
+
|
|
27
|
+
## Closeout Contract
|
|
28
|
+
|
|
29
|
+
Every substantive completion summary should include:
|
|
30
|
+
|
|
31
|
+
- **Summary**: outcome in one short paragraph.
|
|
32
|
+
- **Files touched**: paths or areas changed.
|
|
33
|
+
- **Verification evidence**: exact commands run, manual checks, user-visible surfaces exercised.
|
|
34
|
+
- **Risks and unverified items**: regressions not tested, assumptions, follow-ups.
|
|
35
|
+
|
|
36
|
+
If intended verification failed and you fall back to a weaker check, say so explicitly.
|