--- name: using-projects-meta version: 1.2.0 description: Use when working across multiple projects on one or many machines — cross-project task aggregation (`mcp__projects-meta__tasks_*`), shared Gitea-backed wiki query / ingest (`mcp__projects-meta__knowledge_*`), or sync diagnostics (`mcp__projects-meta__meta_status`). Triggers on phrases like "across all projects", "what's on the boards", "check shared wiki", "search projects-wiki", "ingest into shared wiki", "что у меня на досках", "по всем проектам", "общая вики", "cross-project status", or any time the user wants to see / mutate state in another repo than the current cwd. v1.1.0 mandates a Step 0 freshness gate (probe `meta_status`, sync if stale, pull `projects-wiki` before shared-wiki writes) — see SKILL body. Mutation tools require two-step preview → confirm. Skip for the **current** project's tasks/wiki — those live on disk in `.tasks/` / `.wiki/`. --- # Using the projects-meta MCP server ## Overview `projects-meta-mcp` is a local stdio MCP server. Two responsibilities: 1. **Cross-project task aggregation** — parses `.tasks/STATUS.md` from every repo on the user's Gitea, caches them in `~/.cache/projects-mcp/tasks.json`. Read tools (`tasks_aggregate`, `tasks_search`, `tasks_get`) hit the cache. Mutations (`tasks_create`, `tasks_update`, `tasks_close`) commit back to Gitea with sha-based optimistic lock. 2. **Shared knowledge wiki** — single Gitea repo (`projects-wiki`) cloned at `~/projects/projects-wiki/` with content at `~/projects/projects-wiki/.wiki/`, structured as packages / concepts / entities / sources / raw. `knowledge_search` + `knowledge_get` for queries, `knowledge_ingest` + `knowledge_promote` for writes. Source of truth: Gitea (`https://git.kzntsv.site`, owner `OpeItcLoc03`). Cache and clone are local convenience. Full reference: `mcp__projects-meta__knowledge_get slug=packages/projects-meta-mcp`. ## Prerequisites This skill assumes `mcp__projects-meta__*` tools are available. If they aren't (tools missing from the session, or calls fail with a connection error), the server isn't running for this session. Trigger the **`setup-projects-meta`** skill to clone, build, write `auth.toml`, and register `mcpServers.projects-meta` in `~/.claude.json`. It's a one-time procedure with confirmation gates. ## Local-first rule **For the current project — read disk directly.** `.tasks/STATUS.md` and `.wiki/` files in cwd are always fresher than the MCP cache. The cache: - May be stale (default sync runs only when triggered). - Hides 🟢 done by default. - May not contain locally-developed projects that aren't pushed to Gitea yet. Use MCP only for **other** projects, **other** machines, or **shared** wiki content. | Question | Where to read | |---|---| | "What's the status of *this* project?" | local `.tasks/STATUS.md` | | "What's on all my boards?" | `mcp__projects-meta__tasks_aggregate` | | "Has *this* project's wiki got a page on X?" | local `.wiki/index.md` + relevant file | | "Has the **shared** wiki got a page on X?" | `mcp__projects-meta__knowledge_search` | | "Sync state across machines?" | `mcp__projects-meta__meta_status` | ## When to use - Cross-project task overview ("what am I working on across projects", "по всем проектам", "across the board"). - Hopping into another repo's task state without cloning it ("what's the status of project X"). - Querying the shared wiki for cross-cutting concepts (patterns, package references, design notes that apply to several repos). - Ingesting a finished design / decision into the shared wiki so other machines / projects can see it. - Creating a task in another project's `.tasks/STATUS.md` from the current repo (cross-project handoff). - Sync diagnostics (when did the cache last refresh, are there errors, how many projects). ## When NOT to use - The current project's own tasks or wiki — read disk. - Anything inside a single project — `.tasks/.md` and `.wiki/.md` are always closer. - One-off questions answered by `git log` or a single file. - Library / framework documentation — that's `using-context7`. - Code search — that's `Glob` / `Grep`. ## Step 0 — Freshness gate (run before any tool) `projects-meta` is a bus between machines. Another host may have pushed minutes ago. Without this gate, reads return stale data and writes hit sha-based optimistic-lock 422s with no explanation. Mandatory pre-flight, every session, every workflow: ``` 1. Call mcp__projects-meta__meta_status. 2. Branch on cache freshness: • If cache_age_minutes > 10 OR errors_count > 0: run `node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js` (or `npm run sync` from ~/projects/.common/lib/projects-meta-mcp). • Else: cache is fresh enough — skip sync, no need to hit the network. 3. For shared-wiki WRITES (knowledge_ingest, knowledge_promote): ALWAYS additionally run `git -C ~/projects/projects-wiki pull --ff-only` regardless of cache age. The MCP server uses sha-based optimistic locking; without an up-to-date local file SHA, the commit will be rejected (422) and the failure mode is opaque to the user. 4. For tasks-mutations (tasks_create, tasks_update, tasks_close): sync via dist/sync.js is enough — there's no local clone of the target tasks repo, mutations go straight through Gitea API. Sync only refreshes the local view so you reason from current state. 5. If sync returns 401 or 403: STOP. The Gitea token in ~/.config/projects-mcp/auth.toml is dead or wrong-scoped. Tell the user explicitly: "Gitea sync failed with <401|403>. Rotate gitea_token in ~/.config/projects-mcp/auth.toml (Gitea: settings/applications) and rerun." Do not pretend sync succeeded. Do not retry silently. ``` **Don't sync unconditionally on every call.** Network overhead + risk of 401 even on a casual "what's on my boards". The 10-minute cache window is the right balance — catches multi-machine drift without burning Gitea round-trips for back-to-back questions. **Don't apply Step 0 to `meta_status` itself** — it's the freshness probe, not a downstream read. ## Workflow ### Read (no confirmation needed) ``` 0. Run Step 0 — Freshness gate (above) first. 1. Identify what you need: cross-project tasks? shared wiki page? sync state? 2. Pick the right read tool (table below). 3. Cite the result with the source slug / project name. ``` ### Mutate (always two-step) ``` 0. Run Step 0 — Freshness gate (above) first. For shared-wiki writes (knowledge_ingest, knowledge_promote): unconditional `git -C ~/projects/projects-wiki pull --ff-only` is part of Step 0. 1. Identify the mutation: tasks_create / tasks_update / tasks_close / knowledge_ingest / knowledge_promote. 2. Call the tool WITHOUT `confirm: true` → returns a dry-run preview (the proposed file diff and the Gitea commit message). 3. Show the preview to the user. Wait for explicit "ok" / "go" / "поехали". 4. Re-call with `confirm: true` to commit. ``` **Never inline `confirm: true` on the first call.** A trigger phrase ("create a task in project X") is permission to *plan*, not to *commit*. ## Tool quick reference ### Read tools | Tool | Required args | Purpose | |---|---|---| | `mcp__projects-meta__tasks_aggregate` | — | All active tasks across all cached projects | | `mcp__projects-meta__tasks_search` | `query` | Substring search across slug + next_action | | `mcp__projects-meta__tasks_get` | `project` | Raw STATUS.md of one project (cached snapshot) | | `mcp__projects-meta__knowledge_search` | `query`; opt `domain`, `limit` | Shared-wiki search; auto-detects domain from cwd, pass `domain="all"` to disable | | `mcp__projects-meta__knowledge_get` | `slug` | Full text of one wiki page (e.g. `packages/projects-meta-mcp`) | | `mcp__projects-meta__knowledge_suggest_promote` | — | Local `.wiki/concepts/` candidates for shared-wiki promotion | | `mcp__projects-meta__meta_status` | — | Sync diagnostics: cache age, project count, error count, page count | ### Mutation tools (need `write:repository` Gitea scope; preview → confirm) | Tool | Required args | Effect | |---|---|---| | `mcp__projects-meta__tasks_create` | `target_project`, `slug`, `description`, `next_action` (+ opt `where_stopped`, `status`, `blocker`, `branch`, `source_project`) | Append block to `/.tasks/STATUS.md` via Gitea commit | | `mcp__projects-meta__tasks_update` | `target_project`, `slug` + ≥1 of `where_stopped` / `next_action` / `blocker` / `branch` / `description` / `status` | Sha-based optimistic lock; 422 on conflict | | `mcp__projects-meta__tasks_close` | `target_project`, `slug` (+ opt `note`) | Sets task to 🟢 done; appends identity-footer | | `mcp__projects-meta__knowledge_ingest` | `target_project`, `type`, `slug`, `body` (+ opt `frontmatter`, `source_project`) | Three commits: `/.md` + `index.md` + `log.md`. `type` ∈ entities / concepts / packages / sources / raw | | `mcp__projects-meta__knowledge_promote` | `target_project`, `slug`, `body` (+ opt `frontmatter`, `source_project`) | Move `raw/.md` → `sources/.md` with auto `raw_path` link | `target_project` is **qualified** `/` (e.g. `victor/books`, `OpeItcLoc03/claude-skills`), or the literal `agenda` for the cross-project meta-board (resolves via `agenda_tasks_repo` in `auth.toml`). Bare names (`books`) are rejected with a hint to use the qualified form. Cross-cutting design: shared wiki → `concepts/projects-meta-multi-owner`. ## Examples ### Read example: cross-project status User: "что у меня на досках?" ``` 1. mcp__projects-meta__tasks_aggregate → 7 projects, 12 active tasks 2. Group by project, summarize 1 line per active task. Cite project name; if a task is stale (cache age > 1h), flag it. ``` ### Read example: shared wiki query User: "есть ли в общей вики что-то про setup-using паттерн?" ``` 1. mcp__projects-meta__knowledge_search query: "setup-using skill pair pattern" domain: "all" → hits include concepts/setup-using-skill-pair 2. mcp__projects-meta__knowledge_get slug: "concepts/setup-using-skill-pair" → full text 3. Summarize, link with markdown to the slug. ``` ### Mutation example: create cross-project task User: "заведи в проекте books задачу на миграцию `settings.json`" ``` 1. mcp__projects-meta__tasks_create target_project: "victor/books" slug: "settings-json-migration" description: "<...>" next_action: "<...>" (no `confirm`) → preview: proposed STATUS.md diff + commit message 2. Show preview to user. 3. User: "ok, go" 4. mcp__projects-meta__tasks_create (same args + confirm: true) → committed to Gitea ``` ### Mutation example: closing a cross-project task User: "close `[projects-meta-skills]` in claude-skills" ``` 1. mcp__projects-meta__tasks_close target_project: "OpeItcLoc03/claude-skills" slug: "projects-meta-skills" note: "" (no `confirm`) → preview 2. User confirms. 3. Re-call with confirm: true. ``` ## Common mistakes | Mistake | Fix | |---|---| | Reading current project's tasks via `tasks_get` instead of disk | Read `.tasks/STATUS.md` directly. MCP is for *other* projects. | | Inlining `confirm: true` on the first mutation call | Always preview first; show user; only then `confirm: true`. | | Using `knowledge_search` for the project's own wiki | The shared wiki is a separate Gitea repo. Local `.wiki/` is in cwd. | | Acting on a stale `tasks_aggregate` without checking `meta_status` | Step 0 — Freshness gate is mandatory. If `cache_age_minutes` > 10 (or errors > 0), run `node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js` first. | | Skipping `git -C ~/projects/projects-wiki pull` before `knowledge_ingest` / `knowledge_promote` | sha-based optimistic lock will reject the commit (422) and the failure is opaque. Pull is unconditional for shared-wiki writes — fast-forward is a no-op when current. | | Treating sync 401/403 as "MCP is fine, the page just doesn't exist yet" | 401/403 means the Gitea token is dead. Stop, tell the user to rotate `gitea_token` in `~/.config/projects-mcp/auth.toml`. Never guess on stale data. | | Calling `knowledge_ingest` with the wrong `type` | `type` must be one of `entities` / `concepts` / `packages` / `sources` / `raw`. Mis-typed pages land in the wrong section and break `index.md`. | | Vague `knowledge_search` queries ("auth", "config") | Specific multi-word queries return targeted snippets; vague ones return noise. | | Forgetting `domain="all"` when searching across families | Default `domain` is auto-detected from cwd; use `"all"` if the wiki page lives in a different family. | | Passing bare project name (`target_project: "books"`) to mutation tools | v2.x rejects bare names. Use qualified `/` (e.g. `victor/books`, `OpeItcLoc03/claude-skills`). Literal `agenda` is the only exception (cross-project meta-board). | ## Red flags - "I'll just commit it directly" → no. Mutation tools have a preview step for a reason — silent writes to another repo are a recipe for drift. - "The cache is fresh enough" → run Step 0. The 10-minute window is the threshold; below it skip sync, above it sync. Don't eyeball this — the bus moves fast in cross-machine sessions. - "I'll skip the pull, my last write was 30 seconds ago" → another machine pushed in between. Always pull before shared-wiki writes; the sha-lock check is your only safety net. - "I'll skip the wiki page" → if you're answering a cross-cutting question and there's no wiki page, that's a `knowledge_ingest` candidate. Surface it to the user.