@ikieaneh/opencode-kit 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +23 -0
- package/.opencode/plugins/opencode-kit.js +201 -0
- package/LICENSE +21 -0
- package/README.md +417 -0
- package/package.json +47 -0
- package/rules/rules.json +123 -0
- package/rules/validation.sh +145 -0
- package/skills/adr-generator/SKILL.md +42 -0
- package/skills/learner/SKILL.md +44 -0
- package/skills/orchestration-template/SKILL.md +41 -0
- package/skills/qa-expert/SKILL.md +26 -0
- package/skills/scoring-pipeline/SKILL.md +43 -0
- package/skills/system-analyst/SKILL.md +28 -0
- package/skills/token-optimize/SKILL.md +32 -0
- package/skills/verification-before-completion/SKILL.md +60 -0
- package/src/adr.sh +139 -0
- package/src/cli.js +47 -0
- package/src/global-config.sh +81 -0
- package/src/init.sh +165 -0
- package/src/platform.sh +34 -0
- package/src/postflight.sh +132 -0
- package/src/preflight.sh +121 -0
- package/src/telemetry.sh +66 -0
- package/src/update.sh +180 -0
- package/src/verify.sh +67 -0
- package/templates/agents/code-reviewer.md +88 -0
- package/templates/agents/fixer.md +56 -0
- package/templates/agents/learner.md +87 -0
- package/templates/agents/orchestrator.md +110 -0
- package/templates/agents/planner.md +89 -0
- package/templates/agents/task-manager.md +97 -0
- package/templates/contract.json +86 -0
- package/templates/judge-prompt.md +55 -0
- package/templates/opencode-kit.schema.json +83 -0
- package/templates/superpowers-contract.json +8 -0
package/src/adr.sh
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
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
|
+
. "$SCRIPT_DIR/platform.sh"
|
|
9
|
+
|
|
10
|
+
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
11
|
+
|
|
12
|
+
RED='\033[0;31m'
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
CYAN='\033[0;36m'
|
|
16
|
+
NC='\033[0m'
|
|
17
|
+
|
|
18
|
+
# --- Check contract exists ---
|
|
19
|
+
if [ ! -f "$CONTRACT_FILE" ]; then
|
|
20
|
+
echo -e "${RED}❌ $CONTRACT_FILE not found. Run 'opencode-kit init' first.${NC}"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# --- Parse args ---
|
|
25
|
+
TITLE=""
|
|
26
|
+
CONTEXT=""
|
|
27
|
+
DECISION=""
|
|
28
|
+
ALTERNATIVES=""
|
|
29
|
+
CONSEQUENCES=""
|
|
30
|
+
|
|
31
|
+
while [ $# -gt 0 ]; do
|
|
32
|
+
case "$1" in
|
|
33
|
+
--title|-t) TITLE="$2"; shift 2 ;;
|
|
34
|
+
--context|-c) CONTEXT="$2"; shift 2 ;;
|
|
35
|
+
--decision|-d) DECISION="$2"; shift 2 ;;
|
|
36
|
+
--alternatives|-a) ALTERNATIVES="$2"; shift 2 ;;
|
|
37
|
+
--consequences|-q) CONSEQUENCES="$2"; shift 2 ;;
|
|
38
|
+
*) TITLE="$1"; shift ;;
|
|
39
|
+
esac
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
# --- Interactive mode if no title provided ---
|
|
43
|
+
if [ -z "$TITLE" ]; then
|
|
44
|
+
echo -e "${CYAN}[opencode-kit ADR]${NC} Interactive mode"
|
|
45
|
+
echo ""
|
|
46
|
+
read -r -p "Title: " TITLE
|
|
47
|
+
[ -z "$TITLE" ] && { echo -e "${RED}Title required${NC}"; exit 1; }
|
|
48
|
+
read -r -p "Context (why this decision?): " CONTEXT
|
|
49
|
+
read -r -p "Decision (what we decided): " DECISION
|
|
50
|
+
read -r -p "Alternatives considered: " ALTERNATIVES
|
|
51
|
+
read -r -p "Consequences (positive + negative): " CONSEQUENCES
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# --- Validate required fields ---
|
|
55
|
+
if [ -z "$TITLE" ]; then
|
|
56
|
+
echo -e "${RED}❌ Title is required. Use: bash src/adr.sh \"Your Decision Title\"${NC}"
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# --- Compute next ADR ID ---
|
|
61
|
+
NEXT_ID=$($PYTHON_CMD -c "
|
|
62
|
+
import json
|
|
63
|
+
with open('$CONTRACT_FILE') as f: d = json.load(f)
|
|
64
|
+
log = d.get('decisions', {}).get('adr_log', [])
|
|
65
|
+
if not log:
|
|
66
|
+
print('ADR-001')
|
|
67
|
+
else:
|
|
68
|
+
last = max(int(entry.get('id','ADR-000').replace('ADR-','')) for entry in log)
|
|
69
|
+
print(f'ADR-{last+1:03d}')
|
|
70
|
+
")
|
|
71
|
+
|
|
72
|
+
# --- Check for duplicate title ---
|
|
73
|
+
DUP=$($PYTHON_CMD -c "
|
|
74
|
+
import json
|
|
75
|
+
with open('$CONTRACT_FILE') as f: d = json.load(f)
|
|
76
|
+
log = d.get('decisions', {}).get('adr_log', [])
|
|
77
|
+
for entry in log:
|
|
78
|
+
if entry.get('title','').lower().strip() == '$TITLE'.lower().strip():
|
|
79
|
+
print(entry.get('id',''))
|
|
80
|
+
break
|
|
81
|
+
" 2>/dev/null)
|
|
82
|
+
if [ -n "$DUP" ]; then
|
|
83
|
+
echo -e "${YELLOW}⚠️ Duplicate title found: $DUP — '$TITLE'${NC}"
|
|
84
|
+
echo " Skipping. Update existing ADR instead."
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# --- Build ADR entry ---
|
|
89
|
+
# --- Build ADR entry via heredoc to avoid nested quote issues ---
|
|
90
|
+
$PYTHON_CMD -c "
|
|
91
|
+
import json, sys, os
|
|
92
|
+
|
|
93
|
+
title = '$TITLE'
|
|
94
|
+
date_val = '$(date +%Y-%m-%d)'
|
|
95
|
+
next_id = '$NEXT_ID'
|
|
96
|
+
|
|
97
|
+
entry = {
|
|
98
|
+
'id': next_id,
|
|
99
|
+
'date': date_val,
|
|
100
|
+
'title': title,
|
|
101
|
+
'context': '''$(echo "$CONTEXT" | sed "s/'/\\\\'/g")''',
|
|
102
|
+
'decision': '''$(echo "$DECISION" | sed "s/'/\\\\'/g")''',
|
|
103
|
+
'alternatives': '''$(echo "$ALTERNATIVES" | sed "s/'/\\\\'/g")''',
|
|
104
|
+
'consequences': '''$(echo "$CONSEQUENCES" | sed "s/'/\\\\'/g")'''
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
with open('/tmp/opencode-adr-entry.json', 'w') as f:
|
|
108
|
+
json.dump(entry, f, indent=2)
|
|
109
|
+
print('Entry written')
|
|
110
|
+
"
|
|
111
|
+
|
|
112
|
+
# --- Inject into contract.json ---
|
|
113
|
+
$PYTHON_CMD -c "
|
|
114
|
+
import json
|
|
115
|
+
|
|
116
|
+
with open('$CONTRACT_FILE') as f:
|
|
117
|
+
contract = json.load(f)
|
|
118
|
+
|
|
119
|
+
with open('/tmp/opencode-adr-entry.json') as f:
|
|
120
|
+
entry = json.load(f)
|
|
121
|
+
|
|
122
|
+
if 'decisions' not in contract:
|
|
123
|
+
contract['decisions'] = {}
|
|
124
|
+
if 'adr_log' not in contract['decisions']:
|
|
125
|
+
contract['decisions']['adr_log'] = []
|
|
126
|
+
|
|
127
|
+
contract['decisions']['adr_log'].append(entry)
|
|
128
|
+
|
|
129
|
+
with open('$CONTRACT_FILE', 'w') as f:
|
|
130
|
+
json.dump(contract, f, indent=2)
|
|
131
|
+
|
|
132
|
+
print(json.dumps(entry, indent=2))
|
|
133
|
+
"
|
|
134
|
+
|
|
135
|
+
echo ""
|
|
136
|
+
echo -e "${GREEN}[opencode-kit] ✅ ADR recorded: $NEXT_ID${NC}"
|
|
137
|
+
echo " Title: $TITLE"
|
|
138
|
+
echo " File: $CONTRACT_FILE"
|
|
139
|
+
echo " Next: Persist via: lean-ctx ctx_knowledge remember key orchestration-contract value \"\$(cat $CONTRACT_FILE)\""
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* opencode-kit CLI — version and help
|
|
4
|
+
* Usage: npx opencode-kit --version
|
|
5
|
+
* npx opencode-kit --help
|
|
6
|
+
*/
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const pkgPath = path.resolve(__dirname, '../package.json');
|
|
13
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
|
|
17
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
18
|
+
console.log(pkg.version);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
|
|
23
|
+
console.log(`
|
|
24
|
+
opencode-kit v${pkg.version}
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
npx opencode-kit [command]
|
|
28
|
+
|
|
29
|
+
Commands:
|
|
30
|
+
init [--force] Scaffold orchestration framework into project
|
|
31
|
+
update [--dry-run] Pull latest templates from GitHub
|
|
32
|
+
--version, -v Print version
|
|
33
|
+
--help, -h Print this help
|
|
34
|
+
|
|
35
|
+
Plugin mode:
|
|
36
|
+
Add "opencode-kit" to opencode.json plugin array (FIRST position).
|
|
37
|
+
Skills are auto-registered. Contract auto-initialized on first run.
|
|
38
|
+
|
|
39
|
+
Config resolution:
|
|
40
|
+
1. .opencode/ (project override)
|
|
41
|
+
2. ~/.config/opencode-kit/ (global defaults)
|
|
42
|
+
3. plugin templates/ (shipped defaults)
|
|
43
|
+
|
|
44
|
+
Docs: ${pkg.homepage}
|
|
45
|
+
`);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
. "$SCRIPT_DIR/platform.sh"
|
|
8
|
+
|
|
9
|
+
PLUGIN_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
10
|
+
GLOBAL_CONFIG_DIR="$HOME/.config/opencode-kit"
|
|
11
|
+
|
|
12
|
+
# Resolve a config file from the lookup chain:
|
|
13
|
+
# 1. .opencode/<path> (project override)
|
|
14
|
+
# 2. ~/.config/opencode-kit/<path> (global defaults)
|
|
15
|
+
# 3. <plugin>/<path> (plugin defaults)
|
|
16
|
+
# Returns: path to the first file found, or empty string if none found
|
|
17
|
+
resolve_config() {
|
|
18
|
+
local rel_path="$1"
|
|
19
|
+
|
|
20
|
+
# 1. Project override
|
|
21
|
+
if [ -f ".opencode/$rel_path" ]; then
|
|
22
|
+
echo ".opencode/$rel_path"
|
|
23
|
+
return 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# 2. Global defaults
|
|
27
|
+
if [ -f "$GLOBAL_CONFIG_DIR/$rel_path" ]; then
|
|
28
|
+
echo "$GLOBAL_CONFIG_DIR/$rel_path"
|
|
29
|
+
return 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# 3. Plugin defaults (templates/)
|
|
33
|
+
if [ -f "$PLUGIN_ROOT/templates/$rel_path" ]; then
|
|
34
|
+
echo "$PLUGIN_ROOT/templates/$rel_path"
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# 4. Plugin defaults (root/)
|
|
39
|
+
if [ -f "$PLUGIN_ROOT/$rel_path" ]; then
|
|
40
|
+
echo "$PLUGIN_ROOT/$rel_path"
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
echo ""
|
|
45
|
+
return 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Initialize global config from plugin defaults
|
|
49
|
+
# Copies templates/* and rules/* to ~/.config/opencode-kit/
|
|
50
|
+
init_global_config() {
|
|
51
|
+
echo "[opencode-kit] Initializing global config at $GLOBAL_CONFIG_DIR"
|
|
52
|
+
|
|
53
|
+
mkdir -p "$GLOBAL_CONFIG_DIR/orchestration" "$GLOBAL_CONFIG_DIR/rules"
|
|
54
|
+
|
|
55
|
+
# Copy contract template
|
|
56
|
+
if [ ! -f "$GLOBAL_CONFIG_DIR/orchestration/contract.json" ]; then
|
|
57
|
+
cp "$PLUGIN_ROOT/templates/contract.json" "$GLOBAL_CONFIG_DIR/orchestration/contract.json"
|
|
58
|
+
echo " ✅ contract.json → global config"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Copy rules
|
|
62
|
+
if [ ! -f "$GLOBAL_CONFIG_DIR/rules/rules.json" ]; then
|
|
63
|
+
cp "$PLUGIN_ROOT/rules/rules.json" "$GLOBAL_CONFIG_DIR/rules/rules.json"
|
|
64
|
+
echo " ✅ rules.json → global config"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
echo "[opencode-kit] ✅ Global config initialized"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Detect if the opencode-kit plugin is active
|
|
71
|
+
is_plugin_active() {
|
|
72
|
+
# Check if .opencode/plugins/opencode-kit.js exists (sign of plugin mode)
|
|
73
|
+
[ -f ".opencode/plugins/opencode-kit.js" ] && return 0
|
|
74
|
+
# Check if ~/.config/opencode-kit exists (sign of global config)
|
|
75
|
+
[ -d "$GLOBAL_CONFIG_DIR" ] && return 0
|
|
76
|
+
return 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Export functions
|
|
80
|
+
export -f resolve_config init_global_config is_plugin_active
|
|
81
|
+
export GLOBAL_CONFIG_DIR
|
package/src/init.sh
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# opencode-kit init — scaffold orchestration framework into target project
|
|
3
|
+
# Usage: npx opencode-kit init [--force]
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
. "$SCRIPT_DIR/platform.sh"
|
|
8
|
+
. "$SCRIPT_DIR/global-config.sh"
|
|
9
|
+
KIT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
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
|
+
FORCE="${1:-}"
|
|
18
|
+
TARGET_DIR="${PWD}"
|
|
19
|
+
TIMESTAMP=$(date +%Y%m%d%H%M%S)
|
|
20
|
+
|
|
21
|
+
echo "[opencode-kit] 🚀 Initializing orchestration framework in $TARGET_DIR"
|
|
22
|
+
|
|
23
|
+
# --- Dependency check ---
|
|
24
|
+
echo ""
|
|
25
|
+
echo "[opencode-kit] Checking dependencies..."
|
|
26
|
+
|
|
27
|
+
deps_ok=0
|
|
28
|
+
for cmd in git node; do
|
|
29
|
+
if command -v "$cmd" &>/dev/null; then
|
|
30
|
+
echo " ✅ $cmd: $(command -v $cmd)"
|
|
31
|
+
else
|
|
32
|
+
echo " ❌ $cmd: NOT FOUND — install $cmd first"
|
|
33
|
+
deps_ok=1
|
|
34
|
+
fi
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
# Check lean-ctx via MCP key (soft check — warn, don't block)
|
|
38
|
+
if command -v lean-ctx &>/dev/null; then
|
|
39
|
+
echo " ✅ lean-ctx available"
|
|
40
|
+
else
|
|
41
|
+
echo " ⚠️ lean-ctx not detected — ensure it's configured in MCP"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if [ "$deps_ok" -eq 1 ]; then
|
|
45
|
+
echo -e "${RED}❌ Missing dependencies. Install them and retry.${NC}"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# --- Git check ---
|
|
50
|
+
if ! git rev-parse --git-dir &>/dev/null; then
|
|
51
|
+
echo ""
|
|
52
|
+
echo "[opencode-kit] Not a git repository. Initializing..."
|
|
53
|
+
git init
|
|
54
|
+
echo " ✅ git initialized"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# --- Detect plugin mode ---
|
|
58
|
+
PLUGIN_MODE=false
|
|
59
|
+
if is_plugin_active; then
|
|
60
|
+
PLUGIN_MODE=true
|
|
61
|
+
echo ""
|
|
62
|
+
echo -e "${CYAN}[opencode-kit] Plugin detected — scaffolding project data only${NC}"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# --- Handle existing .opencode/ ---
|
|
66
|
+
if [ -d ".opencode" ]; then
|
|
67
|
+
if [ "$FORCE" = "--force" ]; then
|
|
68
|
+
BACKUP=".opencode.bak.$TIMESTAMP"
|
|
69
|
+
echo ""
|
|
70
|
+
echo -e "${YELLOW}⚠️ --force: Backing up existing .opencode/ to $BACKUP${NC}"
|
|
71
|
+
cp -r ".opencode" "$BACKUP"
|
|
72
|
+
rm -rf ".opencode"
|
|
73
|
+
echo " ✅ Backed up to $BACKUP"
|
|
74
|
+
else
|
|
75
|
+
echo ""
|
|
76
|
+
echo -e "${YELLOW}⚠️ .opencode/ already exists. Use --force to overwrite (backup + clean scaffold).${NC}"
|
|
77
|
+
echo " Missing files will be added. Existing files will NOT be overwritten."
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# --- Scaffold directories ---
|
|
82
|
+
mkdir -p .opencode/orchestration .opencode/rules .opencode/agents .opencode/src
|
|
83
|
+
|
|
84
|
+
# --- Copy templates ---
|
|
85
|
+
echo ""
|
|
86
|
+
echo "[opencode-kit] Scaffolding files..."
|
|
87
|
+
|
|
88
|
+
cp "$KIT_DIR/templates/contract.json" .opencode/orchestration/contract.json
|
|
89
|
+
echo " ✅ contract.json"
|
|
90
|
+
|
|
91
|
+
cp "$KIT_DIR/templates/superpowers-contract.json" .opencode/templates/superpowers-contract.json
|
|
92
|
+
echo " ✅ superpowers-contract.json"
|
|
93
|
+
|
|
94
|
+
cp "$KIT_DIR/rules/rules.json" .opencode/rules/rules.json
|
|
95
|
+
echo " ✅ rules.json"
|
|
96
|
+
|
|
97
|
+
cp "$KIT_DIR/src/verify.sh" .opencode/src/verify.sh
|
|
98
|
+
chmod +x .opencode/src/verify.sh
|
|
99
|
+
echo " ✅ verify.sh (executable)"
|
|
100
|
+
|
|
101
|
+
cp "$KIT_DIR/src/platform.sh" .opencode/src/platform.sh
|
|
102
|
+
chmod +x .opencode/src/platform.sh
|
|
103
|
+
echo " ✅ platform.sh (executable)"
|
|
104
|
+
|
|
105
|
+
# Plugin-specific: scripts that exist locally for CLI access
|
|
106
|
+
if [ "$PLUGIN_MODE" = false ]; then
|
|
107
|
+
# Non-plugin mode: copy all shell scripts
|
|
108
|
+
cp "$KIT_DIR/rules/validation.sh" .opencode/rules/validation.sh
|
|
109
|
+
chmod +x .opencode/rules/validation.sh
|
|
110
|
+
echo " ✅ rules/validation.sh"
|
|
111
|
+
|
|
112
|
+
cp "$KIT_DIR/src/preflight.sh" .opencode/src/preflight.sh
|
|
113
|
+
chmod +x .opencode/src/preflight.sh
|
|
114
|
+
echo " ✅ preflight.sh (executable)"
|
|
115
|
+
|
|
116
|
+
cp "$KIT_DIR/src/postflight.sh" .opencode/src/postflight.sh
|
|
117
|
+
chmod +x .opencode/src/postflight.sh
|
|
118
|
+
echo " ✅ postflight.sh (executable)"
|
|
119
|
+
|
|
120
|
+
cp "$KIT_DIR/src/update.sh" .opencode/src/update.sh
|
|
121
|
+
chmod +x .opencode/src/update.sh
|
|
122
|
+
echo " ✅ update.sh (executable)"
|
|
123
|
+
|
|
124
|
+
cp "$KIT_DIR/src/adr.sh" .opencode/src/adr.sh
|
|
125
|
+
chmod +x .opencode/src/adr.sh
|
|
126
|
+
echo " ✅ adr.sh (executable)"
|
|
127
|
+
|
|
128
|
+
cp "$KIT_DIR/src/telemetry.sh" .opencode/src/telemetry.sh
|
|
129
|
+
chmod +x .opencode/src/telemetry.sh
|
|
130
|
+
echo " ✅ telemetry.sh (executable)"
|
|
131
|
+
|
|
132
|
+
# --- Copy agent templates (pre-flight gates) ---
|
|
133
|
+
for agent in orchestrator planner task-manager code-reviewer learner fixer; do
|
|
134
|
+
if [ -f "$KIT_DIR/templates/agents/$agent.md" ]; then
|
|
135
|
+
cp "$KIT_DIR/templates/agents/$agent.md" ".opencode/agents/$agent.md"
|
|
136
|
+
echo " ✅ agents/$agent.md"
|
|
137
|
+
fi
|
|
138
|
+
done
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# --- Git ignore .opencode/src (scripts are project-specific) ---
|
|
142
|
+
if [ -f ".gitignore" ]; then
|
|
143
|
+
if ! grep -q ".opencode/src" .gitignore 2>/dev/null; then
|
|
144
|
+
echo ".opencode/src/" >> .gitignore
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# --- Verify ---
|
|
149
|
+
echo ""
|
|
150
|
+
echo "[opencode-kit] Running verification..."
|
|
151
|
+
if "$KIT_DIR/src/verify.sh"; then
|
|
152
|
+
echo ""
|
|
153
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
154
|
+
echo -e "${GREEN} ✅ opencode-kit v0.5.0 initialized${NC}"
|
|
155
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
156
|
+
echo ""
|
|
157
|
+
echo " Next steps:"
|
|
158
|
+
echo " 1. Set GOAL & SCOPE in .opencode/orchestration/contract.json"
|
|
159
|
+
echo " 2. Set your project rules in .opencode/rules/rules.json"
|
|
160
|
+
echo " 3. Read AGENTS.md for writing conventions"
|
|
161
|
+
echo " 4. Start with: Load contract → Plan → Execute → Review"
|
|
162
|
+
else
|
|
163
|
+
echo -e "${RED}❌ Verification failed. Check errors above.${NC}"
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
package/src/platform.sh
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
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
|
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
. "$SCRIPT_DIR/platform.sh"
|
|
8
|
+
|
|
9
|
+
CONTRACT_KEY="orchestration-contract"
|
|
10
|
+
CONTRACT_FILE=".opencode/orchestration/contract.json"
|
|
11
|
+
STATE_FILE="STATE.md"
|
|
12
|
+
TELEMETRY_DIR=".opencode/telemetry"
|
|
13
|
+
START_TIME_FILE=".opencode/telemetry/.phase_start"
|
|
14
|
+
|
|
15
|
+
echo "[opencode-kit] Post-flight: persisting state..."
|
|
16
|
+
|
|
17
|
+
# --- Telemetry: record phase completion ---
|
|
18
|
+
mkdir -p "$TELEMETRY_DIR"
|
|
19
|
+
PHASE_START=$(cat "$START_TIME_FILE" 2>/dev/null || echo "")
|
|
20
|
+
if [ -n "$PHASE_START" ]; then
|
|
21
|
+
PHASE_ELAPSED=$(( $(date +%s) - PHASE_START ))
|
|
22
|
+
# Read current state from contract
|
|
23
|
+
if [ -f "$CONTRACT_FILE" ]; then
|
|
24
|
+
CURRENT_STATE=$($PYTHON_CMD -c "
|
|
25
|
+
import json
|
|
26
|
+
with open('$CONTRACT_FILE') as f: d=json.load(f)
|
|
27
|
+
print(d.get('state','UNKNOWN'))
|
|
28
|
+
" 2>/dev/null || echo "UNKNOWN")
|
|
29
|
+
PREV_STATE=""
|
|
30
|
+
[ -f "$TELEMETRY_DIR/phases.jsonl" ] && PREV_STATE=$(tail -1 "$TELEMETRY_DIR/phases.jsonl" 2>/dev/null | $PYTHON_CMD -c "import sys,json; print(json.load(sys.stdin).get('to','INIT'))" 2>/dev/null || echo "INIT")
|
|
31
|
+
echo "{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"from\":\"$PREV_STATE\",\"to\":\"$CURRENT_STATE\",\"elapsed_ms\":$((PHASE_ELAPSED * 1000))}" >> "$TELEMETRY_DIR/phases.jsonl"
|
|
32
|
+
echo " 📊 Telemetry: $PREV_STATE → $CURRENT_STATE (${PHASE_ELAPSED}s)"
|
|
33
|
+
fi
|
|
34
|
+
rm -f "$START_TIME_FILE"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# --- Step 1: Re-read contract from lean-ctx (the source of truth) ---
|
|
38
|
+
CURRENT_CONTRACT=$(lean-ctx ctx_knowledge recall --query "$CONTRACT_KEY" 2>/dev/null || cat "$CONTRACT_FILE")
|
|
39
|
+
|
|
40
|
+
# --- Step 2: Write back to lean-ctx ---
|
|
41
|
+
lean-ctx ctx_knowledge remember \
|
|
42
|
+
category architecture \
|
|
43
|
+
key "$CONTRACT_KEY" \
|
|
44
|
+
value "$CURRENT_CONTRACT" 2>/dev/null && \
|
|
45
|
+
echo " ✅ Contract persisted to lean-ctx" || \
|
|
46
|
+
echo " ⚠️ lean-ctx persist failed"
|
|
47
|
+
|
|
48
|
+
# --- Step 3: Sync STATE.md ---
|
|
49
|
+
mkdir -p "$(dirname "$STATE_FILE")"
|
|
50
|
+
if [ -f "$CONTRACT_FILE" ]; then
|
|
51
|
+
STATE=$(echo "$CURRENT_CONTRACT" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); print(d.get('state','UNKNOWN'))" 2>/dev/null || echo "UNKNOWN")
|
|
52
|
+
PHASE=$(echo "$CURRENT_CONTRACT" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); r=d.get('retry',{}); print(r.get('current_phase','none'))" 2>/dev/null || echo "none")
|
|
53
|
+
SCORE=$(echo "$CURRENT_CONTRACT" | $PYTHON_CMD -c "import sys,json; d=json.load(sys.stdin); s=d.get('score',{}); print(s.get('combined','?'))" 2>/dev/null || echo "?")
|
|
54
|
+
echo " 📝 Contract state: $STATE (phase: $PHASE, score: $SCORE)"
|
|
55
|
+
|
|
56
|
+
# Create or update STATE.md with current focus
|
|
57
|
+
cat > "$STATE_FILE" << STATEMD
|
|
58
|
+
# Project State
|
|
59
|
+
|
|
60
|
+
## Current Focus
|
|
61
|
+
Agent orchestration — $STATE (phase: ${PHASE:-none}). Score: $SCORE.
|
|
62
|
+
|
|
63
|
+
## Known Blockers
|
|
64
|
+
$(echo "$CURRENT_CONTRACT" | $PYTHON_CMD -c "
|
|
65
|
+
import sys,json
|
|
66
|
+
d=json.load(sys.stdin)
|
|
67
|
+
r=d.get('retry',{})
|
|
68
|
+
issues=r.get('issues',[])
|
|
69
|
+
if issues:
|
|
70
|
+
for i in issues: print(f'- {i}')
|
|
71
|
+
else:
|
|
72
|
+
print('None')
|
|
73
|
+
" 2>/dev/null || echo "None")
|
|
74
|
+
|
|
75
|
+
## Active Decisions
|
|
76
|
+
$(echo "$CURRENT_CONTRACT" | $PYTHON_CMD -c "
|
|
77
|
+
import sys,json
|
|
78
|
+
d=json.load(sys.stdin)
|
|
79
|
+
log=d.get('decisions',{}).get('adr_log',[])
|
|
80
|
+
if log:
|
|
81
|
+
for entry in log[-3:]:
|
|
82
|
+
print(f'- {entry.get(\"id\",\"?\")}: {entry.get(\"title\",\"\")}')
|
|
83
|
+
else:
|
|
84
|
+
print('No ADRs recorded')
|
|
85
|
+
" 2>/dev/null || echo "None")
|
|
86
|
+
|
|
87
|
+
## Recent Changes
|
|
88
|
+
- Last state transition: $(echo "$CURRENT_CONTRACT" | $PYTHON_CMD -c "
|
|
89
|
+
import sys,json
|
|
90
|
+
d=json.load(sys.stdin)
|
|
91
|
+
phases=d.get('metrics',{}).get('phases_completed',[])
|
|
92
|
+
print(phases[-1] if phases else 'INIT')
|
|
93
|
+
" 2>/dev/null || echo "INIT")
|
|
94
|
+
STATEMD
|
|
95
|
+
echo " ✅ STATE.md synced"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# --- Step 4: Save ctx_session ---
|
|
99
|
+
lean-ctx ctx_session save 2>/dev/null && \
|
|
100
|
+
echo " ✅ Session saved" || \
|
|
101
|
+
echo " ⚠️ ctx_session save skipped (not available)"
|
|
102
|
+
|
|
103
|
+
# --- Step 5: Update telemetry summary ---
|
|
104
|
+
if [ -f "$TELEMETRY_DIR/phases.jsonl" ] && [ -n "$PYTHON_CMD" ]; then
|
|
105
|
+
$PYTHON_CMD -c "
|
|
106
|
+
import json
|
|
107
|
+
total_ms = 0
|
|
108
|
+
agents = set()
|
|
109
|
+
phases = []
|
|
110
|
+
with open('$TELEMETRY_DIR/phases.jsonl') as f:
|
|
111
|
+
for line in f:
|
|
112
|
+
line=line.strip()
|
|
113
|
+
if not line: continue
|
|
114
|
+
try:
|
|
115
|
+
entry=json.loads(line)
|
|
116
|
+
total_ms+=entry.get('elapsed_ms',0)
|
|
117
|
+
phases.append(entry.get('to',''))
|
|
118
|
+
except: pass
|
|
119
|
+
|
|
120
|
+
summary = {
|
|
121
|
+
'phases_completed': phases,
|
|
122
|
+
'total_elapsed_ms': total_ms,
|
|
123
|
+
'total_elapsed_s': round(total_ms/1000, 1),
|
|
124
|
+
'updated_at': '$(date -u +%Y-%m-%dT%H:%M:%SZ)'
|
|
125
|
+
}
|
|
126
|
+
with open('$TELEMETRY_DIR/summary.json', 'w') as f:
|
|
127
|
+
json.dump(summary, f, indent=2)
|
|
128
|
+
print(f' 📈 Telemetry summary: {len(phases)} phases, {total_ms/1000:.0f}s total')
|
|
129
|
+
" 2>/dev/null || true
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
echo "[opencode-kit] ✅ Post-flight complete."
|