@ikieaneh/opencode-kit 0.5.7 → 0.5.9
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/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/opencode-kit.js +0 -1
- package/README.md +116 -26
- package/docs/guides/contract-protocol.md +67 -0
- package/docs/guides/scoring-pipeline.md +60 -0
- package/docs/guides/troubleshooting.md +78 -0
- package/package.json +1 -1
- package/rules/rules.json +1 -1
- package/src/adr.sh +27 -14
- package/src/analytics.sh +2 -1
- package/src/diff.sh +95 -0
- package/src/doctor.sh +1 -0
- package/src/global-config.sh +1 -0
- package/src/init.sh +9 -4
- package/src/postflight.sh +42 -0
- package/src/preflight.sh +1 -0
- package/src/status.sh +6 -7
- package/src/telemetry.sh +2 -1
- package/src/update.sh +1 -0
- package/templates/contract.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-kit",
|
|
3
3
|
"description": "Standardized orchestration framework — contract-based, rules-enforced, zero-touch agent workflow",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.8",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rizki Rachman",
|
|
7
7
|
"url": "https://github.com/RizkiRachman"
|
|
@@ -184,7 +184,6 @@ export const OpencodeKitPlugin = async ({ client, directory }) => {
|
|
|
184
184
|
|
|
185
185
|
// Register user project skills FIRST (higher priority)
|
|
186
186
|
const userSkillsDir = path.join(projectDir, '.opencode/skills');
|
|
187
|
-
const userSkillsDir = path.join(projectDir, '.opencode/skills');
|
|
188
187
|
if (fs.existsSync(userSkillsDir) && !config.skills.paths.includes(userSkillsDir)) {
|
|
189
188
|
config.skills.paths.push(userSkillsDir);
|
|
190
189
|
log('info', `Registered user skills: ${userSkillsDir}`);
|
package/README.md
CHANGED
|
@@ -59,25 +59,67 @@
|
|
|
59
59
|
<!-- ABOUT THE PROJECT -->
|
|
60
60
|
## About The Project
|
|
61
61
|
|
|
62
|
-
`opencode-kit` is a
|
|
62
|
+
`opencode-kit` is a **workflow enforcement framework** for AI coding agents. It ensures agents follow a structured process — plan, build, review, learn — instead of jumping straight to implementation.
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
### The Problem
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
AI agents (Claude, Copilot, etc.) are powerful but **unpredictable**. When given a task, they often:
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
- Skip project conventions and jump directly to writing code
|
|
69
|
+
- Ignore shared state — each session starts from scratch
|
|
70
|
+
- Bypass quality gates — no review, no scoring, no learning
|
|
71
|
+
- Use different approaches on every run — inconsistent results
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
Traditional frameworks try to fix this with **prose in .md files** ("load the contract before any tool call"). But agents routinely ignore prose because there's no enforcement — it's a suggestion, not a requirement.
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
2. **`contract.json`** — shared state machine every agent MUST read/write (local or global)
|
|
74
|
-
3. **`rules.json`** — machine-readable rules with CRITICAL/HIGH severity
|
|
75
|
-
4. **3 auto-registered skills** — `orchestration-template`, `scoring-pipeline`, `adr-generator`
|
|
76
|
-
5. **Config resolution** — `.opencode/` → `~/.config/opencode-kit/` → plugin defaults
|
|
75
|
+
### The Solution
|
|
77
76
|
|
|
78
|
-
|
|
77
|
+
`opencode-kit` replaces prose conventions with **machine-readable enforcement**:
|
|
79
78
|
|
|
80
|
-
|
|
79
|
+
| Instead of ... | opencode-kit uses ... |
|
|
80
|
+
|:---------------|:----------------------|
|
|
81
|
+
| "read the state file" | `contract.json` — a JSON state machine agents MUST read/write |
|
|
82
|
+
| "follow the rules" | `rules.json` — CRITICAL rules BLOCK agents, HIGH rules FLAG them |
|
|
83
|
+
| "check before editing" | `preflight.sh` — an enforcement gate that fails if rules aren't met |
|
|
84
|
+
| "review your work" | **Scoring pipeline** — every output is scored (≥70 PASS, <50 BLOCK) |
|
|
85
|
+
| "remember what you learned" | `postflight.sh` — auto-persists state + telemetry + lessons learned |
|
|
86
|
+
|
|
87
|
+
The result: **zero-touch agent workflow**. Set a goal, and the system self-executes through Plan → Build → Review → Learn, pausing only when BLOCKED.
|
|
88
|
+
|
|
89
|
+
### How It Works
|
|
90
|
+
|
|
91
|
+
As an OpenCode **plugin**, `opencode-kit` injects enforcement into every session globally:
|
|
92
|
+
|
|
93
|
+
1. **Plugin bootstrap** — every session auto-loads the orchestration contract before any work
|
|
94
|
+
2. **Pre-flight gate** — validates branch, contract state, and rule compliance. BLOCKs on CRITICAL violations
|
|
95
|
+
3. **Contract protocol** — shared state machine (`contract.json`) tracks phase, decisions, scores, telemetry
|
|
96
|
+
4. **Scoring pipeline** — every subagent output scored. ≥70 PASS, 50-69 RETRY, <50 BLOCKED
|
|
97
|
+
5. **ADR logging** — every architectural decision recorded in `decisions.adr_log[]`
|
|
98
|
+
6. **Extension model** — project-specific skills in `.opencode/skills/` override plugin defaults
|
|
99
|
+
|
|
100
|
+
### The 9 Built-in Skills
|
|
101
|
+
|
|
102
|
+
| Skill | Purpose |
|
|
103
|
+
|-------|---------|
|
|
104
|
+
| `orchestration-template` | Contract protocol, state machine, persistence rules |
|
|
105
|
+
| `scoring-pipeline` | Tier 1 rule checks + Tier 2 LLM judge + verdict |
|
|
106
|
+
| `adr-generator` | Architecture Decision Record format and auto-ID |
|
|
107
|
+
| `qa-expert` | Test standards, edge cases, coverage goals |
|
|
108
|
+
| `system-analyst` | Impact analysis, execution tracing, architecture checks |
|
|
109
|
+
| `token-optimize` | Efficient reading, batching, and delegation |
|
|
110
|
+
| `verification-before-completion` | Quality gates — formatting, compile, test, verify |
|
|
111
|
+
| `learner` | Post-execution learning, memory persistence |
|
|
112
|
+
| *(extensible)* | Create your own skills in `.opencode/skills/` |
|
|
113
|
+
|
|
114
|
+
### Built for Cost-Efficient Models
|
|
115
|
+
|
|
116
|
+
`opencode-kit` was designed for teams using **cost-optimized models** (DeepSeek V4 Flash, Gemini Flash, GPT-4o Mini, etc.) who want to compete with premium models through **process rigor**, not raw intelligence. The scoring pipeline and pre-flight enforcement create a quality floor that cheap models can meet through architecture, not model power.
|
|
117
|
+
|
|
118
|
+
### Platform Support
|
|
119
|
+
|
|
120
|
+
- **macOS M-Series** (Apple Silicon) — primary development target
|
|
121
|
+
- **Linux** (Ubuntu) — CI-verified via GitHub Actions
|
|
122
|
+
- **Any OpenCode-compatible environment** with `lean-ctx`, `gitnexus`, `graphify`
|
|
81
123
|
|
|
82
124
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
83
125
|
|
|
@@ -122,6 +164,17 @@ node --version # ≥ 18
|
|
|
122
164
|
git --version # any recent version
|
|
123
165
|
```
|
|
124
166
|
|
|
167
|
+
### Documentation Guides
|
|
168
|
+
|
|
169
|
+
| Guide | Description |
|
|
170
|
+
|-------|-------------|
|
|
171
|
+
| [Contract Protocol](docs/guides/contract-protocol.md) | State machine, fields, resolution order |
|
|
172
|
+
| [Scoring Pipeline](docs/guides/scoring-pipeline.md) | Tier 1 + Tier 2 + verdict |
|
|
173
|
+
| [Troubleshooting](docs/guides/troubleshooting.md) | Common issues and solutions |
|
|
174
|
+
| [Quickstart](docs/examples/QUICKSTART.md) | 6-step setup from scratch |
|
|
175
|
+
| [Extension Skills](docs/examples/extension-skill-template.md) | Create project-specific skills |
|
|
176
|
+
| [Model Configs](docs/examples/model-configs.md) | Provider configuration examples |
|
|
177
|
+
|
|
125
178
|
### Installation
|
|
126
179
|
|
|
127
180
|
#### Option 1: Install as plugin (recommended)
|
|
@@ -224,6 +277,25 @@ INIT → PLAN → PLAN_SCORED → EXECUTE → EXECUTE_SCORED → REVIEW → REVI
|
|
|
224
277
|
|
|
225
278
|
Each phase transition requires a score ≥70 to proceed.
|
|
226
279
|
|
|
280
|
+
### CLI Commands
|
|
281
|
+
|
|
282
|
+
Once installed, run these from the project root:
|
|
283
|
+
|
|
284
|
+
| Command | Purpose |
|
|
285
|
+
|---------|---------|
|
|
286
|
+
| `bash .opencode/src/status.sh` | Dashboard — contract state, telemetry, rules |
|
|
287
|
+
| `bash .opencode/src/doctor.sh` | Diagnostics — MCPs, contract, git, persistence |
|
|
288
|
+
| `bash .opencode/src/analytics.sh` | Telemetry aggregation — phase times, cost estimates |
|
|
289
|
+
| `bash .opencode/src/diff.sh` [branch1] [branch2] | Compare contract state between branches |
|
|
290
|
+
| `bash .opencode/src/adr.sh` | Record a new Architecture Decision Record |
|
|
291
|
+
| `bash .opencode/src/telemetry.sh` | View telemetry details |
|
|
292
|
+
| `bash .opencode/src/new-skill.sh` <name> | Scaffold a new skill SKILL.md |
|
|
293
|
+
| `bash .opencode/rules/validation.sh` | Validate rules compliance |
|
|
294
|
+
| `npx opencode-kit --version` | Print version |
|
|
295
|
+
| `npx opencode-kit --help` | Print help |
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
227
299
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
228
300
|
|
|
229
301
|
<!-- STRUCTURE -->
|
|
@@ -233,24 +305,42 @@ Each phase transition requires a score ≥70 to proceed.
|
|
|
233
305
|
opencode-kit/
|
|
234
306
|
├── rules/
|
|
235
307
|
│ ├── rules.json ← Machine-enforceable rules (CRITICAL/HIGH/state machine)
|
|
236
|
-
│ └── validation.sh ← Validates agent actions against rules
|
|
308
|
+
│ └── validation.sh ← Validates agent actions against rules
|
|
237
309
|
├── src/
|
|
238
310
|
│ ├── init.sh ← Scaffold into target project
|
|
239
311
|
│ ├── preflight.sh ← Envelope load gate (zero deps, fails if rules violated)
|
|
240
|
-
│ ├── postflight.sh ← Auto-persist + scoring pipeline
|
|
241
|
-
│
|
|
312
|
+
│ ├── postflight.sh ← Auto-persist + scoring pipeline + contract migration
|
|
313
|
+
│ ├── doctor.sh ← Diagnostic command
|
|
314
|
+
│ ├── status.sh ← Dashboard view
|
|
315
|
+
│ ├── diff.sh ← Compare contract across branches
|
|
316
|
+
│ ├── analytics.sh ← Telemetry aggregation
|
|
317
|
+
│ ├── adr.sh ← ADR auto-generator
|
|
318
|
+
│ ├── telemetry.sh ← Phase telemetry viewer
|
|
319
|
+
│ ├── new-skill.sh ← Skill template generator
|
|
320
|
+
│ ├── update.sh ← Pull latest from GitHub
|
|
321
|
+
│ ├── verify.sh ← Installation health check
|
|
322
|
+
│ ├── platform.sh ← Cross-platform detection
|
|
323
|
+
│ ├── global-config.sh ← Config resolution chain
|
|
324
|
+
│ └── cli.js ← --version / --help
|
|
325
|
+
├── skills/ ← 9 auto-registered skills
|
|
242
326
|
├── templates/
|
|
243
|
-
│ ├── contract.json ← Shared state contract
|
|
244
|
-
│ ├──
|
|
245
|
-
│
|
|
246
|
-
│
|
|
247
|
-
|
|
248
|
-
│
|
|
249
|
-
│
|
|
250
|
-
│
|
|
251
|
-
|
|
252
|
-
├──
|
|
253
|
-
|
|
327
|
+
│ ├── contract.json ← Shared state contract
|
|
328
|
+
│ ├── opencode-kit.schema.json ← Agent config schema
|
|
329
|
+
│ ├── judge-prompt.md ← LLM judge prompt template
|
|
330
|
+
│ └── agents/ ← 6 agent .md templates
|
|
331
|
+
├── docs/
|
|
332
|
+
│ ├── guides/ ← Usage guides (contract, scoring, troubleshooting)
|
|
333
|
+
│ ├── examples/ ← Quickstart, model configs, extension skills
|
|
334
|
+
│ └── plans/ ← Implementation plans
|
|
335
|
+
├── test/ ← Integration + E2E tests (16 total)
|
|
336
|
+
├── .github/workflows/ ← CI (ShellCheck + scaffold + tests)
|
|
337
|
+
├── .opencode/plugins/ ← Plugin entry point
|
|
338
|
+
├── .claude-plugin/ ← Plugin metadata
|
|
339
|
+
├── package.json ← npm publish
|
|
340
|
+
├── CHANGELOG.md
|
|
341
|
+
├── CONTRIBUTING.md
|
|
342
|
+
├── RELEASE.md
|
|
343
|
+
└── ROADMAP.md
|
|
254
344
|
```
|
|
255
345
|
|
|
256
346
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Contract Protocol
|
|
2
|
+
|
|
3
|
+
The orchestrator contract (`contract.json`) is the single source of truth for every agent session.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
- Tracks **state** — what phase the workflow is in (INIT → PLAN → ... → COMPLETE)
|
|
8
|
+
- Stores **requirements** — the goal, acceptance criteria, and constraints
|
|
9
|
+
- Logs **decisions** — Architecture Decision Records (ADRs)
|
|
10
|
+
- Records **scores** — quality metrics from the scoring pipeline
|
|
11
|
+
- Captures **telemetry** — elapsed time, phases completed, agents used
|
|
12
|
+
|
|
13
|
+
## State Machine
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
INIT → PLAN → PLAN_SCORED → EXECUTE → EXECUTE_SCORED → REVIEW → REVIEW_SCORED → COMPLETE
|
|
17
|
+
↘
|
|
18
|
+
BLOCKED (any phase) → user intervention → retry
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Transition rules:**
|
|
22
|
+
- Each phase transition requires score ≥ 70
|
|
23
|
+
- Score < 50 → BLOCKED
|
|
24
|
+
- Max 3 retry attempts before escalation
|
|
25
|
+
|
|
26
|
+
## Key Fields
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"state": "INIT",
|
|
31
|
+
"contract_version": "0.5.8",
|
|
32
|
+
"requirements": {
|
|
33
|
+
"goal": "What we're building",
|
|
34
|
+
"acceptance_criteria": ["testable condition 1"],
|
|
35
|
+
"constraints": ["must not..."]
|
|
36
|
+
},
|
|
37
|
+
"governance": {
|
|
38
|
+
"active_agent": "orchestrator",
|
|
39
|
+
"current_guidance": "Instructions for this session",
|
|
40
|
+
"extension_skills": ["java-conventions"],
|
|
41
|
+
"permissions": { "do": [], "dont": ["push to main"] }
|
|
42
|
+
},
|
|
43
|
+
"decisions": {
|
|
44
|
+
"adr_log": [
|
|
45
|
+
{ "id": "ADR-001", "date": "2026-06-11", "title": "Use contract protocol", ... }
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"score": {
|
|
49
|
+
"combined": 85,
|
|
50
|
+
"verdict": "PASS"
|
|
51
|
+
},
|
|
52
|
+
"metrics": {
|
|
53
|
+
"phases_completed": ["INIT", "PLAN", "PLAN_SCORED"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Resolution Order
|
|
59
|
+
|
|
60
|
+
1. `.opencode/orchestration/contract.json` — project override
|
|
61
|
+
2. `~/.config/opencode-kit/contract.json` — global defaults
|
|
62
|
+
3. Plugin `templates/contract.json` — shipped defaults
|
|
63
|
+
|
|
64
|
+
## CLI
|
|
65
|
+
|
|
66
|
+
View contract state: `bash .opencode/src/status.sh`
|
|
67
|
+
Compare across branches: `bash .opencode/src/diff.sh [branch1] [branch2]`
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Scoring Pipeline
|
|
2
|
+
|
|
3
|
+
Every subagent output is scored before the next phase begins.
|
|
4
|
+
|
|
5
|
+
## Tier 1 — Rule Checks
|
|
6
|
+
|
|
7
|
+
Automatic checks run by the orchestrator. Start at 100, deduct per violation:
|
|
8
|
+
|
|
9
|
+
| Check | Deduction |
|
|
10
|
+
|-------|:---------:|
|
|
11
|
+
| Schema valid (required fields present) | -15 |
|
|
12
|
+
| Permissions violated | -40 |
|
|
13
|
+
| Blast radius HIGH/CRITICAL | -40 |
|
|
14
|
+
| Writing order wrong | -15 |
|
|
15
|
+
| Fields missing | -15 |
|
|
16
|
+
|
|
17
|
+
If subtotal < 70 → skip Tier 2, use subtotal as combined score.
|
|
18
|
+
|
|
19
|
+
## Tier 2 — LLM Judge
|
|
20
|
+
|
|
21
|
+
If Tier 1 score ≥ 70, the orchestrator runs a judge via `subtask()`.
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"score": 85,
|
|
26
|
+
"rationale": "All requirements met. Writing order correct.",
|
|
27
|
+
"missing_items": ["Test for null boundary case"]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Dimensions:**
|
|
32
|
+
| Dimension | Max | What it evaluates |
|
|
33
|
+
|-----------|:---:|-------------------|
|
|
34
|
+
| Requirements | 40 | Does output satisfy goal + acceptance criteria? |
|
|
35
|
+
| Governance | 30 | Follows rules.json + writing order? |
|
|
36
|
+
| Completeness | 20 | All files created? Edge cases documented? |
|
|
37
|
+
| Edge cases | 10 | Nulls, errors, boundaries covered? |
|
|
38
|
+
|
|
39
|
+
## Tier 3 — Verdict
|
|
40
|
+
|
|
41
|
+
| Combined Score | Verdict | Action |
|
|
42
|
+
|:--------------:|:-------:|--------|
|
|
43
|
+
| ≥ 70 | **PASS** | Advance to next phase |
|
|
44
|
+
| 50–69 | **RETRY** | Increment retry count, re-delegate |
|
|
45
|
+
| < 50 | **BLOCKED** | Escalate to user |
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Thresholds and deductions are in `rules.json` → `scoring`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"scoring": {
|
|
54
|
+
"tier1": { "schema_valid_deduction": 15, ... },
|
|
55
|
+
"thresholds": { "pass": 70, "retry": 50, "max_attempts": 3 }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Projects can override rule severity via `contract.json` → `validation.rule_overrides`.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Troubleshooting Guide
|
|
2
|
+
|
|
3
|
+
## Common Issues
|
|
4
|
+
|
|
5
|
+
### Plugin doesn't load
|
|
6
|
+
|
|
7
|
+
**Symptom**: Skills not available, contract not injected.
|
|
8
|
+
|
|
9
|
+
**Checks:**
|
|
10
|
+
1. Is `@ikieaneh/opencode-kit` in `opencode.json` plugin array? Must be **first**.
|
|
11
|
+
2. Is it installed? `ls node_modules/@ikieaneh/opencode-kit`
|
|
12
|
+
3. Is the plugin array syntax correct? `"plugin": ["@ikieaneh/opencode-kit"]`
|
|
13
|
+
|
|
14
|
+
### Agent jumps straight to implementation
|
|
15
|
+
|
|
16
|
+
**Symptom**: Agent starts working without loading contract.
|
|
17
|
+
|
|
18
|
+
**Fix:** The plugin's `messages.transform` hook injects the bootstrap. Make sure:
|
|
19
|
+
1. Plugin is first in the array
|
|
20
|
+
2. No other plugin overrides the same hook
|
|
21
|
+
3. Run `bash .opencode/src/doctor.sh` to verify
|
|
22
|
+
|
|
23
|
+
### Contract not found
|
|
24
|
+
|
|
25
|
+
**Symptom**: "Contract not found" error from preflight.
|
|
26
|
+
|
|
27
|
+
**Solution:**
|
|
28
|
+
```sh
|
|
29
|
+
bash .opencode/src/doctor.sh
|
|
30
|
+
# Or manually:
|
|
31
|
+
mkdir -p .opencode/orchestration
|
|
32
|
+
cp node_modules/@ikieaneh/opencode-kit/templates/contract.json .opencode/orchestration/contract.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Score below threshold
|
|
36
|
+
|
|
37
|
+
**Symptom**: Workflow keeps retrying or gets blocked.
|
|
38
|
+
|
|
39
|
+
**Check:**
|
|
40
|
+
- Tier 1 violations (blast radius, permissions)
|
|
41
|
+
- Tier 2 judge feedback — read `judge.missing_items`
|
|
42
|
+
- Retry count — max 3 attempts before BLOCKED
|
|
43
|
+
|
|
44
|
+
### ShellCheck fails in CI
|
|
45
|
+
|
|
46
|
+
**Symptom**: GitHub Actions ShellCheck job fails.
|
|
47
|
+
|
|
48
|
+
**Fix:** Run locally:
|
|
49
|
+
```sh
|
|
50
|
+
shellcheck src/*.sh rules/*.sh
|
|
51
|
+
```
|
|
52
|
+
Look for SC1091 (source path) or SC2001 (sed style) — most are info-level.
|
|
53
|
+
|
|
54
|
+
### Scaffold test fails in CI
|
|
55
|
+
|
|
56
|
+
**Symptom**: init.sh exits with error.
|
|
57
|
+
|
|
58
|
+
**Check:**
|
|
59
|
+
- Are all `mkdir -p` paths created before `cp`?
|
|
60
|
+
- Run locally: `cd /tmp && git init && bash /path/to/init.sh`
|
|
61
|
+
|
|
62
|
+
### Custom skill not loading
|
|
63
|
+
|
|
64
|
+
**Symptom**: Skill referenced in opencode.json not available.
|
|
65
|
+
|
|
66
|
+
**Solution:**
|
|
67
|
+
1. Place skill at `.opencode/skills/<name>/SKILL.md`
|
|
68
|
+
2. Verify frontmatter has `description:` field
|
|
69
|
+
3. Verify SKILL.md starts with `---` (YAML frontmatter)
|
|
70
|
+
4. Run `bash .opencode/src/doctor.sh` to verify
|
|
71
|
+
|
|
72
|
+
## Diagnostics
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
bash .opencode/src/doctor.sh # Full health check
|
|
76
|
+
bash .opencode/src/status.sh # Dashboard view
|
|
77
|
+
bash .opencode/src/analytics.sh # Telemetry analysis
|
|
78
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikieaneh/opencode-kit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
4
4
|
"description": "Standardized OpenCode orchestration framework — contract-based, rules-enforced, zero-touch agent workflow. Install as plugin.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "RizkiRachman",
|
package/rules/rules.json
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"id": "WRITE_001",
|
|
91
|
-
"severity": "
|
|
91
|
+
"severity": "HIGH",
|
|
92
92
|
"description": "Agent MUST follow writing order: Port → Service → Mapper → Adapter → Constants → Events → Tests",
|
|
93
93
|
"condition": "writing_order_violation",
|
|
94
94
|
"action": "FLAG",
|
package/src/adr.sh
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
7
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
# shellcheck source=./platform.sh
|
|
8
9
|
. "$SCRIPT_DIR/platform.sh"
|
|
9
10
|
|
|
10
11
|
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
@@ -85,30 +86,42 @@ if [ -n "$DUP" ]; then
|
|
|
85
86
|
exit 0
|
|
86
87
|
fi
|
|
87
88
|
|
|
88
|
-
# --- Build ADR entry ---
|
|
89
|
-
#
|
|
89
|
+
# --- Build ADR entry via temp JSON file (avoids shell injection) ---
|
|
90
|
+
# Write ADR fields to temp file to avoid shell interpolation into Python
|
|
91
|
+
ADR_DATA=$(mktemp /tmp/opencode-adr-data-XXXXX.json)
|
|
92
|
+
cat > "$ADR_DATA" << ADRDATA
|
|
93
|
+
{
|
|
94
|
+
"title": "$TITLE",
|
|
95
|
+
"context": "$CONTEXT",
|
|
96
|
+
"decision": "$DECISION",
|
|
97
|
+
"alternatives": "$ALTERNATIVES",
|
|
98
|
+
"consequences": "$CONSEQUENCES"
|
|
99
|
+
}
|
|
100
|
+
ADRDATA
|
|
101
|
+
|
|
90
102
|
$PYTHON_CMD -c "
|
|
91
|
-
import json
|
|
103
|
+
import json
|
|
92
104
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
next_id = '$NEXT_ID'
|
|
105
|
+
with open('$ADR_DATA') as f:
|
|
106
|
+
data = json.load(f)
|
|
96
107
|
|
|
97
108
|
entry = {
|
|
98
|
-
'id':
|
|
99
|
-
'date':
|
|
100
|
-
'title': title,
|
|
101
|
-
'context': ''
|
|
102
|
-
'decision': ''
|
|
103
|
-
'alternatives': ''
|
|
104
|
-
'consequences': ''
|
|
109
|
+
'id': '$NEXT_ID',
|
|
110
|
+
'date': '$(date +%Y-%m-%d)',
|
|
111
|
+
'title': data['title'],
|
|
112
|
+
'context': data['context'],
|
|
113
|
+
'decision': data['decision'],
|
|
114
|
+
'alternatives': data['alternatives'],
|
|
115
|
+
'consequences': data['consequences']
|
|
105
116
|
}
|
|
106
117
|
|
|
107
118
|
with open('/tmp/opencode-adr-entry.json', 'w') as f:
|
|
108
|
-
|
|
119
|
+
json.dump(entry, f, indent=2)
|
|
109
120
|
print('Entry written')
|
|
110
121
|
"
|
|
111
122
|
|
|
123
|
+
rm -f "$ADR_DATA"
|
|
124
|
+
|
|
112
125
|
# --- Inject into contract.json ---
|
|
113
126
|
$PYTHON_CMD -c "
|
|
114
127
|
import json
|
package/src/analytics.sh
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
# shellcheck source=./platform.sh
|
|
7
8
|
. "$SCRIPT_DIR/platform.sh"
|
|
8
9
|
|
|
9
10
|
TELEMETRY_DIR=".opencode/telemetry"
|
|
@@ -68,7 +69,7 @@ for p in phases:
|
|
|
68
69
|
frm = p.get('from', '?')
|
|
69
70
|
to = p.get('to', '?')
|
|
70
71
|
ms = p.get('elapsed_ms', 0)
|
|
71
|
-
bar = '
|
|
72
|
+
bar = '#' * max(1, int(ms / max(1, total_ms) * 30))
|
|
72
73
|
print(f' {frm:16s} → {to:16s} {ms/1000:6.1f}s {bar}')
|
|
73
74
|
|
|
74
75
|
# Cost estimate (rough: ~$0.15/1M tokens, ~1000 tok/s)
|
package/src/diff.sh
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# opencode-kit diff — compare contract state between branches
|
|
3
|
+
# Usage: bash src/diff.sh [branch1] [branch2]
|
|
4
|
+
# Default: compares current branch vs main
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
# shellcheck source=./platform.sh
|
|
9
|
+
. "$SCRIPT_DIR/platform.sh"
|
|
10
|
+
|
|
11
|
+
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
12
|
+
BRANCH_A="${1:-main}"
|
|
13
|
+
BRANCH_B="${2:-HEAD}"
|
|
14
|
+
|
|
15
|
+
RED='\033[0;31m'
|
|
16
|
+
GREEN='\033[0;32m'
|
|
17
|
+
YELLOW='\033[1;33m'
|
|
18
|
+
CYAN='\033[0;36m'
|
|
19
|
+
NC='\033[0m'
|
|
20
|
+
|
|
21
|
+
echo -e "${CYAN}📋 opencode-kit diff: $BRANCH_A ↔ $BRANCH_B${NC}"
|
|
22
|
+
echo ""
|
|
23
|
+
|
|
24
|
+
# Extract contract from a git ref
|
|
25
|
+
get_contract_state() {
|
|
26
|
+
local branch="$1"
|
|
27
|
+
local content
|
|
28
|
+
content=$(git show "$branch:$CONTRACT_FILE" 2>/dev/null || echo "")
|
|
29
|
+
if [ -z "$content" ]; then
|
|
30
|
+
echo ""
|
|
31
|
+
return 1
|
|
32
|
+
fi
|
|
33
|
+
echo "$content"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
CONTRACT_A=$(get_contract_state "$BRANCH_A")
|
|
37
|
+
CONTRACT_B=$(get_contract_state "$BRANCH_B")
|
|
38
|
+
|
|
39
|
+
if [ -z "$CONTRACT_A" ] && [ -z "$CONTRACT_B" ]; then
|
|
40
|
+
echo -e "${YELLOW}No contract found in either branch.${NC}"
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Show state diff
|
|
45
|
+
if [ -n "$PYTHON_CMD" ]; then
|
|
46
|
+
$PYTHON_CMD -c "
|
|
47
|
+
import json, sys
|
|
48
|
+
|
|
49
|
+
def get_state(c):
|
|
50
|
+
try:
|
|
51
|
+
d = json.loads(c)
|
|
52
|
+
return {
|
|
53
|
+
'state': d.get('state', '?'),
|
|
54
|
+
'goal': (d.get('requirements', {}) or {}).get('goal', '?'),
|
|
55
|
+
'score': (d.get('score', {}) or {}).get('combined', '?'),
|
|
56
|
+
'phases': (d.get('metrics', {}) or {}).get('phases_completed', []),
|
|
57
|
+
'adrs': len((d.get('decisions', {}) or {}).get('adr_log', [])),
|
|
58
|
+
'version': d.get('contract_version', '?')
|
|
59
|
+
}
|
|
60
|
+
except:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
a = get_state('''$CONTRACT_A''') if '''$CONTRACT_A''' else None
|
|
64
|
+
b = get_state('''$CONTRACT_B''') if '''$CONTRACT_B''' else None
|
|
65
|
+
|
|
66
|
+
if a and b:
|
|
67
|
+
print(f' Field $BRANCH_A $BRANCH_B')
|
|
68
|
+
print(f' {"-"*50}')
|
|
69
|
+
for field in ['state', 'goal', 'score', 'version']:
|
|
70
|
+
va = str(a.get(field, '?'))[:20]
|
|
71
|
+
vb = str(b.get(field, '?'))[:20]
|
|
72
|
+
marker = ' ←→' if va != vb else ' '
|
|
73
|
+
print(f' {field:20s} {va:20s} {marker} {vb:20s}')
|
|
74
|
+
print(f' phases {len(a.get(\"phases\",[])):3d} completed {len(b.get(\"phases\",[])):3d} completed')
|
|
75
|
+
print(f' ADRs {a.get(\"adrs\",0):3d} recorded {b.get(\"adrs\",0):3d} recorded')
|
|
76
|
+
elif a and not b:
|
|
77
|
+
print(f' Contract exists in $BRANCH_A but NOT in $BRANCH_B')
|
|
78
|
+
print(f' State: {a.get(\"state\",\"?\")}')
|
|
79
|
+
elif b and not a:
|
|
80
|
+
print(f' Contract exists in $BRANCH_B but NOT in $BRANCH_A')
|
|
81
|
+
print(f' State: {b.get(\"state\",\"?\")}')
|
|
82
|
+
" 2>/dev/null || echo -e "${YELLOW}Could not parse contract JSON${NC}"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Raw git diff
|
|
86
|
+
echo ""
|
|
87
|
+
echo -e "${CYAN}Raw diff:${NC}"
|
|
88
|
+
if git diff "$BRANCH_A" "$BRANCH_B" -- "$CONTRACT_FILE" 2>/dev/null | head -40; then
|
|
89
|
+
if ! git diff --exit-code "$BRANCH_A" "$BRANCH_B" -- "$CONTRACT_FILE" &>/dev/null; then
|
|
90
|
+
:
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
echo ""
|
|
94
|
+
echo -e "Run: ${GREEN}bash .opencode/src/diff.sh${NC} (default: main vs HEAD)"
|
|
95
|
+
echo -e " ${GREEN}bash .opencode/src/diff.sh staging feature-x${NC} (custom branches)"
|
package/src/doctor.sh
CHANGED
package/src/global-config.sh
CHANGED
package/src/init.sh
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
8
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
# shellcheck source=./platform.sh
|
|
9
10
|
. "$SCRIPT_DIR/platform.sh"
|
|
10
11
|
. "$SCRIPT_DIR/global-config.sh"
|
|
11
12
|
KIT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
@@ -88,7 +89,7 @@ if [ -d ".opencode" ]; then
|
|
|
88
89
|
fi
|
|
89
90
|
|
|
90
91
|
# --- Scaffold directories ---
|
|
91
|
-
mkdir -p .opencode/orchestration .opencode/rules .opencode/agents .opencode/src
|
|
92
|
+
mkdir -p .opencode/orchestration .opencode/rules .opencode/agents .opencode/src .opencode/templates
|
|
92
93
|
|
|
93
94
|
# --- Copy templates ---
|
|
94
95
|
echo ""
|
|
@@ -154,6 +155,10 @@ if [ "$PLUGIN_MODE" = false ]; then
|
|
|
154
155
|
chmod +x .opencode/src/analytics.sh
|
|
155
156
|
echo " ✅ analytics.sh (executable)"
|
|
156
157
|
|
|
158
|
+
cp "$KIT_DIR/src/diff.sh" .opencode/src/diff.sh
|
|
159
|
+
chmod +x .opencode/src/diff.sh
|
|
160
|
+
echo " ✅ diff.sh (executable)"
|
|
161
|
+
|
|
157
162
|
# --- Copy agent templates (pre-flight gates) ---
|
|
158
163
|
for agent in orchestrator planner task-manager code-reviewer learner fixer; do
|
|
159
164
|
if [ -f "$KIT_DIR/templates/agents/$agent.md" ]; then
|
|
@@ -175,9 +180,9 @@ echo ""
|
|
|
175
180
|
echo "[opencode-kit] Running verification..."
|
|
176
181
|
if "$KIT_DIR/src/verify.sh"; then
|
|
177
182
|
echo ""
|
|
178
|
-
echo -e "${GREEN}
|
|
179
|
-
echo -e "${GREEN} ✅ opencode-kit
|
|
180
|
-
echo -e "${GREEN}
|
|
183
|
+
echo -e "${GREEN}========================================${NC}"
|
|
184
|
+
echo -e "${GREEN} ✅ opencode-kit initialized (version: $(cat "$KIT_DIR/package.json" | grep '"version"' | head -1 | cut -d'"' -f4))${NC}"
|
|
185
|
+
echo -e "${GREEN}========================================${NC}"
|
|
181
186
|
echo ""
|
|
182
187
|
echo " Next steps:"
|
|
183
188
|
echo " 1. Set GOAL & SCOPE in .opencode/orchestration/contract.json"
|
package/src/postflight.sh
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
# shellcheck source=./platform.sh
|
|
7
8
|
. "$SCRIPT_DIR/platform.sh"
|
|
8
9
|
|
|
9
10
|
CONTRACT_KEY="orchestration-contract"
|
|
@@ -12,6 +13,7 @@ STATE_FILE="STATE.md"
|
|
|
12
13
|
TELEMETRY_DIR=".opencode/telemetry"
|
|
13
14
|
START_TIME_FILE=".opencode/telemetry/.phase_start"
|
|
14
15
|
STATE_BACKUP_DIR=".opencode/state"
|
|
16
|
+
TEMPLATE_FILE=".opencode/templates/contract.json"
|
|
15
17
|
|
|
16
18
|
mkdir -p "$TELEMETRY_DIR" "$STATE_BACKUP_DIR"
|
|
17
19
|
|
|
@@ -46,6 +48,46 @@ if [ -z "$CURRENT_CONTRACT" ]; then
|
|
|
46
48
|
fi
|
|
47
49
|
fi
|
|
48
50
|
|
|
51
|
+
# --- Step 1b: Contract migration (auto-upgrade old schema) ---
|
|
52
|
+
if [ -n "$CURRENT_CONTRACT" ] && [ -f "$TEMPLATE_FILE" ] && [ -n "$PYTHON_CMD" ]; then
|
|
53
|
+
MIGRATED=$($PYTHON_CMD -c "
|
|
54
|
+
import json
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
contract = json.loads('''$CURRENT_CONTRACT''')
|
|
58
|
+
with open('$TEMPLATE_FILE') as f:
|
|
59
|
+
template = json.load(f)
|
|
60
|
+
|
|
61
|
+
# Check version
|
|
62
|
+
old_ver = contract.get('contract_version', '0.0.0')
|
|
63
|
+
new_ver = template.get('contract_version', '0.5.2')
|
|
64
|
+
|
|
65
|
+
if old_ver == new_ver and all(k in contract for k in ['state','requirements','governance','score']):
|
|
66
|
+
print('NO_MIGRATION')
|
|
67
|
+
else:
|
|
68
|
+
# Merge missing top-level fields from template
|
|
69
|
+
for key in template:
|
|
70
|
+
if key not in contract:
|
|
71
|
+
contract[key] = template[key]
|
|
72
|
+
# Merge governance.extension_skills if missing
|
|
73
|
+
if 'extension_skills' not in contract.get('governance', {}):
|
|
74
|
+
if 'governance' not in contract:
|
|
75
|
+
contract['governance'] = {}
|
|
76
|
+
contract['governance']['extension_skills'] = []
|
|
77
|
+
# Update version
|
|
78
|
+
contract['contract_version'] = new_ver
|
|
79
|
+
print(json.dumps(contract))
|
|
80
|
+
except Exception as e:
|
|
81
|
+
print('MIGRATE_ERROR:'+str(e))
|
|
82
|
+
" 2>/dev/null || echo "MIGRATE_ERROR")
|
|
83
|
+
|
|
84
|
+
if [ -n "$MIGRATED" ] && [ "$MIGRATED" != "NO_MIGRATION" ] && [ "$MIGRATED" != "MIGRATE_ERROR" ]; then
|
|
85
|
+
CURRENT_CONTRACT="$MIGRATED"
|
|
86
|
+
echo "$MIGRATED" > "$CONTRACT_FILE"
|
|
87
|
+
echo " 🔄 Contract migrated to v$(echo "$MIGRATED" | $PYTHON_CMD -c "import sys,json; print(json.load(sys.stdin).get('contract_version','?'))" 2>/dev/null)"
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
|
|
49
91
|
# --- Step 2: Persist (try lean-ctx first, fall back to file) ---
|
|
50
92
|
PERSISTED=false
|
|
51
93
|
if lean-ctx ctx_knowledge remember \
|
package/src/preflight.sh
CHANGED
package/src/status.sh
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
# shellcheck source=./platform.sh
|
|
7
8
|
. "$SCRIPT_DIR/platform.sh"
|
|
8
9
|
|
|
9
10
|
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
@@ -18,9 +19,9 @@ BOLD='\033[1m'
|
|
|
18
19
|
NC='\033[0m'
|
|
19
20
|
|
|
20
21
|
echo ""
|
|
21
|
-
echo -e "${CYAN}${BOLD}
|
|
22
|
-
echo -e "${CYAN}${BOLD}
|
|
23
|
-
echo -e "${CYAN}${BOLD}
|
|
22
|
+
echo -e "${CYAN}${BOLD}##══════════════════════════════════════════╗${NC}"
|
|
23
|
+
echo -e "${CYAN}${BOLD}# opencode-kit Dashboard #${NC}"
|
|
24
|
+
echo -e "${CYAN}${BOLD}##══════════════════════════════════════════╝${NC}"
|
|
24
25
|
echo ""
|
|
25
26
|
|
|
26
27
|
# === Contract State ===
|
|
@@ -93,10 +94,8 @@ with open('$RULES_FILE') as f:
|
|
|
93
94
|
rules = r.get('rules', [])
|
|
94
95
|
critical = [x for x in rules if x.get('severity') == 'CRITICAL']
|
|
95
96
|
high = [x for x in rules if x.get('severity') == 'HIGH']
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
mcps = list(r.get('required_mcps', {}).keys())
|
|
99
|
-
mcps = [m for m in mcps if m != 'description']
|
|
97
|
+
mcps = list(r.get('required_mcps', {}).keys())
|
|
98
|
+
mcps = [m for m in mcps if m != 'description']
|
|
100
99
|
print(f' {len(critical)} CRITICAL rules, {len(high)} HIGH rules')
|
|
101
100
|
if mcps:
|
|
102
101
|
print(f' Required MCPs: {\", \".join(mcps)}')
|
package/src/telemetry.sh
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
# shellcheck source=./platform.sh
|
|
7
8
|
. "$SCRIPT_DIR/platform.sh"
|
|
8
9
|
|
|
9
10
|
TELEMETRY_DIR=".opencode/telemetry"
|
|
@@ -36,7 +37,7 @@ case "$MODE" in
|
|
|
36
37
|
FROM=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('from','?'))" 2>/dev/null)
|
|
37
38
|
TO=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('to','?'))" 2>/dev/null)
|
|
38
39
|
MS=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('elapsed_ms',0))" 2>/dev/null)
|
|
39
|
-
printf " %-20s → %-20s %5.1fs\n" "$FROM" "$TO" "$(
|
|
40
|
+
printf " %-20s → %-20s %5.1fs\n" "$FROM" "$TO" "$($PYTHON_CMD -c "print($MS/1000)" 2>/dev/null || echo "0.0")"
|
|
40
41
|
done
|
|
41
42
|
else
|
|
42
43
|
echo -e "${YELLOW}No phase data yet.${NC}"
|
package/src/update.sh
CHANGED
package/templates/contract.json
CHANGED