@forloop-cc/forloop-opencode-plugin-planner 2.2.1
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/CHANGELOG.md +225 -0
- package/CODE_OF_CONDUCT.md +77 -0
- package/CONTRIBUTING.md +176 -0
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/SECURITY.md +69 -0
- package/docs/CONFIGURATION.md +283 -0
- package/docs/DEVELOPER-VS-USER.md +129 -0
- package/docs/IMPROVED-AUTHENTICATION.md +317 -0
- package/docs/INSTALLATION-SCRIPTS.md +304 -0
- package/docs/PLANNER-AGENT-IMPROVEMENTS.md +88 -0
- package/docs/PLUGIN-ARCHITECTURE.md +308 -0
- package/docs/QUICKSTART-NON-DEV.md +94 -0
- package/docs/README-FOR-HUMANS-AND-AI.md +109 -0
- package/docs/README.md +70 -0
- package/docs/SKILLS-GAP-ANALYSIS.md +157 -0
- package/docs/USER-INTERACTION-MODEL.md +106 -0
- package/docs/file-management/2026-04-12-forloop-aivy-documents-sync.md +395 -0
- package/docs/file-management/FILE_UPLOAD_ENHANCEMENT.md +431 -0
- package/docs/file-management/FILE_UPLOAD_FLOW.md +300 -0
- package/docs/images/forloop-opencode-flow.png +0 -0
- package/docs/images/forloop-opencode-header.png +0 -0
- package/docs/superpowers/plans/2026-05-03-sprint-isolated-directory-structure.md +1450 -0
- package/docs/superpowers/plans/2026-05-29-install-system.md +850 -0
- package/docs/superpowers/specs/2026-05-03-sprint-isolated-directory-structure-design.md +149 -0
- package/docs/superpowers/specs/2026-05-19-opencode-message-recording-design.md +468 -0
- package/docs/superpowers/specs/2026-05-29-install-system-design.md +135 -0
- package/install.sh +317 -0
- package/package.json +87 -0
- package/plugins/capabilities/api-client.ts +556 -0
- package/plugins/capabilities/auth.ts +120 -0
- package/plugins/capabilities/config.ts +195 -0
- package/plugins/capabilities/context-resolver.ts +106 -0
- package/plugins/capabilities/token-storage.ts +98 -0
- package/plugins/forloop-plugin.ts +186 -0
- package/plugins/hooks/message-recorder.ts +258 -0
- package/plugins/hooks/shell-env.ts +62 -0
- package/plugins/tools/agent-sprint-tools.ts +100 -0
- package/plugins/tools/agent-suggestion-tools.ts +275 -0
- package/plugins/tools/agent-tools.ts +114 -0
- package/plugins/tools/ai-trigger-tools.ts +79 -0
- package/plugins/tools/developer-status-tool.ts +97 -0
- package/plugins/tools/file-tools.ts +240 -0
- package/plugins/tools/folder-tools.ts +75 -0
- package/plugins/tools/organization-tools.ts +213 -0
- package/plugins/tools/schedule-tools.ts +173 -0
- package/plugins/tools/sprint-tools.ts +287 -0
- package/plugins/tools/story-tools.ts +320 -0
- package/plugins/tools/sync-tools.ts +509 -0
- package/plugins/tools/template-tools.ts +170 -0
- package/plugins/tools/user-tools.ts +146 -0
- package/scripts/migrate-tokens.js +90 -0
- package/scripts/use-prod.sh +11 -0
- package/tsconfig.json +17 -0
- package/update.sh +172 -0
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
# Install System Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Build curl-pipeable `install.sh` and `update.sh` scripts with npm+git support, local+global install, and jq-based config merging that preserves existing opencode configuration.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Two standalone bash scripts at repo root. `install.sh` handles first-time setup (git clone or npm install, dep checks, config merge, token setup). `update.sh` handles upgrades (auto-detect install, git pull or npm update). Both are curl-pipeable from GitHub raw URLs. `package.json` updated with `files` field for npm publishing.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Bash 3.2+ (macOS/Linux/Windows Git Bash), jq, git, npm, curl
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Structure
|
|
14
|
+
|
|
15
|
+
| File | Action | Responsibility |
|
|
16
|
+
|------|--------|---------------|
|
|
17
|
+
| `install.sh` | Rewrite | One-liner installer — dep checks, git clone/npm install, config merge, token setup |
|
|
18
|
+
| `update.sh` | Rewrite | Auto-detect install location, git pull or npm update, show changelog |
|
|
19
|
+
| `package.json` | Modify | Add `files` field for npm publishing |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### Task 1: Rewrite `install.sh`
|
|
24
|
+
|
|
25
|
+
**Files:**
|
|
26
|
+
- Rewrite: `forloop-opencode-plugin-planner/install.sh`
|
|
27
|
+
- Read reference: `forloop-opencode-plugin-planner/plugins/forloop-plugin.ts` (to verify entry point path)
|
|
28
|
+
|
|
29
|
+
- [ ] **Step 1: Write the install.sh script**
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
#!/usr/bin/env bash
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
|
|
35
|
+
# ForLoop Plugin Installer
|
|
36
|
+
# https://github.com/forloop-cc/forloop-opencode-plugin-planner
|
|
37
|
+
#
|
|
38
|
+
# Usage:
|
|
39
|
+
# curl -fsSL https://raw.githubusercontent.com/forloop-cc/forloop-opencode-plugin-planner/main/install.sh | bash
|
|
40
|
+
# curl -fsSL ... | bash -s -- --global
|
|
41
|
+
# bash install.sh --local --force
|
|
42
|
+
# bash install.sh --npm --global
|
|
43
|
+
|
|
44
|
+
# ── Platform & colors ────────────────────────────────────────────────────────
|
|
45
|
+
PLATFORM="$(uname -s)"
|
|
46
|
+
case "$PLATFORM" in
|
|
47
|
+
Darwin*) PLATFORM_LABEL="macOS";;
|
|
48
|
+
Linux*) PLATFORM_LABEL="Linux";;
|
|
49
|
+
CYGWIN*|MINGW*|MSYS*) PLATFORM_LABEL="Windows";;
|
|
50
|
+
*) PLATFORM_LABEL="Unknown";;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
if [ "$PLATFORM_LABEL" = "Windows" ] && [ -z "$WT_SESSION" ] && [ -z "$ConEmuPID" ]; then
|
|
54
|
+
RED=''; GREEN=''; YELLOW=''; BLUE=''; CYAN=''; BOLD=''; NC=''
|
|
55
|
+
else
|
|
56
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
|
57
|
+
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# ── Configuration ────────────────────────────────────────────────────────────
|
|
61
|
+
REPO="https://github.com/forloop-cc/forloop-opencode-plugin-planner.git"
|
|
62
|
+
RAW_URL="https://raw.githubusercontent.com/forloop-cc/forloop-opencode-plugin-planner/main"
|
|
63
|
+
BRANCH="${FORLOOP_BRANCH:-main}"
|
|
64
|
+
PLUGIN_DIRNAME="forloop-planner"
|
|
65
|
+
GLOBAL_INSTALL_DIR="$HOME/.config/opencode/plugins/$PLUGIN_DIRNAME"
|
|
66
|
+
LOCAL_INSTALL_DIR=".opencode/plugins/$PLUGIN_DIRNAME"
|
|
67
|
+
ENTRY_POINT="plugins/forloop-plugin.ts"
|
|
68
|
+
|
|
69
|
+
INSTALL_TYPE="" # global | local | custom
|
|
70
|
+
INSTALL_DIR="" # resolved path
|
|
71
|
+
USE_NPM=false
|
|
72
|
+
FORCE=false
|
|
73
|
+
NON_INTERACTIVE=false
|
|
74
|
+
|
|
75
|
+
print_header() { echo -e "${BLUE}${BOLD}╔══════════════════════════════════════════╗\n║ ForLoop Plugin Installer ║\n╚══════════════════════════════════════════╝${NC}\n"; }
|
|
76
|
+
print_success() { echo -e "${GREEN}✓${NC} $1"; }
|
|
77
|
+
print_info() { echo -e "${BLUE}ℹ${NC} $1"; }
|
|
78
|
+
print_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
|
79
|
+
print_error() { echo -e "${RED}✗${NC} $1" >&2; }
|
|
80
|
+
print_step() { echo -e "\n${CYAN}${BOLD}▶${NC} $1\n"; }
|
|
81
|
+
|
|
82
|
+
show_help() {
|
|
83
|
+
cat << EOF
|
|
84
|
+
Usage: install.sh [OPTIONS]
|
|
85
|
+
|
|
86
|
+
Options:
|
|
87
|
+
-g, --global Install globally (~/.config/opencode/plugins/)
|
|
88
|
+
-l, --local Install locally (./.opencode/plugins/)
|
|
89
|
+
-d, --dir PATH Custom install directory
|
|
90
|
+
-n, --npm Use npm install instead of git clone
|
|
91
|
+
-b, --branch NAME Clone a specific branch (default: $BRANCH)
|
|
92
|
+
-f, --force Overwrite existing installation
|
|
93
|
+
-h, --help Show this help
|
|
94
|
+
|
|
95
|
+
Examples:
|
|
96
|
+
curl -fsSL .../install.sh | bash # Interactive
|
|
97
|
+
curl -fsSL .../install.sh | bash -s -- -g # Global, non-interactive
|
|
98
|
+
bash install.sh --local --force # Local, force overwrite
|
|
99
|
+
bash install.sh --npm --global # npm install, global
|
|
100
|
+
EOF
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ── Dependency checks ────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
check_git() {
|
|
106
|
+
if ! command -v git &>/dev/null; then
|
|
107
|
+
print_error "git is required but not installed"
|
|
108
|
+
case "$PLATFORM_LABEL" in
|
|
109
|
+
macOS) echo " brew install git" ;;
|
|
110
|
+
Linux) echo " sudo apt-get install git (Ubuntu/Debian)"
|
|
111
|
+
echo " sudo dnf install git (Fedora/RHEL)" ;;
|
|
112
|
+
Windows) echo " https://git-scm.com/download/win" ;;
|
|
113
|
+
esac
|
|
114
|
+
return 1
|
|
115
|
+
fi
|
|
116
|
+
return 0
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
check_node() {
|
|
120
|
+
if ! command -v node &>/dev/null; then
|
|
121
|
+
print_warning "Node.js not found (needed for npm install)"
|
|
122
|
+
case "$PLATFORM_LABEL" in
|
|
123
|
+
macOS) echo " brew install node" ;;
|
|
124
|
+
Linux) echo " curl -fsSL https://deb.nodesource.com/setup_20.x | bash -"
|
|
125
|
+
echo " sudo apt-get install -y nodejs" ;;
|
|
126
|
+
Windows) echo " https://nodejs.org" ;;
|
|
127
|
+
esac
|
|
128
|
+
if [ "$NON_INTERACTIVE" = true ]; then
|
|
129
|
+
print_info "Continuing anyway (npm install will be attempted)..."
|
|
130
|
+
else
|
|
131
|
+
read -p "Continue anyway? [y/N] " -r; echo
|
|
132
|
+
[[ ! $REPLY =~ ^[Yy]$ ]] && return 1
|
|
133
|
+
fi
|
|
134
|
+
fi
|
|
135
|
+
return 0
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
check_jq() {
|
|
139
|
+
if ! command -v jq &>/dev/null; then
|
|
140
|
+
print_error "jq is required for config merging"
|
|
141
|
+
case "$PLATFORM_LABEL" in
|
|
142
|
+
macOS) echo " brew install jq" ;;
|
|
143
|
+
Linux) echo " sudo apt-get install jq (Ubuntu/Debian)"
|
|
144
|
+
echo " sudo dnf install jq (Fedora/RHEL)" ;;
|
|
145
|
+
Windows) echo " scoop install jq or https://stedolan.github.io/jq/download/" ;;
|
|
146
|
+
esac
|
|
147
|
+
return 1
|
|
148
|
+
fi
|
|
149
|
+
return 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
check_opencode() {
|
|
153
|
+
if command -v opencode &>/dev/null; then
|
|
154
|
+
print_success "OpenCode CLI found: $(which opencode)"
|
|
155
|
+
else
|
|
156
|
+
print_warning "OpenCode CLI not found"
|
|
157
|
+
echo " Install: curl -fsSL https://opencode.ai/install.sh | bash"
|
|
158
|
+
if [ "$NON_INTERACTIVE" != true ]; then
|
|
159
|
+
read -p "Continue anyway? [y/N] " -r; echo
|
|
160
|
+
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
check_all_deps() {
|
|
166
|
+
print_step "Checking dependencies..."
|
|
167
|
+
local missing=0
|
|
168
|
+
check_git || true # always check git
|
|
169
|
+
[ "$USE_NPM" != true ] && { check_git || missing=$((missing + 1)); }
|
|
170
|
+
check_node || missing=$((missing + 1))
|
|
171
|
+
check_jq || missing=$((missing + 1))
|
|
172
|
+
if [ "$missing" -gt 0 ]; then
|
|
173
|
+
print_error "Missing $missing required dependencies. Install them and try again."
|
|
174
|
+
exit 1
|
|
175
|
+
fi
|
|
176
|
+
print_success "All dependencies found"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# ── Install location ─────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
resolve_install_dir() {
|
|
182
|
+
case "$INSTALL_TYPE" in
|
|
183
|
+
global) INSTALL_DIR="$GLOBAL_INSTALL_DIR" ;;
|
|
184
|
+
local) INSTALL_DIR="$LOCAL_INSTALL_DIR" ;;
|
|
185
|
+
custom) ;; # INSTALL_DIR already set by --dir flag
|
|
186
|
+
esac
|
|
187
|
+
|
|
188
|
+
local parent_dir
|
|
189
|
+
parent_dir="$(dirname "$INSTALL_DIR")"
|
|
190
|
+
if [ ! -d "$parent_dir" ]; then
|
|
191
|
+
print_info "Creating parent directory: $parent_dir"
|
|
192
|
+
mkdir -p "$parent_dir"
|
|
193
|
+
fi
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
prompt_install_type() {
|
|
197
|
+
echo -e "${BOLD}Choose installation type:${NC}"
|
|
198
|
+
echo " 1) Global — available for all projects (~/.config/opencode/plugins/)"
|
|
199
|
+
echo " 2) Local — only for this project (./.opencode/plugins/)"
|
|
200
|
+
echo " 3) Cancel"
|
|
201
|
+
read -p "Select [1-3]: " -r; echo
|
|
202
|
+
case "$REPLY" in
|
|
203
|
+
1) INSTALL_TYPE="global" ;;
|
|
204
|
+
2) INSTALL_TYPE="local" ;;
|
|
205
|
+
*) print_info "Installation cancelled"; exit 0 ;;
|
|
206
|
+
esac
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# ── Installation ─────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
install_via_git() {
|
|
212
|
+
print_step "Cloning plugin repository..."
|
|
213
|
+
local clone_dir="$INSTALL_DIR"
|
|
214
|
+
|
|
215
|
+
if [ -d "$clone_dir" ] && [ "$FORCE" = true ]; then
|
|
216
|
+
print_warning "Removing existing installation..."
|
|
217
|
+
rm -rf "$clone_dir"
|
|
218
|
+
elif [ -d "$clone_dir" ]; then
|
|
219
|
+
print_warning "Plugin already installed at: $clone_dir"
|
|
220
|
+
if [ "$NON_INTERACTIVE" = true ]; then
|
|
221
|
+
print_error "Use --force to overwrite in non-interactive mode"
|
|
222
|
+
exit 1
|
|
223
|
+
fi
|
|
224
|
+
read -p "Overwrite? [y/N] " -r; echo
|
|
225
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
226
|
+
rm -rf "$clone_dir"
|
|
227
|
+
else
|
|
228
|
+
print_info "Installation cancelled"
|
|
229
|
+
exit 0
|
|
230
|
+
fi
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
mkdir -p "$clone_dir"
|
|
234
|
+
git clone --depth 1 --branch "$BRANCH" "$REPO" "$clone_dir" --quiet
|
|
235
|
+
|
|
236
|
+
print_success "Repository cloned to: $clone_dir"
|
|
237
|
+
|
|
238
|
+
print_step "Installing dependencies..."
|
|
239
|
+
cd "$clone_dir"
|
|
240
|
+
npm install --omit=dev --silent
|
|
241
|
+
print_success "Dependencies installed"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
install_via_npm() {
|
|
245
|
+
print_step "Installing via npm..."
|
|
246
|
+
local npm_package="@forloop/opencode-plugin-planner"
|
|
247
|
+
|
|
248
|
+
if npm install "$npm_package" --prefix "$INSTALL_DIR" --silent 2>/dev/null; then
|
|
249
|
+
print_success "npm package installed: $npm_package"
|
|
250
|
+
else
|
|
251
|
+
print_error "npm install failed. Is the package published?"
|
|
252
|
+
echo " Try: npm install $npm_package (manually)"
|
|
253
|
+
echo " Or run without --npm to use git clone instead"
|
|
254
|
+
exit 1
|
|
255
|
+
fi
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# ── Config merging ───────────────────────────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
get_config_file() {
|
|
261
|
+
if [ "$INSTALL_TYPE" = "global" ]; then
|
|
262
|
+
mkdir -p "$HOME/.config/opencode"
|
|
263
|
+
echo "$HOME/.config/opencode/config.json"
|
|
264
|
+
else
|
|
265
|
+
echo "$(pwd)/opencode.json"
|
|
266
|
+
fi
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
merge_plugin_into_config() {
|
|
270
|
+
local config_file
|
|
271
|
+
config_file="$(get_config_file)"
|
|
272
|
+
local plugin_path="file://$INSTALL_DIR"
|
|
273
|
+
|
|
274
|
+
print_step "Updating OpenCode configuration..."
|
|
275
|
+
|
|
276
|
+
if [ ! -f "$config_file" ]; then
|
|
277
|
+
echo "{}" > "$config_file"
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
local backup="${config_file}.backup.$(date +%Y%m%d%H%M%S)"
|
|
281
|
+
cp "$config_file" "$backup"
|
|
282
|
+
|
|
283
|
+
local tmp
|
|
284
|
+
tmp="$(mktemp)"
|
|
285
|
+
|
|
286
|
+
jq --arg p "$plugin_path" '
|
|
287
|
+
if .plugin then
|
|
288
|
+
.plugin += [$p] | .plugin |= unique
|
|
289
|
+
else
|
|
290
|
+
. + {"plugin": [$p]}
|
|
291
|
+
end
|
|
292
|
+
' "$config_file" > "$tmp"
|
|
293
|
+
|
|
294
|
+
if [ -s "$tmp" ]; then
|
|
295
|
+
mv "$tmp" "$config_file"
|
|
296
|
+
print_success "Configuration updated: $config_file"
|
|
297
|
+
print_info "Backup saved: $backup"
|
|
298
|
+
else
|
|
299
|
+
print_error "Config merge produced empty file — restoring backup"
|
|
300
|
+
mv "$backup" "$config_file"
|
|
301
|
+
rm -f "$tmp"
|
|
302
|
+
exit 1
|
|
303
|
+
fi
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
# ── Token setup ──────────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
setup_token() {
|
|
309
|
+
echo ""
|
|
310
|
+
print_step "Setting up API token..."
|
|
311
|
+
echo " Create a token at: https://forloop.cc/profile?tab=api-tokens"
|
|
312
|
+
echo " Scopes needed: sprint:read, sprint:write, story:read, story:write, agent:query, profile:read"
|
|
313
|
+
echo ""
|
|
314
|
+
|
|
315
|
+
if [ "$NON_INTERACTIVE" = true ]; then
|
|
316
|
+
print_info "Non-interactive mode — set your token later:"
|
|
317
|
+
echo " opencode run 'forloopTokenSet --token floop_YOUR_TOKEN'"
|
|
318
|
+
return
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
read -p "Have you created a token? [y/N] " -r; echo
|
|
322
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
323
|
+
print_info "Set it later: opencode run 'forloopTokenSet --token floop_YOUR_TOKEN'"
|
|
324
|
+
return
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
read -p "Enter your token: " -s TOKEN; echo
|
|
328
|
+
if [ -z "$TOKEN" ]; then
|
|
329
|
+
print_info "No token entered, skipping"
|
|
330
|
+
return
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
local token_dir="$HOME/.config/forloop"
|
|
334
|
+
mkdir -p "$token_dir"
|
|
335
|
+
cat > "$token_dir/tokens.json" << TOKENEOF
|
|
336
|
+
{
|
|
337
|
+
"version": "1.0",
|
|
338
|
+
"note": "ForLoop API tokens — do not share",
|
|
339
|
+
"default": "$TOKEN",
|
|
340
|
+
"lastUpdated": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")"
|
|
341
|
+
}
|
|
342
|
+
TOKENEOF
|
|
343
|
+
chmod 600 "$token_dir/tokens.json"
|
|
344
|
+
print_success "Token saved to: $token_dir/tokens.json"
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# ── Verification ─────────────────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
verify_installation() {
|
|
350
|
+
print_step "Verifying installation..."
|
|
351
|
+
local entry="$INSTALL_DIR/$ENTRY_POINT"
|
|
352
|
+
|
|
353
|
+
if [ -f "$entry" ]; then
|
|
354
|
+
print_success "Plugin verified: $entry"
|
|
355
|
+
else
|
|
356
|
+
print_error "Installation failed: entry point not found at $entry"
|
|
357
|
+
echo " Expected: $entry"
|
|
358
|
+
echo " Check the installation directory: $INSTALL_DIR"
|
|
359
|
+
exit 1
|
|
360
|
+
fi
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
show_summary() {
|
|
364
|
+
echo ""
|
|
365
|
+
echo -e "${GREEN}${BOLD}========================================${NC}"
|
|
366
|
+
echo -e "${GREEN}${BOLD} Installation Complete!${NC}"
|
|
367
|
+
echo -e "${GREEN}${BOLD}========================================${NC}"
|
|
368
|
+
echo ""
|
|
369
|
+
echo " Type: $INSTALL_TYPE"
|
|
370
|
+
echo " Location: $INSTALL_DIR"
|
|
371
|
+
echo ""
|
|
372
|
+
echo " Next steps:"
|
|
373
|
+
echo " 1. Start opencode: opencode"
|
|
374
|
+
echo " 2. List sprints: forloopSprintList"
|
|
375
|
+
echo " 3. Create a story: forloopStoryCreate --title 'My feature'"
|
|
376
|
+
echo ""
|
|
377
|
+
echo " Docs: https://github.com/forloop-cc/forloop-opencode-plugin-planner"
|
|
378
|
+
echo ""
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
# ── Main ─────────────────────────────────────────────────────────────────────
|
|
382
|
+
|
|
383
|
+
# Parse CLI flags
|
|
384
|
+
while [[ $# -gt 0 ]]; do
|
|
385
|
+
case "$1" in
|
|
386
|
+
-g|--global) INSTALL_TYPE="global"; shift ;;
|
|
387
|
+
-l|--local) INSTALL_TYPE="local"; shift ;;
|
|
388
|
+
-d|--dir)
|
|
389
|
+
[ -z "${2:-}" ] && { print_error "--dir requires a path"; exit 1; }
|
|
390
|
+
INSTALL_TYPE="custom"; INSTALL_DIR="$2"; shift 2 ;;
|
|
391
|
+
-n|--npm) USE_NPM=true; shift ;;
|
|
392
|
+
-b|--branch)
|
|
393
|
+
[ -z "${2:-}" ] && { print_error "--branch requires a name"; exit 1; }
|
|
394
|
+
BRANCH="$2"; shift 2 ;;
|
|
395
|
+
-f|--force) FORCE=true; shift ;;
|
|
396
|
+
-h|--help) show_help; exit 0 ;;
|
|
397
|
+
*) print_error "Unknown option: $1"; show_help; exit 1 ;;
|
|
398
|
+
esac
|
|
399
|
+
done
|
|
400
|
+
|
|
401
|
+
# Detect non-interactive mode (stdin is a pipe, not terminal)
|
|
402
|
+
if [ ! -t 0 ]; then
|
|
403
|
+
NON_INTERACTIVE=true
|
|
404
|
+
if [ -z "$INSTALL_TYPE" ]; then
|
|
405
|
+
print_info "Running in non-interactive mode, defaulting to local install"
|
|
406
|
+
INSTALL_TYPE="local"
|
|
407
|
+
fi
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
print_header
|
|
411
|
+
|
|
412
|
+
# Interactive: prompt for install type if not given
|
|
413
|
+
if [ -z "$INSTALL_TYPE" ] && [ "$NON_INTERACTIVE" != true ]; then
|
|
414
|
+
prompt_install_type
|
|
415
|
+
fi
|
|
416
|
+
[ -z "$INSTALL_TYPE" ] && { print_error "Installation type required"; exit 1; }
|
|
417
|
+
|
|
418
|
+
resolve_install_dir
|
|
419
|
+
check_all_deps
|
|
420
|
+
check_opencode
|
|
421
|
+
|
|
422
|
+
if [ "$USE_NPM" = true ]; then
|
|
423
|
+
install_via_npm
|
|
424
|
+
else
|
|
425
|
+
install_via_git
|
|
426
|
+
fi
|
|
427
|
+
|
|
428
|
+
merge_plugin_into_config
|
|
429
|
+
setup_token
|
|
430
|
+
verify_installation
|
|
431
|
+
show_summary
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
- [ ] **Step 2: Set executable permissions**
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
chmod +x forloop-opencode-plugin-planner/install.sh
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
- [ ] **Step 3: Dry-run test — check the script is valid bash**
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
bash -n forloop-opencode-plugin-planner/install.sh
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Expected: No syntax errors.
|
|
447
|
+
|
|
448
|
+
- [ ] **Step 4: Test --help flag**
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
bash forloop-opencode-plugin-planner/install.sh --help
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Expected: Help text printed with all flags listed.
|
|
455
|
+
|
|
456
|
+
- [ ] **Step 5: Commit**
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
git add forloop-opencode-plugin-planner/install.sh
|
|
460
|
+
git commit -m "feat: rewrite install.sh with npm/git support, jq config merge, non-interactive mode"
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
### Task 2: Rewrite `update.sh`
|
|
466
|
+
|
|
467
|
+
**Files:**
|
|
468
|
+
- Rewrite: `forloop-opencode-plugin-planner/update.sh`
|
|
469
|
+
|
|
470
|
+
- [ ] **Step 1: Write the update.sh script**
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
#!/usr/bin/env bash
|
|
474
|
+
set -euo pipefail
|
|
475
|
+
|
|
476
|
+
# ForLoop Plugin Updater
|
|
477
|
+
# https://github.com/forloop-cc/forloop-opencode-plugin-planner
|
|
478
|
+
#
|
|
479
|
+
# Usage:
|
|
480
|
+
# curl -fsSL https://raw.githubusercontent.com/forloop-cc/forloop-opencode-plugin-planner/main/update.sh | bash
|
|
481
|
+
# bash update.sh --force --changelog
|
|
482
|
+
# bash update.sh --dir ~/my-custom-install
|
|
483
|
+
|
|
484
|
+
# ── Colors ───────────────────────────────────────────────────────────────────
|
|
485
|
+
PLATFORM="$(uname -s)"
|
|
486
|
+
if [[ "$PLATFORM" =~ MINGW|MSYS|CYGWIN ]] && [ -z "$WT_SESSION" ] && [ -z "$ConEmuPID" ]; then
|
|
487
|
+
RED=''; GREEN=''; YELLOW=''; BLUE=''; CYAN=''; BOLD=''; NC=''
|
|
488
|
+
else
|
|
489
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
|
490
|
+
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
|
491
|
+
fi
|
|
492
|
+
|
|
493
|
+
# ── Configuration ────────────────────────────────────────────────────────────
|
|
494
|
+
PLUGIN_DIRNAME="forloop-planner"
|
|
495
|
+
GLOBAL_INSTALL_DIR="$HOME/.config/opencode/plugins/$PLUGIN_DIRNAME"
|
|
496
|
+
LOCAL_INSTALL_DIR=".opencode/plugins/$PLUGIN_DIRNAME"
|
|
497
|
+
|
|
498
|
+
PLUGIN_DIR=""
|
|
499
|
+
FORCE=false
|
|
500
|
+
SHOW_CHANGELOG=false
|
|
501
|
+
|
|
502
|
+
print_success() { echo -e "${GREEN}✓${NC} $1"; }
|
|
503
|
+
print_info() { echo -e "${BLUE}ℹ${NC} $1"; }
|
|
504
|
+
print_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
|
505
|
+
print_error() { echo -e "${RED}✗${NC} $1" >&2; }
|
|
506
|
+
print_step() { echo -e "\n${CYAN}${BOLD}▶${NC} $1\n"; }
|
|
507
|
+
|
|
508
|
+
show_help() {
|
|
509
|
+
cat << EOF
|
|
510
|
+
Usage: update.sh [OPTIONS]
|
|
511
|
+
|
|
512
|
+
Options:
|
|
513
|
+
-d, --dir PATH Update a specific install directory
|
|
514
|
+
-f, --force Pull even if already up to date
|
|
515
|
+
-c, --changelog Show recent changelog after update
|
|
516
|
+
-h, --help Show this help
|
|
517
|
+
|
|
518
|
+
Auto-detection order:
|
|
519
|
+
1. --dir flag
|
|
520
|
+
2. FORLOOP_PLUGIN_DIR env var
|
|
521
|
+
3. ./.opencode/plugins/forloop-planner/
|
|
522
|
+
4. ~/.config/opencode/plugins/forloop-planner/
|
|
523
|
+
EOF
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
# ── Auto-detection ───────────────────────────────────────────────────────────
|
|
527
|
+
|
|
528
|
+
find_installation() {
|
|
529
|
+
# 1. Check env var
|
|
530
|
+
if [ -n "${FORLOOP_PLUGIN_DIR:-}" ] && [ -d "$FORLOOP_PLUGIN_DIR/.git" ]; then
|
|
531
|
+
PLUGIN_DIR="$FORLOOP_PLUGIN_DIR"
|
|
532
|
+
print_info "Found via FORLOOP_PLUGIN_DIR: $PLUGIN_DIR"
|
|
533
|
+
return 0
|
|
534
|
+
fi
|
|
535
|
+
|
|
536
|
+
# 2. Check local
|
|
537
|
+
if [ -d "$LOCAL_INSTALL_DIR/.git" ]; then
|
|
538
|
+
PLUGIN_DIR="$LOCAL_INSTALL_DIR"
|
|
539
|
+
print_info "Found local install: $PLUGIN_DIR"
|
|
540
|
+
return 0
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
# 3. Check global
|
|
544
|
+
if [ -d "$GLOBAL_INSTALL_DIR/.git" ]; then
|
|
545
|
+
PLUGIN_DIR="$GLOBAL_INSTALL_DIR"
|
|
546
|
+
print_info "Found global install: $PLUGIN_DIR"
|
|
547
|
+
return 0
|
|
548
|
+
fi
|
|
549
|
+
|
|
550
|
+
print_error "No ForLoop plugin installation found"
|
|
551
|
+
echo ""
|
|
552
|
+
echo " Checked:"
|
|
553
|
+
echo " 1. FORLOOP_PLUGIN_DIR env var"
|
|
554
|
+
echo " 2. $LOCAL_INSTALL_DIR"
|
|
555
|
+
echo " 3. $GLOBAL_INSTALL_DIR"
|
|
556
|
+
echo ""
|
|
557
|
+
echo " Specify location: bash update.sh --dir <path>"
|
|
558
|
+
echo " Or install first: curl -fsSL .../install.sh | bash"
|
|
559
|
+
return 1
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
# ── Update logic ─────────────────────────────────────────────────────────────
|
|
563
|
+
|
|
564
|
+
update_via_git() {
|
|
565
|
+
print_step "Checking for updates..."
|
|
566
|
+
cd "$PLUGIN_DIR"
|
|
567
|
+
|
|
568
|
+
local current
|
|
569
|
+
current="$(git rev-parse --short HEAD)"
|
|
570
|
+
|
|
571
|
+
# Ensure on main branch
|
|
572
|
+
local branch
|
|
573
|
+
branch="$(git rev-parse --abbrev-ref HEAD)"
|
|
574
|
+
if [ "$branch" != "main" ] && [ "$branch" != "master" ]; then
|
|
575
|
+
print_info "Switching from '$branch' to main"
|
|
576
|
+
git checkout main --quiet 2>/dev/null || git checkout master --quiet 2>/dev/null || true
|
|
577
|
+
fi
|
|
578
|
+
|
|
579
|
+
git fetch origin --quiet
|
|
580
|
+
|
|
581
|
+
local remote
|
|
582
|
+
remote="$(git rev-parse --short origin/main 2>/dev/null || git rev-parse --short origin/master 2>/dev/null)"
|
|
583
|
+
|
|
584
|
+
if [ "$current" = "$remote" ] && [ "$FORCE" != true ]; then
|
|
585
|
+
print_success "Already up to date ($current)"
|
|
586
|
+
return 0
|
|
587
|
+
fi
|
|
588
|
+
|
|
589
|
+
print_info "Updating: $current → $remote"
|
|
590
|
+
git pull --quiet origin main 2>/dev/null || git pull --quiet origin master 2>/dev/null
|
|
591
|
+
|
|
592
|
+
# Reinstall dependencies
|
|
593
|
+
print_step "Installing dependencies..."
|
|
594
|
+
npm install --omit=dev --silent
|
|
595
|
+
print_success "Dependencies updated"
|
|
596
|
+
|
|
597
|
+
local new_ver
|
|
598
|
+
new_ver="$(git rev-parse --short HEAD)"
|
|
599
|
+
print_success "Updated to: $new_ver"
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
update_via_npm() {
|
|
603
|
+
print_step "Updating via npm..."
|
|
604
|
+
cd "$PLUGIN_DIR"
|
|
605
|
+
|
|
606
|
+
local before
|
|
607
|
+
before="$(npm list @forloop/opencode-plugin-planner --depth=0 2>/dev/null || echo 'unknown')"
|
|
608
|
+
|
|
609
|
+
npm update @forloop/opencode-plugin-planner --silent
|
|
610
|
+
|
|
611
|
+
local after
|
|
612
|
+
after="$(npm list @forloop/opencode-plugin-planner --depth=0 2>/dev/null || echo 'updated')"
|
|
613
|
+
|
|
614
|
+
print_success "$after"
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
# ── Changelog ────────────────────────────────────────────────────────────────
|
|
618
|
+
|
|
619
|
+
show_recent_changelog() {
|
|
620
|
+
local changelog="$PLUGIN_DIR/CHANGELOG.md"
|
|
621
|
+
if [ -f "$changelog" ]; then
|
|
622
|
+
echo ""
|
|
623
|
+
print_info "Recent changes:"
|
|
624
|
+
echo "────────────────────────────────────────────"
|
|
625
|
+
head -60 "$changelog" | head -40
|
|
626
|
+
echo "────────────────────────────────────────────"
|
|
627
|
+
else
|
|
628
|
+
print_warning "No CHANGELOG.md found"
|
|
629
|
+
fi
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
# ── Verification ─────────────────────────────────────────────────────────────
|
|
633
|
+
|
|
634
|
+
verify_update() {
|
|
635
|
+
local entry="$PLUGIN_DIR/plugins/forloop-plugin.ts"
|
|
636
|
+
if [ -f "$entry" ]; then
|
|
637
|
+
print_success "Plugin verified: $entry"
|
|
638
|
+
else
|
|
639
|
+
print_error "Update may have failed — entry point not found: $entry"
|
|
640
|
+
exit 1
|
|
641
|
+
fi
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
# ── Main ─────────────────────────────────────────────────────────────────────
|
|
645
|
+
|
|
646
|
+
while [[ $# -gt 0 ]]; do
|
|
647
|
+
case "$1" in
|
|
648
|
+
-d|--dir)
|
|
649
|
+
[ -z "${2:-}" ] && { print_error "--dir requires a path"; exit 1; }
|
|
650
|
+
PLUGIN_DIR="$2"; shift 2 ;;
|
|
651
|
+
-f|--force) FORCE=true; shift ;;
|
|
652
|
+
-c|--changelog) SHOW_CHANGELOG=true; shift ;;
|
|
653
|
+
-h|--help) show_help; exit 0 ;;
|
|
654
|
+
*) print_error "Unknown option: $1"; show_help; exit 1 ;;
|
|
655
|
+
esac
|
|
656
|
+
done
|
|
657
|
+
|
|
658
|
+
echo ""
|
|
659
|
+
echo -e "${BLUE}${BOLD}╔══════════════════════════════════════════╗"
|
|
660
|
+
echo -e "║ ForLoop Plugin Updater ║"
|
|
661
|
+
echo -e "╚══════════════════════════════════════════╝${NC}"
|
|
662
|
+
echo ""
|
|
663
|
+
|
|
664
|
+
if [ -z "$PLUGIN_DIR" ]; then
|
|
665
|
+
find_installation || exit 1
|
|
666
|
+
fi
|
|
667
|
+
|
|
668
|
+
if [ ! -d "$PLUGIN_DIR" ]; then
|
|
669
|
+
print_error "Plugin directory not found: $PLUGIN_DIR"
|
|
670
|
+
exit 1
|
|
671
|
+
fi
|
|
672
|
+
|
|
673
|
+
# Detect install method
|
|
674
|
+
if [ -d "$PLUGIN_DIR/.git" ]; then
|
|
675
|
+
update_via_git
|
|
676
|
+
elif [ -f "$PLUGIN_DIR/package.json" ]; then
|
|
677
|
+
update_via_npm
|
|
678
|
+
else
|
|
679
|
+
print_error "Not a valid ForLoop plugin installation: $PLUGIN_DIR"
|
|
680
|
+
exit 1
|
|
681
|
+
fi
|
|
682
|
+
|
|
683
|
+
if [ "$SHOW_CHANGELOG" = true ]; then
|
|
684
|
+
show_recent_changelog
|
|
685
|
+
fi
|
|
686
|
+
|
|
687
|
+
verify_update
|
|
688
|
+
|
|
689
|
+
echo ""
|
|
690
|
+
print_success "Update complete!"
|
|
691
|
+
echo " Location: $PLUGIN_DIR"
|
|
692
|
+
echo ""
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
- [ ] **Step 2: Set executable permissions**
|
|
696
|
+
|
|
697
|
+
```bash
|
|
698
|
+
chmod +x forloop-opencode-plugin-planner/update.sh
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
- [ ] **Step 3: Syntax check**
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
bash -n forloop-opencode-plugin-planner/update.sh
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
Expected: No syntax errors.
|
|
708
|
+
|
|
709
|
+
- [ ] **Step 4: Test --help flag**
|
|
710
|
+
|
|
711
|
+
```bash
|
|
712
|
+
bash forloop-opencode-plugin-planner/update.sh --help
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
Expected: Help text with all flags and auto-detection order.
|
|
716
|
+
|
|
717
|
+
- [ ] **Step 5: Commit**
|
|
718
|
+
|
|
719
|
+
```bash
|
|
720
|
+
git add forloop-opencode-plugin-planner/update.sh
|
|
721
|
+
git commit -m "feat: rewrite update.sh with auto-detection, npm support, changelog display"
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
726
|
+
### Task 3: Update `package.json` for npm publishing
|
|
727
|
+
|
|
728
|
+
**Files:**
|
|
729
|
+
- Modify: `forloop-opencode-plugin-planner/package.json`
|
|
730
|
+
|
|
731
|
+
- [ ] **Step 1: Read current package.json to verify current state**
|
|
732
|
+
|
|
733
|
+
```bash
|
|
734
|
+
cat forloop-opencode-plugin-planner/package.json | jq '.name, .version, .main'
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
Expected: Shows current name, version, main entry.
|
|
738
|
+
|
|
739
|
+
- [ ] **Step 2: Add `files` field to package.json**
|
|
740
|
+
|
|
741
|
+
Location: After the `"license"` line, before `"repository"`. The `files` field ensures only distributable files are published to npm.
|
|
742
|
+
|
|
743
|
+
Edit `forloop-opencode-plugin-planner/package.json`, adding these fields:
|
|
744
|
+
|
|
745
|
+
```json
|
|
746
|
+
"main": "./plugins/forloop-plugin.ts",
|
|
747
|
+
"files": [
|
|
748
|
+
"plugins/",
|
|
749
|
+
"skills/",
|
|
750
|
+
"agents/",
|
|
751
|
+
"scripts/migrate-tokens.js",
|
|
752
|
+
"scripts/use-prod.sh",
|
|
753
|
+
"docs/",
|
|
754
|
+
"install.sh",
|
|
755
|
+
"update.sh",
|
|
756
|
+
"opencode.json",
|
|
757
|
+
"tsconfig.json",
|
|
758
|
+
"README.md",
|
|
759
|
+
"LICENSE",
|
|
760
|
+
"CHANGELOG.md",
|
|
761
|
+
"CODE_OF_CONDUCT.md",
|
|
762
|
+
"CONTRIBUTING.md",
|
|
763
|
+
"SECURITY.md"
|
|
764
|
+
],
|
|
765
|
+
"engines": {
|
|
766
|
+
"node": ">=18.0.0"
|
|
767
|
+
},
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
- [ ] **Step 3: Verify the files field with npm pack (dry run)**
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
cd forloop-opencode-plugin-planner && npm pack --dry-run 2>&1 | head -30
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
Expected: Shows only the files listed above. No test/, .github/, .gitignore, node_modules/, etc.
|
|
777
|
+
|
|
778
|
+
- [ ] **Step 4: Commit**
|
|
779
|
+
|
|
780
|
+
```bash
|
|
781
|
+
git add forloop-opencode-plugin-planner/package.json
|
|
782
|
+
git commit -m "feat: add files field for npm publishing"
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
---
|
|
786
|
+
|
|
787
|
+
### Task 4: End-to-End Verification
|
|
788
|
+
|
|
789
|
+
**Files:**
|
|
790
|
+
- No file changes — manual testing run
|
|
791
|
+
|
|
792
|
+
- [ ] **Step 1: Create a temporary test directory**
|
|
793
|
+
|
|
794
|
+
```bash
|
|
795
|
+
mkdir -p /tmp/forloop-install-test && cd /tmp/forloop-install-test
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
- [ ] **Step 2: Test local install (simulating curl-pipe)**
|
|
799
|
+
|
|
800
|
+
```bash
|
|
801
|
+
bash /Users/toshilam/Documents/projects/phase4-opencode-plugin-planner/forloop-opencode-plugin-planner/install.sh --local --force
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
Expected:
|
|
805
|
+
- Clones repo to `./.opencode/plugins/forloop-planner/`
|
|
806
|
+
- Runs `npm install --omit=dev`
|
|
807
|
+
- Creates `opencode.json` with plugin path
|
|
808
|
+
- Verifies `plugins/forloop-plugin.ts` exists
|
|
809
|
+
- Outputs success summary
|
|
810
|
+
|
|
811
|
+
- [ ] **Step 3: Verify the installed files**
|
|
812
|
+
|
|
813
|
+
```bash
|
|
814
|
+
ls .opencode/plugins/forloop-planner/plugins/forloop-plugin.ts && echo "EXISTS"
|
|
815
|
+
ls .opencode/plugins/forloop-planner/agents/forLoopPlanner.md && echo "EXISTS"
|
|
816
|
+
ls .opencode/plugins/forloop-planner/skills/ && echo "DIR"
|
|
817
|
+
ls .opencode/plugins/forloop-planner/node_modules/@opencode-ai/plugin && echo "DEPS"
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
Expected: All files and dependencies present.
|
|
821
|
+
|
|
822
|
+
- [ ] **Step 4: Verify config merge correctness**
|
|
823
|
+
|
|
824
|
+
```bash
|
|
825
|
+
cat opencode.json | jq '.plugin'
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
Expected: Contains `"file://./.opencode/plugins/forloop-planner"`.
|
|
829
|
+
|
|
830
|
+
- [ ] **Step 5: Test update (no changes expected)**
|
|
831
|
+
|
|
832
|
+
```bash
|
|
833
|
+
bash /Users/toshilam/Documents/projects/phase4-opencode-plugin-planner/forloop-opencode-plugin-planner/update.sh --dir ./.opencode/plugins/forloop-planner
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
Expected: "Already up to date" or similar.
|
|
837
|
+
|
|
838
|
+
- [ ] **Step 6: Test --force update**
|
|
839
|
+
|
|
840
|
+
```bash
|
|
841
|
+
bash /Users/toshilam/Documents/projects/phase4-opencode-plugin-planner/forloop-opencode-plugin-planner/update.sh --dir ./.opencode/plugins/forloop-planner --force
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
Expected: Runs git pull + npm install even if current.
|
|
845
|
+
|
|
846
|
+
- [ ] **Step 7: Clean up**
|
|
847
|
+
|
|
848
|
+
```bash
|
|
849
|
+
rm -rf /tmp/forloop-install-test
|
|
850
|
+
```
|