chore: public workspace with GitHub submodules
This commit is contained in:
commit
68548c0afe
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Local run artefacts of scripts/dev-host.sh
|
||||
.dev-host.log
|
||||
.dev-host.pid
|
||||
8
.gitmodules
vendored
Normal file
8
.gitmodules
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
[submodule "cloudcli"]
|
||||
path = cloudcli
|
||||
url = https://github.com/bvn13/claudecodeui.git
|
||||
branch = dev/taskwork
|
||||
[submodule "plugin-taskwork"]
|
||||
path = plugin-taskwork
|
||||
url = https://github.com/bvn13/cloudcli-plugin-taskwork.git
|
||||
branch = main
|
||||
138
README.md
Normal file
138
README.md
Normal file
@ -0,0 +1,138 @@
|
||||
# cloudcli-taskwork — workspace
|
||||
|
||||
Meta-repository for the **Task Work** plugin for CloudCLI UI (`siteboon/claudecodeui`).
|
||||
It holds the specification, the developer scripts and two submodules. It contains
|
||||
**no code of its own** — no `package.json`, no dependencies.
|
||||
|
||||
Specification: [`spec/SPEC-v3.md`](spec/SPEC-v3.md) (current), design input:
|
||||
[`spec/DESIGN-DOC.md`](spec/DESIGN-DOC.md).
|
||||
|
||||
## Repository map
|
||||
|
||||
| Path | Repository | Role |
|
||||
|---|---|---|
|
||||
| *(this repo)* | `gitea:ai-first/cloudcli-taskwork` | Workspace: spec, scripts, submodule pins |
|
||||
| `plugin-taskwork/` | `gitea:ai-first/cloudcli-plugin-taskwork` | **The product**, developed here; `manifest.json` lives in its root |
|
||||
| — | `github:bvn13/cloudcli-plugin-taskwork` | **Where users install the plugin from** — public, anonymous HTTPS clone |
|
||||
| `cloudcli/` | `github:bvn13/claudecodeui` | Fork of the host; source of the three upstream PRs |
|
||||
| — | `github:siteboon/claudecodeui` | Upstream: source of updates, target of the PRs |
|
||||
| — | `gitea:ai-first/cloudcli` | Pull mirror of the fork, **read-only** |
|
||||
|
||||
The plugin lives in two places on purpose: development and the submodule pin stay
|
||||
on Gitea, while the host installs it over anonymous HTTPS, which a private Gitea
|
||||
repository cannot serve. The GitHub copy is a **push mirror** — pushing to Gitea
|
||||
is enough, it syncs on its own. Do not add a GitHub remote to `plugin-taskwork/`
|
||||
and do not push there by hand.
|
||||
|
||||
> ⚠️ Never push to `gitea:ai-first/cloudcli`. It is a pull mirror — anything pushed
|
||||
> there is silently overwritten on the next sync. The `cloudcli/` submodule points
|
||||
> at the GitHub fork, and the mirror is deliberately not wired up as a remote.
|
||||
|
||||
## Getting started
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules ssh://git@gitea.bvn13.me:2221/ai-first/cloudcli-taskwork.git
|
||||
cd cloudcli-taskwork
|
||||
./scripts/bootstrap.sh
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
./scripts/dev-host.sh start # installs the built plugin and starts the host
|
||||
```
|
||||
|
||||
It prints the URL — `http://localhost:5183` by default. `stop`, `restart`,
|
||||
`status` and `logs` do what they say; `SERVER_PORT`/`VITE_PORT` override the
|
||||
ports.
|
||||
|
||||
The host runs against your **real** environment: the same `~/.claude` projects,
|
||||
the same `~/.cloudcli/auth.db`, the same `~/.claude-code-ui/plugins`. Nothing is
|
||||
sandboxed, so you see what a user would see. Ports default to 3010/5183 rather
|
||||
than 3001/5173 so an already installed CloudCLI keeps running — but note that
|
||||
both instances then share one database. Set `DATABASE_PATH` to separate them.
|
||||
|
||||
After changing the plugin:
|
||||
|
||||
```bash
|
||||
cd plugin-taskwork && npm run build
|
||||
cd .. && ./scripts/link-plugin.sh # the host reads a copy, not a symlink
|
||||
```
|
||||
|
||||
Client changes need only the plugin surface reopened; server changes need the host
|
||||
restarted (`./scripts/dev-host.sh restart`), because the plugin's backend process
|
||||
is spawned at host startup.
|
||||
|
||||
The plugin stores its data **outside** its own directory, in
|
||||
`~/.claude-code-ui/taskwork/tasks.json` — the plugin directory is wiped on *Update*.
|
||||
|
||||
### Access
|
||||
|
||||
All submodule URLs are SSH, so both submodules are writable with the same key
|
||||
setup: `ssh://git@gitea.bvn13.me:2221/…` for the plugin (the Gitea repositories
|
||||
are private) and `git@github.com:bvn13/claudecodeui.git` for the fork. The
|
||||
`upstream` remote stays on HTTPS — it is fetch-only and public.
|
||||
|
||||
Cloning therefore needs an SSH key registered with both hosts; check with
|
||||
`ssh -T git@github.com` and `ssh -T -p 2221 git@gitea.bvn13.me`.
|
||||
|
||||
Distribution goes through the public GitHub copy
|
||||
(`github:bvn13/cloudcli-plugin-taskwork`), because the host clones a plugin
|
||||
anonymously over HTTPS and the Gitea repository is private. The submodule keeps
|
||||
pointing at Gitea: that is where the plugin is developed and pushed first.
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| `scripts/bootstrap.sh` | Init submodules, wire the `upstream` remote, install deps, build the host's native modules and the plugin |
|
||||
| `scripts/dev-host.sh` | Run the patched host locally: `start`, `stop`, `restart`, `status`, `logs` |
|
||||
| `scripts/export-patches.sh` | Regenerate `plugin-taskwork/patches/*.patch` from the fork's feature branches (§12.0) |
|
||||
| `scripts/link-plugin.sh` | Symlink the local plugin into the host's plugin directory (`--unlink` to remove) |
|
||||
| `scripts/check-submodule-pins.sh` | Verify every pinned submodule commit is reachable from its origin |
|
||||
|
||||
## Branches in `cloudcli/`
|
||||
|
||||
Each feature branch is cut **from the upstream tag `v1.37.2`**, never from another
|
||||
feature branch — that is what keeps the three pull requests independent.
|
||||
|
||||
| Branch | Base | Purpose |
|
||||
|---|---|---|
|
||||
| `main` | tracks `upstream/main` | Upstream sync. Carries no commits of ours |
|
||||
| `feat/resizable-sidebar` | `v1.37.2` | PR-1 — resizable sidebar |
|
||||
| `feat/plugin-host-api` | `v1.37.2` | PR-2 — authenticated host API for plugins |
|
||||
| `feat/plugin-sidebar-surface` | `v1.37.2` | PR-3 — sidebar surface for plugins |
|
||||
| `dev/taskwork` | merge of the three | Local run and debugging; the submodule is pinned here |
|
||||
|
||||
`dev/taskwork` never takes part in a pull request and may be recreated at any time.
|
||||
|
||||
## Working with the submodules
|
||||
|
||||
- Commit code **inside** the submodule, push it, and only then record the new pin
|
||||
here with a separate commit `chore: bump <submodule> to <sha>`.
|
||||
- Update with `git submodule update --remote --merge`.
|
||||
- Run `./scripts/check-submodule-pins.sh` before pushing a pin bump.
|
||||
|
||||
## Status
|
||||
|
||||
| Piece | State |
|
||||
|---|---|
|
||||
| Plugin | `v1.0.0`, pushed to Gitea; installs, builds and runs (`npm install --ignore-scripts` + `npm run build` in ~3 s) |
|
||||
| `feat/resizable-sidebar` | Pushed to the fork. PR-1 not opened yet; no issue needed |
|
||||
| `feat/plugin-host-api` | Pushed to the fork. Issue text in `spec/upstream/issue-1-plugin-host-api.md`, PR text in `plugin-taskwork/docs/upstream-pr-2.md` |
|
||||
| `feat/plugin-sidebar-surface` | Pushed to the fork. Issue text in `spec/upstream/issue-2-plugin-sidebar-surface.md`, PR text in `plugin-taskwork/docs/upstream-pr-3.md` |
|
||||
| `dev/taskwork` | Merge of the three plus one integration commit (host API in the sidebar surface) that never goes upstream; the `cloudcli` pin points here |
|
||||
|
||||
Remaining before upstream contact (§19 of the spec): open issue #1, then PR-2;
|
||||
issue #2, then PR-3; PR-1 can go on its own. Also outstanding: the manual
|
||||
acceptance checklist (§15.4), which needs a running host in a browser.
|
||||
|
||||
## Release
|
||||
|
||||
See §13.2 of the spec. In short: export patches → bump the plugin version in
|
||||
`package.json` and `manifest.json` → build, typecheck, test → commit `build/`,
|
||||
`patches/`, `CHANGELOG.md` → tag `vX.Y.Z` in the plugin repo → bump the pins here.
|
||||
|
||||
## Licence
|
||||
|
||||
The plugin and the fork are AGPL-3.0-or-later, following the upstream host.
|
||||
1
cloudcli
Submodule
1
cloudcli
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit dfc8ba98022e8e0827a3741b32dc71d832579a09
|
||||
1
plugin-taskwork
Submodule
1
plugin-taskwork
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 3dbc55cce18a207a1d2eabd5f96c98c0dd58043c
|
||||
59
scripts/bootstrap.sh
Executable file
59
scripts/bootstrap.sh
Executable file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
# Brings the workspace up: initialises both submodules and installs their deps.
|
||||
# Usage: ./scripts/bootstrap.sh
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
echo "==> Initialising submodules"
|
||||
git submodule update --init --recursive
|
||||
|
||||
echo "==> Configuring the host fork remotes"
|
||||
if ! git -C cloudcli remote | grep -qx upstream; then
|
||||
git -C cloudcli remote add upstream https://github.com/siteboon/claudecodeui.git
|
||||
fi
|
||||
# upstream is fetch-only: pull requests are opened from origin (the fork).
|
||||
git -C cloudcli remote set-url --push upstream DISABLED_fetch_only
|
||||
git -C cloudcli fetch upstream --tags --quiet
|
||||
|
||||
echo "==> Installing host dependencies (cloudcli)"
|
||||
# Not `npm ci`: the upstream lockfile pins a few tarballs on registry.npmmirror.com,
|
||||
# and npm >= 12 refuses remote tarball URLs (EALLOWREMOTE) unless allowed explicitly.
|
||||
(cd cloudcli && npm install --allow-remote=all --no-audit --no-fund)
|
||||
|
||||
echo "==> Building the host's native modules"
|
||||
# npm >= 12 also blocks install scripts, so prebuilt binaries are never fetched
|
||||
# and the server cannot start. Build only what the server actually needs, and
|
||||
# only when it is missing (electron and friends stay unbuilt on purpose).
|
||||
build_native() {
|
||||
local module="$1" builder="$2"
|
||||
if (cd "cloudcli" && node -e "require('$module')") >/dev/null 2>&1; then
|
||||
echo " $module: ok"
|
||||
return
|
||||
fi
|
||||
|
||||
echo " $module: building"
|
||||
(cd "cloudcli/node_modules/$module" && eval "$builder") >/dev/null 2>&1 || true
|
||||
|
||||
if (cd "cloudcli" && node -e "require('$module')") >/dev/null 2>&1; then
|
||||
echo " $module: built"
|
||||
else
|
||||
echo " $module: FAILED — the host server will not start" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
build_native better-sqlite3 "npx --no-install prebuild-install || npx --no-install node-gyp rebuild --release"
|
||||
build_native bcrypt "npx --no-install node-gyp-build"
|
||||
build_native node-pty "node scripts/prebuild.js || npx --no-install node-gyp rebuild"
|
||||
|
||||
echo "==> Building the plugin (plugin-taskwork)"
|
||||
(cd plugin-taskwork && npm ci && npm run build)
|
||||
|
||||
cat <<'EOF'
|
||||
|
||||
Done.
|
||||
|
||||
Run the host: ./scripts/dev-host.sh start (http://localhost:5183)
|
||||
Install the build: ./scripts/link-plugin.sh (dev-host.sh start does this too)
|
||||
EOF
|
||||
32
scripts/check-submodule-pins.sh
Executable file
32
scripts/check-submodule-pins.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verifies that every pinned submodule commit is reachable from its origin.
|
||||
# Catches the classic "pinned to a local commit nobody else can fetch" mistake.
|
||||
#
|
||||
# Usage: ./scripts/check-submodule-pins.sh
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
fail=0
|
||||
|
||||
while read -r _mode _type sha path; do
|
||||
[ -n "${path:-}" ] || continue
|
||||
|
||||
if [ ! -d "$path/.git" ] && [ ! -f "$path/.git" ]; then
|
||||
echo "FAIL $path: submodule is not initialised (git submodule update --init)"
|
||||
fail=1
|
||||
continue
|
||||
fi
|
||||
|
||||
git -C "$path" fetch origin --quiet --prune 2>/dev/null || true
|
||||
|
||||
if git -C "$path" branch -r --contains "$sha" 2>/dev/null | grep -q .; then
|
||||
echo "ok $path: $sha is reachable from origin"
|
||||
else
|
||||
echo "FAIL $path: $sha is NOT pushed to origin — push the submodule first"
|
||||
fail=1
|
||||
fi
|
||||
done < <(git ls-tree HEAD | grep '^160000')
|
||||
|
||||
exit $fail
|
||||
168
scripts/dev-host.sh
Executable file
168
scripts/dev-host.sh
Executable file
@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs the patched host (cloudcli/, branch dev/taskwork) against your real
|
||||
# environment — the same ~/.claude projects, the same ~/.cloudcli/auth.db, the
|
||||
# same ~/.claude-code-ui/plugins as an installed CloudCLI. Nothing is sandboxed,
|
||||
# so what you see is what a user would see.
|
||||
#
|
||||
# ./scripts/dev-host.sh start install the built plugin and start the host
|
||||
# ./scripts/dev-host.sh stop stop it
|
||||
# ./scripts/dev-host.sh status ports, pid, plugin version
|
||||
# ./scripts/dev-host.sh logs follow the log
|
||||
#
|
||||
# Ports default to 3010/5183, not 3001/5173, so an already installed CloudCLI
|
||||
# keeps running untouched. Override with SERVER_PORT / VITE_PORT.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
HOST_DIR="$ROOT/cloudcli"
|
||||
LOG_FILE="$ROOT/.dev-host.log"
|
||||
PID_FILE="$ROOT/.dev-host.pid"
|
||||
|
||||
SERVER_PORT="${SERVER_PORT:-3010}"
|
||||
VITE_PORT="${VITE_PORT:-5183}"
|
||||
READY_TIMEOUT_SECONDS=60
|
||||
|
||||
running_pid() {
|
||||
[ -f "$PID_FILE" ] || return 1
|
||||
local pid
|
||||
pid="$(cat "$PID_FILE")"
|
||||
[ -n "$pid" ] && kill -0 "$pid" 2>/dev/null && echo "$pid"
|
||||
}
|
||||
|
||||
port_owner() {
|
||||
ss -ltnp 2>/dev/null | awk -v port=":$1" '$4 ~ port' | grep -oE 'pid=[0-9]+' | head -1
|
||||
}
|
||||
|
||||
require_environment() {
|
||||
[ -d "$HOST_DIR/.git" ] || { echo "cloudcli/ is not initialised — run ./scripts/bootstrap.sh" >&2; exit 1; }
|
||||
[ -d "$HOST_DIR/node_modules" ] || { echo "cloudcli/node_modules is missing — run ./scripts/bootstrap.sh" >&2; exit 1; }
|
||||
|
||||
# The server needs these three compiled; npm >= 12 blocks install scripts by
|
||||
# default, which leaves them unbuilt and the server dead on arrival.
|
||||
local missing=()
|
||||
for module in better-sqlite3 bcrypt node-pty; do
|
||||
(cd "$HOST_DIR" && node -e "require('$module')") >/dev/null 2>&1 || missing+=("$module")
|
||||
done
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
echo "native modules not built: ${missing[*]}" >&2
|
||||
echo "run ./scripts/bootstrap.sh, which builds them" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
warn_about_other_instances() {
|
||||
local other
|
||||
other="$(port_owner 3001 || true)"
|
||||
[ -n "$other" ] || return 0
|
||||
|
||||
cat >&2 <<EOF
|
||||
note: another CloudCLI is listening on :3001 (${other}).
|
||||
This instance shares its database (~/.cloudcli/auth.db) and its plugin
|
||||
directory. That is intentional here — set DATABASE_PATH to run against a
|
||||
separate database instead.
|
||||
EOF
|
||||
}
|
||||
|
||||
start_host() {
|
||||
local pid
|
||||
if pid="$(running_pid)"; then
|
||||
echo "already running (pid $pid) — http://localhost:$VITE_PORT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
require_environment
|
||||
warn_about_other_instances
|
||||
|
||||
for port in "$SERVER_PORT" "$VITE_PORT"; do
|
||||
if ss -ltn 2>/dev/null | grep -q ":$port "; then
|
||||
echo "port $port is already in use — pick another with SERVER_PORT/VITE_PORT" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -d "$ROOT/plugin-taskwork/build" ]; then
|
||||
# Captured rather than piped into `head -1`: head closes the pipe after the
|
||||
# first line, link-plugin.sh dies of SIGPIPE on its next write, and pipefail
|
||||
# turns that into a silent exit here.
|
||||
local plugin_output
|
||||
plugin_output="$("$ROOT/scripts/link-plugin.sh")"
|
||||
printf '%s\n' "${plugin_output%%$'\n'*}"
|
||||
else
|
||||
echo "note: plugin-taskwork/build is missing, the host will start without the plugin"
|
||||
fi
|
||||
|
||||
# setsid detaches from this shell's process group, so the host survives the
|
||||
# terminal (or the agent turn) that started it.
|
||||
(
|
||||
cd "$HOST_DIR"
|
||||
SERVER_PORT="$SERVER_PORT" VITE_PORT="$VITE_PORT" \
|
||||
setsid nohup npm run dev > "$LOG_FILE" 2>&1 < /dev/null &
|
||||
echo $! > "$PID_FILE"
|
||||
)
|
||||
|
||||
printf 'starting'
|
||||
for _ in $(seq "$READY_TIMEOUT_SECONDS"); do
|
||||
if curl -sf "http://localhost:$SERVER_PORT/api/auth/status" >/dev/null 2>&1; then
|
||||
echo
|
||||
echo "host http://localhost:$VITE_PORT (api :$SERVER_PORT, pid $(cat "$PID_FILE"))"
|
||||
grep -m1 '\[Plugins\] Server started' "$LOG_FILE" 2>/dev/null || echo "note: no plugin server in the log yet"
|
||||
echo "log $LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
printf '.'
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo
|
||||
echo "the host did not answer within ${READY_TIMEOUT_SECONDS}s — see $LOG_FILE" >&2
|
||||
tail -5 "$LOG_FILE" >&2 || true
|
||||
exit 1
|
||||
}
|
||||
|
||||
stop_host() {
|
||||
local pid
|
||||
if ! pid="$(running_pid)"; then
|
||||
echo "not running"
|
||||
rm -f "$PID_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# npm run dev spawns the server and vite; killing the group takes all of them.
|
||||
kill -- "-$pid" 2>/dev/null || kill "$pid" 2>/dev/null || true
|
||||
for _ in $(seq 10); do
|
||||
kill -0 "$pid" 2>/dev/null || break
|
||||
sleep 0.5
|
||||
done
|
||||
kill -9 -- "-$pid" 2>/dev/null || true
|
||||
|
||||
rm -f "$PID_FILE"
|
||||
echo "stopped"
|
||||
}
|
||||
|
||||
show_status() {
|
||||
local pid
|
||||
if pid="$(running_pid)"; then
|
||||
echo "running pid $pid, http://localhost:$VITE_PORT (api :$SERVER_PORT)"
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
|
||||
echo "branch $(git -C "$HOST_DIR" branch --show-current 2>/dev/null || echo '?')"
|
||||
|
||||
local installed="$HOME/.claude-code-ui/plugins/cloudcli-plugin-taskwork/manifest.json"
|
||||
if [ -f "$installed" ]; then
|
||||
echo "plugin $(node -p "require('$installed').version") installed in ~/.claude-code-ui/plugins"
|
||||
else
|
||||
echo "plugin not installed — ./scripts/link-plugin.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-start}" in
|
||||
start) start_host ;;
|
||||
stop) stop_host ;;
|
||||
restart) stop_host; start_host ;;
|
||||
status) show_status ;;
|
||||
logs) tail -f "$LOG_FILE" ;;
|
||||
*) echo "usage: $0 [start|stop|restart|status|logs]" >&2; exit 1 ;;
|
||||
esac
|
||||
49
scripts/export-patches.sh
Executable file
49
scripts/export-patches.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerates plugin-taskwork/patches/*.patch from the fork's feature branches.
|
||||
#
|
||||
# The branches in cloudcli/ are the single source of truth (SPEC-v3 §12.0);
|
||||
# the .patch files are a derived artefact and are never edited by hand.
|
||||
#
|
||||
# Usage: ./scripts/export-patches.sh [base-tag]
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
BASE_TAG="${1:-v1.37.2}"
|
||||
SRC="$ROOT/cloudcli"
|
||||
OUT="$ROOT/plugin-taskwork/patches"
|
||||
|
||||
# branch:output-file — the numeric prefix mirrors the publication order (§12.4),
|
||||
# not a dependency: every patch applies to the base tag on its own.
|
||||
BRANCHES=(
|
||||
"feat/resizable-sidebar:0001-feat-resizable-sidebar.patch"
|
||||
"feat/plugin-host-api:0002-feat-plugin-host-api.patch"
|
||||
"feat/plugin-sidebar-surface:0003-feat-plugin-sidebar-surface.patch"
|
||||
)
|
||||
|
||||
[ -d "$SRC/.git" ] || { echo "cloudcli/ submodule is not initialised — run ./scripts/bootstrap.sh" >&2; exit 1; }
|
||||
[ -d "$OUT" ] || { echo "plugin-taskwork/patches/ is missing — run ./scripts/bootstrap.sh" >&2; exit 1; }
|
||||
|
||||
git -C "$SRC" rev-parse --verify --quiet "$BASE_TAG^{commit}" >/dev/null \
|
||||
|| { echo "base tag $BASE_TAG not found in cloudcli/ (git fetch upstream --tags)" >&2; exit 1; }
|
||||
|
||||
for entry in "${BRANCHES[@]}"; do
|
||||
branch="${entry%%:*}"
|
||||
file="${entry##*:}"
|
||||
|
||||
if ! git -C "$SRC" rev-parse --verify --quiet "$branch" >/dev/null; then
|
||||
echo "skip $branch (branch does not exist yet)"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Independence check: a feature branch must sit directly on the base tag,
|
||||
# otherwise its patch would silently carry another PR's commits (§4.4).
|
||||
base="$(git -C "$SRC" merge-base "$BASE_TAG" "$branch")"
|
||||
if [ "$base" != "$(git -C "$SRC" rev-parse "$BASE_TAG^{commit}")" ]; then
|
||||
echo "ERROR: $branch is not based on $BASE_TAG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git -C "$SRC" format-patch "$BASE_TAG..$branch" --stdout > "$OUT/$file"
|
||||
commits="$(git -C "$SRC" rev-list --count "$BASE_TAG..$branch")"
|
||||
echo "wrote patches/$file ($commits commit(s))"
|
||||
done
|
||||
55
scripts/link-plugin.sh
Executable file
55
scripts/link-plugin.sh
Executable file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installs the locally built plugin into the host's plugin directory, so the
|
||||
# host picks up `npm run build` output without a reinstall from its URL.
|
||||
#
|
||||
# It copies rather than symlinks, and that is not a matter of taste:
|
||||
# `scanPlugins()` walks the plugins directory with
|
||||
# `readdirSync(dir, { withFileTypes: true })` and skips every entry whose
|
||||
# `entry.isDirectory()` is false — which is exactly what a symlink reports. A
|
||||
# symlinked plugin is silently invisible to the host.
|
||||
# Symlinking the files *inside* a real directory fails too: the asset route
|
||||
# compares the `realpath` of each asset against the `realpath` of the plugin
|
||||
# directory and 404s anything pointing outside it.
|
||||
#
|
||||
# Usage: ./scripts/link-plugin.sh [--remove]
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
DIR_NAME="cloudcli-plugin-taskwork" # last URL segment — how the host names it
|
||||
TARGET="${HOME}/.claude-code-ui/plugins/${DIR_NAME}"
|
||||
SOURCE="$ROOT/plugin-taskwork"
|
||||
|
||||
# Everything the host needs at runtime: manifest, entry points, icon, and the
|
||||
# package.json it reads to decide whether to run a build.
|
||||
CONTENTS=(manifest.json package.json icon.svg build)
|
||||
|
||||
if [ "${1:-}" = "--remove" ]; then
|
||||
[ -e "$TARGET" ] && rm -rf "$TARGET" && echo "removed $TARGET" || echo "nothing to remove"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -f "$SOURCE/manifest.json" ] || { echo "plugin-taskwork/manifest.json not found — run ./scripts/bootstrap.sh" >&2; exit 1; }
|
||||
[ -d "$SOURCE/build" ] || { echo "plugin-taskwork/build missing — run npm run build in plugin-taskwork/" >&2; exit 1; }
|
||||
|
||||
if [ -L "$TARGET" ]; then
|
||||
echo "removing a stale symlink at $TARGET (the host cannot see symlinked plugins)"
|
||||
rm "$TARGET"
|
||||
fi
|
||||
|
||||
mkdir -p "$TARGET"
|
||||
rm -rf "${TARGET:?}/build"
|
||||
for item in "${CONTENTS[@]}"; do
|
||||
cp -r "$SOURCE/$item" "$TARGET/"
|
||||
done
|
||||
# Intermediate tsc output is a build artefact, not part of the plugin.
|
||||
rm -rf "$TARGET/build/.tsc-client"
|
||||
|
||||
echo "installed $TARGET (copy of $SOURCE)"
|
||||
|
||||
cat <<'EOF'
|
||||
|
||||
Re-run this script after every `npm run build`; a copy does not track the source.
|
||||
|
||||
Client changes need the plugin surface reopened, server changes need the host
|
||||
restarted — the plugin's backend process is spawned once at host startup.
|
||||
EOF
|
||||
22
scripts/start-proxy.sh
Executable file
22
scripts/start-proxy.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HTTP_PROXY_PORT=${HTTP_PROXY_PORT:-25345}
|
||||
|
||||
# Proxy settings (v2rayN / local HTTP proxy)
|
||||
export HTTP_PROXY="${HTTP_PROXY:-http://127.0.0.1:${HTTP_PROXY_PORT}}"
|
||||
export HTTPS_PROXY="${HTTPS_PROXY:-http://127.0.0.1:${HTTP_PROXY_PORT}}"
|
||||
export NO_PROXY="${NO_PROXY:-localhost,127.0.0.1,::1,.corp.local,ahcode-01.digital.achrf.ru}"
|
||||
|
||||
# Some libs read lowercase variants
|
||||
export http_proxy="$HTTP_PROXY"
|
||||
export https_proxy="$HTTPS_PROXY"
|
||||
export no_proxy="$NO_PROXY"
|
||||
|
||||
echo "-------"
|
||||
echo "HTTP_PROXY=${HTTP_PROXY}"
|
||||
echo "-------"
|
||||
|
||||
|
||||
# Run claude with args
|
||||
bash dev-host.sh "$@"
|
||||
Loading…
x
Reference in New Issue
Block a user