@ikieaneh/opencode-kit 0.6.1 → 0.6.3
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/plugins/opencode-kit.js +101 -84
- package/package.json +9 -7
- package/src/init.sh +2 -2
- package/templates/opencode-kit.schema.json +1 -0
- package/templates/superpowers-contract.json +1 -0
- package/.claude-plugin/plugin.json +0 -23
- package/docs/examples/QUICKSTART.md +0 -123
- package/docs/examples/extension-skill-template.md +0 -108
- package/docs/examples/model-configs.md +0 -117
- package/docs/guides/contract-protocol.md +0 -67
- package/docs/guides/scoring-pipeline.md +0 -60
- package/docs/guides/troubleshooting.md +0 -78
- package/docs/images/logo.svg +0 -9
- package/docs/plans/2026-06-11-plugin-architecture.md +0 -55
- package/src/adr.sh +0 -152
- package/src/analytics.sh +0 -80
- package/src/diff.sh +0 -95
- package/src/doctor.sh +0 -160
- package/src/global-config.sh +0 -82
- package/src/new-skill.sh +0 -60
- package/src/platform.sh +0 -34
- package/src/postflight.py +0 -211
- package/src/postflight.sh +0 -82
- package/src/preflight.sh +0 -138
- package/src/status.sh +0 -113
- package/src/telemetry.sh +0 -67
- package/src/update.sh +0 -181
- package/src/verify.sh +0 -67
|
@@ -1,67 +0,0 @@
|
|
|
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]`
|
|
@@ -1,60 +0,0 @@
|
|
|
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`.
|
|
@@ -1,78 +0,0 @@
|
|
|
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/docs/images/logo.svg
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" fill="none">
|
|
2
|
-
<rect width="80" height="80" rx="16" fill="#1a1a2e"/>
|
|
3
|
-
<path d="M20 25h40v30H20z" stroke="#e94560" stroke-width="2" fill="none"/>
|
|
4
|
-
<path d="M20 40h40" stroke="#e94560" stroke-width="2"/>
|
|
5
|
-
<circle cx="30" cy="35" r="3" fill="#0f3460"/>
|
|
6
|
-
<circle cx="50" cy="35" r="3" fill="#0f3460"/>
|
|
7
|
-
<path d="M25 48l10-10M45 48l10-10" stroke="#16213e" stroke-width="2"/>
|
|
8
|
-
<text x="40" y="62" text-anchor="middle" font-size="8" fill="#e94560" font-family="monospace">KIT</text>
|
|
9
|
-
</svg>
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Plugin Architecture — Implementation Plan
|
|
2
|
-
|
|
3
|
-
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
|
|
4
|
-
|
|
5
|
-
**Goal:** Convert opencode-kit from a per-project scaffold to a global OpenCode plugin with local-override config
|
|
6
|
-
|
|
7
|
-
**Architecture:** Pure JS plugin (superpowers pattern) using `@opencode-ai/plugin` SDK. Global config at `~/.config/opencode-kit/`, project override at `.opencode/`. Plugin registers skills + system prompt transform.
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** Node.js, @opencode-ai/plugin SDK, shell scripts
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
### Task 1: Plugin Entry Point
|
|
14
|
-
|
|
15
|
-
**Files:**
|
|
16
|
-
- Create: `.opencode/plugins/opencode-kit.js` — main plugin JS
|
|
17
|
-
- Create: `.claude-plugin/plugin.json` — metadata
|
|
18
|
-
- Modify: `package.json` — add plugin entry, `@opencode-ai/plugin` dependency
|
|
19
|
-
|
|
20
|
-
- [ ] Create `.opencode/plugins/opencode-kit.js` — system prompt transform hook that injects contract loading + preflight
|
|
21
|
-
- [ ] Create `.claude-plugin/plugin.json` — name, description, version, author
|
|
22
|
-
- [ ] Update `package.json` — `"main": ".opencode/plugins/opencode-kit.js"`, add `@opencode-ai/plugin` dep
|
|
23
|
-
|
|
24
|
-
### Task 2: Global Config Resolution
|
|
25
|
-
|
|
26
|
-
**Files:**
|
|
27
|
-
- Create: `src/global-config.sh` — resolve config from local → global → plugin default
|
|
28
|
-
|
|
29
|
-
- [ ] Write resolution chain: `.opencode/` → `~/.config/opencode-kit/` → plugin `templates/`
|
|
30
|
-
- [ ] Add `init-global` command to copy plugin defaults to `~/.config/opencode-kit/`
|
|
31
|
-
|
|
32
|
-
### Task 3: Plugin Schema
|
|
33
|
-
|
|
34
|
-
**Files:**
|
|
35
|
-
- Create: `templates/opencode-kit.schema.json` — validate opencode.json agent config
|
|
36
|
-
|
|
37
|
-
- [ ] Schema defines default agents (orchestrator, planner, task-manager, etc.)
|
|
38
|
-
- [ ] Documents plugin ordering requirement (must be first in plugin array)
|
|
39
|
-
|
|
40
|
-
### Task 4: Init Coexistence
|
|
41
|
-
|
|
42
|
-
**Files:**
|
|
43
|
-
- Modify: `src/init.sh` — detect plugin, skip plugin-handled tasks
|
|
44
|
-
|
|
45
|
-
- [ ] If plugin detected (via `.opencode/plugins/opencode-kit.js`), skip skill/agent scaffolding
|
|
46
|
-
- [ ] Only scaffold per-project data: contract.json goal/scope, STATE.md, PROJECT.md
|
|
47
|
-
|
|
48
|
-
### Task 5: Documentation
|
|
49
|
-
|
|
50
|
-
**Files:**
|
|
51
|
-
- Modify: `README.md` — plugin installation, ordering requirement
|
|
52
|
-
- Modify: `CHANGELOG.md`
|
|
53
|
-
|
|
54
|
-
- [ ] Add "Install as plugin" section to README
|
|
55
|
-
- [ ] Document global vs local resolution
|
package/src/adr.sh
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# opencode-kit ADR — auto-generate Architecture Decision Record
|
|
3
|
-
# Usage: bash src/adr.sh <title>
|
|
4
|
-
# Then enter context, decision, alternatives, consequences interactively.
|
|
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
|
-
|
|
13
|
-
RED='\033[0;31m'
|
|
14
|
-
GREEN='\033[0;32m'
|
|
15
|
-
YELLOW='\033[1;33m'
|
|
16
|
-
CYAN='\033[0;36m'
|
|
17
|
-
NC='\033[0m'
|
|
18
|
-
|
|
19
|
-
# --- Check contract exists ---
|
|
20
|
-
if [ ! -f "$CONTRACT_FILE" ]; then
|
|
21
|
-
echo -e "${RED}❌ $CONTRACT_FILE not found. Run 'opencode-kit init' first.${NC}"
|
|
22
|
-
exit 1
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# --- Parse args ---
|
|
26
|
-
TITLE=""
|
|
27
|
-
CONTEXT=""
|
|
28
|
-
DECISION=""
|
|
29
|
-
ALTERNATIVES=""
|
|
30
|
-
CONSEQUENCES=""
|
|
31
|
-
|
|
32
|
-
while [ $# -gt 0 ]; do
|
|
33
|
-
case "$1" in
|
|
34
|
-
--title|-t) TITLE="$2"; shift 2 ;;
|
|
35
|
-
--context|-c) CONTEXT="$2"; shift 2 ;;
|
|
36
|
-
--decision|-d) DECISION="$2"; shift 2 ;;
|
|
37
|
-
--alternatives|-a) ALTERNATIVES="$2"; shift 2 ;;
|
|
38
|
-
--consequences|-q) CONSEQUENCES="$2"; shift 2 ;;
|
|
39
|
-
*) TITLE="$1"; shift ;;
|
|
40
|
-
esac
|
|
41
|
-
done
|
|
42
|
-
|
|
43
|
-
# --- Interactive mode if no title provided ---
|
|
44
|
-
if [ -z "$TITLE" ]; then
|
|
45
|
-
echo -e "${CYAN}[opencode-kit ADR]${NC} Interactive mode"
|
|
46
|
-
echo ""
|
|
47
|
-
read -r -p "Title: " TITLE
|
|
48
|
-
[ -z "$TITLE" ] && { echo -e "${RED}Title required${NC}"; exit 1; }
|
|
49
|
-
read -r -p "Context (why this decision?): " CONTEXT
|
|
50
|
-
read -r -p "Decision (what we decided): " DECISION
|
|
51
|
-
read -r -p "Alternatives considered: " ALTERNATIVES
|
|
52
|
-
read -r -p "Consequences (positive + negative): " CONSEQUENCES
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# --- Validate required fields ---
|
|
56
|
-
if [ -z "$TITLE" ]; then
|
|
57
|
-
echo -e "${RED}❌ Title is required. Use: bash src/adr.sh \"Your Decision Title\"${NC}"
|
|
58
|
-
exit 1
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
# --- Compute next ADR ID ---
|
|
62
|
-
NEXT_ID=$($PYTHON_CMD -c "
|
|
63
|
-
import json
|
|
64
|
-
with open('$CONTRACT_FILE') as f: d = json.load(f)
|
|
65
|
-
log = d.get('decisions', {}).get('adr_log', [])
|
|
66
|
-
if not log:
|
|
67
|
-
print('ADR-001')
|
|
68
|
-
else:
|
|
69
|
-
last = max(int(entry.get('id','ADR-000').replace('ADR-','')) for entry in log)
|
|
70
|
-
print(f'ADR-{last+1:03d}')
|
|
71
|
-
")
|
|
72
|
-
|
|
73
|
-
# --- Check for duplicate title ---
|
|
74
|
-
DUP=$($PYTHON_CMD -c "
|
|
75
|
-
import json
|
|
76
|
-
with open('$CONTRACT_FILE') as f: d = json.load(f)
|
|
77
|
-
log = d.get('decisions', {}).get('adr_log', [])
|
|
78
|
-
for entry in log:
|
|
79
|
-
if entry.get('title','').lower().strip() == '$TITLE'.lower().strip():
|
|
80
|
-
print(entry.get('id',''))
|
|
81
|
-
break
|
|
82
|
-
" 2>/dev/null)
|
|
83
|
-
if [ -n "$DUP" ]; then
|
|
84
|
-
echo -e "${YELLOW}⚠️ Duplicate title found: $DUP — '$TITLE'${NC}"
|
|
85
|
-
echo " Skipping. Update existing ADR instead."
|
|
86
|
-
exit 0
|
|
87
|
-
fi
|
|
88
|
-
|
|
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
|
-
|
|
102
|
-
$PYTHON_CMD -c "
|
|
103
|
-
import json
|
|
104
|
-
|
|
105
|
-
with open('$ADR_DATA') as f:
|
|
106
|
-
data = json.load(f)
|
|
107
|
-
|
|
108
|
-
entry = {
|
|
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']
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
with open('/tmp/opencode-adr-entry.json', 'w') as f:
|
|
119
|
-
json.dump(entry, f, indent=2)
|
|
120
|
-
print('Entry written')
|
|
121
|
-
"
|
|
122
|
-
|
|
123
|
-
rm -f "$ADR_DATA"
|
|
124
|
-
|
|
125
|
-
# --- Inject into contract.json ---
|
|
126
|
-
$PYTHON_CMD -c "
|
|
127
|
-
import json
|
|
128
|
-
|
|
129
|
-
with open('$CONTRACT_FILE') as f:
|
|
130
|
-
contract = json.load(f)
|
|
131
|
-
|
|
132
|
-
with open('/tmp/opencode-adr-entry.json') as f:
|
|
133
|
-
entry = json.load(f)
|
|
134
|
-
|
|
135
|
-
if 'decisions' not in contract:
|
|
136
|
-
contract['decisions'] = {}
|
|
137
|
-
if 'adr_log' not in contract['decisions']:
|
|
138
|
-
contract['decisions']['adr_log'] = []
|
|
139
|
-
|
|
140
|
-
contract['decisions']['adr_log'].append(entry)
|
|
141
|
-
|
|
142
|
-
with open('$CONTRACT_FILE', 'w') as f:
|
|
143
|
-
json.dump(contract, f, indent=2)
|
|
144
|
-
|
|
145
|
-
print(json.dumps(entry, indent=2))
|
|
146
|
-
"
|
|
147
|
-
|
|
148
|
-
echo ""
|
|
149
|
-
echo -e "${GREEN}[opencode-kit] ✅ ADR recorded: $NEXT_ID${NC}"
|
|
150
|
-
echo " Title: $TITLE"
|
|
151
|
-
echo " File: $CONTRACT_FILE"
|
|
152
|
-
echo " Next: Persist via: lean-ctx ctx_knowledge remember key orchestration-contract value \"\$(cat $CONTRACT_FILE)\""
|
package/src/analytics.sh
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# opencode-kit analytics — aggregate telemetry across phases
|
|
3
|
-
# Usage: bash src/analytics.sh [--json]
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
# shellcheck source=./platform.sh
|
|
8
|
-
. "$SCRIPT_DIR/platform.sh"
|
|
9
|
-
|
|
10
|
-
TELEMETRY_DIR=".opencode/telemetry"
|
|
11
|
-
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
12
|
-
|
|
13
|
-
RED='\033[0;31m'
|
|
14
|
-
GREEN='\033[0;32m'
|
|
15
|
-
YELLOW='\033[1;33m'
|
|
16
|
-
CYAN='\033[0;36m'
|
|
17
|
-
NC='\033[0m'
|
|
18
|
-
|
|
19
|
-
MODE="${1:-table}"
|
|
20
|
-
|
|
21
|
-
echo -e "${CYAN}📊 opencode-kit Analytics${NC}"
|
|
22
|
-
echo ""
|
|
23
|
-
|
|
24
|
-
if [ ! -f "$TELEMETRY_DIR/phases.jsonl" ] || [ ! -f "$TELEMETRY_DIR/summary.json" ]; then
|
|
25
|
-
echo -e "${YELLOW}No telemetry data yet. Run a few phases first.${NC}"
|
|
26
|
-
exit 0
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
if [ -z "$PYTHON_CMD" ]; then
|
|
30
|
-
echo -e "${RED}Python required for analytics.${NC}"
|
|
31
|
-
exit 1
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
$PYTHON_CMD -c "
|
|
35
|
-
import json
|
|
36
|
-
|
|
37
|
-
# Load phases
|
|
38
|
-
phases = []
|
|
39
|
-
try:
|
|
40
|
-
with open('$TELEMETRY_DIR/phases.jsonl') as f:
|
|
41
|
-
for line in f:
|
|
42
|
-
line = line.strip()
|
|
43
|
-
if line:
|
|
44
|
-
phases.append(json.loads(line))
|
|
45
|
-
except:
|
|
46
|
-
pass
|
|
47
|
-
|
|
48
|
-
# Load summary
|
|
49
|
-
summary = {}
|
|
50
|
-
try:
|
|
51
|
-
with open('$TELEMETRY_DIR/summary.json') as f:
|
|
52
|
-
summary = json.load(f)
|
|
53
|
-
except:
|
|
54
|
-
pass
|
|
55
|
-
|
|
56
|
-
if not phases:
|
|
57
|
-
print(' No phases recorded')
|
|
58
|
-
exit(0)
|
|
59
|
-
|
|
60
|
-
total_ms = sum(p.get('elapsed_ms', 0) for p in phases)
|
|
61
|
-
avg_ms = total_ms / len(phases) if phases else 0
|
|
62
|
-
|
|
63
|
-
print(f' Total sessions: {len(phases)}')
|
|
64
|
-
print(f' Total time: {total_ms/1000:.1f}s ({total_ms/60000:.1f}m)')
|
|
65
|
-
print(f' Avg per phase: {avg_ms/1000:.1f}s')
|
|
66
|
-
print('')
|
|
67
|
-
print(f' Phase Breakdown:')
|
|
68
|
-
for p in phases:
|
|
69
|
-
frm = p.get('from', '?')
|
|
70
|
-
to = p.get('to', '?')
|
|
71
|
-
ms = p.get('elapsed_ms', 0)
|
|
72
|
-
bar = '#' * max(1, int(ms / max(1, total_ms) * 30))
|
|
73
|
-
print(f' {frm:16s} → {to:16s} {ms/1000:6.1f}s {bar}')
|
|
74
|
-
|
|
75
|
-
# Cost estimate (rough: ~$0.15/1M tokens, ~1000 tok/s)
|
|
76
|
-
est_tokens = int(total_ms / 1000 * 1000) # rough: 1000 tok/sec
|
|
77
|
-
est_cost = est_tokens / 1_000_000 * 0.15
|
|
78
|
-
print(f'')
|
|
79
|
-
print(f' Estimated tokens: {est_tokens:,} (~{est_cost:.4f} USD at $0.15/1M tok)')
|
|
80
|
-
" 2>/dev/null || echo " ⚠️ Analytics failed"
|
package/src/diff.sh
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
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)"
|