@ikieaneh/opencode-kit 0.6.2 → 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.
@@ -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,148 +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=$(CONTRACT_FILE="$CONTRACT_FILE" $PYTHON_CMD -c "
63
- import json, os
64
- with open(os.environ['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 = 0
70
- for entry in log:
71
- try:
72
- num = int(entry.get('id','ADR-000').replace('ADR-',''))
73
- if num > last: last = num
74
- except ValueError:
75
- continue
76
- print(f'ADR-{last+1:03d}')
77
- ")
78
-
79
- # --- Check for duplicate title ---
80
- DUP=$(CONTRACT_FILE="$CONTRACT_FILE" TITLE="$TITLE" $PYTHON_CMD -c "
81
- import json, os
82
- with open(os.environ['CONTRACT_FILE']) as f: d = json.load(f)
83
- log = d.get('decisions', {}).get('adr_log', [])
84
- search_title = os.environ.get('TITLE', '').lower().strip()
85
- for entry in log:
86
- if entry.get('title','').lower().strip() == search_title:
87
- print(entry.get('id',''))
88
- break
89
- " 2>/dev/null)
90
- if [ -n "$DUP" ]; then
91
- echo -e "${YELLOW}⚠️ Duplicate title found: $DUP — '$TITLE'${NC}"
92
- echo " Skipping. Update existing ADR instead."
93
- exit 0
94
- fi
95
-
96
- # --- Build ADR entry via Python (avoids shell injection) ---
97
- ENTRY_FILE=$(mktemp /tmp/opencode-adr-entry-XXXXX.json)
98
- trap 'rm -f "$ENTRY_FILE"' EXIT INT TERM
99
-
100
- TITLE="$TITLE" CONTEXT="$CONTEXT" DECISION="$DECISION" ALTERNATIVES="$ALTERNATIVES" CONSEQUENCES="$CONSEQUENCES" NEXT_ID="$NEXT_ID" ENTRY_FILE="$ENTRY_FILE" $PYTHON_CMD -c "
101
- import json, os
102
- from datetime import date
103
-
104
- entry = {
105
- 'id': os.environ['NEXT_ID'],
106
- 'date': date.today().isoformat(),
107
- 'title': os.environ.get('TITLE', ''),
108
- 'context': os.environ.get('CONTEXT', ''),
109
- 'decision': os.environ.get('DECISION', ''),
110
- 'alternatives': os.environ.get('ALTERNATIVES', ''),
111
- 'consequences': os.environ.get('CONSEQUENCES', '')
112
- }
113
-
114
- with open(os.environ['ENTRY_FILE'], 'w') as f:
115
- json.dump(entry, f, indent=2)
116
- print('Entry written')
117
- "
118
-
119
- # --- Inject into contract.json ---
120
- CONTRACT_FILE="$CONTRACT_FILE" ENTRY_FILE="$ENTRY_FILE" $PYTHON_CMD -c "
121
- import json, os
122
-
123
- with open(os.environ['CONTRACT_FILE']) as f:
124
- contract = json.load(f)
125
-
126
- with open(os.environ['ENTRY_FILE']) as f:
127
- entry = json.load(f)
128
-
129
- if 'decisions' not in contract:
130
- contract['decisions'] = {}
131
- if 'adr_log' not in contract['decisions']:
132
- contract['decisions']['adr_log'] = []
133
-
134
- contract['decisions']['adr_log'].append(entry)
135
-
136
- with open(os.environ['CONTRACT_FILE'], 'w') as f:
137
- json.dump(contract, f, indent=2)
138
-
139
- print(json.dumps(entry, indent=2))
140
- "
141
-
142
- rm -f "$ENTRY_FILE"
143
-
144
- echo ""
145
- echo -e "${GREEN}[opencode-kit] ✅ ADR recorded: $NEXT_ID${NC}"
146
- echo " Title: $TITLE"
147
- echo " File: $CONTRACT_FILE"
148
- 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,109 +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
- # Write contract data to temp files for safe Python consumption
46
- CONTRACT_A_FILE=$(mktemp /tmp/opencode-diff-a-XXXXXX.json 2>/dev/null || mktemp /tmp/opencode-diff-a-XXXXXX)
47
- CONTRACT_B_FILE=$(mktemp /tmp/opencode-diff-b-XXXXXX.json 2>/dev/null || mktemp /tmp/opencode-diff-b-XXXXXX)
48
- trap 'rm -f "$CONTRACT_A_FILE" "$CONTRACT_B_FILE"' EXIT INT TERM
49
-
50
- echo "$CONTRACT_A" > "$CONTRACT_A_FILE"
51
- echo "$CONTRACT_B" > "$CONTRACT_B_FILE"
52
-
53
- if [ -n "$PYTHON_CMD" ]; then
54
- $PYTHON_CMD -c "
55
- import json, sys
56
-
57
- def get_state(filepath):
58
- try:
59
- with open(filepath) as f:
60
- content = f.read().strip()
61
- if not content:
62
- return None
63
- d = json.loads(content)
64
- return {
65
- 'state': d.get('state', '?'),
66
- 'goal': (d.get('requirements', {}) or {}).get('goal', '?'),
67
- 'score': (d.get('score', {}) or {}).get('combined', '?'),
68
- 'phases': (d.get('metrics', {}) or {}).get('phases_completed', []),
69
- 'adrs': len((d.get('decisions', {}) or {}).get('adr_log', [])),
70
- 'version': d.get('contract_version', '?')
71
- }
72
- except:
73
- return None
74
-
75
- a = get_state('$CONTRACT_A_FILE')
76
- b = get_state('$CONTRACT_B_FILE')
77
-
78
- if a and b:
79
- print(f' Field $BRANCH_A $BRANCH_B')
80
- print(f' {\"-\"*50}')
81
- for field in ['state', 'goal', 'score', 'version']:
82
- va = str(a.get(field, '?'))[:20]
83
- vb = str(b.get(field, '?'))[:20]
84
- marker = ' ←→' if va != vb else ' '
85
- print(f' {field:20s} {va:20s} {marker} {vb:20s}')
86
- print(f' phases {len(a.get(\"phases\",[])):3d} completed {len(b.get(\"phases\",[])):3d} completed')
87
- print(f' ADRs {a.get(\"adrs\",0):3d} recorded {b.get(\"adrs\",0):3d} recorded')
88
- elif a and not b:
89
- print(f' Contract exists in $BRANCH_A but NOT in $BRANCH_B')
90
- print(f' State: {a.get(\"state\",\"?\")}')
91
- elif b and not a:
92
- print(f' Contract exists in $BRANCH_B but NOT in $BRANCH_A')
93
- print(f' State: {b.get(\"state\",\"?\")}')
94
- " 2>/dev/null || echo -e "${YELLOW}Could not parse contract JSON${NC}"
95
-
96
- rm -f "$CONTRACT_A_FILE" "$CONTRACT_B_FILE"
97
- fi
98
-
99
- # Raw git diff
100
- echo ""
101
- echo -e "${CYAN}Raw diff:${NC}"
102
- if git diff "$BRANCH_A" "$BRANCH_B" -- "$CONTRACT_FILE" 2>/dev/null | head -40; then
103
- if ! git diff --exit-code "$BRANCH_A" "$BRANCH_B" -- "$CONTRACT_FILE" &>/dev/null; then
104
- :
105
- fi
106
- fi
107
- echo ""
108
- echo -e "Run: ${GREEN}bash .opencode/src/diff.sh${NC} (default: main vs HEAD)"
109
- echo -e " ${GREEN}bash .opencode/src/diff.sh staging feature-x${NC} (custom branches)"
package/src/doctor.sh DELETED
@@ -1,160 +0,0 @@
1
- #!/usr/bin/env bash
2
- # opencode-kit doctor — diagnostic command
3
- # Checks: MCPs, contract, rules, permissions, git branch, agent configs
4
- # Usage: bash src/doctor.sh [--json] [--fix]
5
- set -euo pipefail
6
-
7
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
- # shellcheck source=./platform.sh
9
- . "$SCRIPT_DIR/platform.sh"
10
- . "$SCRIPT_DIR/global-config.sh"
11
-
12
- RULES_FILE=".opencode/rules/rules.json"
13
- CONTRACT_FILE=".opencode/orchestration/contract.json"
14
- OPENCODE_JSON="opencode.json"
15
-
16
- RED='\033[0;31m'
17
- GREEN='\033[0;32m'
18
- YELLOW='\033[1;33m'
19
- CYAN='\033[0;36m'
20
- NC='\033[0m'
21
-
22
- ISSUES=0
23
- mode="${1:-}"
24
-
25
- echo -e "${CYAN}🔍 opencode-kit doctor${NC}"
26
- echo ""
27
-
28
- # === 1. Contract check ===
29
- echo -e "${CYAN}[CONTRACT]${NC} Checking orchestration contract..."
30
- if [ ! -f "$CONTRACT_FILE" ]; then
31
- echo -e " ${RED}❌ contract.json not found — run 'opencode-kit init'${NC}"
32
- ISSUES=$((ISSUES + 1))
33
- else
34
- if [ -n "$PYTHON_CMD" ]; then
35
- STATE=$($PYTHON_CMD -c "import json; d=json.load(open('$CONTRACT_FILE')); print(d.get('state','?'))" 2>/dev/null || echo "parse_error")
36
- if [ "$STATE" = "parse_error" ]; then
37
- echo -e " ${RED}❌ contract.json is malformed JSON${NC}"
38
- ISSUES=$((ISSUES + 1))
39
- else
40
- echo -e " ✅ State: $STATE"
41
- fi
42
- fi
43
- fi
44
-
45
- # === 1b. Contract schema validation ===
46
- SCHEMA_FILE=".opencode/templates/contract.schema.json"
47
- if [ -f "$CONTRACT_FILE" ] && [ -f "$SCHEMA_FILE" ] && [ -n "$PYTHON_CMD" ]; then
48
- if $PYTHON_CMD -c "
49
- import json, sys
50
- with open('$CONTRACT_FILE') as f: contract = json.load(f)
51
- with open('$SCHEMA_FILE') as f: schema = json.load(f)
52
- # Basic required-field validation
53
- errors = []
54
- if not contract.get('state'): errors.append('Missing state')
55
- if not contract.get('requirements', {}).get('goal'): errors.append('Missing requirements.goal')
56
- if contract.get('state') not in ['INIT','PLAN','PLAN_SCORED','EXECUTE','EXECUTE_SCORED','REVIEW','REVIEW_SCORED','COMPLETE','BLOCKED']:
57
- errors.append('Invalid state')
58
- if errors:
59
- print('; '.join(errors))
60
- sys.exit(1)
61
- else:
62
- print('OK')
63
- " 2>/dev/null; then
64
- echo -e " ✅ Contract schema valid"
65
- else
66
- echo -e " ⚠️ Contract schema issues found (non-blocking)"
67
- ISSUES=$((ISSUES + 1))
68
- fi
69
- fi
70
-
71
- # === 2. Rules check ===
72
- echo -e "${CYAN}[RULES]${NC} Checking rules.json..."
73
- if [ ! -f "$RULES_FILE" ]; then
74
- echo -e " ${RED}❌ rules.json not found${NC}"
75
- ISSUES=$((ISSUES + 1))
76
- else
77
- if [ -n "$PYTHON_CMD" ]; then
78
- RULE_COUNT=$($PYTHON_CMD -c "import json; d=json.load(open('$RULES_FILE')); print(len(d.get('rules',[])))" 2>/dev/null || echo "0")
79
- echo -e " ✅ $RULE_COUNT rules loaded"
80
- fi
81
- fi
82
-
83
- # === 3a. MCP CLI checks ===
84
- echo -e "${CYAN}[MCP]${NC} Checking required MCPs..."
85
- if [ -f "$RULES_FILE" ] && [ -n "$PYTHON_CMD" ]; then
86
- $PYTHON_CMD -c "
87
- import json, subprocess, sys
88
- with open('$RULES_FILE') as f:
89
- rules = json.load(f)
90
- mcps = rules.get('required_mcps', {})
91
- mcps.pop('description', None)
92
- for name, cfg in mcps.items():
93
- cli = cfg.get('check_cli', '')
94
- severity = cfg.get('severity', 'optional')
95
- result = subprocess.run(cli, shell=True, capture_output=True, timeout=5)
96
- ok = result.returncode == 0
97
- if ok:
98
- print(f' ✅ {name}: available')
99
- elif severity == 'required':
100
- print(f' ❌ {name}: MISSING (required)')
101
- sys.exit(1)
102
- else:
103
- print(f' ⚠️ {name}: not detected (optional)')
104
- " 2>/dev/null || ISSUES=$((ISSUES + 1))
105
- fi
106
-
107
- # === 3b. MCP connectivity ===
108
- echo -e "${CYAN}[MCP_CONNECT]${NC} Testing MCP connectivity..."
109
- if command -v lean-ctx &>/dev/null; then
110
- if lean-ctx ctx_knowledge status &>/dev/null 2>&1; then
111
- echo -e " ✅ lean-ctx MCP: responding"
112
- else
113
- echo -e " ⚠️ lean-ctx CLI found but MCP not responding (expected in CI)"
114
- fi
115
- fi
116
-
117
- # === 4. Git branch ===
118
- echo -e "${CYAN}[GIT]${NC} Checking branch..."
119
- BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
120
- if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
121
- echo -e " ${YELLOW}⚠️ On '$BRANCH' — create a feature branch for development${NC}"
122
- else
123
- echo -e " ✅ Branch: $BRANCH"
124
- fi
125
-
126
- # === 5. Lean-ctx persistence ===
127
- echo -e "${CYAN}[PERSIST]${NC} Checking persistence..."
128
- if command -v lean-ctx &>/dev/null; then
129
- echo -e " ✅ lean-ctx CLI available"
130
- LEAN_OK=$(lean-ctx ctx_knowledge recall --query "orchestration-contract" &>/dev/null && echo "yes" || echo "no")
131
- if [ "$LEAN_OK" = "yes" ]; then
132
- echo -e " ✅ Contract found in lean-ctx"
133
- else
134
- echo -e " ⚠️ Contract not in lean-ctx (file fallback active)"
135
- fi
136
- else
137
- echo -e " ⚠️ lean-ctx not detected (file fallback active)"
138
- fi
139
-
140
- # === 6. Plugin in opencode.json ===
141
- echo -e "${CYAN}[PLUGIN]${NC} Checking plugin configuration..."
142
- if [ -f "$OPENCODE_JSON" ]; then
143
- if grep -q "@ikieaneh/opencode-kit" "$OPENCODE_JSON" 2>/dev/null; then
144
- echo -e " ✅ Plugin registered in opencode.json"
145
- else
146
- echo -e " ${YELLOW}⚠️ Plugin not found in opencode.json — add to your plugin array${NC}"
147
- fi
148
- else
149
- echo -e " ${YELLOW}⚠️ No opencode.json found${NC}"
150
- fi
151
-
152
- # === Summary ===
153
- echo ""
154
- if [ "$ISSUES" -eq 0 ]; then
155
- echo -e "${GREEN}✅ All checks passed. System healthy.${NC}"
156
- exit 0
157
- else
158
- echo -e "${RED}❌ $ISSUES issue(s) found. Review warnings above.${NC}"
159
- exit 1
160
- fi
@@ -1,82 +0,0 @@
1
- #!/usr/bin/env bash
2
- # opencode-kit global-config — resolve config from local → global → plugin default
3
- # Usage: source src/global-config.sh && resolve_config "contract.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
- PLUGIN_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11
- GLOBAL_CONFIG_DIR="$HOME/.config/opencode-kit"
12
-
13
- # Resolve a config file from the lookup chain:
14
- # 1. .opencode/<path> (project override)
15
- # 2. ~/.config/opencode-kit/<path> (global defaults)
16
- # 3. <plugin>/<path> (plugin defaults)
17
- # Returns: path to the first file found, or empty string if none found
18
- resolve_config() {
19
- local rel_path="$1"
20
-
21
- # 1. Project override
22
- if [ -f ".opencode/$rel_path" ]; then
23
- echo ".opencode/$rel_path"
24
- return 0
25
- fi
26
-
27
- # 2. Global defaults
28
- if [ -f "$GLOBAL_CONFIG_DIR/$rel_path" ]; then
29
- echo "$GLOBAL_CONFIG_DIR/$rel_path"
30
- return 0
31
- fi
32
-
33
- # 3. Plugin defaults (templates/)
34
- if [ -f "$PLUGIN_ROOT/templates/$rel_path" ]; then
35
- echo "$PLUGIN_ROOT/templates/$rel_path"
36
- return 0
37
- fi
38
-
39
- # 4. Plugin defaults (root/)
40
- if [ -f "$PLUGIN_ROOT/$rel_path" ]; then
41
- echo "$PLUGIN_ROOT/$rel_path"
42
- return 0
43
- fi
44
-
45
- echo ""
46
- return 1
47
- }
48
-
49
- # Initialize global config from plugin defaults
50
- # Copies templates/* and rules/* to ~/.config/opencode-kit/
51
- init_global_config() {
52
- echo "[opencode-kit] Initializing global config at $GLOBAL_CONFIG_DIR"
53
-
54
- mkdir -p "$GLOBAL_CONFIG_DIR/orchestration" "$GLOBAL_CONFIG_DIR/rules"
55
-
56
- # Copy contract template
57
- if [ ! -f "$GLOBAL_CONFIG_DIR/orchestration/contract.json" ]; then
58
- cp "$PLUGIN_ROOT/templates/contract.json" "$GLOBAL_CONFIG_DIR/orchestration/contract.json"
59
- echo " ✅ contract.json → global config"
60
- fi
61
-
62
- # Copy rules
63
- if [ ! -f "$GLOBAL_CONFIG_DIR/rules/rules.json" ]; then
64
- cp "$PLUGIN_ROOT/rules/rules.json" "$GLOBAL_CONFIG_DIR/rules/rules.json"
65
- echo " ✅ rules.json → global config"
66
- fi
67
-
68
- echo "[opencode-kit] ✅ Global config initialized"
69
- }
70
-
71
- # Detect if the opencode-kit plugin is active
72
- is_plugin_active() {
73
- # Check if .opencode/plugins/opencode-kit.js exists (sign of plugin mode)
74
- [ -f ".opencode/plugins/opencode-kit.js" ] && return 0
75
- # Check if ~/.config/opencode-kit exists (sign of global config)
76
- [ -d "$GLOBAL_CONFIG_DIR" ] && return 0
77
- return 1
78
- }
79
-
80
- # Export functions
81
- export -f resolve_config init_global_config is_plugin_active
82
- export GLOBAL_CONFIG_DIR
package/src/new-skill.sh DELETED
@@ -1,60 +0,0 @@
1
- #!/usr/bin/env bash
2
- # opencode-kit new skill — scaffold a new skill SKILL.md
3
- # Usage: bash src/new-skill.sh <skill-name> [description]
4
- set -euo pipefail
5
-
6
- RED='\033[0;31m'
7
- GREEN='\033[0;32m'
8
- CYAN='\033[0;36m'
9
- NC='\033[0m'
10
-
11
- NAME="${1:-}"
12
- DESC="${2:-A custom skill for this project}"
13
- SKILLS_DIR=".opencode/skills"
14
-
15
- if [ -z "$NAME" ]; then
16
- echo -e "${RED}Usage: bash src/new-skill.sh <skill-name> [description]${NC}"
17
- echo " Example: bash src/new-skill.sh python-conventions \"Python/Django conventions\""
18
- exit 1
19
- fi
20
-
21
- SKILL_PATH="$SKILLS_DIR/$NAME"
22
- SKILL_FILE="$SKILL_PATH/SKILL.md"
23
-
24
- if [ -d "$SKILL_PATH" ]; then
25
- echo -e "${RED}❌ Skill already exists: $SKILL_PATH${NC}"
26
- exit 1
27
- fi
28
-
29
- mkdir -p "$SKILL_PATH"
30
-
31
- cat > "$SKILL_FILE" << SKILLEOF
32
- ---
33
- description: $DESC
34
- ---
35
-
36
- # $NAME
37
-
38
- ## Conventions
39
-
40
- ...
41
-
42
- ## Commands
43
-
44
- | Action | Command |
45
- |--------|---------|
46
- | Test | ... |
47
- | Build | ... |
48
- | Format | ... |
49
-
50
- ## Rules
51
-
52
- ...
53
- SKILLEOF
54
-
55
- echo -e "${GREEN}✅ Skill created: $SKILL_FILE${NC}"
56
- echo ""
57
- echo " To use it, add to opencode.json:"
58
- echo ' "skills": ["'$NAME'", "orchestration-template"]'
59
- echo ""
60
- echo " Or load it ad-hoc with: /skill $NAME"