@ikieaneh/opencode-kit 0.6.2 → 0.6.4

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/src/platform.sh DELETED
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env bash
2
- # opencode-kit platform — cross-platform detection and helpers
3
- # Source this from any script: . "$(dirname "$0")/platform.sh"
4
- set -euo pipefail
5
-
6
- # --- OS Detection ---
7
- OS="unknown"
8
- case "$(uname -s)" in
9
- Darwin) OS="macos" ;;
10
- Linux) OS="linux" ;;
11
- *) OS="other" ;;
12
- esac
13
-
14
- # --- Architecture ---
15
- ARCH="unknown"
16
- case "$(uname -m)" in
17
- arm64|aarch64) ARCH="arm64" ;;
18
- x86_64|amd64) ARCH="amd64" ;;
19
- *) ARCH="other" ;;
20
- esac
21
-
22
- # --- Python command (python3 on macOS, python on some Linux) ---
23
- PYTHON_CMD=""
24
- if command -v python3 &>/dev/null; then
25
- PYTHON_CMD="python3"
26
- elif command -v python &>/dev/null; then
27
- PYTHON_CMD="python"
28
- fi
29
-
30
- # --- Bash compatibility ---
31
- BASH_VERSION=$(bash --version | head -1 | grep -oE '[0-9]+\.[0-9]+' | head -1 || echo "0")
32
-
33
- # --- Export ---
34
- export OS ARCH PYTHON_CMD BASH_VERSION
package/src/postflight.py DELETED
@@ -1,211 +0,0 @@
1
- #!/usr/bin/env python3
2
- """postflight helper — batch all contract/telemetry/STATE.md operations in one call.
3
-
4
- Replaces 11 separate inline Python invocations from postflight.sh with a single
5
- script that handles: contract read/migration, phase telemetry, summary update,
6
- STATE.md generation, and contract file persistence.
7
-
8
- Usage:
9
- postflight.py CONTRACT_FILE TELEMETRY_DIR STATE_FILE STATE_BACKUP_DIR TEMPLATE_FILE [NEW_STATE]
10
-
11
- Returns JSON to stdout with extracted values for shell-level echo/log messages.
12
- """
13
- import json
14
- import os
15
- import sys
16
- import datetime
17
-
18
-
19
- def main() -> int:
20
- # --- Parse arguments ---
21
- contract_file = sys.argv[1] if len(sys.argv) > 1 else '.opencode/orchestration/contract.json'
22
- telemetry_dir = sys.argv[2] if len(sys.argv) > 2 else '.opencode/telemetry'
23
- state_file = sys.argv[3] if len(sys.argv) > 3 else 'STATE.md'
24
- state_backup_dir = sys.argv[4] if len(sys.argv) > 4 else '.opencode/state'
25
- template_file = sys.argv[5] if len(sys.argv) > 5 else '.opencode/templates/contract.json'
26
- new_state = sys.argv[6] if len(sys.argv) > 6 else None
27
-
28
- start_time_file = os.path.join(telemetry_dir, '.phase_start')
29
- phases_file = os.path.join(telemetry_dir, 'phases.jsonl')
30
- summary_file = os.path.join(telemetry_dir, 'summary.json')
31
-
32
- now = datetime.datetime.now(datetime.UTC)
33
- now_iso = now.strftime('%Y-%m-%dT%H:%M:%SZ')
34
-
35
- # --- Load contract (file or template fallback) ---
36
- contract = {}
37
- if os.path.exists(contract_file):
38
- with open(contract_file) as f:
39
- contract = json.load(f)
40
- elif os.path.exists(template_file):
41
- with open(template_file) as f:
42
- contract = json.load(f)
43
-
44
- prev_state = contract.get('state', 'INIT')
45
-
46
- # --- Update state if provided ---
47
- if new_state:
48
- contract['state'] = new_state
49
-
50
- current_state = contract.get('state', 'UNKNOWN')
51
-
52
- # --- Read start time and calculate phase elapsed ---
53
- os.makedirs(telemetry_dir, exist_ok=True)
54
- phase_elapsed_ms = 0
55
- if os.path.exists(start_time_file):
56
- with open(start_time_file) as f:
57
- raw = f.read().strip()
58
- if raw:
59
- try:
60
- start_ts = int(raw)
61
- phase_elapsed_ms = int((now.timestamp() - start_ts) * 1000)
62
- except ValueError:
63
- pass
64
- os.remove(start_time_file)
65
-
66
- # --- Read last 'to' state from phases.jsonl for telemetry ---
67
- last_to_state = 'INIT'
68
- if os.path.exists(phases_file):
69
- lines = [l.strip() for l in open(phases_file) if l.strip()]
70
- if lines:
71
- try:
72
- last_to_state = json.loads(lines[-1]).get('to', 'INIT')
73
- except json.JSONDecodeError:
74
- pass
75
-
76
- # --- Record phase telemetry ---
77
- phase_entry = {
78
- 'ts': now_iso,
79
- 'from': last_to_state,
80
- 'to': current_state,
81
- 'elapsed_ms': phase_elapsed_ms,
82
- }
83
- with open(phases_file, 'a') as f:
84
- f.write(json.dumps(phase_entry) + '\n')
85
-
86
- # --- Contract migration (merge missing fields from template) ---
87
- migrated = False
88
- if os.path.exists(template_file):
89
- with open(template_file) as f:
90
- template = json.load(f)
91
-
92
- old_ver = contract.get('contract_version', '0.0.0')
93
- new_ver = template.get('contract_version', '0.5.2')
94
-
95
- needs_migration = (
96
- old_ver != new_ver
97
- or not all(k in contract for k in ['state', 'requirements', 'governance', 'score'])
98
- )
99
-
100
- if needs_migration:
101
- for key in template:
102
- if key not in contract:
103
- contract[key] = template[key]
104
- if 'extension_skills' not in contract.get('governance', {}):
105
- contract.setdefault('governance', {})['extension_skills'] = []
106
- contract['contract_version'] = new_ver
107
- migrated = True
108
-
109
- # --- Write contract to primary location ---
110
- os.makedirs(os.path.dirname(contract_file) or '.', exist_ok=True)
111
- with open(contract_file, 'w') as f:
112
- json.dump(contract, f, indent=2)
113
-
114
- # --- Backup contract to state backup dir ---
115
- os.makedirs(state_backup_dir, exist_ok=True)
116
- with open(os.path.join(state_backup_dir, 'contract.json'), 'w') as f:
117
- json.dump(contract, f, indent=2)
118
-
119
- # --- Update telemetry summary.json ---
120
- total_ms = 0
121
- phases = []
122
- if os.path.exists(phases_file):
123
- with open(phases_file) as f:
124
- for line in f:
125
- line = line.strip()
126
- if not line:
127
- continue
128
- try:
129
- entry = json.loads(line)
130
- total_ms += entry.get('elapsed_ms', 0)
131
- phases.append(entry.get('to', ''))
132
- except json.JSONDecodeError:
133
- pass
134
-
135
- summary = {
136
- 'phases_completed': phases,
137
- 'total_elapsed_ms': total_ms,
138
- 'total_elapsed_s': round(total_ms / 1000, 1),
139
- 'updated_at': now_iso,
140
- }
141
- with open(summary_file, 'w') as f:
142
- json.dump(summary, f, indent=2)
143
-
144
- # --- Extract values for STATE.md ---
145
- retry = contract.get('retry', {})
146
- score_obj = contract.get('score', {})
147
- state_val = contract.get('state', 'UNKNOWN')
148
- phase_val = retry.get('current_phase', 'none')
149
- score_combined = str(score_obj.get('combined', '?'))
150
-
151
- # Issues / blockers
152
- issues = retry.get('issues', [])
153
- issues_blockers = '\n'.join(f'- {i}' for i in issues) if issues else 'None'
154
-
155
- # ADRs (last 3)
156
- adr_log = contract.get('decisions', {}).get('adr_log', [])
157
- if adr_log:
158
- adr_lines = '\n'.join(
159
- f'- {e.get("id", "?")}: {e.get("title", "")}' for e in adr_log[-3:]
160
- )
161
- else:
162
- adr_lines = 'No ADRs recorded'
163
-
164
- # Last completed phase
165
- metrics_phases = contract.get('metrics', {}).get('phases_completed', [])
166
- last_phase = metrics_phases[-1] if metrics_phases else 'INIT'
167
-
168
- # --- Write STATE.md ---
169
- state_md = (
170
- '# Project State\n'
171
- '\n'
172
- '## Current Focus\n'
173
- f'Agent orchestration — {state_val} (phase: {phase_val}). Score: {score_combined}.\n'
174
- '\n'
175
- '## Known Blockers\n'
176
- f'{issues_blockers}\n'
177
- '\n'
178
- '## Active Decisions\n'
179
- f'{adr_lines}\n'
180
- '\n'
181
- '## Recent Changes\n'
182
- f'- Last state transition: {last_phase}\n'
183
- )
184
- state_dir = os.path.dirname(state_file)
185
- if state_dir:
186
- os.makedirs(state_dir, exist_ok=True)
187
- with open(state_file, 'w') as f:
188
- f.write(state_md)
189
-
190
- # --- Output JSON summary for shell script consumption ---
191
- output = {
192
- 'state': state_val,
193
- 'phase': phase_val,
194
- 'score': score_combined,
195
- 'prev_state': prev_state,
196
- 'current_state': current_state,
197
- 'phase_elapsed_ms': phase_elapsed_ms,
198
- 'phase_elapsed_s': round(phase_elapsed_ms / 1000, 1),
199
- 'migrated': migrated,
200
- 'contract_version': contract.get('contract_version', '?'),
201
- 'phases_count': len(phases),
202
- 'total_elapsed_s': summary['total_elapsed_s'],
203
- 'issues_count': len(issues),
204
- 'last_to_state': last_to_state,
205
- }
206
- print(json.dumps(output))
207
- return 0
208
-
209
-
210
- if __name__ == '__main__':
211
- sys.exit(main())
package/src/postflight.sh DELETED
@@ -1,82 +0,0 @@
1
- #!/usr/bin/env bash
2
- # opencode-kit postflight — persist contract + telemetry + update STATE.md
3
- # Run after every delegation or phase change.
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
- CONTRACT_FILE=".opencode/orchestration/contract.json"
12
- STATE_FILE="STATE.md"
13
- TELEMETRY_DIR=".opencode/telemetry"
14
- STATE_BACKUP_DIR=".opencode/state"
15
- TEMPLATE_FILE=".opencode/templates/contract.json"
16
-
17
- mkdir -p "$TELEMETRY_DIR" "$STATE_BACKUP_DIR"
18
-
19
- echo "[opencode-kit] Post-flight: persisting state..."
20
-
21
- # --- Resolve contract: try lean-ctx first, fall back to file ---
22
- LEAN_CTX_CONTRACT=$(lean-ctx ctx_knowledge recall --query "$CONTRACT_KEY" 2>/dev/null || true)
23
- if [ -n "$LEAN_CTX_CONTRACT" ]; then
24
- echo "$LEAN_CTX_CONTRACT" > "$CONTRACT_FILE"
25
- fi
26
-
27
- # --- Single Python call: batch all contract/telemetry/STATE.md operations ---
28
- PYTHON_OUTPUT=""
29
- STATE=""
30
- if [ -n "$PYTHON_CMD" ]; then
31
- PYTHON_OUTPUT=$($PYTHON_CMD "$SCRIPT_DIR/postflight.py" \
32
- "$CONTRACT_FILE" "$TELEMETRY_DIR" "$STATE_FILE" \
33
- "$STATE_BACKUP_DIR" "$TEMPLATE_FILE" \
34
- )
35
-
36
- # Single Python parse call: extract all values as pipe-delimited string
37
- IFS='|' read -r STATE PHASE SCORE PREV_STATE CURRENT_STATE ELAPSED_S MIGRATED CONTRACT_VER PHASES_COUNT TOTAL_ELAPSED_S LAST_TO_STATE <<< \
38
- "$(echo "$PYTHON_OUTPUT" | $PYTHON_CMD -c "
39
- import sys, json
40
- d = json.load(sys.stdin)
41
- print('|'.join([str(d.get(k, '')) for k in [
42
- 'state', 'phase', 'score', 'prev_state', 'current_state',
43
- 'phase_elapsed_s', 'migrated', 'contract_version',
44
- 'phases_count', 'total_elapsed_s', 'last_to_state'
45
- ]]))
46
- " 2>/dev/null || echo "UNKNOWN|none|?|INIT|UNKNOWN|0|false|?|0|0|INIT")"
47
-
48
- # --- Echo status messages ---
49
- echo " 📊 Telemetry: $PREV_STATE → $CURRENT_STATE (${ELAPSED_S}s)"
50
-
51
- if [ "$MIGRATED" = "true" ]; then
52
- echo " 🔄 Contract migrated to v${CONTRACT_VER}"
53
- fi
54
-
55
- echo " 📝 Contract state: $STATE (phase: $PHASE, score: $SCORE)"
56
- echo " ✅ STATE.md synced"
57
-
58
- echo " 📈 Telemetry summary: ${PHASES_COUNT} phases, ${TOTAL_ELAPSED_S}s total"
59
- else
60
- echo " ⚠️ Python not available, skipping postflight processing"
61
- fi
62
-
63
- # --- Persist to lean-ctx ---
64
- if [ -n "$PYTHON_CMD" ] && [ -f "$CONTRACT_FILE" ]; then
65
- CONTRACT_JSON=$(cat "$CONTRACT_FILE")
66
- if lean-ctx ctx_knowledge remember \
67
- category architecture \
68
- key "$CONTRACT_KEY" \
69
- value "$CONTRACT_JSON" 2>/dev/null; then
70
- echo " ✅ Contract persisted to lean-ctx"
71
- fi
72
- fi
73
-
74
- # File fallback always written by Python script to $STATE_BACKUP_DIR/contract.json
75
- echo " ✅ Contract persisted to file: $STATE_BACKUP_DIR/contract.json"
76
-
77
- # --- Save ctx_session ---
78
- lean-ctx ctx_session save 2>/dev/null && \
79
- echo " ✅ Session saved" || \
80
- echo " ⚠️ ctx_session save skipped (not available)"
81
-
82
- echo "[opencode-kit] ✅ Post-flight complete."
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,83 +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
- $PYTHON_CMD -c "
36
- import sys, json
37
- for line in sys.stdin:
38
- line = line.strip()
39
- if not line:
40
- continue
41
- d = json.loads(line)
42
- from_ = d.get('from', '?')
43
- to_ = d.get('to', '?')
44
- ms = d.get('elapsed_ms', 0)
45
- print(f' {from_:20s} → {to_:20s} {ms/1000:5.1f}s')
46
- " < "$TELEMETRY_DIR/phases.jsonl" 2>/dev/null || echo -e "${YELLOW}Could not parse phase data${NC}"
47
- else
48
- echo -e "${YELLOW}No phase data yet.${NC}"
49
- fi
50
- ;;
51
- --summary|*)
52
- if [ -f "$TELEMETRY_DIR/summary.json" ]; then
53
- read -r TOTAL_S PHASES < <(TELEMETRY_DIR="$TELEMETRY_DIR" $PYTHON_CMD -c "
54
- import json, os
55
- d = json.load(open(os.environ['TELEMETRY_DIR'] + '/summary.json'))
56
- print(d.get('total_elapsed_s', 0))
57
- print(len(d.get('phases_completed', [])))
58
- " 2>/dev/null || echo -e "0\n0")
59
- echo " Total elapsed: ${TOTAL_S}s"
60
- echo " Phases completed: $PHASES"
61
- echo ""
62
- echo " Latest phases:"
63
- tail -5 "$TELEMETRY_DIR/phases.jsonl" 2>/dev/null | $PYTHON_CMD -c "
64
- import sys, json
65
- for line in sys.stdin:
66
- line = line.strip()
67
- if not line:
68
- continue
69
- d = json.loads(line)
70
- ts = d.get('ts', '?')
71
- if ts != '?':
72
- ts = ts.split('T')[1].split('.')[0] if 'T' in ts else ts
73
- from_ = d.get('from', '?')
74
- to_ = d.get('to', '?')
75
- ms = d.get('elapsed_ms', 0)
76
- print(f' {ts} {from_} → {to_} ({ms//1000}s)')
77
- " 2>/dev/null || echo -e "${YELLOW}Could not parse phase data${NC}"
78
- else
79
- echo -e "${YELLOW}No telemetry data yet. Run a phase first.${NC}"
80
- echo " Phases are recorded automatically by postflight.sh"
81
- fi
82
- ;;
83
- esac