@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
package/src/preflight.sh
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ⛔ opencode-kit preflight — MANDATORY enforcement gate
|
|
3
|
-
# Must run before any tool call. Exits with error if rules violated.
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
# shellcheck source=./platform.sh
|
|
8
|
-
. "$SCRIPT_DIR/platform.sh"
|
|
9
|
-
|
|
10
|
-
CONTRACT_KEY="orchestration-contract"
|
|
11
|
-
RULES_FILE=".opencode/rules/rules.json"
|
|
12
|
-
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
13
|
-
|
|
14
|
-
RED='\033[0;31m'
|
|
15
|
-
GREEN='\033[0;32m'
|
|
16
|
-
YELLOW='\033[1;33m'
|
|
17
|
-
NC='\033[0m'
|
|
18
|
-
|
|
19
|
-
echo "[opencode-kit] ⛔ Pre-flight check..."
|
|
20
|
-
|
|
21
|
-
# --- Check 1: contract.json exists on disk ---
|
|
22
|
-
if [ ! -f "$CONTRACT_FILE" ]; then
|
|
23
|
-
echo -e "${RED}⛔ FAILED: $CONTRACT_FILE not found. Run 'opencode-kit init' first.${NC}"
|
|
24
|
-
exit 1
|
|
25
|
-
fi
|
|
26
|
-
echo " ✅ contract.json exists"
|
|
27
|
-
|
|
28
|
-
# --- Check 2: not on main ---
|
|
29
|
-
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
30
|
-
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
|
|
31
|
-
echo -e "${RED}⛔ FAILED: On '$BRANCH' branch. Create a feature branch first.${NC}"
|
|
32
|
-
echo " → git checkout -b feature/<YYYYMMDD>-<description>"
|
|
33
|
-
exit 1
|
|
34
|
-
fi
|
|
35
|
-
echo " ✅ Branch: $BRANCH (safe)"
|
|
36
|
-
|
|
37
|
-
# --- Check 3: MCP Availability (from rules.json) ---
|
|
38
|
-
echo ""
|
|
39
|
-
echo " Checking MCP availability from rules.json..."
|
|
40
|
-
|
|
41
|
-
MCP_FAIL=0
|
|
42
|
-
|
|
43
|
-
if [ -n "$PYTHON_CMD" ] && [ -f "$RULES_FILE" ]; then
|
|
44
|
-
# Parse required_mcps from rules.json
|
|
45
|
-
$PYTHON_CMD -c "
|
|
46
|
-
import json, sys, subprocess, os
|
|
47
|
-
|
|
48
|
-
with open('$RULES_FILE') as f:
|
|
49
|
-
rules = json.load(f)
|
|
50
|
-
|
|
51
|
-
mcps = rules.get('required_mcps', {})
|
|
52
|
-
if not isinstance(mcps, dict) or 'description' in mcps:
|
|
53
|
-
# Skip the meta-description field
|
|
54
|
-
mcps = {k: v for k, v in mcps.items() if k != 'description' and isinstance(v, dict)}
|
|
55
|
-
|
|
56
|
-
if not mcps:
|
|
57
|
-
print(' ℹ️ No required_mcps defined in rules.json — skipping MCP checks')
|
|
58
|
-
sys.exit(0)
|
|
59
|
-
|
|
60
|
-
failures = []
|
|
61
|
-
for name, cfg in mcps.items():
|
|
62
|
-
cli_check = cfg.get('check_cli', '')
|
|
63
|
-
tool_check = cfg.get('check_tool', '')
|
|
64
|
-
severity = cfg.get('severity', 'optional')
|
|
65
|
-
desc = cfg.get('description', name)
|
|
66
|
-
|
|
67
|
-
available = False
|
|
68
|
-
# Try CLI check first
|
|
69
|
-
if cli_check:
|
|
70
|
-
try:
|
|
71
|
-
result = subprocess.run(cli_check, shell=True, capture_output=True, timeout=5)
|
|
72
|
-
if result.returncode == 0:
|
|
73
|
-
available = True
|
|
74
|
-
except:
|
|
75
|
-
pass
|
|
76
|
-
|
|
77
|
-
# Try tool check as fallback
|
|
78
|
-
if not available and tool_check:
|
|
79
|
-
try:
|
|
80
|
-
result = subprocess.run(tool_check, shell=True, capture_output=True, timeout=5)
|
|
81
|
-
if result.returncode == 0:
|
|
82
|
-
available = True
|
|
83
|
-
except:
|
|
84
|
-
pass
|
|
85
|
-
|
|
86
|
-
if available:
|
|
87
|
-
print(f' ✅ {name}: available — {desc}')
|
|
88
|
-
elif severity == 'required':
|
|
89
|
-
print(f' ❌ {name}: NOT DETECTED — {desc}')
|
|
90
|
-
failures.append(name)
|
|
91
|
-
else:
|
|
92
|
-
print(f' ⚠️ {name}: not detected — {desc} (optional)')
|
|
93
|
-
|
|
94
|
-
if failures:
|
|
95
|
-
print('')
|
|
96
|
-
for name in failures:
|
|
97
|
-
print(f' → Ensure {name} is configured in opencode.json MCP servers')
|
|
98
|
-
sys.exit(1)
|
|
99
|
-
else:
|
|
100
|
-
sys.exit(0)
|
|
101
|
-
" 2>&1 || MCP_FAIL=1
|
|
102
|
-
fi
|
|
103
|
-
|
|
104
|
-
echo ""
|
|
105
|
-
|
|
106
|
-
# --- Check 4: rules.json exists ---
|
|
107
|
-
if [ ! -f "$RULES_FILE" ]; then
|
|
108
|
-
echo -e "${YELLOW}⚠️ WARNING: $RULES_FILE not found. Rules enforcement disabled.${NC}"
|
|
109
|
-
else
|
|
110
|
-
echo " ✅ rules.json found"
|
|
111
|
-
fi
|
|
112
|
-
|
|
113
|
-
# --- Telemetry: record phase start ---
|
|
114
|
-
mkdir -p .opencode/telemetry
|
|
115
|
-
echo $(date +%s) > .opencode/telemetry/.phase_start
|
|
116
|
-
|
|
117
|
-
# --- Check 5: contract state validation ---
|
|
118
|
-
if [ -n "$PYTHON_CMD" ] && [ -f "$CONTRACT_FILE" ]; then
|
|
119
|
-
STATE=$($PYTHON_CMD -c "
|
|
120
|
-
import json,sys
|
|
121
|
-
try:
|
|
122
|
-
with open('$CONTRACT_FILE') as f: d=json.load(f)
|
|
123
|
-
print(d.get('state','UNKNOWN'))
|
|
124
|
-
except: print('PARSE_ERROR')
|
|
125
|
-
" 2>/dev/null)
|
|
126
|
-
if [ "$STATE" = "PARSE_ERROR" ] || [ "$STATE" = "UNKNOWN" ]; then
|
|
127
|
-
echo -e "${YELLOW} ⚠️ Contract state: unknown — contract.json may be malformed${NC}"
|
|
128
|
-
else
|
|
129
|
-
echo " ✅ Contract state: $STATE"
|
|
130
|
-
fi
|
|
131
|
-
fi
|
|
132
|
-
|
|
133
|
-
# --- Final verdict ---
|
|
134
|
-
if [ "$MCP_FAIL" -eq 1 ]; then
|
|
135
|
-
echo -e "${YELLOW}[opencode-kit] ⛔ Pre-flight completed with WARNINGS. Missing MCPs may cause failures.${NC}"
|
|
136
|
-
else
|
|
137
|
-
echo "[opencode-kit] ✅ Pre-flight passed. All MCPs available. Proceed."
|
|
138
|
-
fi
|
package/src/status.sh
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# opencode-kit status — pretty terminal dashboard
|
|
3
|
-
# Usage: bash src/status.sh
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
# shellcheck source=./platform.sh
|
|
8
|
-
. "$SCRIPT_DIR/platform.sh"
|
|
9
|
-
|
|
10
|
-
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
11
|
-
RULES_FILE=".opencode/rules/rules.json"
|
|
12
|
-
TELEMETRY_DIR=".opencode/telemetry"
|
|
13
|
-
|
|
14
|
-
RED='\033[0;31m'
|
|
15
|
-
GREEN='\033[0;32m'
|
|
16
|
-
YELLOW='\033[1;33m'
|
|
17
|
-
CYAN='\033[0;36m'
|
|
18
|
-
BOLD='\033[1m'
|
|
19
|
-
NC='\033[0m'
|
|
20
|
-
|
|
21
|
-
echo ""
|
|
22
|
-
echo -e "${CYAN}${BOLD}##══════════════════════════════════════════╗${NC}"
|
|
23
|
-
echo -e "${CYAN}${BOLD}# opencode-kit Dashboard #${NC}"
|
|
24
|
-
echo -e "${CYAN}${BOLD}##══════════════════════════════════════════╝${NC}"
|
|
25
|
-
echo ""
|
|
26
|
-
|
|
27
|
-
# === Contract State ===
|
|
28
|
-
if [ -f "$CONTRACT_FILE" ] && [ -n "$PYTHON_CMD" ]; then
|
|
29
|
-
$PYTHON_CMD -c "
|
|
30
|
-
import json
|
|
31
|
-
with open('$CONTRACT_FILE') as f:
|
|
32
|
-
c = json.load(f)
|
|
33
|
-
|
|
34
|
-
state = c.get('state', 'UNKNOWN')
|
|
35
|
-
ver = c.get('contract_version', '?')
|
|
36
|
-
goal = c.get('requirements', {}).get('goal', 'Not set')
|
|
37
|
-
phases = c.get('metrics', {}).get('phases_completed', [])
|
|
38
|
-
score = c.get('score', {}).get('combined', 0)
|
|
39
|
-
verdict = c.get('score', {}).get('verdict', 'PENDING')
|
|
40
|
-
adrs = len(c.get('decisions', {}).get('adr_log', []))
|
|
41
|
-
ext_skills = c.get('governance', {}).get('extension_skills', [])
|
|
42
|
-
|
|
43
|
-
# State color
|
|
44
|
-
state_colors = {
|
|
45
|
-
'INIT': '\033[0;36m',
|
|
46
|
-
'PLAN': '\033[1;33m',
|
|
47
|
-
'PLAN_SCORED': '\033[1;33m',
|
|
48
|
-
'EXECUTE': '\033[0;32m',
|
|
49
|
-
'EXECUTE_SCORED': '\033[0;32m',
|
|
50
|
-
'REVIEW': '\033[0;34m',
|
|
51
|
-
'REVIEW_SCORED': '\033[0;34m',
|
|
52
|
-
'COMPLETE': '\033[0;32m',
|
|
53
|
-
'BLOCKED': '\033[0;31m',
|
|
54
|
-
}
|
|
55
|
-
color = state_colors.get(state, '\033[0m')
|
|
56
|
-
nc = '\033[0m'
|
|
57
|
-
|
|
58
|
-
print(f' ${BOLD}Contract State:${NC} {color}{state}{nc} (v{ver})')
|
|
59
|
-
print(f' ${BOLD}Goal:${NC} {goal[:70]}...' if len(goal) > 70 else f' ${BOLD}Goal:${NC} {goal}')
|
|
60
|
-
print(f' ${BOLD}Score:${NC} {score}/100 ({verdict})')
|
|
61
|
-
print(f' ${BOLD}Phases:${NC} {len(phases)} completed: {\" → \".join(phases[-4:])}')
|
|
62
|
-
print(f' ${BOLD}ADRs:${NC} {adrs} recorded')
|
|
63
|
-
if ext_skills:
|
|
64
|
-
print(f' ${BOLD}Extension Skills:${NC} {\", \".join(ext_skills)}')
|
|
65
|
-
" 2>/dev/null || echo " ⚠️ Could not parse contract"
|
|
66
|
-
else
|
|
67
|
-
echo -e " ${YELLOW}⚠️ No contract found${NC}"
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
# === Telemetry ===
|
|
71
|
-
echo ""
|
|
72
|
-
echo -e "${BOLD}⏱ Telemetry${NC}"
|
|
73
|
-
if [ -f "$TELEMETRY_DIR/summary.json" ] && [ -n "$PYTHON_CMD" ]; then
|
|
74
|
-
$PYTHON_CMD -c "
|
|
75
|
-
import json
|
|
76
|
-
with open('$TELEMETRY_DIR/summary.json') as f:
|
|
77
|
-
t = json.load(f)
|
|
78
|
-
total_s = t.get('total_elapsed_s', 0)
|
|
79
|
-
phases = t.get('phases_completed', [])
|
|
80
|
-
print(f' Total time: {total_s}s across {len(phases)} phases')
|
|
81
|
-
" 2>/dev/null
|
|
82
|
-
else
|
|
83
|
-
echo -e " ${YELLOW}No telemetry data yet${NC}"
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
# === Rules ===
|
|
87
|
-
echo ""
|
|
88
|
-
echo -e "${BOLD}📋 Rules${NC}"
|
|
89
|
-
if [ -f "$RULES_FILE" ] && [ -n "$PYTHON_CMD" ]; then
|
|
90
|
-
$PYTHON_CMD -c "
|
|
91
|
-
import json
|
|
92
|
-
with open('$RULES_FILE') as f:
|
|
93
|
-
r = json.load(f)
|
|
94
|
-
rules = r.get('rules', [])
|
|
95
|
-
critical = [x for x in rules if x.get('severity') == 'CRITICAL']
|
|
96
|
-
high = [x for x in rules if x.get('severity') == 'HIGH']
|
|
97
|
-
mcps = list(r.get('required_mcps', {}).keys())
|
|
98
|
-
mcps = [m for m in mcps if m != 'description']
|
|
99
|
-
print(f' {len(critical)} CRITICAL rules, {len(high)} HIGH rules')
|
|
100
|
-
if mcps:
|
|
101
|
-
print(f' Required MCPs: {\", \".join(mcps)}')
|
|
102
|
-
" 2>/dev/null
|
|
103
|
-
else
|
|
104
|
-
echo -e " ${YELLOW}No rules.json${NC}"
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
# === Quick actions ===
|
|
108
|
-
echo ""
|
|
109
|
-
echo -e "${BOLD}⚡ Quick Actions${NC}"
|
|
110
|
-
echo " bash .opencode/src/doctor.sh — Run diagnostics"
|
|
111
|
-
echo " bash .opencode/src/telemetry.sh — View telemetry details"
|
|
112
|
-
echo " bash .opencode/src/adr.sh — Record new ADR"
|
|
113
|
-
echo ""
|
package/src/telemetry.sh
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# opencode-kit telemetry — view phase telemetry
|
|
3
|
-
# Usage: bash src/telemetry.sh [--json|--summary|--phases]
|
|
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:-summary}"
|
|
20
|
-
|
|
21
|
-
echo -e "${CYAN}[opencode-kit] 📊 Telemetry${NC}"
|
|
22
|
-
echo ""
|
|
23
|
-
|
|
24
|
-
case "$MODE" in
|
|
25
|
-
--json)
|
|
26
|
-
if [ -f "$TELEMETRY_DIR/summary.json" ]; then
|
|
27
|
-
cat "$TELEMETRY_DIR/summary.json"
|
|
28
|
-
else
|
|
29
|
-
echo -e "${YELLOW}No telemetry data yet. Run a phase first.${NC}"
|
|
30
|
-
fi
|
|
31
|
-
;;
|
|
32
|
-
--phases)
|
|
33
|
-
if [ -f "$TELEMETRY_DIR/phases.jsonl" ]; then
|
|
34
|
-
echo "Phase transitions:"
|
|
35
|
-
cat "$TELEMETRY_DIR/phases.jsonl" | while IFS= read -r line; do
|
|
36
|
-
[ -z "$line" ] && continue
|
|
37
|
-
FROM=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('from','?'))" 2>/dev/null)
|
|
38
|
-
TO=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('to','?'))" 2>/dev/null)
|
|
39
|
-
MS=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('elapsed_ms',0))" 2>/dev/null)
|
|
40
|
-
printf " %-20s → %-20s %5.1fs\n" "$FROM" "$TO" "$($PYTHON_CMD -c "print($MS/1000)" 2>/dev/null || echo "0.0")"
|
|
41
|
-
done
|
|
42
|
-
else
|
|
43
|
-
echo -e "${YELLOW}No phase data yet.${NC}"
|
|
44
|
-
fi
|
|
45
|
-
;;
|
|
46
|
-
--summary|*)
|
|
47
|
-
if [ -f "$TELEMETRY_DIR/summary.json" ]; then
|
|
48
|
-
TOTAL_S=$($PYTHON_CMD -c "import json; d=json.load(open('$TELEMETRY_DIR/summary.json')); print(d.get('total_elapsed_s',0))" 2>/dev/null || echo "0")
|
|
49
|
-
PHASES=$($PYTHON_CMD -c "import json; d=json.load(open('$TELEMETRY_DIR/summary.json')); print(len(d.get('phases_completed',[])))" 2>/dev/null || echo "0")
|
|
50
|
-
echo " Total elapsed: ${TOTAL_S}s"
|
|
51
|
-
echo " Phases completed: $PHASES"
|
|
52
|
-
echo ""
|
|
53
|
-
echo " Latest phases:"
|
|
54
|
-
tail -5 "$TELEMETRY_DIR/phases.jsonl" 2>/dev/null | while IFS= read -r line; do
|
|
55
|
-
[ -z "$line" ] && continue
|
|
56
|
-
TS=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('ts','?'))" 2>/dev/null | cut -dT -f2 | cut -d. -f1)
|
|
57
|
-
FROM=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('from','?'))" 2>/dev/null)
|
|
58
|
-
TO=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('to','?'))" 2>/dev/null)
|
|
59
|
-
MS=$(echo "$line" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('elapsed_ms',0))" 2>/dev/null)
|
|
60
|
-
echo " $TS $FROM → $TO ($((MS/1000))s)"
|
|
61
|
-
done
|
|
62
|
-
else
|
|
63
|
-
echo -e "${YELLOW}No telemetry data yet. Run a phase first.${NC}"
|
|
64
|
-
echo " Phases are recorded automatically by postflight.sh"
|
|
65
|
-
fi
|
|
66
|
-
;;
|
|
67
|
-
esac
|
package/src/update.sh
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# opencode-kit update — pull latest templates and scripts from GitHub
|
|
3
|
-
# Preserves existing contract.json state (goal, scope, decisions).
|
|
4
|
-
# Usage: bash src/update.sh [--dry-run] [--version <tag>]
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
-
# shellcheck source=./platform.sh
|
|
9
|
-
. "$SCRIPT_DIR/platform.sh"
|
|
10
|
-
|
|
11
|
-
RED='\033[0;31m'
|
|
12
|
-
GREEN='\033[0;32m'
|
|
13
|
-
YELLOW='\033[1;33m'
|
|
14
|
-
CYAN='\033[0;36m'
|
|
15
|
-
NC='\033[0m'
|
|
16
|
-
|
|
17
|
-
DRY_RUN=false
|
|
18
|
-
VERSION="main"
|
|
19
|
-
REPO_URL="https://github.com/RizkiRachman/opencode-kit.git"
|
|
20
|
-
|
|
21
|
-
# --- Parse args ---
|
|
22
|
-
while [ $# -gt 0 ]; do
|
|
23
|
-
case "$1" in
|
|
24
|
-
--dry-run) DRY_RUN=true; shift ;;
|
|
25
|
-
--version) VERSION="$2"; shift 2 ;;
|
|
26
|
-
-v) VERSION="$2"; shift 2 ;;
|
|
27
|
-
*) echo -e "${RED}Unknown: $1${NC}"; exit 1 ;;
|
|
28
|
-
esac
|
|
29
|
-
done
|
|
30
|
-
|
|
31
|
-
echo -e "${CYAN}[opencode-kit] 🔄 Update check${NC}"
|
|
32
|
-
echo " Current dir: $PWD"
|
|
33
|
-
echo " Source: $REPO_URL (branch: $VERSION)"
|
|
34
|
-
echo " Dry run: $DRY_RUN"
|
|
35
|
-
echo ""
|
|
36
|
-
|
|
37
|
-
# --- Check we're in an opencode-kit project ---
|
|
38
|
-
if [ ! -d ".opencode" ]; then
|
|
39
|
-
echo -e "${RED}❌ No .opencode/ directory found. Are you in an opencode-kit project?${NC}"
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
# --- Clone latest to temp ---
|
|
44
|
-
TEMP_DIR=$(mktemp -d /tmp/opencode-kit-XXXXX)
|
|
45
|
-
echo " Cloning latest version to $TEMP_DIR..."
|
|
46
|
-
|
|
47
|
-
if ! git clone --depth 1 --branch "$VERSION" "$REPO_URL" "$TEMP_DIR" 2>/dev/null; then
|
|
48
|
-
echo -e "${RED}❌ Failed to clone $REPO_URL (branch: $VERSION)${NC}"
|
|
49
|
-
rm -rf "$TEMP_DIR"
|
|
50
|
-
exit 1
|
|
51
|
-
fi
|
|
52
|
-
echo " ✅ Cloned"
|
|
53
|
-
|
|
54
|
-
# --- Read versions ---
|
|
55
|
-
CURRENT_VERSION=""
|
|
56
|
-
if [ -f ".opencode/orchestration/contract.json" ]; then
|
|
57
|
-
CURRENT_VERSION=$($PYTHON_CMD -c "
|
|
58
|
-
import json
|
|
59
|
-
with open('.opencode/orchestration/contract.json') as f:
|
|
60
|
-
d=json.load(f)
|
|
61
|
-
print(d.get('contract_version', 'unknown'))
|
|
62
|
-
" 2>/dev/null || echo "unknown")
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
LATEST_VERSION=$($PYTHON_CMD -c "
|
|
66
|
-
import json
|
|
67
|
-
with open('$TEMP_DIR/templates/contract.json') as f:
|
|
68
|
-
d=json.load(f)
|
|
69
|
-
print(d.get('contract_version', 'unknown'))
|
|
70
|
-
" 2>/dev/null || echo "unknown")
|
|
71
|
-
|
|
72
|
-
echo " Current version: $CURRENT_VERSION"
|
|
73
|
-
echo " Latest version: $LATEST_VERSION"
|
|
74
|
-
echo ""
|
|
75
|
-
|
|
76
|
-
if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ] && [ "$VERSION" = "main" ]; then
|
|
77
|
-
echo -e "${GREEN}✅ Already up to date (v$CURRENT_VERSION)${NC}"
|
|
78
|
-
rm -rf "$TEMP_DIR"
|
|
79
|
-
exit 0
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
# --- Backup contract state ---
|
|
83
|
-
echo " Backing up contract state..."
|
|
84
|
-
STATE_BACKUP=$(mktemp /tmp/opencode-contract-state-XXXXX.json)
|
|
85
|
-
$PYTHON_CMD -c "
|
|
86
|
-
import json
|
|
87
|
-
with open('.opencode/orchestration/contract.json') as f:
|
|
88
|
-
d = json.load(f)
|
|
89
|
-
# Extract only the state fields to preserve
|
|
90
|
-
state = {
|
|
91
|
-
'requirements': d.get('requirements', {}),
|
|
92
|
-
'scope': d.get('scope', {}),
|
|
93
|
-
'decisions': d.get('decisions', {}),
|
|
94
|
-
'governance': d.get('governance', {}),
|
|
95
|
-
'metrics': d.get('metrics', {}),
|
|
96
|
-
'lessons_learned': d.get('lessons_learned', []),
|
|
97
|
-
'retry': d.get('retry', {}),
|
|
98
|
-
'score': d.get('score', {}),
|
|
99
|
-
'outputs': d.get('outputs', {})
|
|
100
|
-
}
|
|
101
|
-
with open('$STATE_BACKUP', 'w') as f:
|
|
102
|
-
json.dump(state, f, indent=2)
|
|
103
|
-
" 2>/dev/null || echo " ⚠️ Could not backup contract state"
|
|
104
|
-
echo " ✅ State backed up"
|
|
105
|
-
|
|
106
|
-
# --- Files to update ---
|
|
107
|
-
echo ""
|
|
108
|
-
echo " Files to update:"
|
|
109
|
-
UPDATES=0
|
|
110
|
-
|
|
111
|
-
update_file() {
|
|
112
|
-
local src="$1"
|
|
113
|
-
local dst="$2"
|
|
114
|
-
local label="$3"
|
|
115
|
-
if [ -f "$src" ]; then
|
|
116
|
-
if [ "$DRY_RUN" = true ]; then
|
|
117
|
-
echo " [DRY-RUN] Would update: $label"
|
|
118
|
-
else
|
|
119
|
-
cp "$src" "$dst"
|
|
120
|
-
chmod +x "$dst" 2>/dev/null || true
|
|
121
|
-
echo " ✅ Updated: $label"
|
|
122
|
-
fi
|
|
123
|
-
UPDATES=$((UPDATES + 1))
|
|
124
|
-
else
|
|
125
|
-
echo " ⚠️ Source not found: $src"
|
|
126
|
-
fi
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
# Update scripts
|
|
130
|
-
for script in preflight.sh postflight.sh verify.sh adr.sh platform.sh; do
|
|
131
|
-
update_file "$TEMP_DIR/src/$script" ".opencode/src/$script" "src/$script"
|
|
132
|
-
done
|
|
133
|
-
|
|
134
|
-
# Update init.sh (for future --force re-inits)
|
|
135
|
-
update_file "$TEMP_DIR/src/init.sh" ".opencode/src/init.sh" "src/init.sh"
|
|
136
|
-
|
|
137
|
-
# Update update.sh itself
|
|
138
|
-
update_file "$TEMP_DIR/src/update.sh" ".opencode/src/update.sh" "src/update.sh"
|
|
139
|
-
|
|
140
|
-
# Update rules
|
|
141
|
-
update_file "$TEMP_DIR/rules/rules.json" ".opencode/rules/rules.json" "rules/rules.json"
|
|
142
|
-
update_file "$TEMP_DIR/rules/validation.sh" ".opencode/rules/validation.sh" "rules/validation.sh"
|
|
143
|
-
|
|
144
|
-
# Update agent templates (but NOT contract.json — preserve state)
|
|
145
|
-
for agent in orchestrator planner task-manager code-reviewer learner fixer; do
|
|
146
|
-
update_file "$TEMP_DIR/templates/agents/$agent.md" ".opencode/agents/$agent.md" "agents/$agent.md"
|
|
147
|
-
done
|
|
148
|
-
|
|
149
|
-
# Update superpowers contract template
|
|
150
|
-
update_file "$TEMP_DIR/templates/superpowers-contract.json" ".opencode/templates/superpowers-contract.json" "superpowers-contract.json"
|
|
151
|
-
|
|
152
|
-
# --- Restore contract state ---
|
|
153
|
-
if [ "$DRY_RUN" = false ] && [ -f "$STATE_BACKUP" ]; then
|
|
154
|
-
$PYTHON_CMD -c "
|
|
155
|
-
import json
|
|
156
|
-
with open('.opencode/orchestration/contract.json') as f:
|
|
157
|
-
contract = json.load(f)
|
|
158
|
-
with open('$STATE_BACKUP') as f:
|
|
159
|
-
state = json.load(f)
|
|
160
|
-
# Merge preserved state back into new contract
|
|
161
|
-
for key, val in state.items():
|
|
162
|
-
if val: # only overwrite if backup has data
|
|
163
|
-
contract[key] = val
|
|
164
|
-
# Update contract_version to latest
|
|
165
|
-
contract['contract_version'] = '$LATEST_VERSION'
|
|
166
|
-
with open('.opencode/orchestration/contract.json', 'w') as f:
|
|
167
|
-
json.dump(contract, f, indent=2)
|
|
168
|
-
" 2>/dev/null && echo " ✅ Contract state restored" || echo " ⚠️ Contract state restore failed"
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
# --- Cleanup ---
|
|
172
|
-
rm -rf "$TEMP_DIR" "$STATE_BACKUP"
|
|
173
|
-
|
|
174
|
-
# --- Summary ---
|
|
175
|
-
echo ""
|
|
176
|
-
if [ "$DRY_RUN" = true ]; then
|
|
177
|
-
echo -e "${YELLOW}[opencode-kit] 🔄 Dry run complete. $UPDATES files would be updated.${NC}"
|
|
178
|
-
else
|
|
179
|
-
echo -e "${GREEN}[opencode-kit] ✅ Update complete. $UPDATES files updated.${NC}"
|
|
180
|
-
echo " Run .opencode/src/verify.sh to verify installation."
|
|
181
|
-
fi
|
package/src/verify.sh
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# opencode-kit verify — check installation health
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
-
|
|
7
|
-
echo "[opencode-kit] 🔍 Verify: checking installation..."
|
|
8
|
-
FAIL=0
|
|
9
|
-
|
|
10
|
-
# --- Check 1: required files exist ---
|
|
11
|
-
for f in \
|
|
12
|
-
".opencode/orchestration/contract.json" \
|
|
13
|
-
".opencode/rules/rules.json" \
|
|
14
|
-
".opencode/templates/superpowers-contract.json"; do
|
|
15
|
-
if [ -f "$f" ]; then
|
|
16
|
-
echo " ✅ $f"
|
|
17
|
-
else
|
|
18
|
-
echo " ❌ $f MISSING"
|
|
19
|
-
FAIL=1
|
|
20
|
-
fi
|
|
21
|
-
done
|
|
22
|
-
|
|
23
|
-
# --- Check 2: agent .md files exist ---
|
|
24
|
-
for agent in orchestrator planner task-manager code-reviewer learner fixer; do
|
|
25
|
-
FILE=".opencode/agents/$agent.md"
|
|
26
|
-
if [ -f "$FILE" ]; then
|
|
27
|
-
# Check pre-flight gate exists in file
|
|
28
|
-
if grep -q "load contract" "$FILE" 2>/dev/null; then
|
|
29
|
-
echo " ✅ agents/$agent.md (has pre-flight gate)"
|
|
30
|
-
else
|
|
31
|
-
echo " ⚠️ agents/$agent.md (MISSING pre-flight gate)"
|
|
32
|
-
fi
|
|
33
|
-
else
|
|
34
|
-
echo " ❌ agents/$agent.md MISSING"
|
|
35
|
-
FAIL=1
|
|
36
|
-
fi
|
|
37
|
-
done
|
|
38
|
-
|
|
39
|
-
# --- Check 3: telemetry directory ---
|
|
40
|
-
mkdir -p .opencode/telemetry 2>/dev/null
|
|
41
|
-
echo " ✅ telemetry directory ready"
|
|
42
|
-
|
|
43
|
-
# --- Check 4: scripts executable ---
|
|
44
|
-
for script in ".opencode/src/preflight.sh" ".opencode/src/postflight.sh" ".opencode/src/telemetry.sh" ".opencode/src/doctor.sh" ".opencode/src/status.sh"; do
|
|
45
|
-
if [ -x "$script" ]; then
|
|
46
|
-
echo " ✅ $script (executable)"
|
|
47
|
-
elif [ -f "$script" ]; then
|
|
48
|
-
echo " ⚠️ $script (not executable — run chmod +x)"
|
|
49
|
-
else
|
|
50
|
-
echo " ❌ $script MISSING"
|
|
51
|
-
FAIL=1
|
|
52
|
-
fi
|
|
53
|
-
done
|
|
54
|
-
|
|
55
|
-
# --- Check 5: not on main ---
|
|
56
|
-
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
57
|
-
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
|
|
58
|
-
echo " ⚠️ On '$BRANCH' branch — create a feature branch"
|
|
59
|
-
fi
|
|
60
|
-
echo " ℹ️ Branch: $BRANCH"
|
|
61
|
-
|
|
62
|
-
if [ "$FAIL" -eq 1 ]; then
|
|
63
|
-
echo "[opencode-kit] ❌ Verify FAILED — run 'opencode-kit init' to repair"
|
|
64
|
-
exit 1
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
echo "[opencode-kit] ✅ All checks passed"
|