--- name: setup-projects-meta version: 1.1.0 description: Installs and configures the local `projects-meta-mcp` stdio server — clones the repo to `~/projects/.common/lib/projects-meta-mcp`, builds it, writes `~/.config/projects-mcp/auth.toml` with the user's Gitea token, clones the shared wiki to `~/projects/.wiki/` (content lives in root), and registers `mcpServers.projects-meta` in `~/.claude.json`. Use this skill when the user says "install projects-meta", "set up projects-meta", "configure projects-meta", "настрой projects-meta", "установи projects-meta", "projects-meta не работает", "projects-meta isn't working", or whenever the `mcp__projects-meta__*` tools are missing in a session that needs cross-project task aggregation or the shared Gitea wiki. Cross-platform — Windows / Linux / macOS. Mutates user-level config and writes secrets; pauses for confirmation before every write. --- # setup-projects-meta > One-time skill that gets the local `projects-meta-mcp` server running with the user's Gitea credentials. Stops at confirmation gates because the procedure clones a repo, writes a secret-bearing TOML file, and edits `~/.claude.json`. Reference: full `projects-meta-mcp` docs live in the shared wiki at `packages/projects-meta-mcp` (fetch via `mcp__projects-meta__knowledge_get` once the server is up). ## When to use - User explicitly asks: install / set up / configure projects-meta. - A `using-projects-meta`-driven task fails because `mcp__projects-meta__*` tools aren't available. - Migrating a stale install (token expired, repo moved, broken cache) — same procedure, Phase 1 detects what's already in place. - New machine in the user's multi-machine fleet (recall: this is a cross-machine workflow). ## Out of scope - Issuing or rotating Gitea tokens. This skill *uses* a token the user already has; if there's no token, point them at Gitea's settings page (`https://git.kzntsv.site/user/settings/applications`) and stop until they paste one. - Running `projects-meta-mcp` itself (the MCP harness spawns it). - Editing `.tasks/STATUS.md` or wiki content — that's `using-projects-meta` / `using-tasks` / `using-wiki`. - Any other MCP server. ## Hard rule: don't auto-mutate config The procedure clones a repo, writes `~/.config/projects-mcp/auth.toml` (carries the Gitea token), and edits `~/.claude.json`. **Always pause for explicit confirmation between Phase 1 (discovery, read-only) and Phase 2 (plan), and again before Phase 3 (backup + writes).** A trigger phrase is permission to run discovery, not permission to clone or write secrets. ## Procedure ### Phase 0 — Environment sanity - Confirm Claude Code is the current harness (need `mcpServers` registration in `~/.claude.json`). - Confirm `git`, `node`, `npm` are on `PATH`. Node ≥ 18 (the server uses ES modules). - Confirm network reachability to `https://git.kzntsv.site` (the Gitea host). On HTTP 401/403 later, the token is dead — stop and ask for a new one. - Pick paths: `~/projects/.common/lib/projects-meta-mcp`, `~/.config/projects-mcp/`, `~/.cache/projects-mcp/`, `~/projects/.wiki/` (shared wiki clone). POSIX-style `~/...` resolves correctly under git-bash on Windows. ### Phase 1 — Discovery (read-only) Search, in order. Report only "found at ", never echo token values. **Existing Gitea token.** Look in priority order: 1. `~/.config/projects-mcp/auth.toml` → `gitea_token = "..."` 2. Env var `PROJECTS_META_GITEA_TOKEN` 3. Existing `~/.claude.json` → `mcpServers.projects-meta` block (rare; legacy installs sometimes inline `env.GITEA_TOKEN`) The first hit wins. Capture internally for Phase 5; **never echo it in chat**. **Repo install state.** Check whether `~/projects/.common/lib/projects-meta-mcp/.git` exists. If yes, `git -C ~/projects/.common/lib/projects-meta-mcp rev-parse HEAD` → record the SHA so Phase 4 can decide *clone* vs *pull*. **Build artifact.** Check `~/projects/.common/lib/projects-meta-mcp/dist/server.js`. If absent, Phase 4 will run `npm install && npm run build`. **Shared wiki clone.** Check if `~/projects/.wiki/.git` exists and `origin` matches `https://git.kzntsv.site/OpeItcLoc03/projects-wiki(.git)?`. If non-matching `origin`, stop and ask — the user may have an unrelated wiki there. **MCP registration.** Read `~/.claude.json` and check `mcpServers.projects-meta`. Note the command + args. If args point at a stale path, Phase 6 will fix it. **Cache state.** List `~/.cache/projects-mcp/` (if it exists). Just for the report — don't rely on it. ### Phase 2 — Plan + confirm Present a single-block plan to the user: ``` Token: | NOT FOUND — will ask> Repo: | will clone> Build artifact: Wiki clone: MCP entry: Backups: ~/.claude.json.bak-, ~/.config/projects-mcp/auth.toml.bak- (if exists) ``` Wait for explicit confirmation ("ok", "go", "поехали"). Anything else → stop. If no token was found in Phase 1 — first ask: "Paste a Gitea personal access token (scope: `read:repository` for read-only, `write:repository` to enable mutations), or open `https://git.kzntsv.site/user/settings/applications` to create one." Don't proceed past Phase 2 without a token. ### Phase 3 — Backup Copy each file we will modify to `.bak-YYYYMMDD-HHMMSS`: ```bash TS=$(date +%Y%m%d-%H%M%S) [ -f ~/.claude.json ] && cp ~/.claude.json ~/.claude.json.bak-$TS [ -f ~/.config/projects-mcp/auth.toml ] && cp ~/.config/projects-mcp/auth.toml ~/.config/projects-mcp/auth.toml.bak-$TS ``` Confirm both backups exist (when their source existed) before any further edit. The repo and wiki clones don't need backup — git is the backup. ### Phase 4 — Clone + build ```bash # Server if [ -d ~/projects/.common/lib/projects-meta-mcp/.git ]; then git -C ~/projects/.common/lib/projects-meta-mcp pull --ff-only else git clone https://git.kzntsv.site/OpeItcLoc03/projects-meta-mcp ~/projects/.common/lib/projects-meta-mcp fi cd ~/projects/.common/lib/projects-meta-mcp npm install npm run build # Shared wiki mkdir -p ~/projects if [ -d ~/projects/.wiki/.git ]; then git -C ~/projects/.wiki pull --ff-only else git clone https://git.kzntsv.site/OpeItcLoc03/projects-wiki ~/projects/.wiki fi ``` Verify `~/projects/.common/lib/projects-meta-mcp/dist/server.js` exists after build. If not — abort, the build failed; ask the user to run `npm run build` manually and paste the output. ### Phase 5 — Write `auth.toml` ```bash mkdir -p ~/.config/projects-mcp ``` If `~/.config/projects-mcp/auth.toml` already exists and Phase 1 found a valid `gitea_token` line — skip the write. Otherwise, write the file with the token captured in Phase 1 (or freshly pasted in Phase 2): ```toml gitea_url = "https://git.kzntsv.site" gitea_user = "OpeItcLoc03" # acting identity (commit author) gitea_token = "" gitea_owners = ["victor", "cancel_music"] # additional Gitea owners to sync agenda_tasks_repo = "OpeItcLoc03/agenda" # cross-project meta-board (qualified) # gitea_aggregate_skip_owners = ["OpeItcLoc03"] # opt: sync but hide from `tasks_aggregate` ``` **Schema notes (v2.x server):** - `gitea_owners` is an array of owners whose repos are scanned by `sync.js` and surfaced in aggregation views. `gitea_user` is acting identity only (commit author footer), not necessarily aggregated. - `agenda_tasks_repo` is **qualified** (`/`). The literal `agenda` in `target_project` resolves through this field. - `gitea_aggregate_skip_owners` (optional, v2.2.0+) — visited by sync (so mutations work via cache lookup) but hidden from `tasks_aggregate` / `tasks_search`. Useful for keeping infra repos write-able without polluting the dashboard. - Backwards-compat: legacy installs with only `gitea_user = "X"` and no `gitea_owners` → server reads as `gitea_owners = ["X"]`. - Legacy `meta_tasks_repo` / `meta_wiki_repo` → renamed to `agenda_tasks_repo` / built-in `projects-wiki`. Old keys ignored on v2.x. Permissions: on Linux / macOS run `chmod 600 ~/.config/projects-mcp/auth.toml`. On Windows the default ACL is per-user, no extra step. ### Phase 6 — Register in `~/.claude.json` Edit `~/.claude.json`. Add or update the `mcpServers.projects-meta` block: ```json { "mcpServers": { "projects-meta": { "command": "node", "args": ["/dist/server.js"] } } } ``` Absolute path resolution: | Platform | `` | |---|---| | Windows | `C:/Users//projects/.common/lib/projects-meta-mcp` (forward slashes; works in JSON without escaping) | | Linux | `/home//projects/.common/lib/projects-meta-mcp` | | macOS | `/Users//projects/.common/lib/projects-meta-mcp` | After each edit, validate JSON: ```bash # Windows (git-bash) powershell.exe -NoProfile -c "Get-Content '' -Raw | ConvertFrom-Json | Out-Null" # Linux / macOS jq empty # fallback python -c "import json; json.load(open(''))" ``` If validation fails → restore from `.bak-*` and abort. ### Phase 7 — Smoke test (best-effort) + run initial sync Best-effort: call `mcp__projects-meta__meta_status`. If it returns a JSON blob with `synced_at` / `wiki_pages_count` — the server is reachable in *this* session. Then run a one-shot sync to populate the cache: ```bash cd ~/projects/.common/lib/projects-meta-mcp node dist/sync.js ``` Expect a non-zero `projects_count` and a fresh `~/.cache/projects-mcp/tasks.json`. On 401 / 403 → token is wrong scope or expired; rotate via `https://git.kzntsv.site/user/settings/applications` and re-edit `auth.toml`. **Important caveat to relay to the user:** in the *same* session that just ran setup, the MCP server you're talking to is whatever was bound at session start. So a passing `meta_status` only proves "some projects-meta is alive" — not "the registration we just wrote is what's serving it". The real test is after Claude Code restart. ### Phase 8 — Restart guidance + final report Tell the user: ``` ✅ Setup complete. Restart Claude Code so the new mcpServers.projects-meta registration binds to a fresh stdio session. After restart: • mcp__projects-meta__* tools serve from ~/projects/.common/lib/projects-meta-mcp/dist/server.js • Cache lives at ~/.cache/projects-mcp/tasks.json (refresh: node dist/sync.js) • Shared wiki clone at ~/projects/.wiki/ — `git -C ~/projects/.wiki pull --ff-only` for fresh anchors • Backups saved at ~/.claude.json.bak- (and auth.toml.bak- if it existed before) If something breaks after restart: • Restore from .bak-* and tell me — we'll roll back together. ``` ## Rollback procedure If a problem surfaces (now or after restart): 1. Stop. Don't try to fix forward. 2. Find the most recent `.bak-YYYYMMDD-HHMMSS` next to `~/.claude.json` (and `~/.config/projects-mcp/auth.toml` if applicable). 3. `cp .bak- ` for each. 4. Optional: `rm -rf ~/projects/.common/lib/projects-meta-mcp` and `rm -rf ~/.cache/projects-mcp`. Keep `~/projects/.wiki/` — it's a useful clone regardless of MCP state. 5. Restart Claude Code. 6. Confirm `mcp__projects-meta__*` is gone (or back to the pre-existing version). 7. Report what went wrong so we can fix the procedure. ## Cross-platform notes The procedure is platform-agnostic. Only auxiliary tooling differs: | | JSON validate | Backup | Permissions on auth.toml | |---|---|---|---| | Windows (git-bash) | `powershell.exe -NoProfile -c "Get-Content '' -Raw \| ConvertFrom-Json \| Out-Null"` | `cp` | per-user ACL by default | | Linux | `jq empty ` (or `python -c "import json; json.load(open(''))"`) | `cp` | `chmod 600` | | macOS | same as Linux | `cp` | `chmod 600` | Path forms (`~/.local/...`, `~/.config/...`, `~/projects/...`) are identical on all three. ## Common mistakes - **Skipping Phase 1.** "User just said 'install projects-meta' — let's go." No — find existing token / repo / wiki first; re-cloning over an existing install loses any local commits in the wiki. - **Echoing the token.** It's a secret. Edit / Write tool calls inevitably contain it (that's how it gets into `auth.toml`), but no chat output should. - **Cloning over an unrelated `~/projects/.wiki/`.** If it exists with a different `origin`, stop. The user may have an unrelated wiki there. - **Writing `auth.toml` with `0644` perms on Linux/macOS.** Token leak. Always `chmod 600` after write. - **Treating in-session `meta_status` as proof.** Same as the context7 caveat — the active MCP connection was bound at session start. - **Auto-running on every "use projects-meta".** This skill is intrusive. Trigger only on explicit "install/setup/configure projects-meta", or when MCP tools are missing and the user is blocked. - **Forgetting `node dist/sync.js`.** Without an initial sync, the cache is empty and `tasks_aggregate` returns nothing — the user thinks setup failed.